Automate NPM package metadata generation in release workflow
Replace envsubst templating with Node scripts for package.json and README.md. Set release version using tag or Cargo.toml. Switch to npm for install/build.
Seto Elkahfi committed
Mar 28, 2026 at 19:24 UTC
633e60aa40645195b9547d9d7f8ff70d205f5980
3 files changed
+152
-14
.github/workflows/release-npm.yml
+22
-14
@@ -19,7 +19,7 @@ env:
19
jobs:
20
publish-npm-binaries:
21
name: Publish NPM packages
22
- runs-on: ${{ matrix.build.os }}
22
+ runs-on: ${{ matrix.build.OS }}
23
strategy:
24
fail-fast: false
25
matrix:
@@ -67,7 +67,14 @@ jobs:
67
68
- name: Set the release version
69
shell: bash
70
- run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV
70
+ run: |
71
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
72
+ release_version="${GITHUB_REF_NAME#v}"
73
+ else
74
+ release_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1)"
75
+ fi
76
+
77
+ echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
78
79
- name: Install toolkit
80
uses: dtolnay/rust-toolchain@master
@@ -125,10 +132,8 @@ jobs:
132
fi
133
# create the package directory
134
mkdir -p "${node_pkg}/bin"
128
- # generate package.json from the template
129
- envsubst < package.json.tmpl > "${node_pkg}/package.json"
130
- # generate readme.md from the template
131
- envsubst < README.md.tmpl > "${node_pkg}/README.md"
135
+ # generate package metadata
136
+ node ./scripts/render-platform-package.cjs "${node_pkg}" "${node_version}" "${node_os}" "${node_arch}"
137
# copy the binary into the package
138
# note: windows binaries has '.exe' extension
139
if [ "${{ matrix.build.OS }}" = "windows-2022" ]; then
@@ -151,7 +156,14 @@ jobs:
156
157
- name: Set the release version
158
shell: bash
154
- run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV
159
+ run: |
160
+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
161
+ release_version="${GITHUB_REF_NAME#v}"
162
+ else
163
+ release_version="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/cli/Cargo.toml | head -n 1)"
164
+ fi
165
+
166
+ echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
167
168
- name: Install node
169
uses: actions/setup-node@v6
@@ -164,14 +176,10 @@ jobs:
176
run: |
177
cd npm/smbcloud-cli
178
167
- # set the version
168
- export release_version="${{ env.RELEASE_VERSION }}"
169
-
170
- # use the tag version
171
- envsubst < ../package-main.json.tmpl > "./package.json"
179
+ node ../scripts/render-main-package.cjs "./package.json" "${{ env.RELEASE_VERSION }}"
180
173
- yarn install # requires optional dependencies to be present in the registry
174
- yarn build
181
+ npm install
182
+ npm run build
183
npm publish --access public
184
env:
185
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
npm/scripts/render-main-package.cjs
new
+78
@@ -0,0 +1,78 @@
1
+#!/usr/bin/env node
2
+
3
+const fs = require("fs");
4
+const path = require("path");
5
+
6
+const [outputPath, version] = process.argv.slice(2);
7
+
8
+if (!outputPath || !version) {
9
+ throw new Error("Usage: render-main-package.cjs <output-path> <version>");
10
+}
11
+
12
+const packageJson = {
13
+ name: "@smbcloud/cli",
14
+ version,
15
+ keywords: [
16
+ "smbcloud",
17
+ "cli",
18
+ "smb",
19
+ "pndk",
20
+ "smbcloud-cli",
21
+ "cloud",
22
+ "smbcloud",
23
+ "developer",
24
+ "tools",
25
+ "web3",
26
+ "web3js",
27
+ "web3.js",
28
+ "web3js",
29
+ "bitcoin",
30
+ "btc",
31
+ "ethereum",
32
+ "solana",
33
+ "eth",
34
+ "sol",
35
+ "blockchain",
36
+ "smart-contracts",
37
+ "dapps",
38
+ "dapp",
39
+ "dapp-tools",
40
+ "dapp-tool",
41
+ "dapp-toolkit",
42
+ "dapp-toolkit-cli",
43
+ ],
44
+ bin: {
45
+ smb: "lib/index.js",
46
+ },
47
+ files: ["lib", "README.md"],
48
+ description: "A CLI for accessing the smbCloud platform.",
49
+ license: "Apache-2.0",
50
+ repository: {
51
+ type: "git",
52
+ url: "git+https://github.com/smbcloudXYZ/smbcloud-cli.git",
53
+ },
54
+ scripts: {
55
+ typecheck: "tsc --noEmit",
56
+ lint: "eslint .",
57
+ "lint:fix": "eslint . --fix",
58
+ build: "tsc",
59
+ dev: "npm run build && node lib/index.js",
60
+ },
61
+ devDependencies: {
62
+ "@types/node": "^18.15.11",
63
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
64
+ "@typescript-eslint/parser": "^5.48.0",
65
+ eslint: "^8.31.0",
66
+ typescript: "^5.0.0",
67
+ },
68
+ optionalDependencies: {
69
+ "@smbcloud/cli-darwin-arm64": version,
70
+ "@smbcloud/cli-darwin-x64": version,
71
+ "@smbcloud/cli-linux-x64": version,
72
+ "@smbcloud/cli-windows-arm64": version,
73
+ "@smbcloud/cli-windows-x64": version,
74
+ },
75
+};
76
+
77
+fs.writeFileSync(path.resolve(outputPath), `${JSON.stringify(packageJson, null, 2)}
78
+`);
npm/scripts/render-platform-package.cjs
new
+52
@@ -0,0 +1,52 @@
1
+#!/usr/bin/env node
2
+
3
+const fs = require("fs");
4
+const path = require("path");
5
+
6
+const [packageName, version, operatingSystem, architecture] = process.argv.slice(2);
7
+
8
+if (!packageName || !version || !operatingSystem || !architecture) {
9
+ throw new Error(
10
+ "Usage: render-platform-package.cjs <package-name> <version> <os> <arch>"
11
+ );
12
+}
13
+
14
+const packageDirectory = path.resolve(__dirname, "..", packageName);
15
+
16
+fs.mkdirSync(packageDirectory, { recursive: true });
17
+
18
+const packageJson = {
19
+ name: `@smbcloud/${packageName}`,
20
+ version,
21
+ description: "Platform binary for the smbCloud CLI.",
22
+ license: "Apache-2.0",
23
+ repository: {
24
+ type: "git",
25
+ url: "git+https://github.com/smbcloudXYZ/smbcloud-cli.git",
26
+ },
27
+ keywords: [
28
+ packageName,
29
+ "cli",
30
+ "binary",
31
+ "node",
32
+ "nodejs",
33
+ "npm",
34
+ "package",
35
+ "smbcloud",
36
+ ],
37
+ os: [operatingSystem],
38
+ cpu: [architecture],
39
+ files: ["bin"],
40
+};
41
+
42
+const readme = `# ${packageName}
43
+
44
+Not meant to be used directly. Please see [@smbcloud/cli](https://www.npmjs.com/package/@smbcloud/cli).
45
+`;
46
+
47
+fs.writeFileSync(
48
+ path.join(packageDirectory, "package.json"),
49
+ `${JSON.stringify(packageJson, null, 2)}
50
+`
51
+);
52
+fs.writeFileSync(path.join(packageDirectory, "README.md"), readme);