@the-open-engine-company/zeroshot 10.8.0 → 10.9.0
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/lib/install.js +38 -14
- package/lib/release-artifacts.js +49 -24
- package/package.json +1 -1
- package/targets.json +10 -5
package/lib/install.js
CHANGED
|
@@ -7,7 +7,7 @@ const { URL } = require('url');
|
|
|
7
7
|
const { installSkills } = require('./skills');
|
|
8
8
|
const {
|
|
9
9
|
archiveName,
|
|
10
|
-
|
|
10
|
+
extractExecutables,
|
|
11
11
|
parseChecksumManifest,
|
|
12
12
|
targetForHost,
|
|
13
13
|
verifyArchive,
|
|
@@ -25,8 +25,40 @@ const HOST_TARGETS = Object.freeze(
|
|
|
25
25
|
);
|
|
26
26
|
|
|
27
27
|
function selectTarget(platform = process.platform, arch = process.arch) {
|
|
28
|
-
const { target, executable } = targetForHost(TARGETS, platform, arch);
|
|
29
|
-
return { target, executable };
|
|
28
|
+
const { target, executable, resticExecutable } = targetForHost(TARGETS, platform, arch);
|
|
29
|
+
return { target, executable, resticExecutable };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function installExecutables(nativeDirectory, entries) {
|
|
33
|
+
fs.mkdirSync(nativeDirectory, { recursive: true });
|
|
34
|
+
const stage = fs.mkdtempSync(path.join(nativeDirectory, '.install-'));
|
|
35
|
+
const replaced = [];
|
|
36
|
+
const backups = [];
|
|
37
|
+
try {
|
|
38
|
+
for (const [name, contents] of entries) {
|
|
39
|
+
fs.writeFileSync(path.join(stage, name), contents, { mode: 0o755, flag: 'wx' });
|
|
40
|
+
}
|
|
41
|
+
for (const [name] of entries) {
|
|
42
|
+
const destination = path.join(nativeDirectory, name);
|
|
43
|
+
if (fs.existsSync(destination)) {
|
|
44
|
+
const backup = path.join(stage, `${name}.previous`);
|
|
45
|
+
fs.renameSync(destination, backup);
|
|
46
|
+
backups.push([destination, backup]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const [name] of entries) {
|
|
50
|
+
const destination = path.join(nativeDirectory, name);
|
|
51
|
+
fs.renameSync(path.join(stage, name), destination);
|
|
52
|
+
replaced.push(destination);
|
|
53
|
+
if (process.platform !== 'win32') fs.chmodSync(destination, 0o755);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
for (const destination of replaced.reverse()) fs.rmSync(destination, { force: true });
|
|
57
|
+
for (const [destination, backup] of backups.reverse()) fs.renameSync(backup, destination);
|
|
58
|
+
throw error;
|
|
59
|
+
} finally {
|
|
60
|
+
fs.rmSync(stage, { recursive: true, force: true });
|
|
61
|
+
}
|
|
30
62
|
}
|
|
31
63
|
|
|
32
64
|
function download(url, maximumBytes, redirects = 0) {
|
|
@@ -108,19 +140,11 @@ async function install(options = {}) {
|
|
|
108
140
|
const manifest = await fetchBuffer(`${baseUrl}/SHA256SUMS`, MAX_MANIFEST_BYTES);
|
|
109
141
|
const archive = await fetchBuffer(`${baseUrl}/${filename}`, MAX_ARCHIVE_BYTES);
|
|
110
142
|
verifyArchive(filename, archive, manifest.toString('utf8'));
|
|
111
|
-
const
|
|
143
|
+
const executables = extractExecutables(archive, [selected.executable, selected.resticExecutable]);
|
|
112
144
|
|
|
113
145
|
const nativeDirectory = path.join(packageRoot, 'bin', 'native');
|
|
114
146
|
const destination = path.join(nativeDirectory, selected.executable);
|
|
115
|
-
|
|
116
|
-
fs.mkdirSync(nativeDirectory, { recursive: true });
|
|
117
|
-
try {
|
|
118
|
-
fs.writeFileSync(temporary, executable, { mode: 0o755, flag: 'wx' });
|
|
119
|
-
fs.renameSync(temporary, destination);
|
|
120
|
-
if (process.platform !== 'win32') fs.chmodSync(destination, 0o755);
|
|
121
|
-
} finally {
|
|
122
|
-
fs.rmSync(temporary, { force: true });
|
|
123
|
-
}
|
|
147
|
+
installExecutables(nativeDirectory, executables);
|
|
124
148
|
const skillResults = installSkills({
|
|
125
149
|
packageRoot,
|
|
126
150
|
homeDirectory: options.homeDirectory,
|
|
@@ -136,7 +160,7 @@ module.exports = {
|
|
|
136
160
|
RELEASE_BASE_URL,
|
|
137
161
|
RELEASE_TAG_PREFIX,
|
|
138
162
|
archiveName,
|
|
139
|
-
|
|
163
|
+
extractExecutables,
|
|
140
164
|
install,
|
|
141
165
|
parseChecksumManifest,
|
|
142
166
|
selectTarget,
|
package/lib/release-artifacts.js
CHANGED
|
@@ -58,43 +58,68 @@ function parseTarSize(header) {
|
|
|
58
58
|
return Number.parseInt(value, 8);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function
|
|
62
|
-
let tar;
|
|
61
|
+
function decompressArchive(archive) {
|
|
63
62
|
try {
|
|
64
|
-
|
|
63
|
+
return zlib.gunzipSync(archive);
|
|
65
64
|
} catch (error) {
|
|
66
65
|
throw new Error(`ARCHIVE_INVALID: cannot decompress release archive: ${error.message}`);
|
|
67
66
|
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function readTarEntry(tar, offset, expected, executables) {
|
|
70
|
+
const header = tar.subarray(offset, offset + 512);
|
|
71
|
+
if (header.every((byte) => byte === 0)) {
|
|
72
|
+
if (!tar.subarray(offset).every((byte) => byte === 0)) {
|
|
73
|
+
throw new Error('ARCHIVE_INVALID: unexpected data after tar terminator');
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const name = header.subarray(0, 100).toString('utf8').replace(/\0.*$/, '');
|
|
78
|
+
const size = parseTarSize(header.subarray(124, 136));
|
|
79
|
+
const start = offset + 512;
|
|
80
|
+
const end = start + size;
|
|
81
|
+
if (end > tar.length) throw new Error('ARCHIVE_INVALID: truncated tar entry');
|
|
82
|
+
if (!expected.has(name) || ![0, '0'.charCodeAt(0)].includes(header[156])) {
|
|
83
|
+
throw new Error(`ARCHIVE_INVALID: unexpected archive entry ${name}`);
|
|
84
|
+
}
|
|
85
|
+
if (executables.has(name)) throw new Error(`ARCHIVE_INVALID: duplicate ${name}`);
|
|
86
|
+
executables.set(name, Buffer.from(tar.subarray(start, end)));
|
|
87
|
+
return start + Math.ceil(size / 512) * 512;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function requireExpectedExecutables(expected, executables) {
|
|
91
|
+
for (const name of expected) {
|
|
92
|
+
if (!executables.has(name)) {
|
|
93
|
+
throw new Error(`ARCHIVE_INVALID: archive does not contain ${name}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function extractExecutables(archive, expectedNames) {
|
|
99
|
+
const expected = new Set(expectedNames);
|
|
100
|
+
if (expected.size !== expectedNames.length || expected.size === 0) {
|
|
101
|
+
throw new Error('ARCHIVE_INVALID: expected executable names must be unique');
|
|
102
|
+
}
|
|
103
|
+
const tar = decompressArchive(archive);
|
|
68
104
|
let offset = 0;
|
|
69
|
-
let
|
|
105
|
+
let terminated = false;
|
|
106
|
+
const executables = new Map();
|
|
70
107
|
while (offset + 512 <= tar.length) {
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
throw new Error('ARCHIVE_INVALID: unexpected data after tar terminator');
|
|
75
|
-
}
|
|
108
|
+
const next = readTarEntry(tar, offset, expected, executables);
|
|
109
|
+
if (next === null) {
|
|
110
|
+
terminated = true;
|
|
76
111
|
break;
|
|
77
112
|
}
|
|
78
|
-
|
|
79
|
-
const size = parseTarSize(header.subarray(124, 136));
|
|
80
|
-
const start = offset + 512;
|
|
81
|
-
const end = start + size;
|
|
82
|
-
if (end > tar.length) throw new Error('ARCHIVE_INVALID: truncated tar entry');
|
|
83
|
-
if (name === expectedName) {
|
|
84
|
-
if (executable) throw new Error(`ARCHIVE_INVALID: duplicate ${expectedName}`);
|
|
85
|
-
executable = Buffer.from(tar.subarray(start, end));
|
|
86
|
-
} else {
|
|
87
|
-
throw new Error(`ARCHIVE_INVALID: unexpected archive entry ${name}`);
|
|
88
|
-
}
|
|
89
|
-
offset = start + Math.ceil(size / 512) * 512;
|
|
113
|
+
offset = next;
|
|
90
114
|
}
|
|
91
|
-
if (!
|
|
92
|
-
|
|
115
|
+
if (!terminated) throw new Error('ARCHIVE_INVALID: archive has no tar terminator');
|
|
116
|
+
requireExpectedExecutables(expected, executables);
|
|
117
|
+
return executables;
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
module.exports = {
|
|
96
121
|
archiveName,
|
|
97
|
-
|
|
122
|
+
extractExecutables,
|
|
98
123
|
parseChecksumManifest,
|
|
99
124
|
sha256,
|
|
100
125
|
targetForHost,
|
package/package.json
CHANGED
package/targets.json
CHANGED
|
@@ -3,30 +3,35 @@
|
|
|
3
3
|
"platform": "linux",
|
|
4
4
|
"arch": "x64",
|
|
5
5
|
"target": "x86_64-unknown-linux-musl",
|
|
6
|
-
"executable": "zeroshot"
|
|
6
|
+
"executable": "zeroshot",
|
|
7
|
+
"resticExecutable": "restic"
|
|
7
8
|
},
|
|
8
9
|
{
|
|
9
10
|
"platform": "linux",
|
|
10
11
|
"arch": "arm64",
|
|
11
12
|
"target": "aarch64-unknown-linux-musl",
|
|
12
|
-
"executable": "zeroshot"
|
|
13
|
+
"executable": "zeroshot",
|
|
14
|
+
"resticExecutable": "restic"
|
|
13
15
|
},
|
|
14
16
|
{
|
|
15
17
|
"platform": "darwin",
|
|
16
18
|
"arch": "x64",
|
|
17
19
|
"target": "x86_64-apple-darwin",
|
|
18
|
-
"executable": "zeroshot"
|
|
20
|
+
"executable": "zeroshot",
|
|
21
|
+
"resticExecutable": "restic"
|
|
19
22
|
},
|
|
20
23
|
{
|
|
21
24
|
"platform": "darwin",
|
|
22
25
|
"arch": "arm64",
|
|
23
26
|
"target": "aarch64-apple-darwin",
|
|
24
|
-
"executable": "zeroshot"
|
|
27
|
+
"executable": "zeroshot",
|
|
28
|
+
"resticExecutable": "restic"
|
|
25
29
|
},
|
|
26
30
|
{
|
|
27
31
|
"platform": "win32",
|
|
28
32
|
"arch": "x64",
|
|
29
33
|
"target": "x86_64-pc-windows-msvc",
|
|
30
|
-
"executable": "zeroshot.exe"
|
|
34
|
+
"executable": "zeroshot.exe",
|
|
35
|
+
"resticExecutable": "restic.exe"
|
|
31
36
|
}
|
|
32
37
|
]
|