Skip to content

Commit 89c1d6d

Browse files
committed
feat(publish): add standardized publish runner with validation
Aligns socket-registry with other Socket repos by adding a standardized publish.mjs wrapper that delegates to the existing package-npm-publish script. ## Changes **New scripts/publish.mjs**: - Standardized publish runner following Socket repo patterns - Delegates to existing scripts/npm/publish-npm-packages.mjs - Validates build artifacts before publish (registry/dist/index.js) - Supports all flags: --force-publish, --force-registry, --skip-npm-packages - Consistent help text and error handling **package.json**: - Added publish script: "node scripts/publish.mjs" - Added publish:ci script for CI workflows - Added prepublishOnly guard (requires GitHub Actions) ## Safety Features Build artifact validation: - Checks registry/dist/index.js exists - Fails with clear error if missing - Prevents publishing incomplete builds CI workflow integration: - Delegates to existing package-npm-publish logic - Passes through DIST_TAG environment variable - Maintains all existing publish behavior ## Benefits - Consistent publish interface across all Socket repos - Better error messages and validation - Safer CI workflows with artifact checks - Easier to maintain and update
1 parent 0a8dfcc commit 89c1d6d

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed

β€Žpackage.jsonβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
"perf": "node scripts/perf.mjs",
4040
"precommit": "pnpm run check --lint --staged",
4141
"prepare": "husky && pnpm run build",
42+
"prepublishOnly": "echo 'ERROR: Use GitHub Actions workflow for publishing' && exit 1",
43+
"publish": "node scripts/publish.mjs",
44+
"publish:ci": "node scripts/publish.mjs --skip-build --tag ${DIST_TAG:-latest}",
4245
"release-npm": "node scripts/npm/release-npm-packages.mjs",
4346
"test": "node scripts/test.mjs",
4447
"update": "node scripts/update.mjs",

β€Žscripts/publish.mjsβ€Ž

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/**
2+
* @fileoverview Standardized publish runner for Socket projects.
3+
* Supports both simple single-package and complex multi-package publishing.
4+
*/
5+
6+
import { existsSync } from 'node:fs'
7+
import path from 'node:path'
8+
import { fileURLToPath } from 'node:url'
9+
10+
import { parseArgs } from '@socketsecurity/lib/argv/parse'
11+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
12+
import { spawn } from '@socketsecurity/lib/spawn'
13+
14+
const logger = getDefaultLogger()
15+
16+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
17+
const rootPath = path.join(__dirname, '..')
18+
const WIN32 = process.platform === 'win32'
19+
20+
/**
21+
* Run a command with spawn.
22+
*/
23+
async function runCommand(command, args = [], options = {}) {
24+
const result = await spawn(command, args, {
25+
stdio: 'inherit',
26+
cwd: rootPath,
27+
...(WIN32 && { shell: true }),
28+
...options,
29+
})
30+
return result.code ?? 1
31+
}
32+
33+
/**
34+
* Validate that build artifacts exist.
35+
*/
36+
async function validateBuildArtifacts() {
37+
logger.step('Validating build artifacts')
38+
39+
// Check for registry package dist directory
40+
const registryDist = path.join(rootPath, 'registry', 'dist')
41+
if (!existsSync(registryDist)) {
42+
logger.error('Missing registry/dist directory')
43+
return false
44+
}
45+
46+
// Check for registry package main entry point
47+
const registryIndex = path.join(registryDist, 'index.js')
48+
if (!existsSync(registryIndex)) {
49+
logger.error('Missing registry/dist/index.js')
50+
return false
51+
}
52+
53+
logger.success('Build artifacts validated')
54+
return true
55+
}
56+
57+
/**
58+
* Publish packages using the complex multi-package flow.
59+
* Delegates to scripts/npm/publish-npm-packages.mjs.
60+
*/
61+
async function publishComplex(options = {}) {
62+
const {
63+
force = false,
64+
forcePublish = false,
65+
forceRegistry = false,
66+
skipNpmPackages = false,
67+
tag = 'latest',
68+
} = options
69+
70+
logger.step('Publishing packages')
71+
72+
// Build args for the npm publish script
73+
const publishArgs = ['run', 'package-npm-publish']
74+
75+
const flags = []
76+
if (force || forcePublish) {
77+
flags.push('--force-publish')
78+
}
79+
if (forceRegistry) {
80+
flags.push('--force-registry')
81+
}
82+
if (skipNpmPackages) {
83+
flags.push('--skip-npm-packages')
84+
}
85+
86+
if (flags.length > 0) {
87+
publishArgs.push('--', ...flags)
88+
}
89+
90+
// Set DIST_TAG environment variable for the publish script
91+
const exitCode = await runCommand('pnpm', publishArgs, {
92+
env: {
93+
...process.env,
94+
DIST_TAG: tag,
95+
},
96+
})
97+
98+
if (exitCode !== 0) {
99+
logger.error('Publish failed')
100+
return false
101+
}
102+
103+
logger.success('Publish complete')
104+
return true
105+
}
106+
107+
async function main() {
108+
try {
109+
// Parse arguments
110+
const { values } = parseArgs({
111+
options: {
112+
help: {
113+
type: 'boolean',
114+
default: false,
115+
},
116+
force: {
117+
type: 'boolean',
118+
default: false,
119+
},
120+
'force-publish': {
121+
type: 'boolean',
122+
default: false,
123+
},
124+
'force-registry': {
125+
type: 'boolean',
126+
default: false,
127+
},
128+
'skip-build': {
129+
type: 'boolean',
130+
default: false,
131+
},
132+
'skip-checks': {
133+
type: 'boolean',
134+
default: false,
135+
},
136+
'skip-npm-packages': {
137+
type: 'boolean',
138+
default: false,
139+
},
140+
tag: {
141+
type: 'string',
142+
default: 'latest',
143+
},
144+
},
145+
allowPositionals: false,
146+
strict: false,
147+
})
148+
149+
// Show help if requested
150+
if (values.help) {
151+
logger.log('\nUsage: pnpm publish [options]')
152+
logger.log('\nOptions:')
153+
logger.log(' --help Show this help message')
154+
logger.log(' --force Force publish even with warnings')
155+
logger.log(
156+
' --force-publish Force publish all packages without commit checks',
157+
)
158+
logger.log(
159+
' --force-registry Force publish @socketsecurity/registry',
160+
)
161+
logger.log(
162+
' --skip-build Skip build validation (validates artifacts exist)',
163+
)
164+
logger.log(' --skip-checks Skip pre-publish checks')
165+
logger.log(
166+
' --skip-npm-packages Skip publishing npm override packages',
167+
)
168+
logger.log(' --tag <tag> npm dist-tag (default: latest)')
169+
logger.log('\nExamples:')
170+
logger.log(' pnpm publish # Standard publish flow')
171+
logger.log(
172+
' pnpm publish --force-registry # Force publish registry package',
173+
)
174+
logger.log(
175+
' pnpm publish --skip-npm-packages # Only publish registry package',
176+
)
177+
process.exitCode = 0
178+
return
179+
}
180+
181+
logger.log('\n────────────────────────────────────────────────────────────')
182+
logger.log(' Publish Runner')
183+
logger.log('────────────────────────────────────────────────────────────')
184+
185+
// Validate build artifacts if not skipping
186+
if (values['skip-build']) {
187+
const artifactsExist = await validateBuildArtifacts()
188+
if (!artifactsExist && !values.force) {
189+
logger.error('Build artifacts missing - run pnpm build first')
190+
process.exitCode = 1
191+
return
192+
}
193+
}
194+
195+
// Publish using complex flow (delegates to package-npm-publish script)
196+
const publishSuccess = await publishComplex({
197+
force: values.force,
198+
forcePublish: values['force-publish'],
199+
forceRegistry: values['force-registry'],
200+
skipNpmPackages: values['skip-npm-packages'],
201+
tag: values.tag,
202+
})
203+
204+
if (!publishSuccess && !values.force) {
205+
logger.error('Publish failed')
206+
process.exitCode = 1
207+
return
208+
}
209+
210+
logger.log('\n────────────────────────────────────────────────────────────')
211+
logger.success('Publish completed successfully!')
212+
process.exitCode = 0
213+
} catch (error) {
214+
logger.error(`Publish runner failed: ${error.message}`)
215+
process.exitCode = 1
216+
}
217+
}
218+
219+
main().catch(e => {
220+
logger.error(e)
221+
process.exitCode = 1
222+
})

0 commit comments

Comments
Β (0)