@journeyapps/cloudcode-build-agent 0.0.0-dev.2b3e822 → 0.0.0-dev.55699c3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder.d.ts +1 -7
- package/dist/builder.js +3 -88
- package/dist/builder.js.map +1 -1
- package/dist/cli.js +22 -17
- package/dist/cli.js.map +1 -1
- package/dist/detect_tasks.js +0 -1
- package/dist/detect_tasks.js.map +1 -1
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/installer.d.ts +1 -11
- package/dist/installer.js +61 -99
- package/dist/installer.js.map +1 -1
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +99 -0
- package/dist/utils.js.map +1 -0
- package/package.json +2 -5
- package/src/builder.ts +5 -88
- package/src/cli.ts +31 -22
- package/src/detect_tasks.ts +0 -1
- package/src/errors.ts +22 -0
- package/src/index.ts +2 -0
- package/src/installer.ts +78 -109
- package/src/utils.ts +82 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/run.d.ts +0 -10
- package/dist/run.js +0 -90
- package/dist/run.js.map +0 -1
- package/src/run.ts +0 -112
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeyapps/cloudcode-build-agent",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.55699c3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index",
|
|
6
6
|
"types": "./dist/index",
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
"cloudcode-build-agent": "./bin.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"child-process-promise": "^2.2.1",
|
|
13
|
-
"debug": "^2.6.0",
|
|
14
12
|
"fs-jetpack": "5.1.0",
|
|
15
13
|
"lodash": "4.17.21",
|
|
16
14
|
"node-fetch": "<3",
|
|
@@ -19,9 +17,8 @@
|
|
|
19
17
|
"yargs": "17.6.2"
|
|
20
18
|
},
|
|
21
19
|
"devDependencies": {
|
|
22
|
-
"@types/child-process-promise": "2.2.2",
|
|
23
20
|
"@types/lodash": "4.14.191",
|
|
24
|
-
"@types/node": "
|
|
21
|
+
"@types/node": "16.11.7",
|
|
25
22
|
"@types/node-fetch": "2.6.2",
|
|
26
23
|
"@types/semver": "7.3.13",
|
|
27
24
|
"@types/tar": "6.1.4",
|
package/src/builder.ts
CHANGED
|
@@ -1,94 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as jetpack from 'fs-jetpack';
|
|
3
|
-
import * as semver from 'semver';
|
|
4
|
-
import * as utils from './installer';
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
5
2
|
import { detectTasks } from './detect_tasks';
|
|
6
|
-
import { runCommand } from './run';
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export type GeneralTaskConfig = {
|
|
11
|
-
app_id: string;
|
|
12
|
-
env: string;
|
|
13
|
-
backend_id: string;
|
|
14
|
-
backend_url: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export async function buildTasks(project_path: string, dest: string, config: GeneralTaskConfig, only?: string) {
|
|
4
|
+
// Detect tasks in given path and build them.
|
|
5
|
+
export async function buildTasks(project_path: string, dest: string, config: any, only?: string) {
|
|
18
6
|
const tasks = await detectTasks(project_path, only);
|
|
19
7
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// FIXME: Maybe refactor this section into an ensureNodeVersion (or something) that returns the relevant toolpaths?
|
|
24
|
-
const tool_paths: Record<string, { bin_path: string; node_bin: string; npm_bin: string }> = {};
|
|
25
|
-
// Use the version of nodejs that's running this script as the default.
|
|
26
|
-
const default_tool_paths = {
|
|
27
|
-
bin_path: path.dirname(process.execPath),
|
|
28
|
-
node_bin: process.execPath,
|
|
29
|
-
npm_bin: path.join(path.dirname(process.execPath), 'npm')
|
|
30
|
-
};
|
|
31
|
-
tool_paths[process.version] = default_tool_paths;
|
|
32
|
-
|
|
33
|
-
for (const required_node_version of install_node_versions) {
|
|
34
|
-
// FIXME: put this in another directory to avoid leaking into the builder
|
|
35
|
-
const custom_node_path = path.resolve(`node-${semver.major(required_node_version)}`);
|
|
36
|
-
|
|
37
|
-
if (!jetpack.exists(path.join(custom_node_path, 'bin/node'))) {
|
|
38
|
-
console.debug(`installing to ${custom_node_path}`);
|
|
39
|
-
await jetpack.dirAsync(custom_node_path);
|
|
40
|
-
await utils.downloadAndInstallNode(required_node_version, custom_node_path);
|
|
41
|
-
} else {
|
|
42
|
-
console.debug(`already installed in ${custom_node_path}`);
|
|
43
|
-
}
|
|
44
|
-
tool_paths[required_node_version] = utils.nodePaths(custom_node_path);
|
|
45
|
-
}
|
|
46
|
-
console.debug('----');
|
|
47
|
-
|
|
48
|
-
for (const task of tasks) {
|
|
49
|
-
const debug = require('debug')(`${task.task_name}`);
|
|
50
|
-
const node_version = task.required_node_version ?? process.version;
|
|
51
|
-
const builder_package = task.builder_package;
|
|
52
|
-
const builder_script = task.builder_script;
|
|
53
|
-
const { bin_path, node_bin, npm_bin } = tool_paths[node_version];
|
|
54
|
-
const builder_bin = path.resolve(bin_path, builder_script);
|
|
55
|
-
|
|
56
|
-
if (!jetpack.exists(node_bin)) {
|
|
57
|
-
throw new Error(`Node binary not found: ${node_bin}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// console.debug(`[${task.task_name}] Installing builder script "${builder_package}" for node ${node_version}`);
|
|
61
|
-
debug(`Installing builder script "${builder_package}" for node ${node_version}`);
|
|
62
|
-
// TODO: This may need to be replaced with the something like the spawn-stream stuff in build-agent? OR what to do with the stdout/stderr and errorhandling?
|
|
63
|
-
await runCommand(
|
|
64
|
-
node_bin,
|
|
65
|
-
[npm_bin, '--global', 'install', builder_package],
|
|
66
|
-
{ cwd: project_path },
|
|
67
|
-
{ task_name: task.task_name }
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (!jetpack.exists(builder_bin)) {
|
|
71
|
-
console.error(`[${task.task_name}] ${builder_bin} not found`);
|
|
72
|
-
throw new Error(`Builder script not found for task ${task.task_name}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const builder_args: string[] = [];
|
|
76
|
-
// --src ./ --out ./dist/echo --task echo --appId 'appid' --env 'testing' --backendId 'backendid' --backendUrl 'http://run.journeyapps.test'
|
|
77
|
-
builder_args.push(builder_bin);
|
|
78
|
-
builder_args.push('--src', project_path);
|
|
79
|
-
builder_args.push(`--out`, path.join(dest, task.task_name));
|
|
80
|
-
builder_args.push(`--zip`, path.join(dest, `${task.task_name}.zip`));
|
|
81
|
-
builder_args.push(`--task`, `${task.task_name}`);
|
|
82
|
-
builder_args.push(`--appId`, config.app_id);
|
|
83
|
-
builder_args.push(`--env`, config.env);
|
|
84
|
-
builder_args.push(`--backendId`, config.backend_id);
|
|
85
|
-
builder_args.push(`--backendUrl`, config.backend_url);
|
|
86
|
-
// builder_args.push(`--runInstallScripts`, 'true'); // TODO: handle this from feature flags somehow
|
|
87
|
-
|
|
88
|
-
// console.debug(`[${task.task_name}] Trying: ${node_bin} ${builder_args.join(' ')}`);
|
|
89
|
-
debug(`Trying: ${node_bin} ${builder_args.join(' ')}`);
|
|
90
|
-
|
|
91
|
-
await runCommand(node_bin, builder_args, { cwd: project_path }, { task_name: task.task_name });
|
|
92
|
-
console.debug('----');
|
|
8
|
+
for (const t of tasks) {
|
|
9
|
+
// await t.drink()
|
|
93
10
|
}
|
|
94
11
|
}
|
package/src/cli.ts
CHANGED
|
@@ -2,36 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
import * as yargs from 'yargs';
|
|
4
4
|
|
|
5
|
-
import { buildTasks } from './';
|
|
5
|
+
import { ProcessError, buildTasks, prepareBuildEnvForTasks } from './';
|
|
6
6
|
|
|
7
7
|
const DEFAULT_SRC_PATH = './';
|
|
8
|
-
const DEFAULT_OUT_PATH = '
|
|
8
|
+
const DEFAULT_OUT_PATH = '';
|
|
9
9
|
|
|
10
10
|
yargs
|
|
11
11
|
.command(
|
|
12
12
|
['build', '$0'], // $0 means this is the default command
|
|
13
13
|
'build tasks',
|
|
14
14
|
(yargs) => {
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.option('out', { default: DEFAULT_OUT_PATH })
|
|
20
|
-
// TODO: investigate yargs.config() for reading task config blob from a file. But how to ensure required args?
|
|
21
|
-
.option('appId', { string: true, demandOption: true, describe: "CloudCode's reference id" })
|
|
22
|
-
.option('env', { string: true, demandOption: true })
|
|
23
|
-
.option('backendId', { string: true, demandOption: true })
|
|
24
|
-
.option('backendUrl', { string: true, demandOption: true })
|
|
25
|
-
);
|
|
15
|
+
return yargs
|
|
16
|
+
.option('only', { string: true })
|
|
17
|
+
.option('path', { default: DEFAULT_SRC_PATH })
|
|
18
|
+
.option('out', { default: DEFAULT_OUT_PATH });
|
|
26
19
|
},
|
|
27
20
|
handled(async (argv) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
// iterate over task directories and run: 'yarn cloudcode-build'.
|
|
22
|
+
// optionally filtered by `argv.only` to build a single task.
|
|
23
|
+
|
|
24
|
+
await buildTasks(argv.path, argv.out, argv.only);
|
|
25
|
+
})
|
|
26
|
+
)
|
|
27
|
+
.command(
|
|
28
|
+
'prepare-env',
|
|
29
|
+
'Install node environments and packages for each task',
|
|
30
|
+
(yargs) => {
|
|
31
|
+
return yargs.option('only', { string: true }).option('path', { default: DEFAULT_SRC_PATH });
|
|
32
|
+
},
|
|
33
|
+
handled(async (argv) => {
|
|
34
|
+
// iterate over task directories:
|
|
35
|
+
// ensure required node version for task CC version is installed - What if too old - error?
|
|
36
|
+
// run yarn install
|
|
37
|
+
|
|
38
|
+
await prepareBuildEnvForTasks(argv.path, argv.only);
|
|
35
39
|
})
|
|
36
40
|
)
|
|
37
41
|
.option('verbose', {
|
|
@@ -44,8 +48,13 @@ function handled<T>(fn: (argv: T) => Promise<void>): (argv: T) => Promise<void>
|
|
|
44
48
|
try {
|
|
45
49
|
await fn(argv);
|
|
46
50
|
} catch (err) {
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
if (err instanceof ProcessError) {
|
|
52
|
+
console.error(err.message);
|
|
53
|
+
process.exit(err.status);
|
|
54
|
+
} else {
|
|
55
|
+
console.error(err.stack);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
};
|
|
51
60
|
}
|
package/src/detect_tasks.ts
CHANGED
|
@@ -40,7 +40,6 @@ export async function detectTasks(project_path: string, only?: string) {
|
|
|
40
40
|
|
|
41
41
|
const task_name = task_package.name; // CAVEAT: the pkg name _must_ match the dir.
|
|
42
42
|
const task_version = task_package?.cloudcode?.runtime;
|
|
43
|
-
// FIXME: Do we want to filter out disabled tasks from the build process?
|
|
44
43
|
|
|
45
44
|
console.debug(`Detected task version ${task_version}`);
|
|
46
45
|
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SpawnSyncReturns } from 'child_process';
|
|
2
|
+
|
|
3
|
+
export class ProcessError extends Error {
|
|
4
|
+
cause?: Error;
|
|
5
|
+
status: number;
|
|
6
|
+
|
|
7
|
+
constructor(public result: SpawnSyncReturns<string>) {
|
|
8
|
+
super(constructMessage(result));
|
|
9
|
+
|
|
10
|
+
if (result.error) {
|
|
11
|
+
this.cause = result.error;
|
|
12
|
+
}
|
|
13
|
+
this.status = result.status || 1;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function constructMessage(result: SpawnSyncReturns<string>): string {
|
|
18
|
+
if (result.error) {
|
|
19
|
+
return result.error.message;
|
|
20
|
+
}
|
|
21
|
+
return `${result.stdout}${result.stderr}`.trim();
|
|
22
|
+
}
|
package/src/index.ts
CHANGED
package/src/installer.ts
CHANGED
|
@@ -1,113 +1,82 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
1
|
import * as fs from 'node:fs';
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export async function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
bin_path: path.resolve(destination, 'bin'),
|
|
32
|
-
node_bin: path.resolve(destination, 'bin', 'node'),
|
|
33
|
-
npm_bin: path.resolve(destination, 'bin', 'npm')
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function downloadAndExtractTarball(url: string, dest: string) {
|
|
38
|
-
const tmpdir = os.tmpdir();
|
|
39
|
-
|
|
40
|
-
const filename = path.basename(URL.parse(url).pathname as string);
|
|
41
|
-
const base = path.basename(filename, '.tar.gz');
|
|
42
|
-
const download = path.join(tmpdir, filename);
|
|
43
|
-
|
|
44
|
-
if (fs.existsSync(download)) {
|
|
45
|
-
console.debug(`deleting old ${download}`);
|
|
46
|
-
fs.unlinkSync(download);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
await new Promise(async (resolve, reject) => {
|
|
50
|
-
console.log(`fetching ${url} into ${download}`);
|
|
51
|
-
|
|
52
|
-
const response = await fetch(url);
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
const errorBody = await response.statusText;
|
|
55
|
-
throw new Error(`Failed to download: ${errorBody}`);
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as jetpack from 'fs-jetpack';
|
|
4
|
+
import * as semver from 'semver';
|
|
5
|
+
import * as utils from './utils';
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
import { detectTasks } from './detect_tasks';
|
|
8
|
+
|
|
9
|
+
import * as _ from 'lodash';
|
|
10
|
+
|
|
11
|
+
export async function prepareBuildEnvForTasks(project_path: string, only?: string) {
|
|
12
|
+
const tasks = await detectTasks(project_path, only);
|
|
13
|
+
|
|
14
|
+
const required_node_versions = _.compact(tasks.map((t) => t.required_node_version));
|
|
15
|
+
const install_node_versions = _.uniq(required_node_versions);
|
|
16
|
+
|
|
17
|
+
const tool_paths: Record<string, { bin_path: string; node_bin: string; npm_bin: string }> = {};
|
|
18
|
+
for (const required_node_version of install_node_versions) {
|
|
19
|
+
// FIXME: maybe a path prefix.
|
|
20
|
+
const custom_node_path = path.resolve(`node-${semver.major(required_node_version)}`);
|
|
21
|
+
|
|
22
|
+
if (!jetpack.exists(custom_node_path)) {
|
|
23
|
+
console.log(`installing to ${custom_node_path}`);
|
|
24
|
+
await jetpack.dirAsync(custom_node_path);
|
|
25
|
+
await utils.downloadAndInstallNode(required_node_version, custom_node_path);
|
|
26
|
+
} else {
|
|
27
|
+
console.log(`already installed in ${custom_node_path}`);
|
|
56
28
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*/
|
|
105
|
-
// fs.unlinkSync(file.path);
|
|
106
|
-
|
|
107
|
-
resolve(null);
|
|
108
|
-
} catch (err) {
|
|
109
|
-
reject(err);
|
|
29
|
+
tool_paths[required_node_version] = utils.nodePaths(custom_node_path);
|
|
30
|
+
}
|
|
31
|
+
console.log('----');
|
|
32
|
+
|
|
33
|
+
for (const task of tasks) {
|
|
34
|
+
const node_version = task.required_node_version ?? process.version;
|
|
35
|
+
const builder_package = task.builder_package;
|
|
36
|
+
const builder_script = task.builder_script;
|
|
37
|
+
console.log(`[${task.task_name}] Installing builder script "${builder_package}" for node ${node_version}`);
|
|
38
|
+
const { bin_path, node_bin, npm_bin } = tool_paths[node_version];
|
|
39
|
+
const builder_bin = path.resolve(bin_path, builder_script);
|
|
40
|
+
|
|
41
|
+
// TODO: This may need to be replaced with the something like the spawn-stream stuff in build-agent? OR what to do with the stdout/stderr and errorhandling?
|
|
42
|
+
const spawnResult = spawnSync(node_bin, [npm_bin, '--global', 'install', builder_package], { cwd: project_path });
|
|
43
|
+
if (spawnResult.status == 0 && jetpack.exists(builder_bin)) {
|
|
44
|
+
// DEV HACK: this needs to move to the build thingie.
|
|
45
|
+
|
|
46
|
+
const builder_args: string[] = [];
|
|
47
|
+
// --src ~/src/cool-app-bro-master --out ~/src/dist --task echo --appId 'appid' --env 'testing' --backendId 'backendid' --backendUrl 'http://run.journeyapps.test'
|
|
48
|
+
builder_args.push(builder_bin);
|
|
49
|
+
// // builder_args.push('--version')
|
|
50
|
+
builder_args.push('--src', './');
|
|
51
|
+
builder_args.push(`--out`, `./dist/${task.task_name}`);
|
|
52
|
+
builder_args.push(`--task`, `${task.task_name}`);
|
|
53
|
+
builder_args.push(
|
|
54
|
+
`--appId`,
|
|
55
|
+
'appid',
|
|
56
|
+
`--env`,
|
|
57
|
+
'testing',
|
|
58
|
+
`--backendId`,
|
|
59
|
+
'backendid',
|
|
60
|
+
`--backendUrl`,
|
|
61
|
+
'http://run.journeyapps.test'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
console.log(`[${task.task_name}] Trying: ${node_bin} ${builder_args.join(' ')}`);
|
|
65
|
+
|
|
66
|
+
const builderSpawnResult = spawnSync(node_bin, builder_args, { cwd: project_path });
|
|
67
|
+
|
|
68
|
+
if (builderSpawnResult.status !== 0) {
|
|
69
|
+
console.log(`[${task.task_name}] FAILED TO RUN BUILDER SCRIPT`);
|
|
70
|
+
console.log(`[${task.task_name}] OUTPUT: ${builderSpawnResult.stdout}`);
|
|
71
|
+
console.log(`[${task.task_name}] ERROR: ${builderSpawnResult.stderr}`);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(`[${task.task_name}] SCRIPT SUCCESS?`);
|
|
74
|
+
console.log(`[${task.task_name}] OUTPUT: ${builderSpawnResult.stdout}`);
|
|
75
|
+
console.log(`[${task.task_name}] ERROR: ${builderSpawnResult.stderr}`);
|
|
110
76
|
}
|
|
111
|
-
|
|
112
|
-
|
|
77
|
+
console.log('----');
|
|
78
|
+
} else {
|
|
79
|
+
console.log(`[${task.task_name}] failed to install ${builder_package}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
113
82
|
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as URL from 'node:url';
|
|
6
|
+
import * as tar from 'tar';
|
|
7
|
+
|
|
8
|
+
/* Basically this, but for different dirs.
|
|
9
|
+
curl https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${PLATFORM}64.tar.gz -L | tar -xzC /node-dedicated && \
|
|
10
|
+
mv /node-dedicated/node-v${NODE_VERSION}-linux-${PLATFORM}64/bin/node /node-dedicated/node
|
|
11
|
+
*/
|
|
12
|
+
// TODO: feature to find the latest minor and patch for a given major version?
|
|
13
|
+
export async function downloadAndInstallNode(node_version: string, destination: string) {
|
|
14
|
+
// eg. https://nodejs.org/dist/v18.14.2/node-v18.14.2-linux-x64.tar.xz
|
|
15
|
+
|
|
16
|
+
// const PLATFORM = 'x';
|
|
17
|
+
// const node_download_url = `https://nodejs.org/dist/v${node_version}/node-v${node_version}-linux-${PLATFORM}64.tar.gz`;
|
|
18
|
+
|
|
19
|
+
// TODO: this is just for dev for now. Find a better fix.
|
|
20
|
+
const ARCH = os.arch();
|
|
21
|
+
const PLATFORM = os.platform();
|
|
22
|
+
const node_download_url = `https://nodejs.org/dist/v${node_version}/node-v${node_version}-${PLATFORM}-${ARCH}.tar.gz`;
|
|
23
|
+
|
|
24
|
+
await downloadAndExtractTarball(node_download_url, destination);
|
|
25
|
+
// TODO: move binaries into local bin path?
|
|
26
|
+
return nodePaths(destination);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function nodePaths(destination: string) {
|
|
30
|
+
return {
|
|
31
|
+
bin_path: path.resolve(destination, 'bin'),
|
|
32
|
+
node_bin: path.resolve(destination, 'bin', 'node'),
|
|
33
|
+
npm_bin: path.resolve(destination, 'bin', 'npm')
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function downloadAndExtractTarball(url: string, dest: string) {
|
|
38
|
+
const tmpdir = os.tmpdir();
|
|
39
|
+
|
|
40
|
+
const filename = path.basename(URL.parse(url).pathname as string);
|
|
41
|
+
const base = path.basename(filename, '.tar.gz');
|
|
42
|
+
const download = path.join(tmpdir, filename);
|
|
43
|
+
|
|
44
|
+
console.log(`fetching ${url}`);
|
|
45
|
+
|
|
46
|
+
const file = fs.createWriteStream(download);
|
|
47
|
+
const response = await fetch(url);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const errorBody = await response.statusText;
|
|
50
|
+
throw new Error(`Failed to download: ${errorBody}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// FIXME: replace with webstreams?
|
|
54
|
+
response.body.pipe(file);
|
|
55
|
+
|
|
56
|
+
file.on('finish', async () => {
|
|
57
|
+
console.log('Extracting...', download);
|
|
58
|
+
|
|
59
|
+
const comp = fs.createReadStream(download);
|
|
60
|
+
|
|
61
|
+
tar.extract({
|
|
62
|
+
cwd: tmpdir,
|
|
63
|
+
file: comp.path as string,
|
|
64
|
+
sync: true
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const uncomp = path.join(tmpdir, base);
|
|
68
|
+
|
|
69
|
+
const dist = path.resolve(dest);
|
|
70
|
+
// FIXME: dangerous. Add protection or replace.
|
|
71
|
+
// if (fs.existsSync(dist)) {
|
|
72
|
+
// fs.rmSync(dist, { recursive: true });
|
|
73
|
+
// }
|
|
74
|
+
if (fs.existsSync(uncomp)) {
|
|
75
|
+
console.log(`Moving extracted files from ${uncomp} to ${dist}`);
|
|
76
|
+
fs.renameSync(uncomp, dist);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// FIXME: this seems to sometimes cause errors
|
|
80
|
+
// fs.unlinkSync(file.path);
|
|
81
|
+
});
|
|
82
|
+
}
|