chore: enhance skills validation and update workflows for npm publishing

This commit is contained in:
Mohan Raj Rajamanickam 2026-03-11 14:29:48 -07:00
parent a1ed6e21f2
commit d2989fe74c
No known key found for this signature in database
GPG Key ID: 7CE83B7611FA8B8B
4 changed files with 55 additions and 10 deletions

View File

@ -6,6 +6,10 @@ on:
paths: ['skills/**']
workflow_dispatch:
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
@ -39,7 +43,7 @@ jobs:
- name: Create Github Release
id: release
uses: ncipollo/release-action@v2
uses: ncipollo/release-action@v1
if: ${{ steps.changelog.outputs.skipped == 'false' }}
with:
name: "${{ steps.changelog.outputs.version }}"
@ -53,6 +57,7 @@ jobs:
skipIfReleaseExists: true
- name: Publish to npm
id: publish
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' }}
run: |
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
@ -61,7 +66,7 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Notify cline-fork
if: ${{ steps.changelog.outputs.skipped == 'false' }}
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' && steps.publish.outcome == 'success' }}
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.IDEE_GH_TOKEN }}

View File

@ -9,8 +9,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run validate
- name: Detect changed skill directories
id: changed-skills
run: |
# Collect top-level skill dirs touched by this PR (relative to skills/).
# Passing explicit dirs means pre-existing nested skill categories
# (to be flattened in a separate PR) don't block unrelated skill PRs.
DIRS=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
| grep '^skills/' \
| cut -d'/' -f2 \
| sort -u \
| tr '\n' ' ')
echo "dirs=$DIRS" >> $GITHUB_OUTPUT
echo "Validating skill dirs: $DIRS"
- name: Validate changed skills
run: npx tsx scripts/validate-skills.ts ${{ steps.changed-skills.outputs.dirs }}

View File

@ -6,13 +6,17 @@
"files": [
"skills/"
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"devDependencies": {
"tsx": "^4.21.0",
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "*",
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "*"
},
"scripts": {
"validate": "tsx scripts/validate-skills.ts",
"validate:skills": "tsx scripts/validate-skills.ts",
"sync-react-b2e-sample": "node scripts/sync-react-b2e-sample.js",
"sync-react-b2x-sample": "node scripts/sync-react-b2x-sample.js"
}

View File

@ -1,6 +1,10 @@
#!/usr/bin/env tsx
// Validates the skills/ directory structure and SKILL.md format.
// Exits with code 1 if any violations are found.
//
// Usage:
// tsx scripts/validate-skills.ts # validate all skills
// tsx scripts/validate-skills.ts apex-class # validate specific skill dirs
import fs from "fs"
import path from "path"
@ -143,7 +147,10 @@ function parseFrontmatter(content: string): Record<string, string> | null {
for (const line of match[1].split(/\r?\n/)) {
const colonIdx = line.indexOf(":")
if (colonIdx === -1) continue
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim()
const key = line.slice(0, colonIdx).trim()
// Strip wrapping single or double quotes to match how consumers read values
const raw = line.slice(colonIdx + 1).trim()
result[key] = raw.replace(/^(['"])([\s\S]*)\1$/, "$2")
}
return result
}
@ -158,6 +165,10 @@ function getFrontmatterEnd(content: string): number {
// ---------------------------------------------------------------------------
function validateSkill(dirName: string, dirPath: string): string[] {
if (!fs.existsSync(dirPath)) {
return [`skills/${dirName}: directory not found`]
}
const errors: string[] = []
for (const check of STRUCTURE_CHECKS) {
@ -182,13 +193,18 @@ function validateSkill(dirName: string, dirPath: string): string[] {
}
function main(): void {
const allErrors: string[] = []
let checked = 0
// If skill dir names are passed as arguments, validate only those.
// Otherwise validate all entries in skills/.
const targets = process.argv.slice(2)
const entries = targets.length > 0 ? targets : fs.readdirSync(SKILLS_DIR)
for (const entry of fs.readdirSync(SKILLS_DIR)) {
const allErrors: string[] = []
let passed = 0
for (const entry of entries) {
const entryErrors = validateSkill(entry, path.join(SKILLS_DIR, entry))
allErrors.push(...entryErrors)
if (entryErrors.length === 0) checked++
if (entryErrors.length === 0) passed++
}
if (allErrors.length > 0) {
@ -199,7 +215,7 @@ function main(): void {
console.error("")
process.exit(1)
} else {
console.log(`Skill validation passed: ${checked} skill(s) checked.`)
console.log(`Skill validation passed: ${passed} of ${entries.length} skill(s) checked.`)
}
}