Skip to content

Commit 2c7bab7

Browse files
alan-agius4devversion
authored andcommitted
feat(ng-dev): add command to generate Bazel Node.js toolchain definitions
This commit introduces a new `ng-dev` command: `misc generate-nodejs-toolchain <version>` This command automates the creation of `nodejs_register_toolchains` Bazel rules by fetching official Node.js release checksums for a given version and outputting the Starlark definition. This simplifies managing Node.js versions in Bazel. Example usage: ``` $ pnpm ng-dev misc generate-nodejs-toolchain 24.0.0 This will output: nodejs_register_toolchains( name = "node24", node_repositories = { "24.0.0-darwin_arm64": ("node-v24.0.0-darwin-arm64.tar.gz", "node-v24.0.0-darwin-arm64", "194e2f3dd3ec8c2adcaa713ed40f44c5ca38467880e160974ceac1659be60121"), "24.0.0-darwin_amd64": ("node-v24.0.0-darwin-x64.tar.gz", "node-v24.0.0-darwin-x64", "f716b3ce14a7e37a6cbf97c9de10d444d7da07ef833cd8da81dd944d111e6a4a"), # ... other platforms }, node_version = "24.0.0", ) ```
1 parent b3df5e3 commit 2c7bab7

File tree

2 files changed

+135
-1
lines changed

2 files changed

+135
-1
lines changed

ng-dev/misc/cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {Argv} from 'yargs';
1010
import {BuildAndLinkCommandModule} from './build-and-link/cli.js';
1111
import {UpdateYarnCommandModule} from './update-yarn/cli.js';
1212
import {GeneratedFilesModule} from './generated-files/cli.js';
13+
import {GeneratedNodeJsToolchainModule} from './generate-nodejs-toolchain/cli.js';
1314

1415
/** Build the parser for the misc commands. */
1516
export function buildMiscParser(localYargs: Argv) {
@@ -18,5 +19,6 @@ export function buildMiscParser(localYargs: Argv) {
1819
.strict()
1920
.command(BuildAndLinkCommandModule)
2021
.command(UpdateYarnCommandModule)
21-
.command(GeneratedFilesModule);
22+
.command(GeneratedFilesModule)
23+
.command(GeneratedNodeJsToolchainModule);
2224
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import https from 'node:https';
2+
import {Log} from '../../utils/logging.js';
3+
import {Arguments, Argv, CommandModule} from 'yargs';
4+
5+
interface RepositoryInfo {
6+
filename: string;
7+
sha: string;
8+
type: string;
9+
}
10+
11+
interface NodeVersionData {
12+
version: string;
13+
repositories: RepositoryInfo[];
14+
}
15+
16+
/** Command line options. */
17+
export interface GenerateNodeJsToolchainOptions {
18+
nodeJsVersion: string;
19+
}
20+
21+
function builder(argv: Argv): Argv<GenerateNodeJsToolchainOptions> {
22+
return argv
23+
.positional('nodeJsVersion', {
24+
type: 'string',
25+
demandOption: true,
26+
})
27+
.check(({nodeJsVersion}) => {
28+
if (!/^\d+\.\d+\.\d+$/.test(nodeJsVersion)) {
29+
throw new Error(
30+
`Invalid version format "${nodeJsVersion}". Expected X.Y.Z (Example: 22.11.0)`,
31+
);
32+
}
33+
34+
return true;
35+
});
36+
}
37+
38+
/** CLI command module. */
39+
export const GeneratedNodeJsToolchainModule: CommandModule<{}, GenerateNodeJsToolchainOptions> = {
40+
builder,
41+
handler,
42+
command: 'generate-nodejs-toolchain <nodeJsVersion>',
43+
describe: 'Generates a Bazel toolchain definition for a specific Node.js version.',
44+
};
45+
46+
const REPOSITORY_TYPES: Record<string, string> = {
47+
'darwin-arm64.tar.gz': 'darwin_arm64',
48+
'darwin-x64.tar.gz': 'darwin_amd64',
49+
'linux-x64.tar.xz': 'linux_amd64',
50+
'linux-arm64.tar.xz': 'linux_arm64',
51+
'linux-s390x.tar.xz': 'linux_s390x',
52+
'win-x64.zip': 'windows_amd64',
53+
'linux-ppc64le.tar.xz': 'linux_ppc64le',
54+
};
55+
56+
function getText(url: string): Promise<string> {
57+
return new Promise<string>((resolve, reject) => {
58+
const request = https.get(url, (res) => {
59+
if (res.statusCode !== 200) {
60+
return reject(
61+
new Error(`Failed to get ${url}. Status Code: ${res.statusCode ?? 'unknown'}`),
62+
);
63+
}
64+
65+
const body: string[] = [];
66+
res.on('data', (chunk) => body.push(chunk));
67+
res.on('end', () => resolve(body.join('')));
68+
});
69+
70+
request.on('error', (err) => reject(err));
71+
});
72+
}
73+
74+
async function getNodeJsRepositories(version: string): Promise<NodeVersionData> {
75+
const text = await getText(`https://nodejs.org/dist/v${version}/SHASUMS256.txt`);
76+
77+
const repositories: RepositoryInfo[] = text
78+
.split('\n')
79+
.filter(Boolean) // Remove empty lines
80+
.map((line: string): RepositoryInfo | undefined => {
81+
const [sha, filename] = line.trim().split(/\s+/);
82+
if (!filename) {
83+
return undefined;
84+
}
85+
86+
// Extract the part of the filename that matches REPOSITORY_TYPES keys
87+
// Example: "node-v22.2.0-darwin-arm64.tar.gz" -> "darwin-arm64.tar.gz"
88+
const fileTypeSuffix = filename.replace(/^node-v[\d.]+-/, '');
89+
const type = REPOSITORY_TYPES[fileTypeSuffix];
90+
91+
return type ? {filename, sha, type} : undefined;
92+
})
93+
.filter((repo): repo is RepositoryInfo => repo !== undefined);
94+
95+
return {
96+
version,
97+
repositories,
98+
};
99+
}
100+
101+
async function handler({nodeJsVersion}: Arguments<GenerateNodeJsToolchainOptions>): Promise<void> {
102+
try {
103+
const {version, repositories} = await getNodeJsRepositories(nodeJsVersion);
104+
if (!repositories?.length) {
105+
Log.error(
106+
` ✘ Could not find any downloadable files for Node.js version ${version}. ` +
107+
`Please check if the version exists and has published binaries at https://nodejs.org/dist/v${version}/`,
108+
);
109+
process.exit(1);
110+
}
111+
112+
const [majorVersion] = version.split('.');
113+
console.log(`nodejs_register_toolchains(`);
114+
console.log(` name = "node${majorVersion}",`);
115+
console.log(` node_repositories = {`);
116+
117+
for (const {filename, sha, type} of repositories) {
118+
// Remove file extension (.zip, .tar.xr .tar.gz etc...)
119+
const strippedFilename = filename.replace(/(\.tar)?\.[^.]+$/, '');
120+
console.log(
121+
` "${version}-${type}": ("${filename}", "${strippedFilename}", "${sha}"),`,
122+
);
123+
}
124+
125+
console.log(` },`);
126+
console.log(` node_version = "${version}",`);
127+
console.log(`)\n`);
128+
} catch (error) {
129+
Log.error(` ✘ Aborted due to an error:\n${error}`);
130+
process.exit(1);
131+
}
132+
}

0 commit comments

Comments
 (0)