Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 506d277

Browse files
committed
feat(nitr-node/postinstall): download latest release for nitro by default
Can also override the version by setting NITRO_VERSION environment variable
1 parent 1133759 commit 506d277

File tree

1 file changed

+121
-46
lines changed

1 file changed

+121
-46
lines changed

nitro-node/src/scripts/download-nitro.ts

Lines changed: 121 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,86 @@ import path from "node:path";
33
import download from "download";
44
import { Duplex } from "node:stream";
55

6-
// Define nitro version to download in env variable
7-
const NITRO_VERSION = process.env.NITRO_VERSION || "0.2.11";
6+
// Define nitro version to download in env variable ("latest" or tag "v1.2.3")
7+
const NITRO_VERSION = process.env.NITRO_VERSION || "latest";
88
// The platform OS to download nitro for
99
const PLATFORM = process.env.npm_config_platform || process.platform;
1010
// The platform architecture
1111
//const ARCH = process.env.npm_config_arch || process.arch;
1212

13-
const linuxVariants = {
14-
"linux-amd64": "linux-cpu",
15-
"linux-amd64-cuda-12-0": "linux-cuda-12-0",
16-
"linux-amd64-cuda-11-7": "linux-cuda-11-7",
13+
const releaseUrlPrefixVariants = {
14+
latest: "https://api.github.com/repos/janhq/nitro/releases/",
15+
tag: "https://api.github.com/repos/janhq/nitro/releases/tags/",
1716
};
1817

19-
const darwinVariants = {
20-
"mac-arm64": "mac-arm64",
21-
"mac-amd64": "mac-x64",
22-
};
23-
24-
const win32Variants = {
25-
"win-amd64-cuda-12-0": "win-cuda-12-0",
26-
"win-amd64-cuda-11-7": "win-cuda-11-7",
27-
"win-amd64": "win-cpu",
18+
const getReleaseInfo = async (taggedVersion: string): Promise<any> => {
19+
const releaseUrlPrefix =
20+
taggedVersion === "latest"
21+
? releaseUrlPrefixVariants.latest
22+
: taggedVersion.match(/^\d+\.\d+\.\d+$/gi)
23+
? releaseUrlPrefixVariants.tag
24+
: undefined;
25+
if (!releaseUrlPrefix) throw Error(`Invalid version: ${taggedVersion}`);
26+
const url = `${releaseUrlPrefix}${taggedVersion}`;
27+
console.log(`[Getting release info] ${url}`);
28+
const response = await fetch(url, {
29+
headers: {
30+
Accept: "application/vnd.github+json",
31+
"X-GitHub-Api-Version": "2022-11-28",
32+
},
33+
});
34+
if (!response.ok)
35+
throw Error(`Failed to fetch release info: ${response.status}`);
36+
return await response.json();
2837
};
2938

30-
// Mapping to installation variants
31-
const variantMapping: Record<string, Record<string, string>> = {
32-
darwin: darwinVariants,
33-
linux: linuxVariants,
34-
win32: win32Variants,
39+
const extractDownloadInfo = async (
40+
/* releaseInfo */
41+
{
42+
html_url,
43+
tag_name,
44+
name,
45+
prerelease,
46+
assets,
47+
}: {
48+
html_url: string;
49+
tag_name: string;
50+
name: string;
51+
prerelease: boolean;
52+
assets: {
53+
name: string;
54+
size: number;
55+
browser_download_url: string;
56+
}[];
57+
},
58+
/* variants for filter for in format {suffix: dir} */
59+
suffixVariants: Record<string, string>,
60+
): Promise<Record<string, { url: string; dir: string }>> => {
61+
console.log(`[Release URL][prerelease=${prerelease}] ${html_url}`);
62+
const assetNames = assets.map(({ name }: { name: string }) => name);
63+
const assetsToDownloads = Object.entries(suffixVariants).reduce(
64+
(
65+
dict: Record<string, { url: string; dir: string }>,
66+
[suffix, dir]: [string, string],
67+
) => {
68+
// Skip if suffix is not in asset names
69+
if (!assetNames.includes(`nitro-${name}-${suffix}.tar.gz`)) return dict;
70+
// Else add the download url
71+
dict[suffix] = {
72+
url: `https://github.com/janhq/nitro/releases/download/${tag_name}/nitro-${name}-${suffix}.tar.gz`,
73+
dir,
74+
};
75+
return dict;
76+
},
77+
{},
78+
);
79+
// If empty then no assets were found
80+
if (!Object.keys(assetsToDownloads).length)
81+
throw Error("Failed to find any asset to download");
82+
// Return the dict of download info
83+
return assetsToDownloads;
3584
};
3685

37-
if (!(PLATFORM in variantMapping)) {
38-
throw Error(`Invalid platform: ${PLATFORM}`);
39-
}
40-
// Get download config for this platform
41-
const variantConfig: Record<string, string> = variantMapping[PLATFORM];
42-
43-
// Generate download link for each tarball
44-
const getTarUrl = (version: string, suffix: string) =>
45-
`https://github.com/janhq/nitro/releases/download/v${version}/nitro-${version}-${suffix}.tar.gz`;
46-
4786
// Report download progress
4887
const createProgressReporter =
4988
(variant: string) => (stream: Promise<Buffer> & Duplex) =>
@@ -65,28 +104,26 @@ const createProgressReporter =
65104

66105
// Download single binary
67106
const downloadBinary = async (
68-
version: string,
69107
suffix: string,
70-
destDirPath: string,
108+
{ url, dir }: { url: string; dir: string },
71109
) => {
72-
const tarUrl = getTarUrl(version, suffix);
73-
console.log(`Downloading ${tarUrl} to ${destDirPath}`);
110+
console.log(`Downloading ${url} to ${dir}...`);
74111
const progressReporter = createProgressReporter(suffix);
75112
await progressReporter(
76-
download(tarUrl, destDirPath, {
113+
download(url, dir, {
77114
strip: 1,
78115
extract: true,
79116
}),
80117
);
81118
// Set mode of downloaded binaries to executable
82-
(fs.readdirSync(destDirPath, { recursive: true }) as string[])
119+
(fs.readdirSync(dir, { recursive: true }) as string[])
83120
.filter(
84121
(fname) =>
85-
fname.includes("nitro") &&
86-
fs.lstatSync(path.join(destDirPath, fname)).isFile(),
122+
fname.includes("nitro") && fs.lstatSync(path.join(dir, fname)).isFile(),
87123
)
88124
.forEach((nitroBinary) => {
89-
const absPath = path.join(destDirPath, nitroBinary);
125+
const absPath = path.join(dir, nitroBinary);
126+
// Set mode executable for nitro binary
90127
fs.chmodSync(
91128
absPath,
92129
fs.constants.S_IRWXU | fs.constants.S_IRWXG | fs.constants.S_IRWXO,
@@ -96,13 +133,19 @@ const downloadBinary = async (
96133

97134
// Download the binaries
98135
const downloadBinaries = (
99-
version: string,
100-
config: Record<string, string>,
136+
/* downloadInfo */
137+
downloadInfo: Record<string, { url: string; dir: string }>,
138+
/* The absolute path to the directory where binaries will be downloaded */
101139
absBinDirPath: string,
102140
) => {
103-
return Object.entries(config).reduce(
104-
(p: Promise<any>, [k, v]) =>
105-
p.then(() => downloadBinary(version, k, path.join(absBinDirPath, v))),
141+
return Object.entries(downloadInfo).reduce(
142+
(
143+
p: Promise<any>,
144+
[suffix, { url, dir }]: [string, { url: string; dir: string }],
145+
) =>
146+
p.then(() =>
147+
downloadBinary(suffix, { url, dir: path.join(absBinDirPath, dir) }),
148+
),
106149
Promise.resolve(),
107150
);
108151
};
@@ -126,19 +169,51 @@ const verifyDownloadedBinaries = (absBinDirPath: string) => {
126169
});
127170
};
128171

172+
const linuxVariants = {
173+
"linux-amd64": "linux-cpu",
174+
"linux-amd64-cuda-12-0": "linux-cuda-12-0",
175+
"linux-amd64-cuda-11-7": "linux-cuda-11-7",
176+
};
177+
178+
const darwinVariants = {
179+
"mac-arm64": "mac-arm64",
180+
"mac-amd64": "mac-x64",
181+
};
182+
183+
const win32Variants = {
184+
"win-amd64-cuda-12-0": "win-cuda-12-0",
185+
"win-amd64-cuda-11-7": "win-cuda-11-7",
186+
"win-amd64": "win-cpu",
187+
};
188+
189+
// Mapping to installation variants
190+
const variantMapping: Record<string, Record<string, string>> = {
191+
darwin: darwinVariants,
192+
linux: linuxVariants,
193+
win32: win32Variants,
194+
};
195+
196+
if (!(PLATFORM in variantMapping)) {
197+
throw Error(`Invalid platform: ${PLATFORM}`);
198+
}
199+
200+
// Get download config for this platform
201+
const variantConfig: Record<string, string> = variantMapping[PLATFORM];
129202
// Call the download function with version and config
130203
export const downloadNitro = async (absBinDirPath: string) => {
131204
// Return early without downloading if nitro binaries are already downloaded
132205
if (verifyDownloadedBinaries(absBinDirPath)) {
133206
//console.log("Nitro binaries are already downloaded!");
134207
return;
135208
}
136-
return await downloadBinaries(NITRO_VERSION, variantConfig, absBinDirPath);
209+
const releaseInfo = await getReleaseInfo(NITRO_VERSION);
210+
const downloadInfo = await extractDownloadInfo(releaseInfo, variantConfig);
211+
return await downloadBinaries(downloadInfo, absBinDirPath);
137212
};
138213

139214
// Run script if called directly instead of import as module
140215
if (require.main === module) {
141216
// Assume calling the source typescript files
142217
// bin path will be relative to the source code root
143-
downloadNitro(path.join(__dirname, '..', "..", "bin"));
218+
downloadNitro(path.join(__dirname, "..", "..", "bin"));
144219
}

0 commit comments

Comments
 (0)