Skip to content

Commit 663256e

Browse files
committed
fix(publish): remove dangerous --skip-checks flag and add comprehensive validation
BREAKING: CI publish workflow now runs full test suite before publishing ## Critical Security Fix The publish:ci script was using --skip-checks which bypassed: - Test suite validation - Lint/type checking - Code quality checks This created a dangerous path where broken code could be published to npm, potentially costing thousands in incident response and reputation damage. ## Changes **publish:ci script**: - Changed from --skip-checks to --skip-git - Now only skips git-specific checks (safe in CI) - Still runs full test + lint validation **provenance.yml workflow**: - setup-script now runs: test && check && build - Ensures full validation before publish step - Build artifacts validated before npm publish - Added dist-tag input parameter **scripts/publish.mjs**: - Removed CI restriction on --skip-build - Added validateBuildArtifacts() function - Validates dist/index.js, dist/index.d.ts exist - Updated help text to clarify validation behavior - Removed unused CI constant ## Safety Guarantees CI publish will now FAIL if: - ✗ Any test fails - ✗ Lint/type errors exist - ✗ Build artifacts missing - ✗ Version already published The --skip-build flag is safe because: - setup-script builds first - Artifact validation ensures build succeeded - Fails immediately with clear error if missing ## What We Skip (and Why It's Safe) --skip-git: Safe because CI runs on clean checkouts with branch protection --skip-build: Safe because setup-script already built, we validate artifacts
1 parent 77ae8a7 commit 663256e

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

.github/workflows/provenance.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ name: 📦 Publish
66
on:
77
workflow_dispatch:
88
inputs:
9+
dist-tag:
10+
description: 'npm dist-tag (latest, next, beta, canary, backport, etc.)'
11+
required: false
12+
default: 'latest'
13+
type: string
914
debug:
1015
description: 'Enable debug output'
1116
required: false
@@ -24,6 +29,8 @@ jobs:
2429
uses: SocketDev/socket-registry/.github/workflows/provenance.yml@4709a2443e5a036bb0cd94e5d1559f138f05994c # main
2530
with:
2631
debug: ${{ inputs.debug }}
32+
dist-tag: ${{ inputs.dist-tag }}
2733
package-name: '@socketregistry/packageurl-js'
28-
setup-script: 'pnpm run build'
34+
publish-script: 'publish:ci'
35+
setup-script: 'pnpm test && pnpm check && pnpm build'
2936
use-trusted-publishing: true

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"prepare": "husky",
5151
"prepublishOnly": "echo 'ERROR: Use GitHub Actions workflow for publishing' && exit 1",
5252
"publish": "node scripts/publish.mjs",
53+
"publish:ci": "node scripts/publish.mjs --skip-git --skip-build --tag ${DIST_TAG:-latest}",
5354
"claude": "node scripts/claude.mjs",
5455
"test": "node scripts/test.mjs",
5556
"type": "tsgo --noEmit -p .config/tsconfig.check.json",

scripts/publish.mjs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const rootPath = path.resolve(
1919
'..',
2020
)
2121
const WIN32 = process.platform === 'win32'
22-
const CI = !!process.env.CI
2322

2423
async function runCommand(command, args = [], options = {}) {
2524
try {
@@ -204,6 +203,30 @@ async function runPrePublishChecks(options = {}) {
204203
return true
205204
}
206205

206+
/**
207+
* Validate that build artifacts exist.
208+
*/
209+
async function validateBuildArtifacts() {
210+
logger.step('Validating build artifacts')
211+
212+
// Check for main entry point
213+
const distIndex = path.join(rootPath, 'dist', 'index.js')
214+
if (!existsSync(distIndex)) {
215+
logger.error('Missing dist/index.js')
216+
return false
217+
}
218+
219+
// Check for type definitions
220+
const distIndexDts = path.join(rootPath, 'dist', 'index.d.ts')
221+
if (!existsSync(distIndexDts)) {
222+
logger.error('Missing dist/index.d.ts')
223+
return false
224+
}
225+
226+
logger.success('Build artifacts validated')
227+
return true
228+
}
229+
207230
/**
208231
* Build the project.
209232
*/
@@ -432,7 +455,9 @@ async function main() {
432455
console.log(' --dry-run Perform a dry-run without publishing')
433456
console.log(' --force Force publish even with warnings')
434457
console.log(' --skip-checks Skip pre-publish checks')
435-
console.log(' --skip-build Skip build step (not allowed in CI)')
458+
console.log(
459+
' --skip-build Skip build step (validates artifacts exist)',
460+
)
436461
console.log(' --skip-git Skip git status checks')
437462
console.log(' --skip-tag Skip git tag push')
438463
console.log(' --complex Use complex multi-package flow')
@@ -448,13 +473,6 @@ async function main() {
448473
return
449474
}
450475

451-
// Check CI restrictions
452-
if (CI && values['skip-build']) {
453-
logger.error('--skip-build is not allowed in CI')
454-
process.exitCode = 1
455-
return
456-
}
457-
458476
printHeader('Publish Runner', { width: 56, borderChar: '=' })
459477

460478
// Get current version
@@ -483,6 +501,14 @@ async function main() {
483501
process.exitCode = 1
484502
return
485503
}
504+
} else {
505+
// Validate that build artifacts exist when skipping build
506+
const artifactsExist = await validateBuildArtifacts()
507+
if (!artifactsExist && !values.force) {
508+
logger.error('Build artifacts missing - run pnpm build first')
509+
process.exitCode = 1
510+
return
511+
}
486512
}
487513

488514
// Publish

0 commit comments

Comments
 (0)