Skip to content

Commit 0ecdbc1

Browse files
committed
fix(ci): replace inline shell commands with dedicated validation script
CI workflows incorrectly passed shell operators (&&) as literal script arguments, causing them to be interpreted as test file filters rather than separate commands. Solution: - Add scripts/ci-validate.mjs to orchestrate test/check/build steps - Add ci:validate script to package.json - Update workflows to use ci:validate instead of inline shell commands - Update provenance workflow SHA for smart script name detection - Change publish:ci from --skip-checks to --skip-git flag - Add build artifact validation when skipping build step Follows Socket project pattern: setup-script calls package.json script that invokes .mjs file, avoiding shell operators in YAML. The ci: prefix prevents conflicts with npm lifecycle hooks.
1 parent bc0af62 commit 0ecdbc1

File tree

5 files changed

+124
-14
lines changed

5 files changed

+124
-14
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
lint-script: 'pnpm run lint --all'
2424
node-versions: '["24.10.0"]'
2525
os-versions: '["ubuntu-latest", "windows-latest"]'
26-
test-script: 'pnpm run test --all'
26+
test-script: 'pnpm run test --all --skip-build'
2727
test-setup-script: 'pnpm run build'
28-
type-check-script: 'pnpm run check'
28+
type-check-script: 'pnpm run type'
2929
type-check-setup-script: 'pnpm run build'

.github/workflows/provenance.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ permissions:
2323

2424
jobs:
2525
publish:
26-
uses: SocketDev/socket-registry/.github/workflows/provenance.yml@4709a2443e5a036bb0cd94e5d1559f138f05994c # main
26+
uses: SocketDev/socket-registry/.github/workflows/provenance.yml@0f9229dbc545a2de7b06024756e41fa5e0debc8b # main
2727
with:
2828
debug: ${{ inputs.debug }}
2929
dist-tag: ${{ inputs.dist-tag }}
3030
package-name: '@socketsecurity/sdk'
3131
publish-script: 'publish:ci'
32-
setup-script: 'pnpm run build'
32+
setup-script: 'ci:validate'
3333
use-trusted-publishing: true

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@
4646
"lint": "node scripts/lint.mjs",
4747
"precommit": "pnpm run check --lint --staged",
4848
"prepare": "husky",
49+
"ci:validate": "node scripts/ci-validate.mjs",
4950
"prepublishOnly": "echo 'ERROR: Use GitHub Actions workflow for publishing' && exit 1",
5051
"publish": "node scripts/publish.mjs",
51-
"publish:ci": "node scripts/publish.mjs --skip-checks --skip-build --tag ${DIST_TAG:-latest}",
52+
"publish:ci": "node scripts/publish.mjs --skip-git --skip-build --tag ${DIST_TAG:-latest}",
5253
"claude": "node scripts/claude.mjs",
5354
"test": "node scripts/test.mjs",
5455
"type": "tsgo --noEmit -p .config/tsconfig.check.json",

scripts/ci-validate.mjs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* @fileoverview CI validation script for publishing workflow.
3+
* Runs test, check, and build steps in sequence.
4+
*/
5+
6+
import path from 'node:path'
7+
import { fileURLToPath } from 'node:url'
8+
9+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
10+
import { spawn } from '@socketsecurity/lib/spawn'
11+
import { printHeader } from '@socketsecurity/lib/stdio/header'
12+
13+
const logger = getDefaultLogger()
14+
15+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
16+
const rootPath = path.resolve(__dirname, '..')
17+
18+
async function runCommand(command, args = []) {
19+
return new Promise((resolve, reject) => {
20+
const spawnPromise = spawn(command, args, {
21+
cwd: rootPath,
22+
stdio: 'inherit',
23+
})
24+
25+
const child = spawnPromise.process
26+
27+
child.on('exit', code => {
28+
resolve(code || 0)
29+
})
30+
31+
child.on('error', error => {
32+
reject(error)
33+
})
34+
})
35+
}
36+
37+
async function main() {
38+
try {
39+
printHeader('CI Validation')
40+
41+
// Run tests
42+
logger.step('Running tests')
43+
let exitCode = await runCommand('pnpm', ['test', '--all'])
44+
if (exitCode !== 0) {
45+
logger.error('Tests failed')
46+
process.exitCode = exitCode
47+
return
48+
}
49+
logger.success('Tests passed')
50+
51+
// Run checks
52+
logger.step('Running checks')
53+
exitCode = await runCommand('pnpm', ['check', '--all'])
54+
if (exitCode !== 0) {
55+
logger.error('Checks failed')
56+
process.exitCode = exitCode
57+
return
58+
}
59+
logger.success('Checks passed')
60+
61+
// Run build
62+
logger.step('Building project')
63+
exitCode = await runCommand('pnpm', ['build'])
64+
if (exitCode !== 0) {
65+
logger.error('Build failed')
66+
process.exitCode = exitCode
67+
return
68+
}
69+
logger.success('Build completed')
70+
71+
logger.success('CI validation completed successfully!')
72+
} catch (error) {
73+
logger.error(`CI validation failed: ${error.message}`)
74+
process.exitCode = 1
75+
}
76+
}
77+
78+
main()

scripts/publish.mjs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const logger = getDefaultLogger()
1818
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1919
const rootPath = path.join(__dirname, '..')
2020
const WIN32 = process.platform === 'win32'
21-
const CI = !!process.env.CI
2221

2322
// Simple inline logger.
2423
const log = {
@@ -236,6 +235,37 @@ async function runPrePublishChecks(options = {}) {
236235
return true
237236
}
238237

238+
/**
239+
* Validate that build artifacts exist.
240+
*/
241+
async function validateBuildArtifacts() {
242+
log.step('Validating build artifacts')
243+
244+
// Check for main entry point.
245+
const distIndex = path.join(rootPath, 'dist', 'index.js')
246+
if (!existsSync(distIndex)) {
247+
log.error('Missing dist/index.js')
248+
return false
249+
}
250+
251+
// Check for type definitions.
252+
const distIndexDts = path.join(rootPath, 'dist', 'index.d.ts')
253+
if (!existsSync(distIndexDts)) {
254+
log.error('Missing dist/index.d.ts')
255+
return false
256+
}
257+
258+
// Check for types directory.
259+
const typesDir = path.join(rootPath, 'types')
260+
if (!existsSync(typesDir)) {
261+
log.error('Missing types/ directory')
262+
return false
263+
}
264+
265+
log.success('Build artifacts validated')
266+
return true
267+
}
268+
239269
/**
240270
* Build the project.
241271
*/
@@ -464,7 +494,7 @@ async function main() {
464494
logger.log(' --dry-run Perform a dry-run without publishing')
465495
logger.log(' --force Force publish even with warnings')
466496
logger.log(' --skip-checks Skip pre-publish checks')
467-
logger.log(' --skip-build Skip build step (not allowed in CI)')
497+
logger.log(' --skip-build Skip build step (validates artifacts exist)')
468498
logger.log(' --skip-git Skip git status checks')
469499
logger.log(' --skip-tag Skip git tag push')
470500
logger.log(' --complex Use complex multi-package flow')
@@ -480,13 +510,6 @@ async function main() {
480510
return
481511
}
482512

483-
// Check CI restrictions.
484-
if (CI && values['skip-build']) {
485-
log.error('--skip-build is not allowed in CI')
486-
process.exitCode = 1
487-
return
488-
}
489-
490513
printHeader('Publish Runner')
491514

492515
// Get current version.
@@ -515,6 +538,14 @@ async function main() {
515538
process.exitCode = 1
516539
return
517540
}
541+
} else {
542+
// Validate that build artifacts exist when skipping build.
543+
const artifactsExist = await validateBuildArtifacts()
544+
if (!artifactsExist && !values.force) {
545+
log.error('Build artifacts missing - run pnpm build first')
546+
process.exitCode = 1
547+
return
548+
}
518549
}
519550

520551
// Publish.

0 commit comments

Comments
 (0)