Skip to content

Commit d2989fe

Browse files
committed
chore: enhance skills validation and update workflows for npm publishing
1 parent a1ed6e2 commit d2989fe

4 files changed

Lines changed: 54 additions & 9 deletions

File tree

.github/workflows/release-skills.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ on:
66
paths: ['skills/**']
77
workflow_dispatch:
88

9+
permissions:
10+
contents: write
11+
packages: write
12+
913
jobs:
1014
release:
1115
runs-on: ubuntu-latest
@@ -39,7 +43,7 @@ jobs:
3943

4044
- name: Create Github Release
4145
id: release
42-
uses: ncipollo/release-action@v2
46+
uses: ncipollo/release-action@v1
4347
if: ${{ steps.changelog.outputs.skipped == 'false' }}
4448
with:
4549
name: "${{ steps.changelog.outputs.version }}"
@@ -53,6 +57,7 @@ jobs:
5357
skipIfReleaseExists: true
5458

5559
- name: Publish to npm
60+
id: publish
5661
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' }}
5762
run: |
5863
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
@@ -61,7 +66,7 @@ jobs:
6166
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
6267

6368
- name: Notify cline-fork
64-
if: ${{ steps.changelog.outputs.skipped == 'false' }}
69+
if: ${{ steps.changelog.outputs.skipped == 'false' && steps.release.outputs.id != '' && steps.publish.outcome == 'success' }}
6570
uses: peter-evans/repository-dispatch@v3
6671
with:
6772
token: ${{ secrets.IDEE_GH_TOKEN }}

.github/workflows/validate-skills.yml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,28 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111
- uses: actions/checkout@v4
12+
with:
13+
fetch-depth: 0
14+
1215
- uses: actions/setup-node@v4
1316
with:
1417
node-version-file: '.nvmrc'
18+
1519
- run: npm ci
16-
- run: npm run validate
20+
21+
- name: Detect changed skill directories
22+
id: changed-skills
23+
run: |
24+
# Collect top-level skill dirs touched by this PR (relative to skills/).
25+
# Passing explicit dirs means pre-existing nested skill categories
26+
# (to be flattened in a separate PR) don't block unrelated skill PRs.
27+
DIRS=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
28+
| grep '^skills/' \
29+
| cut -d'/' -f2 \
30+
| sort -u \
31+
| tr '\n' ' ')
32+
echo "dirs=$DIRS" >> $GITHUB_OUTPUT
33+
echo "Validating skill dirs: $DIRS"
34+
35+
- name: Validate changed skills
36+
run: npx tsx scripts/validate-skills.ts ${{ steps.changed-skills.outputs.dirs }}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
"files": [
77
"skills/"
88
],
9+
"publishConfig": {
10+
"access": "public",
11+
"registry": "https://registry.npmjs.org"
12+
},
913
"devDependencies": {
1014
"tsx": "^4.21.0",
1115
"@salesforce/webapp-template-app-react-sample-b2e-experimental": "*",
1216
"@salesforce/webapp-template-app-react-sample-b2x-experimental": "*"
1317
},
1418
"scripts": {
15-
"validate": "tsx scripts/validate-skills.ts",
19+
"validate:skills": "tsx scripts/validate-skills.ts",
1620
"sync-react-b2e-sample": "node scripts/sync-react-b2e-sample.js",
1721
"sync-react-b2x-sample": "node scripts/sync-react-b2x-sample.js"
1822
}

scripts/validate-skills.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#!/usr/bin/env tsx
22
// Validates the skills/ directory structure and SKILL.md format.
33
// Exits with code 1 if any violations are found.
4+
//
5+
// Usage:
6+
// tsx scripts/validate-skills.ts # validate all skills
7+
// tsx scripts/validate-skills.ts apex-class # validate specific skill dirs
48

59
import fs from "fs"
610
import path from "path"
@@ -143,7 +147,10 @@ function parseFrontmatter(content: string): Record<string, string> | null {
143147
for (const line of match[1].split(/\r?\n/)) {
144148
const colonIdx = line.indexOf(":")
145149
if (colonIdx === -1) continue
146-
result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim()
150+
const key = line.slice(0, colonIdx).trim()
151+
// Strip wrapping single or double quotes to match how consumers read values
152+
const raw = line.slice(colonIdx + 1).trim()
153+
result[key] = raw.replace(/^(['"])([\s\S]*)\1$/, "$2")
147154
}
148155
return result
149156
}
@@ -158,6 +165,10 @@ function getFrontmatterEnd(content: string): number {
158165
// ---------------------------------------------------------------------------
159166

160167
function validateSkill(dirName: string, dirPath: string): string[] {
168+
if (!fs.existsSync(dirPath)) {
169+
return [`skills/${dirName}: directory not found`]
170+
}
171+
161172
const errors: string[] = []
162173

163174
for (const check of STRUCTURE_CHECKS) {
@@ -182,13 +193,18 @@ function validateSkill(dirName: string, dirPath: string): string[] {
182193
}
183194

184195
function main(): void {
196+
// If skill dir names are passed as arguments, validate only those.
197+
// Otherwise validate all entries in skills/.
198+
const targets = process.argv.slice(2)
199+
const entries = targets.length > 0 ? targets : fs.readdirSync(SKILLS_DIR)
200+
185201
const allErrors: string[] = []
186-
let checked = 0
202+
let passed = 0
187203

188-
for (const entry of fs.readdirSync(SKILLS_DIR)) {
204+
for (const entry of entries) {
189205
const entryErrors = validateSkill(entry, path.join(SKILLS_DIR, entry))
190206
allErrors.push(...entryErrors)
191-
if (entryErrors.length === 0) checked++
207+
if (entryErrors.length === 0) passed++
192208
}
193209

194210
if (allErrors.length > 0) {
@@ -199,7 +215,7 @@ function main(): void {
199215
console.error("")
200216
process.exit(1)
201217
} else {
202-
console.log(`Skill validation passed: ${checked} skill(s) checked.`)
218+
console.log(`Skill validation passed: ${passed} of ${entries.length} skill(s) checked.`)
203219
}
204220
}
205221

0 commit comments

Comments
 (0)