Skip to content

Commit f211593

Browse files
authored
dx: NPM Publishing Workflow (#44)
1 parent a621209 commit f211593

File tree

6 files changed

+186
-12
lines changed

6 files changed

+186
-12
lines changed

.github/workflows/publish-to-npm.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Publish to NPM
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Install pnpm
15+
uses: pnpm/action-setup@v4
16+
with:
17+
run_install: false
18+
19+
- name: Use Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: 20.x
23+
registry-url: https://registry.npmjs.org/
24+
cache: 'pnpm'
25+
26+
- name: Install dependencies
27+
run: pnpm install --recursive --frozen-lockfile
28+
29+
- run: pnpm publish-package -- --dry-run
30+
working-directory: ./packages/typed-binary
31+
env:
32+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@
4545
"cypress": "^13.13.1",
4646
"socket.io": "^4.7.5",
4747
"socket.io-client": "^4.7.5",
48-
"typescript": "^5.5.2",
49-
"tsx": "^4.16.2",
5048
"tsup": "8.1.0",
49+
"tsx": "^4.16.2",
50+
"typed-binary": "workspace:*",
51+
"typescript": "^5.5.2",
5152
"vitest": "^1.6.0",
52-
"typed-binary": "workspace:*"
53+
"arg": "^5.0.2",
54+
"zod": "^3.24.1"
5355
}
5456
}

packages/typed-binary/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"build": "tsup",
2828
"prepublishOnly": "pnpm -w run check && pnpm -w run build",
2929
"dryPublish": "pnpm publish --dry-run",
30+
"publish-package": "node ../../scripts/publish-to-npm.mjs",
3031
"check": "tsup"
3132
},
3233
"keywords": [

pnpm-lock.yaml

Lines changed: 13 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/_colors.mjs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const colors = {
2+
Reset: '\u001b[0m',
3+
4+
Black: '\u001b[30m',
5+
Red: '\u001b[31m',
6+
Green: '\u001b[32m',
7+
Yellow: '\u001b[33m',
8+
Blue: '\u001b[34m',
9+
Magenta: '\u001b[35m',
10+
Cyan: '\u001b[36m',
11+
White: '\u001b[37m',
12+
13+
BrightBlack: '\u001b[30;1m',
14+
BrightRed: '\u001b[31;1m',
15+
BrightGreen: '\u001b[32;1m',
16+
BrightYellow: '\u001b[33;1m',
17+
BrightBlue: '\u001b[34;1m',
18+
BrightMagenta: '\u001b[35;1m',
19+
BrightCyan: '\u001b[36;1m',
20+
BrightWhite: '\u001b[37;1m',
21+
22+
BgBlack: '\u001b[40m',
23+
BgRed: '\u001b[41m',
24+
BgGreen: '\u001b[42m',
25+
BgYellow: '\u001b[43m',
26+
BgBlue: '\u001b[44m',
27+
BgMagenta: '\u001b[45m',
28+
BgCyan: '\u001b[46m',
29+
BgWhite: '\u001b[47m',
30+
31+
BgBrightBlack: '\u001b[40;1m',
32+
BgBrightRed: '\u001b[41;1m',
33+
BgBrightGreen: '\u001b[42;1m',
34+
BgBrightYellow: '\u001b[43;1m',
35+
BgBrightBlue: '\u001b[44;1m',
36+
BgBrightMagenta: '\u001b[45;1m',
37+
BgBrightCyan: '\u001b[46;1m',
38+
BgBrightWhite: '\u001b[47;1m',
39+
40+
Bold: '\u001b[1m',
41+
};
42+
43+
export default colors;

scripts/publish-to-npm.mjs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// @ts-check
2+
import { spawn } from 'node:child_process';
3+
import fs from 'node:fs/promises';
4+
import process from 'node:process';
5+
import arg from 'arg';
6+
import z from 'zod';
7+
import colors from './_colors.mjs';
8+
9+
const ReleaseChannel = z.enum(['alpha']);
10+
11+
/** @typedef {z.infer<typeof ReleaseChannel>} ReleaseChannel */
12+
13+
const args = arg({});
14+
const cwd = new URL(`file:${process.cwd()}/`);
15+
const packageJsonUrl = new URL('./package.json', cwd);
16+
17+
const pkg = JSON.parse(await fs.readFile(packageJsonUrl, 'utf-8'));
18+
19+
/**
20+
* @param {string} version
21+
* @returns {z.infer<typeof ReleaseChannel> | null}
22+
*/
23+
function extractChannel(version) {
24+
if (/[a-zA-Z]/.test(version)) {
25+
const channel = Object.values(ReleaseChannel.Values).find((c) =>
26+
version.includes(c),
27+
);
28+
29+
if (!channel) {
30+
throw new Error(`Unrecognized channel: ${channel}`);
31+
}
32+
33+
return channel;
34+
}
35+
36+
return null;
37+
}
38+
39+
const channel = extractChannel(pkg.version);
40+
41+
/**
42+
* @param {string} command The command to run
43+
* @param {string[]} params The command to run
44+
* @returns {Promise<number>} The exit code of the process
45+
*/
46+
function promiseSpawn(command, params) {
47+
return new Promise((resolve, reject) => {
48+
const childProcess = spawn(command, params);
49+
50+
childProcess.on('close', (code) => {
51+
if (code === undefined || code !== 0) {
52+
reject(code);
53+
} else {
54+
resolve(code ?? 1);
55+
}
56+
});
57+
58+
childProcess.stdout?.pipe(process.stdout);
59+
childProcess.stderr?.pipe(process.stderr);
60+
});
61+
}
62+
63+
async function main() {
64+
console.log(
65+
`\
66+
Release channel: ${colors.Cyan}${channel ?? '<LATEST>'}${colors.Reset}
67+
`,
68+
);
69+
70+
try {
71+
await promiseSpawn('pnpm', [
72+
'publish',
73+
'--provenance',
74+
...(channel ? ['--tag', channel] : []),
75+
...args._,
76+
]);
77+
} catch (e) {
78+
console.error('pnpm publish error code:', e);
79+
process.exit(1);
80+
}
81+
82+
console.log(
83+
`
84+
85+
-------------------------------------------------------------------------
86+
87+
Package published!
88+
`,
89+
);
90+
}
91+
92+
main();

0 commit comments

Comments
 (0)