Compare commits

..

1 Commits

Author SHA1 Message Date
David Abutbul f76824bdb6 ci(skills): pin clawhub CLI by hash via committed lockfile
Scorecard flags the skill-release workflow's npm install of the clawhub
CLI (code-scanning alerts #25/#26): version pinning alone carries no
integrity guarantee. Install it with npm ci from a committed
package-lock.json instead, so every package (clawhub + 35 transitive
deps) is verified against its sha512 hash at install time.

The publish-payload patch step now resolves the module from the local
node_modules instead of npm root -g.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 14:39:56 +03:00
18 changed files with 4641 additions and 3435 deletions
+406
View File
@@ -0,0 +1,406 @@
{
"name": "clawhub-cli-pin",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "clawhub-cli-pin",
"dependencies": {
"clawhub": "0.7.0"
}
},
"node_modules/@ark/schema": {
"version": "0.56.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@ark/schema/-/schema-0.56.0.tgz",
"integrity": "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==",
"dependencies": {
"@ark/util": "0.56.0"
}
},
"node_modules/@ark/util": {
"version": "0.56.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@ark/util/-/util-0.56.0.tgz",
"integrity": "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA=="
},
"node_modules/@clack/core": {
"version": "0.5.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@clack/core/-/core-0.5.0.tgz",
"integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==",
"dependencies": {
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/@clack/prompts": {
"version": "0.11.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@clack/prompts/-/prompts-0.11.0.tgz",
"integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==",
"dependencies": {
"@clack/core": "0.5.0",
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/arkregex": {
"version": "0.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/arkregex/-/arkregex-0.0.5.tgz",
"integrity": "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==",
"dependencies": {
"@ark/util": "0.56.0"
}
},
"node_modules/arktype": {
"version": "2.2.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/arktype/-/arktype-2.2.0.tgz",
"integrity": "sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==",
"dependencies": {
"@ark/schema": "0.56.0",
"@ark/util": "0.56.0",
"arkregex": "0.0.5"
}
},
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/clawhub": {
"version": "0.7.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/clawhub/-/clawhub-0.7.0.tgz",
"integrity": "sha512-volW6SbX8PawlnRxxCoUTKv5Pi+N3MrBi3hlO5/m9bVaO43UFciEeYti9+01c2U5n/SKhUkw7ASvnleyNmcoSA==",
"dependencies": {
"@clack/prompts": "^0.11.0",
"arktype": "^2.1.29",
"commander": "^14.0.2",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.0.0",
"p-retry": "^7.1.1",
"semver": "^7.7.3",
"undici": "^7.16.0"
},
"bin": {
"clawdhub": "bin/clawdhub.js",
"clawhub": "bin/clawdhub.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/cli-cursor": {
"version": "5.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/cli-cursor/-/cli-cursor-5.0.0.tgz",
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dependencies": {
"restore-cursor": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
"version": "3.4.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/cli-spinners/-/cli-spinners-3.4.0.tgz",
"integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
"engines": {
"node": ">=18.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"engines": {
"node": ">=20"
}
},
"node_modules/fflate": {
"version": "0.8.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/fflate/-/fflate-0.8.3.tgz",
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="
},
"node_modules/get-east-asian-width": {
"version": "1.6.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"engines": {
"node": ">= 4"
}
},
"node_modules/is-interactive": {
"version": "2.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-interactive/-/is-interactive-2.0.0.tgz",
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-network-error": {
"version": "1.3.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-network-error/-/is-network-error-1.3.2.tgz",
"integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-unicode-supported": {
"version": "2.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/log-symbols": {
"version": "7.0.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/log-symbols/-/log-symbols-7.0.1.tgz",
"integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
"dependencies": {
"is-unicode-supported": "^2.0.0",
"yoctocolors": "^2.1.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mime": {
"version": "4.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/mime/-/mime-4.1.0.tgz",
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
"funding": [
"https://github.com/sponsors/broofa"
],
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/mimic-function": {
"version": "5.0.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/mimic-function/-/mimic-function-5.0.1.tgz",
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dependencies": {
"mimic-function": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora": {
"version": "9.4.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ora/-/ora-9.4.0.tgz",
"integrity": "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==",
"dependencies": {
"chalk": "^5.6.2",
"cli-cursor": "^5.0.0",
"cli-spinners": "^3.2.0",
"is-interactive": "^2.0.0",
"is-unicode-supported": "^2.1.0",
"log-symbols": "^7.0.1",
"stdin-discarder": "^0.3.2",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-retry": {
"version": "7.1.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/p-retry/-/p-retry-7.1.1.tgz",
"integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==",
"dependencies": {
"is-network-error": "^1.1.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/restore-cursor/-/restore-cursor-5.1.0.tgz",
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dependencies": {
"onetime": "^7.0.0",
"signal-exit": "^4.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
"version": "7.8.4",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/semver/-/semver-7.8.4.tgz",
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
},
"node_modules/stdin-discarder": {
"version": "0.3.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/stdin-discarder/-/stdin-discarder-0.3.2.tgz",
"integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width": {
"version": "8.2.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/string-width/-/string-width-8.2.1.tgz",
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
"dependencies": {
"get-east-asian-width": "^1.5.0",
"strip-ansi": "^7.1.2"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-ansi": {
"version": "7.2.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"dependencies": {
"ansi-regex": "^6.2.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/undici": {
"version": "7.27.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/undici/-/undici-7.27.2.tgz",
"integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/yoctocolors": {
"version": "2.1.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/yoctocolors/-/yoctocolors-2.1.2.tgz",
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "clawhub-cli-pin",
"private": true,
"description": "Pins the clawhub CLI used by skill-release.yml; package-lock.json provides the integrity hashes. Bump the version here and regenerate the lockfile with: npm install --package-lock-only",
"dependencies": {
"clawhub": "0.7.0"
}
}
+27 -196
View File
@@ -19,8 +19,8 @@ on:
permissions: read-all
env:
CLAWHUB_CLI_VERSION: 0.7.0
# The clawhub CLI version is pinned (with integrity hashes) in
# .github/clawhub-cli/package-lock.json — bump it there.
concurrency:
group: skill-release-${{ github.ref }}
@@ -90,15 +90,7 @@ jobs:
'skills/*/**' \
':(exclude)skills/*/test/**' \
':(exclude)skills/*/tests/**' \
| awk -F/ '
NF >= 3 {
path = tolower($0)
name = tolower($NF)
if (path ~ /(^|\/)(__tests__|test|tests)\//) next
if (name ~ /^(test|spec)[_-]/ || name ~ /\.(test|spec)\./) next
print $1 "/" $2
}
' \
| awk -F/ 'NF >= 3 {print $1 "/" $2}' \
| sort -u > "${touched_skills_file}"
if [ ! -s "${touched_skills_file}" ]; then
@@ -408,15 +400,12 @@ jobs:
}
touched_skills_file="$(mktemp)"
git diff --name-only "${BASE_SHA}...${HEAD_SHA}" -- \
'skills/*/**' \
':(exclude)skills/*/test/**' \
':(exclude)skills/*/tests/**' \
git diff --name-only "${BASE_SHA}...${HEAD_SHA}" -- 'skills/*/skill.json' 'skills/*/SKILL.md' \
| awk -F/ 'NF >= 3 {print $1 "/" $2}' \
| sort -u > "${touched_skills_file}"
if [ ! -s "${touched_skills_file}" ]; then
echo "No release-relevant skill package files changed in this PR."
echo "No skill metadata files changed in this PR."
rm -f "${touched_skills_file}"
exit 0
fi
@@ -442,8 +431,7 @@ jobs:
is_test_release_path() {
local lower="${1,,}"
local name="${lower##*/}"
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == __tests__/* || "$lower" == */test/* || "$lower" == */tests/* || "$lower" == */__tests__/* || "$name" == test_* || "$name" == test-* || "$name" == spec_* || "$name" == spec-* || "$name" == *.test.* || "$name" == *.spec.* ]]
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]]
}
generate_skillspector_report() {
@@ -538,6 +526,11 @@ jobs:
md_version_changed=true
fi
if [ "${json_version_changed}" != "true" ] && [ "${md_version_changed}" != "true" ]; then
echo "No version bump detected for ${skill_dir}; skipping dry-run."
continue
fi
if [ -z "${head_json_version}" ] || [ -z "${head_md_version}" ] || [ "${head_json_version}" != "${head_md_version}" ]; then
echo "::error file=${skill_dir}::Version metadata is invalid for dry-run. Ensure validate-pr-version-sync passes."
failures=$((failures + 1))
@@ -626,9 +619,9 @@ jobs:
# --- Create zip preserving directory structure ---
zip_name="${skill_name}-v${version}.zip"
(cd "${staging_dir}" && zip -qr "${OLDPWD}/${out_assets}/${zip_name}" .)
if unzip -Z1 "${out_assets}/${zip_name}" | grep -Eiq '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.'; then
if unzip -Z1 "${out_assets}/${zip_name}" | grep -Eiq '(^|/)(test|tests)/'; then
echo "::error::Dry-run release archive contains test-only files: ${zip_name}"
unzip -Z1 "${out_assets}/${zip_name}" | grep -Ei '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.' || true
unzip -Z1 "${out_assets}/${zip_name}" | grep -Ei '(^|/)(test|tests)/' || true
failures=$((failures + 1))
fi
@@ -784,177 +777,12 @@ jobs:
fi
if [ "${dry_run_count}" -eq 0 ]; then
echo "No changed skill directories required dry-run assets."
echo "No version bumps detected in changed skill metadata files."
exit 0
fi
echo "Release dry-run completed successfully for ${dry_run_count} changed skill(s)."
- name: Prepare SkillSpector PR report artifact
if: always()
run: |
set -euo pipefail
rm -rf dist/skillspector-pr-reports
mkdir -p dist/dry-run dist/skillspector-pr-reports
found_reports=false
while IFS= read -r report_path; do
tag="${report_path#dist/dry-run/}"
tag="${tag%%/*}"
mkdir -p "dist/skillspector-pr-reports/${tag}"
cp "${report_path}" "dist/skillspector-pr-reports/${tag}/skillspector-report.md"
found_reports=true
done < <(find dist/dry-run -path '*/release-assets/skillspector-report.md' -type f | sort)
if [ "${found_reports}" != "true" ]; then
printf 'No SkillSpector reports were generated for this pull request.\n' > dist/skillspector-pr-reports/NO_SKILLSPECTOR_REPORTS.txt
fi
- name: Upload SkillSpector PR reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: skillspector-pr-reports
path: dist/skillspector-pr-reports
retention-days: 14
comment-skillspector-report:
if: always() && github.event_name == 'pull_request' && needs.release.result != 'cancelled'
needs: release
runs-on: ubuntu-latest
continue-on-error: true
permissions:
actions: read
contents: read
issues: write
pull-requests: read
steps:
- name: Download SkillSpector reports
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: skillspector-pr-reports
path: skillspector-pr-reports
- name: Comment SkillSpector reports
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require("node:fs/promises");
const path = require("node:path");
const root = "skillspector-pr-reports";
const maxCommentLength = 65000;
async function findReports(dir) {
let entries;
try {
entries = await fs.readdir(dir, { withFileTypes: true });
} catch (error) {
if (error.code === "ENOENT") {
return [];
}
throw error;
}
const reports = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
reports.push(...await findReports(fullPath));
} else if (entry.isFile() && entry.name === "skillspector-report.md") {
reports.push(fullPath);
}
}
return reports;
}
function tagFromReportPath(reportPath) {
const parts = reportPath.split(path.sep);
const releaseAssetsIndex = parts.lastIndexOf("release-assets");
if (releaseAssetsIndex > 0) {
return parts[releaseAssetsIndex - 1];
}
return path.basename(path.dirname(reportPath));
}
function sanitizeReportForComment(report) {
const omittedBlock = "_[code block omitted from PR comment; download the workflow artifact for raw details]_";
return report
.replace(/```[\s\S]*?```/g, omittedBlock)
.split(/\r?\n/)
.filter((line) => !/^\s{4,}\S/.test(line))
.join("\n")
.replace(/`[^`\n]*`/g, "`[inline snippet omitted]`")
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted-email]")
.replace(/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, "[redacted-aws-key]")
.replace(/\b(?:ghp|gho|ghu|ghs|ghr|github_pat|glpat|xox[baprs]?|sk|pk)_[A-Za-z0-9_=-]{12,}\b/gi, "[redacted-token]")
.replace(/\b[A-Za-z0-9+/]{40,}={0,2}\b/g, "[redacted-secret-like-value]")
.trimEnd();
}
function buildComment({ tag, report }) {
const marker = `<!-- clawsec-skillspector-report:${tag} -->`;
const sanitizedReport = sanitizeReportForComment(report);
const footer = [
"_Generated by the Skill Release dry-run for `" + tag + "`._",
"_Raw snippets, code blocks, inline code, emails, and token-like values are omitted from this PR comment._",
"_Download the `skillspector-pr-reports` workflow artifact for the full report._",
].join("\n");
let body = `${marker}\n${sanitizedReport}\n\n${footer}`;
if (body.length <= maxCommentLength) {
return body;
}
const truncatedFooter = [
"_Report truncated because it exceeds GitHub's comment size limit._",
"_Download the `skillspector-pr-reports` workflow artifact for the full report._",
footer,
].join("\n");
const budget = maxCommentLength - marker.length - truncatedFooter.length - 8;
return `${marker}\n${sanitizedReport.slice(0, Math.max(0, budget)).trimEnd()}\n\n${truncatedFooter}`;
}
const reports = await findReports(root);
if (reports.length === 0) {
core.info("No SkillSpector reports found; nothing to comment.");
return;
}
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
for (const reportPath of reports.sort()) {
const tag = tagFromReportPath(reportPath);
const report = await fs.readFile(reportPath, "utf8");
const marker = `<!-- clawsec-skillspector-report:${tag} -->`;
const body = buildComment({ tag, report });
const existing = comments.find((comment) => comment.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
core.info(`Updated SkillSpector PR comment for ${tag}.`);
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
core.info(`Created SkillSpector PR comment for ${tag}.`);
}
}
simulate-tag-release-build:
if: github.event_name == 'pull_request'
needs: validate-pr-version-sync
@@ -1237,8 +1065,7 @@ jobs:
is_test_release_path() {
local lower="${1,,}"
local name="${lower##*/}"
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == __tests__/* || "$lower" == */test/* || "$lower" == */tests/* || "$lower" == */__tests__/* || "$name" == test_* || "$name" == test-* || "$name" == spec_* || "$name" == spec-* || "$name" == *.test.* || "$name" == *.spec.* ]]
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]]
}
generate_skillspector_report() {
@@ -1319,9 +1146,9 @@ jobs:
# --- Create zip preserving directory structure ---
ZIP_NAME="${SKILL_NAME}-v${VERSION}.zip"
(cd "$STAGING_DIR" && zip -qr "$OLDPWD/release-assets/$ZIP_NAME" .)
if unzip -Z1 "release-assets/$ZIP_NAME" | grep -Eiq '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.'; then
if unzip -Z1 "release-assets/$ZIP_NAME" | grep -Eiq '(^|/)(test|tests)/'; then
echo "::error::Release archive contains test-only files: $ZIP_NAME"
unzip -Z1 "release-assets/$ZIP_NAME" | grep -Ei '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.' || true
unzip -Z1 "release-assets/$ZIP_NAME" | grep -Ei '(^|/)(test|tests)/' || true
exit 1
fi
@@ -1597,6 +1424,8 @@ jobs:
"",
process.env.QUICK_INSTALL || "",
"",
"### SkillSpector Security Report",
"",
report,
"",
`Download the generated release-payload scan: [skillspector-report.md](https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/skillspector-report.md)`,
@@ -1704,18 +1533,19 @@ jobs:
- name: Install clawhub CLI
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
run: npm install -g clawhub@${CLAWHUB_CLI_VERSION}
run: |
npm ci --prefix .github/clawhub-cli
echo "${GITHUB_WORKSPACE}/.github/clawhub-cli/node_modules/.bin" >> "$GITHUB_PATH"
- name: Patch clawhub publish payload workaround
# Temporary: clawhub@0.7.0 publish payload is missing acceptLicenseTerms.
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
run: |
node <<'NODE'
const { execSync } = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
const npmRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
const npmRoot = path.join(process.env.GITHUB_WORKSPACE, ".github", "clawhub-cli", "node_modules");
const publishScriptPath = path.join(
npmRoot,
"clawhub",
@@ -1894,17 +1724,18 @@ jobs:
run: node scripts/ci/validate_skill_install_docs.mjs --skills "${{ steps.parse.outputs.skill_path }}"
- name: Install clawhub CLI
run: npm install -g clawhub@${CLAWHUB_CLI_VERSION}
run: |
npm ci --prefix .github/clawhub-cli
echo "${GITHUB_WORKSPACE}/.github/clawhub-cli/node_modules/.bin" >> "$GITHUB_PATH"
- name: Patch clawhub publish payload workaround
# Temporary: clawhub@0.7.0 publish payload is missing acceptLicenseTerms.
run: |
node <<'NODE'
const { execSync } = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
const npmRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
const npmRoot = path.join(process.env.GITHUB_WORKSPACE, ".github", "clawhub-cli", "node_modules");
const publishScriptPath = path.join(
npmRoot,
"clawhub",
+1515 -1172
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
jPrlTYwicRwoQgTs5Rk3Y3g6Lz78jNRs9ZNf0R09M4jkJokZENxfvhvHphI9MH4u+7wv0sFZ+yZbQtJ42y+hCQ==
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
M1Jm4YHXsm0msygmd+XCJBRWMrXIjQfv1Y5v7XS8RCachLQwEzUJ1nhhic6CXxItNLmvgmDjVCMPVdHpnOMqDA==
q1EyZ75QcdG2X6FVDkUoAyBtQE3ONA+7k9cmNFmXFgOOuGRPOpSDFUtbSvy86HPqnii26DMoeFJ1hatWJ0lBCQ==
+948 -478
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -31,13 +31,13 @@
"@types/node": "^25.8.0",
"@typescript-eslint/eslint-plugin": "^8.55.0",
"@typescript-eslint/parser": "^8.58.1",
"@vitejs/plugin-react": "^6.0.2",
"@vitejs/plugin-react": "^5.1.4",
"eslint": "^9.39.4",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"fast-check": "^4.7.0",
"typescript": "~5.9.3",
"vite": "^8.0.16"
"vite": "^7.3.2"
},
"overrides": {
"ajv": "6.14.0",
+2 -71
View File
@@ -34,13 +34,6 @@ assert.match(
'Skill release validation must ignore test-only skill changes while inspecting release-relevant skill files',
);
assert.ok(
workflow.includes('name = tolower($NF)')
&& workflow.includes('name ~ /^(test|spec)[_-]/')
&& workflow.includes('name ~ /\\.(test|spec)\\./'),
'Skill release validation must filter test-named skill files such as scripts/test_*.py before selecting dry-run skill directories',
);
assert.doesNotMatch(
workflow,
/No version bump detected for \$\{skill_dir\}; skipping\./,
@@ -95,16 +88,10 @@ assert.match(
'Skill release workflow must generate a SkillSpector report for each released skill',
);
assert.doesNotMatch(
workflow,
/"### SkillSpector Security Report"/,
'GitHub release notes must not add a duplicate SkillSpector heading before the generated report',
);
assert.match(
workflow,
/readFileSync\("release-assets\/skillspector-report\.md", "utf8"\)[\s\S]*report,[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
'GitHub release notes must embed the generated SkillSpector report and include a direct report link',
/### SkillSpector Security Report[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
'GitHub release notes must include a direct SkillSpector report link',
);
assert.match(
@@ -131,20 +118,6 @@ assert.match(
'PR dry-run SkillSpector scan must target the staged release payload, not the source skill directory',
);
assert.match(
workflow,
/Run release dry-run for changed skills[\s\S]*git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
'PR dry-run SkillSpector scan must run when any release-relevant skill package file changes',
);
assert.ok(
workflow.includes('local name="${lower##*/}"')
&& workflow.includes('"$name" == test_*')
&& workflow.includes('"$name" == *.test.*')
&& workflow.includes('(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\\.(test|spec)\\.'),
'Skill release archives must exclude test directories and test-named files from staged release payloads',
);
assert.doesNotMatch(
workflow,
/generate_skillspector_report "\$\{skill_dir\}" "\$\{out_assets\}\/skillspector-report\.md"/,
@@ -214,48 +187,6 @@ assert.match(
'SkillSpector report must be included in the signed checksums manifest',
);
assert.match(
workflow,
/Upload SkillSpector PR reports[\s\S]*actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7\.0\.1[\s\S]*name: skillspector-pr-reports/,
'PR dry-run must upload generated SkillSpector reports as workflow artifacts',
);
assert.match(
workflow,
/comment-skillspector-report:[\s\S]*needs: release[\s\S]*issues: write[\s\S]*actions\/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8\.0\.1/,
'Skill release workflow must download generated SkillSpector reports in a separate PR comment job with comment permissions',
);
assert.match(
workflow,
/comment-skillspector-report:[\s\S]*if: always\(\) && github\.event_name == 'pull_request' && needs\.release\.result != 'cancelled'[\s\S]*Download SkillSpector reports[\s\S]*continue-on-error: true/,
'SkillSpector PR comments must still run when the release dry-run produced reports but the release job failed later',
);
assert.match(
workflow,
/function sanitizeReportForComment\(report\)[\s\S]*code block omitted from PR comment[\s\S]*inline snippet omitted[\s\S]*redacted-email[\s\S]*redacted-token/,
'SkillSpector PR comments must sanitize raw report content before posting to the PR',
);
assert.match(
workflow,
/const sanitizedReport = sanitizeReportForComment\(report\);[\s\S]*`\$\{marker\}\\n\$\{sanitizedReport\}/,
'SkillSpector PR comments must use the sanitized report body, not the raw artifact text',
);
assert.doesNotMatch(
workflow,
/`\$\{marker\}\\n\$\{report\.trimEnd\(\)\}/,
'SkillSpector PR comments must not post report.trimEnd() verbatim',
);
assert.match(
workflow,
/clawsec-skillspector-report:\$\{tag\}[\s\S]*github\.rest\.issues\.updateComment[\s\S]*github\.rest\.issues\.createComment/,
'SkillSpector PR comments must use stable per-skill markers and update existing comments before creating new ones',
);
assert.match(
workflow,
/Simulate tag release build/,
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
jPrlTYwicRwoQgTs5Rk3Y3g6Lz78jNRs9ZNf0R09M4jkJokZENxfvhvHphI9MH4u+7wv0sFZ+yZbQtJ42y+hCQ==
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
-3
View File
@@ -35,7 +35,6 @@
| GitHub API | Deploy/release workflows | Discover releases, download assets, publish outputs. |
| GitHub Pages | Deploy workflow | Serve static site and mirrored artifacts. |
| ClawHub CLI/registry | Install scripts + optional publish jobs | Install and publish skills. |
| [NVIDIA SkillSpector](https://github.com/NVIDIA/SkillSpector) | Skill release workflow | Scan staged skill release payloads and produce Markdown release evidence. |
| Optional local SMTP/sendmail | `openclaw-audit-watchdog` scripts | Deliver audit reports by email. |
## Development Tools
@@ -47,7 +46,6 @@
| Bandit | `bandit -r utils/ -ll` | Python security checks. |
| Trivy | Workflow + optional local run | FS/config vulnerability scans. |
| Gitleaks | `scripts/prepare-to-push.sh` optional local run | Secret leak detection before push. |
| SkillSpector | `.github/workflows/skill-release.yml` | Release-payload scanner used for PR comments and signed release artifacts. |
## Example Snippets
```json
@@ -85,7 +83,6 @@ skips = ["B101"]
- PR validation enforces version parity between `skill.json` and `SKILL.md` frontmatter for bumped skills.
- The public skills index keeps latest discovered version per skill for UI display.
- Signed artifact manifests (`checksums.json`) are versioned per release and include file hashes and URLs.
- SkillSpector reports are generated per release payload and included in signed artifact manifests.
## Source References
- package.json
+1 -2
View File
@@ -15,7 +15,6 @@
| --- | --- |
| Skill Tag | Git tag formatted as `<skill>-v<semver>` used by release automation. |
| Release Assets | Files attached to GitHub release (zip, `skill.json`, checksums, signatures). |
| SkillSpector Report | Markdown security scan evidence generated from a staged skill release payload. |
| Catalog Index | `public/skills/index.json`, generated list consumed by web catalog. |
| Embedded Components | Capability bundle from one skill included in another (for example feed embedded in suite). |
@@ -40,7 +39,7 @@
| --- | --- |
| Poll NVD CVEs Workflow | Scheduled workflow that fetches and transforms NVD CVEs into advisories. |
| Community Advisory Workflow | Issue-label-triggered workflow that publishes approved community advisories. |
| Skill Release Workflow | PR and tag-triggered packaging/signing/publishing pipeline for skills. |
| Skill Release Workflow | Tag-triggered packaging/signing/publishing pipeline for skills. |
| Deploy Pages Workflow | Workflow that builds site assets and mirrors release/advisory artifacts. |
## Source References
+1 -7
View File
@@ -2,7 +2,7 @@
Track translation coverage and freshness versus English source docs.
_Last updated: 2026-06-14_
_Last updated: 2026-04-27_
## README Coverage
@@ -24,12 +24,6 @@ _Last updated: 2026-06-14_
| `wiki/testing.md` | — | pending |
| `wiki/workflow.md` | — | pending |
## English Source Freshness Notes
| Date | Changed pages | Translation impact |
| --- | --- | --- |
| 2026-06-14 | `wiki/workflow.md`, `wiki/modules/automation-release.md`, `wiki/security-signing-runbook.md`, `wiki/dependencies.md`, `wiki/glossary.md` | Added SkillSpector release-pipeline documentation, signed-report behavior, and PR comment behavior. Translation refresh pending. |
## Wiki Coverage (KO)
| Source page | Korean page | Status |
+5 -40
View File
@@ -19,38 +19,11 @@ This module intentionally focuses on automation/release-specific workflow behavi
When a skill is tagged (for example, `soul-guardian-v1.0.0`), the pipeline:
1. Validates `skill.json` version/tag alignment.
2. Enforces signing-key consistency against canonical repo key material.
3. Stages the release payload from SBOM-scoped files and root skill docs.
4. Generates release trust packet files, install instructions, and a SkillSpector security report.
5. Generates `checksums.json` for the archive and release assets.
6. Signs and verifies release checksum artifacts.
7. Publishes GitHub Release assets.
8. Supersedes older releases within the same major version (tags remain).
9. Triggers website catalog refresh.
### PR dry-run behavior
PRs that touch skill packages run the release workflow in validation mode:
- `validate-pr-version-sync` checks changed skill metadata and documentation parity.
- `release` builds dry-run release assets for changed release-relevant skill files.
- `comment-skillspector-report` posts a sanitized SkillSpector summary back to the PR when reports are available.
- `simulate-tag-release-build` exercises the tag-release builder across skills without publishing.
The PR path exists to catch packaging, signing, and release-evidence regressions before a maintainer pushes a real release tag.
### SkillSpector release evidence
The pipeline installs [NVIDIA SkillSpector](https://github.com/NVIDIA/SkillSpector) inside GitHub Actions and runs:
```bash
skillspector scan <staged-release-payload> --no-llm --format markdown --output skillspector-report.md
```
The scan target is the staged payload, not the raw `skills/<name>/` source directory. That matters because release evidence should describe what users install, while source-only tests and fixtures stay outside the packaged payload.
SkillSpector output is used in three places:
- PR dry-run artifact: `skillspector-pr-reports`
- GitHub release asset: `skillspector-report.md`
- Signed checksum manifest: `checksums.json` includes the SkillSpector report hash
PR comments intentionally use a sanitized summary. Raw code blocks, inline snippets, emails, and token-like values are omitted from the comment body, and reviewers can download the workflow artifact when they need the full report.
3. Generates `checksums.json` for SBOM files.
4. Signs and verifies release checksum artifacts.
5. Publishes GitHub Release assets.
6. Supersedes older releases within the same major version (tags remain).
7. Triggers website catalog refresh.
### Signing-key consistency guardrails
Guardrail script:
@@ -67,16 +40,9 @@ Enforced in:
### Release artifacts
Each skill release includes:
- `<skill>-v<version>.zip`
- `checksums.json`
- `checksums.sig`
- `signing-public.pem`
- `skill.json`
- `SKILL.md`
- `skill-card.md`
- `permissions.json`
- `install.md`
- `skillspector-report.md`
- Additional SBOM-scoped files
Operational docs:
@@ -92,7 +58,6 @@ Operational docs:
- `.github/workflows/deploy-pages.yml`: site build + asset mirroring to GitHub Pages.
- `.github/workflows/wiki-sync.yml`: syncs repository `wiki/` into GitHub Wiki.
- `.github/actions/sign-and-verify/action.yml`: shared Ed25519 sign/verify composite action.
- `https://github.com/NVIDIA/SkillSpector`: upstream SkillSpector scanner installed by the release workflow.
- `scripts/prepare-to-push.sh`: local CI-like quality gate.
- `scripts/release-skill.sh`: manual helper for version bump + tag workflow.
+1 -8
View File
@@ -141,18 +141,11 @@ Current behavior:
Current release generator:
- `.github/workflows/skill-release.yml`
Detailed packaging and SkillSpector behavior lives in [Automation and Release Pipelines](modules/automation-release.md). This runbook only records the signing controls operators must verify.
Signing controls:
Current behavior:
- creates `checksums.json`, signs it as `checksums.sig`, and verifies signature before publish
- includes `signing-public.pem` in release assets
- validates generated public-key fingerprint against canonical key material
Operator review points:
- verify `checksums.json` includes the release-evidence files documented in `wiki/modules/automation-release.md`
- verify `checksums.sig` validates against `signing-public.pem`
- review the release workflow run and PR evidence links before pushing or approving follow-up release tags
## 8) Rotation policy and runbook
### Rotation cadence
+1 -7
View File
@@ -32,16 +32,11 @@
## Release Workflow Details
- Version bump and docs parity are enforced for PR/tag paths.
- PR runs validate changed skill packages with a dry-run build before anything is published.
- Tag pushes matching `<skill>-v<semver>` build the real release payload, sign `checksums.json`, verify the signature, and publish GitHub Release assets.
- Skill packaging includes SBOM-declared files, release trust packet files, install instructions, security scan evidence, and integrity manifests.
- Skill packaging includes SBOM-declared files and integrity manifests.
- `checksums.json` is signed and immediately verified in workflow execution.
- Optional publish-to-ClawHub job runs after successful GitHub release when configured.
- Older releases within same major line can be superseded/deleted by automation.
## SkillSpector Release Evidence
Detailed SkillSpector release behavior lives in [Automation and Release Pipelines](modules/automation-release.md). Keep the detailed scanner command, staged-payload rules, PR comment behavior, and release-asset list there so scanner changes have one primary documentation owner.
## Advisory Workflow Details
- NVD workflow determines incremental window from previous feed `updated` timestamp.
- Transform phase maps CVE metrics to severity/type and normalizes affected targets.
@@ -79,7 +74,6 @@ on:
- .github/workflows/poll-nvd-cves.yml
- .github/workflows/community-advisory.yml
- .github/workflows/skill-release.yml
- https://github.com/NVIDIA/SkillSpector
- .github/workflows/deploy-pages.yml
- .github/workflows/pages-verify.yml
- .github/workflows/wiki-sync.yml