Skip to content

Commit

Permalink
dx: NPM Publishing Workflow (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza authored Dec 18, 2024
1 parent a621209 commit f211593
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 12 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/publish-to-npm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Publish to NPM

on:
release:
types: [published]
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
registry-url: https://registry.npmjs.org/
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --recursive --frozen-lockfile

- run: pnpm publish-package -- --dry-run
working-directory: ./packages/typed-binary
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@
"cypress": "^13.13.1",
"socket.io": "^4.7.5",
"socket.io-client": "^4.7.5",
"typescript": "^5.5.2",
"tsx": "^4.16.2",
"tsup": "8.1.0",
"tsx": "^4.16.2",
"typed-binary": "workspace:*",
"typescript": "^5.5.2",
"vitest": "^1.6.0",
"typed-binary": "workspace:*"
"arg": "^5.0.2",
"zod": "^3.24.1"
}
}
1 change: 1 addition & 0 deletions packages/typed-binary/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"build": "tsup",
"prepublishOnly": "pnpm -w run check && pnpm -w run build",
"dryPublish": "pnpm publish --dry-run",
"publish-package": "node ../../scripts/publish-to-npm.mjs",
"check": "tsup"
},
"keywords": [
Expand Down
22 changes: 13 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions scripts/_colors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const colors = {
Reset: '\u001b[0m',

Black: '\u001b[30m',
Red: '\u001b[31m',
Green: '\u001b[32m',
Yellow: '\u001b[33m',
Blue: '\u001b[34m',
Magenta: '\u001b[35m',
Cyan: '\u001b[36m',
White: '\u001b[37m',

BrightBlack: '\u001b[30;1m',
BrightRed: '\u001b[31;1m',
BrightGreen: '\u001b[32;1m',
BrightYellow: '\u001b[33;1m',
BrightBlue: '\u001b[34;1m',
BrightMagenta: '\u001b[35;1m',
BrightCyan: '\u001b[36;1m',
BrightWhite: '\u001b[37;1m',

BgBlack: '\u001b[40m',
BgRed: '\u001b[41m',
BgGreen: '\u001b[42m',
BgYellow: '\u001b[43m',
BgBlue: '\u001b[44m',
BgMagenta: '\u001b[45m',
BgCyan: '\u001b[46m',
BgWhite: '\u001b[47m',

BgBrightBlack: '\u001b[40;1m',
BgBrightRed: '\u001b[41;1m',
BgBrightGreen: '\u001b[42;1m',
BgBrightYellow: '\u001b[43;1m',
BgBrightBlue: '\u001b[44;1m',
BgBrightMagenta: '\u001b[45;1m',
BgBrightCyan: '\u001b[46;1m',
BgBrightWhite: '\u001b[47;1m',

Bold: '\u001b[1m',
};

export default colors;
92 changes: 92 additions & 0 deletions scripts/publish-to-npm.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// @ts-check
import { spawn } from 'node:child_process';
import fs from 'node:fs/promises';
import process from 'node:process';
import arg from 'arg';
import z from 'zod';
import colors from './_colors.mjs';

const ReleaseChannel = z.enum(['alpha']);

/** @typedef {z.infer<typeof ReleaseChannel>} ReleaseChannel */

const args = arg({});
const cwd = new URL(`file:${process.cwd()}/`);
const packageJsonUrl = new URL('./package.json', cwd);

const pkg = JSON.parse(await fs.readFile(packageJsonUrl, 'utf-8'));

/**
* @param {string} version
* @returns {z.infer<typeof ReleaseChannel> | null}
*/
function extractChannel(version) {
if (/[a-zA-Z]/.test(version)) {
const channel = Object.values(ReleaseChannel.Values).find((c) =>
version.includes(c),
);

if (!channel) {
throw new Error(`Unrecognized channel: ${channel}`);
}

return channel;
}

return null;
}

const channel = extractChannel(pkg.version);

/**
* @param {string} command The command to run
* @param {string[]} params The command to run
* @returns {Promise<number>} The exit code of the process
*/
function promiseSpawn(command, params) {
return new Promise((resolve, reject) => {
const childProcess = spawn(command, params);

childProcess.on('close', (code) => {
if (code === undefined || code !== 0) {
reject(code);
} else {
resolve(code ?? 1);
}
});

childProcess.stdout?.pipe(process.stdout);
childProcess.stderr?.pipe(process.stderr);
});
}

async function main() {
console.log(
`\
Release channel: ${colors.Cyan}${channel ?? '<LATEST>'}${colors.Reset}
`,
);

try {
await promiseSpawn('pnpm', [
'publish',
'--provenance',
...(channel ? ['--tag', channel] : []),
...args._,
]);
} catch (e) {
console.error('pnpm publish error code:', e);
process.exit(1);
}

console.log(
`
-------------------------------------------------------------------------
Package published!
`,
);
}

main();

0 comments on commit f211593

Please sign in to comment.