docdex 0.1.11 → 0.2.2
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/CHANGELOG.md +1 -1
- package/README.md +103 -65
- package/bin/docdex.js +145 -5
- package/lib/daemon_version.js +80 -0
- package/lib/install.js +1940 -28
- package/lib/installer_logging.js +134 -0
- package/lib/platform.js +275 -20
- package/lib/platform_matrix.js +127 -0
- package/lib/postinstall_setup.js +885 -0
- package/lib/release_manifest.js +226 -0
- package/lib/release_signing.js +93 -0
- package/package.json +4 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_COMPONENT = "docdex-installer";
|
|
5
|
+
|
|
6
|
+
function parseBooleanEnv(value, defaultValue) {
|
|
7
|
+
if (value == null) return defaultValue;
|
|
8
|
+
const normalized = String(value).trim().toLowerCase();
|
|
9
|
+
if (!normalized) return defaultValue;
|
|
10
|
+
if (["1", "true", "yes", "y", "on"].includes(normalized)) return true;
|
|
11
|
+
if (["0", "false", "no", "n", "off"].includes(normalized)) return false;
|
|
12
|
+
return defaultValue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function redactAuthHeader(value) {
|
|
16
|
+
if (typeof value !== "string") return value;
|
|
17
|
+
return value.replace(/\b(Bearer)\s+([^\s]+)/gi, "$1 [REDACTED]");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function shouldRedactKey(key) {
|
|
21
|
+
return /token|authorization|password|secret|signature|sig|credential|private[_-]?key|api[_-]?key/i.test(String(key));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function shouldRedactUrlParam(key) {
|
|
25
|
+
const k = String(key).toLowerCase();
|
|
26
|
+
if (k.startsWith("x-amz-")) return true;
|
|
27
|
+
return /token|access_token|auth|signature|sig|key|credential|session/i.test(k);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function redactUrl(url) {
|
|
31
|
+
if (typeof url !== "string") return url;
|
|
32
|
+
const value = url.trim();
|
|
33
|
+
if (!value) return value;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const parsed = new URL(value);
|
|
37
|
+
parsed.username = "";
|
|
38
|
+
parsed.password = "";
|
|
39
|
+
for (const [k] of parsed.searchParams.entries()) {
|
|
40
|
+
if (shouldRedactUrlParam(k)) parsed.searchParams.set(k, "REDACTED");
|
|
41
|
+
}
|
|
42
|
+
return parsed.toString();
|
|
43
|
+
} catch {
|
|
44
|
+
// Best-effort redaction for non-URL strings.
|
|
45
|
+
return value
|
|
46
|
+
.replace(/\/\/([^/@:\s]+):([^/@\s]+)@/g, "//REDACTED:REDACTED@")
|
|
47
|
+
.replace(/([?&])(token|access_token|auth|signature|sig|key|credential)=([^&]+)/gi, "$1$2=REDACTED");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function redactValue(value, depth = 0) {
|
|
52
|
+
if (depth > 6) return "[REDACTED_DEPTH]";
|
|
53
|
+
if (value == null) return value;
|
|
54
|
+
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
const withRedactedUrls = value.replace(/https?:\/\/[^\s"'()<>]+/gi, (match) => redactUrl(match));
|
|
57
|
+
const maybeUrl = /^https?:\/\//i.test(withRedactedUrls) ? redactUrl(withRedactedUrls) : withRedactedUrls;
|
|
58
|
+
return redactAuthHeader(maybeUrl);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (Array.isArray(value)) return value.map((v) => redactValue(v, depth + 1));
|
|
62
|
+
|
|
63
|
+
if (typeof value === "object") {
|
|
64
|
+
const out = {};
|
|
65
|
+
for (const [k, v] of Object.entries(value)) {
|
|
66
|
+
if (shouldRedactKey(k)) {
|
|
67
|
+
out[k] = "[REDACTED]";
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (/url$/i.test(k) || /_url$/i.test(k) || /url_/i.test(k) || /Url$/.test(k)) {
|
|
71
|
+
out[k] = redactUrl(typeof v === "string" ? v : String(v ?? ""));
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
out[k] = redactValue(v, depth + 1);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function safeJsonStringify(value) {
|
|
83
|
+
const seen = new WeakSet();
|
|
84
|
+
return JSON.stringify(value, (key, val) => {
|
|
85
|
+
if (val && typeof val === "object") {
|
|
86
|
+
if (seen.has(val)) return "[Circular]";
|
|
87
|
+
seen.add(val);
|
|
88
|
+
}
|
|
89
|
+
return val;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createInstallerStructuredLogger({
|
|
94
|
+
component = DEFAULT_COMPONENT,
|
|
95
|
+
baseFields = {},
|
|
96
|
+
enabled = parseBooleanEnv(process.env.DOCDEX_INSTALLER_STRUCTURED_LOG, false),
|
|
97
|
+
sink = process.stderr
|
|
98
|
+
} = {}) {
|
|
99
|
+
function emit({ level = "info", event, message, fields, error } = {}) {
|
|
100
|
+
if (!enabled) return;
|
|
101
|
+
const payload = {
|
|
102
|
+
ts: new Date().toISOString(),
|
|
103
|
+
level,
|
|
104
|
+
component,
|
|
105
|
+
event: typeof event === "string" ? event : "installer.event",
|
|
106
|
+
message: typeof message === "string" ? message : null,
|
|
107
|
+
...redactValue(baseFields),
|
|
108
|
+
...redactValue(fields || {})
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (error) {
|
|
112
|
+
payload.error = redactValue({
|
|
113
|
+
name: error?.name || null,
|
|
114
|
+
code: typeof error?.code === "string" ? error.code : null,
|
|
115
|
+
message: error?.message || String(error)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
sink.write(`${safeJsonStringify(payload)}\n`);
|
|
121
|
+
} catch {
|
|
122
|
+
// Best-effort: never fail install due to logging.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { emit, enabled };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
createInstallerStructuredLogger,
|
|
131
|
+
parseBooleanEnv,
|
|
132
|
+
redactUrl,
|
|
133
|
+
redactValue
|
|
134
|
+
};
|
package/lib/platform.js
CHANGED
|
@@ -1,48 +1,303 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
PLATFORM_ENTRY_BY_KEY,
|
|
7
|
+
PUBLISHED_PLATFORM_KEYS,
|
|
8
|
+
PUBLISHED_TARGET_TRIPLES,
|
|
9
|
+
assetNameForPlatformKey
|
|
10
|
+
} = require("./platform_matrix");
|
|
11
|
+
|
|
12
|
+
class UnsupportedPlatformError extends Error {
|
|
13
|
+
/**
|
|
14
|
+
* @param {{
|
|
15
|
+
* platform: string,
|
|
16
|
+
* arch: string,
|
|
17
|
+
* libc?: (null|"gnu"|"musl"),
|
|
18
|
+
* candidatePlatformKey?: (string|null),
|
|
19
|
+
* candidateTargetTriple?: (string|null),
|
|
20
|
+
* reason?: (string|null),
|
|
21
|
+
* supportedPlatformKeys: string[],
|
|
22
|
+
* supportedTargetTriples: string[]
|
|
23
|
+
* }} options
|
|
24
|
+
*/
|
|
25
|
+
constructor({
|
|
26
|
+
platform,
|
|
27
|
+
arch,
|
|
28
|
+
libc = null,
|
|
29
|
+
candidatePlatformKey = null,
|
|
30
|
+
candidateTargetTriple = null,
|
|
31
|
+
reason = null,
|
|
32
|
+
supportedPlatformKeys,
|
|
33
|
+
supportedTargetTriples
|
|
34
|
+
}) {
|
|
35
|
+
super(`Unsupported platform: ${platform}/${arch}`);
|
|
36
|
+
this.name = "UnsupportedPlatformError";
|
|
37
|
+
this.code = "DOCDEX_UNSUPPORTED_PLATFORM";
|
|
38
|
+
this.exitCode = 3;
|
|
39
|
+
this.details = {
|
|
40
|
+
targetTriple: null,
|
|
41
|
+
manifestVersion: null,
|
|
42
|
+
assetName: null,
|
|
43
|
+
platform,
|
|
44
|
+
arch,
|
|
45
|
+
libc,
|
|
46
|
+
candidatePlatformKey,
|
|
47
|
+
candidateTargetTriple,
|
|
48
|
+
reason,
|
|
49
|
+
supportedPlatformKeys: supportedPlatformKeys || [],
|
|
50
|
+
supportedTargetTriples: supportedTargetTriples || []
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const SUPPORTED_PLATFORM_KEYS = PUBLISHED_PLATFORM_KEYS;
|
|
56
|
+
const SUPPORTED_TARGET_TRIPLES = PUBLISHED_TARGET_TRIPLES;
|
|
57
|
+
|
|
58
|
+
function normalizeLibc(value) {
|
|
59
|
+
if (value == null) return null;
|
|
60
|
+
const libc = String(value).toLowerCase().trim();
|
|
61
|
+
if (!libc) return null;
|
|
62
|
+
if (libc === "glibc") return "gnu";
|
|
63
|
+
if (libc === "musl" || libc === "gnu") return libc;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readFileSliceSync(fsModule, filePath, offset, length) {
|
|
68
|
+
const fd = fsModule.openSync(filePath, "r");
|
|
69
|
+
try {
|
|
70
|
+
const buf = Buffer.alloc(length);
|
|
71
|
+
const bytesRead = fsModule.readSync(fd, buf, 0, length, offset);
|
|
72
|
+
return bytesRead === length ? buf : buf.subarray(0, bytesRead);
|
|
73
|
+
} finally {
|
|
74
|
+
fsModule.closeSync(fd);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function detectLibcFromElfInterpreter(execPath, fsModule) {
|
|
79
|
+
if (!execPath || typeof execPath !== "string") return null;
|
|
80
|
+
try {
|
|
81
|
+
const header = readFileSliceSync(fsModule, execPath, 0, 64);
|
|
82
|
+
if (header.length < 64) return null;
|
|
83
|
+
if (header[0] !== 0x7f || header[1] !== 0x45 || header[2] !== 0x4c || header[3] !== 0x46) return null;
|
|
84
|
+
|
|
85
|
+
const eiClass = header[4]; // 1=32-bit, 2=64-bit
|
|
86
|
+
const eiData = header[5]; // 1=little-endian
|
|
87
|
+
if (eiData !== 1) return null;
|
|
88
|
+
|
|
89
|
+
const PT_INTERP = 3;
|
|
90
|
+
|
|
91
|
+
let phoff;
|
|
92
|
+
let phentsize;
|
|
93
|
+
let phnum;
|
|
94
|
+
|
|
95
|
+
if (eiClass === 2) {
|
|
96
|
+
phoff = Number(header.readBigUInt64LE(32));
|
|
97
|
+
phentsize = header.readUInt16LE(54);
|
|
98
|
+
phnum = header.readUInt16LE(56);
|
|
99
|
+
} else if (eiClass === 1) {
|
|
100
|
+
phoff = header.readUInt32LE(28);
|
|
101
|
+
phentsize = header.readUInt16LE(42);
|
|
102
|
+
phnum = header.readUInt16LE(44);
|
|
103
|
+
} else {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!phoff || !phentsize || !phnum) return null;
|
|
108
|
+
if (phnum > 64) return null;
|
|
109
|
+
if (phentsize < 32 || phentsize > 128) return null;
|
|
110
|
+
|
|
111
|
+
const tableSize = phentsize * phnum;
|
|
112
|
+
const phTable = readFileSliceSync(fsModule, execPath, phoff, tableSize);
|
|
113
|
+
if (phTable.length < tableSize) return null;
|
|
114
|
+
|
|
115
|
+
for (let i = 0; i < phnum; i++) {
|
|
116
|
+
const base = i * phentsize;
|
|
117
|
+
const pType = phTable.readUInt32LE(base);
|
|
118
|
+
if (pType !== PT_INTERP) continue;
|
|
119
|
+
|
|
120
|
+
let pOffset;
|
|
121
|
+
let pFileSz;
|
|
122
|
+
|
|
123
|
+
if (eiClass === 2) {
|
|
124
|
+
pOffset = Number(phTable.readBigUInt64LE(base + 8));
|
|
125
|
+
pFileSz = Number(phTable.readBigUInt64LE(base + 32));
|
|
126
|
+
} else {
|
|
127
|
+
pOffset = phTable.readUInt32LE(base + 4);
|
|
128
|
+
pFileSz = phTable.readUInt32LE(base + 16);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!pOffset || !pFileSz || pFileSz > 4096) return null;
|
|
132
|
+
const interpBytes = readFileSliceSync(fsModule, execPath, pOffset, pFileSz);
|
|
133
|
+
const interp = interpBytes.toString("utf8").split("\0")[0];
|
|
134
|
+
if (!interp) return null;
|
|
135
|
+
if (interp.includes("ld-musl")) return "musl";
|
|
136
|
+
if (interp.includes("ld-linux")) return "gnu";
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
3
146
|
function detectLibc() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
147
|
+
return detectLibcFromRuntime();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function detectLibcFromRuntime(options) {
|
|
151
|
+
const env = options?.env ?? process.env;
|
|
152
|
+
const fsModule = options?.fs ?? fs;
|
|
153
|
+
|
|
154
|
+
const overrideRaw = env?.DOCDEX_LIBC;
|
|
155
|
+
if (overrideRaw != null && String(overrideRaw).trim() !== "") {
|
|
156
|
+
const override = normalizeLibc(overrideRaw);
|
|
157
|
+
if (!override) {
|
|
158
|
+
throw new Error(`Invalid DOCDEX_LIBC=${overrideRaw}; expected "gnu", "musl", or "glibc"`);
|
|
9
159
|
}
|
|
160
|
+
return override;
|
|
10
161
|
}
|
|
11
162
|
|
|
12
|
-
const report =
|
|
163
|
+
const report =
|
|
164
|
+
options?.report ??
|
|
165
|
+
(typeof process.report?.getReport === "function" ? process.report.getReport() : null);
|
|
166
|
+
|
|
13
167
|
const glibcVersion = report?.header?.glibcVersionRuntime;
|
|
14
|
-
|
|
168
|
+
if (typeof glibcVersion === "string" && glibcVersion.trim()) return "gnu";
|
|
169
|
+
|
|
170
|
+
// Some Node builds include a `musl` field in the report header. Treat it as a positive signal.
|
|
171
|
+
if (report?.header?.musl != null) return "musl";
|
|
172
|
+
|
|
173
|
+
// Alpine is musl-based by default; use it as a strong hint.
|
|
174
|
+
try {
|
|
175
|
+
if (typeof fsModule.existsSync === "function" && fsModule.existsSync("/etc/alpine-release")) return "musl";
|
|
176
|
+
} catch {}
|
|
177
|
+
|
|
178
|
+
const execPath = options?.execPath ?? process.execPath;
|
|
179
|
+
const fromElf = detectLibcFromElfInterpreter(execPath, fsModule);
|
|
180
|
+
if (fromElf) return fromElf;
|
|
181
|
+
|
|
182
|
+
// Deterministic fallback: musl binaries are typically more portable on glibc than the reverse.
|
|
183
|
+
return "musl";
|
|
15
184
|
}
|
|
16
185
|
|
|
17
|
-
function detectPlatformKey() {
|
|
18
|
-
const platform = process.platform;
|
|
19
|
-
const arch = process.arch;
|
|
186
|
+
function detectPlatformKey(options) {
|
|
187
|
+
const platform = options?.platform ?? process.platform;
|
|
188
|
+
const arch = options?.arch ?? process.arch;
|
|
189
|
+
let libc = null;
|
|
190
|
+
let candidatePlatformKey = null;
|
|
191
|
+
let candidateTargetTriple = null;
|
|
20
192
|
|
|
21
193
|
if (platform === "darwin") {
|
|
22
|
-
if (arch === "arm64")
|
|
23
|
-
if (arch === "x64")
|
|
194
|
+
if (arch === "arm64") candidatePlatformKey = "darwin-arm64";
|
|
195
|
+
if (arch === "x64") candidatePlatformKey = "darwin-x64";
|
|
24
196
|
}
|
|
25
197
|
|
|
26
198
|
if (platform === "linux") {
|
|
27
|
-
|
|
28
|
-
if (arch === "arm64")
|
|
29
|
-
if (arch === "x64")
|
|
199
|
+
libc = normalizeLibc(options?.libc) ?? detectLibcFromRuntime(options);
|
|
200
|
+
if (arch === "arm64") candidatePlatformKey = `linux-arm64-${libc}`;
|
|
201
|
+
if (arch === "x64") candidatePlatformKey = `linux-x64-${libc}`;
|
|
30
202
|
}
|
|
31
203
|
|
|
32
204
|
if (platform === "win32") {
|
|
33
|
-
if (arch === "x64")
|
|
34
|
-
if (arch === "arm64")
|
|
205
|
+
if (arch === "x64") candidatePlatformKey = "win32-x64";
|
|
206
|
+
if (arch === "arm64") candidatePlatformKey = "win32-arm64";
|
|
35
207
|
}
|
|
36
208
|
|
|
37
|
-
|
|
209
|
+
if (candidatePlatformKey) {
|
|
210
|
+
const entry = PLATFORM_ENTRY_BY_KEY[candidatePlatformKey];
|
|
211
|
+
candidateTargetTriple = entry?.targetTriple ?? null;
|
|
212
|
+
if (entry?.published) return candidatePlatformKey;
|
|
213
|
+
if (entry && entry.published === false) {
|
|
214
|
+
throw new UnsupportedPlatformError({
|
|
215
|
+
platform,
|
|
216
|
+
arch,
|
|
217
|
+
libc,
|
|
218
|
+
candidatePlatformKey,
|
|
219
|
+
candidateTargetTriple,
|
|
220
|
+
reason: "target_not_published",
|
|
221
|
+
supportedPlatformKeys: SUPPORTED_PLATFORM_KEYS,
|
|
222
|
+
supportedTargetTriples: SUPPORTED_TARGET_TRIPLES
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw new UnsupportedPlatformError({
|
|
228
|
+
platform,
|
|
229
|
+
arch,
|
|
230
|
+
libc,
|
|
231
|
+
candidatePlatformKey,
|
|
232
|
+
candidateTargetTriple,
|
|
233
|
+
reason: "unknown_or_unsupported_runtime",
|
|
234
|
+
supportedPlatformKeys: SUPPORTED_PLATFORM_KEYS,
|
|
235
|
+
supportedTargetTriples: SUPPORTED_TARGET_TRIPLES
|
|
236
|
+
});
|
|
38
237
|
}
|
|
39
238
|
|
|
40
239
|
function artifactName(platformKey) {
|
|
41
|
-
return
|
|
240
|
+
return assetNameForPlatformKey(platformKey);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Human-readable release asset naming pattern for `docdexd` archives.
|
|
245
|
+
* @param {string|null} [platformKey]
|
|
246
|
+
* @param {{includeExample?: boolean, exampleAssetName?: string}} [options]
|
|
247
|
+
*/
|
|
248
|
+
function assetPatternForPlatformKey(platformKey, options) {
|
|
249
|
+
const includeExample = options?.includeExample !== false;
|
|
250
|
+
const exampleAssetName =
|
|
251
|
+
typeof options?.exampleAssetName === "string" && options.exampleAssetName.trim()
|
|
252
|
+
? options.exampleAssetName.trim()
|
|
253
|
+
: null;
|
|
254
|
+
const base = "docdexd-<platformKey>.tar.gz";
|
|
255
|
+
if (!platformKey || typeof platformKey !== "string") return base;
|
|
256
|
+
if (!includeExample) return base;
|
|
257
|
+
return `${base} (e.g. ${exampleAssetName || assetNameForPlatformKey(platformKey)})`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function targetTripleForPlatformKey(platformKey) {
|
|
261
|
+
const triple = PLATFORM_ENTRY_BY_KEY[platformKey]?.targetTriple;
|
|
262
|
+
if (triple) return triple;
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Unsupported platform key: ${platformKey}. Supported: ${Object.keys(PLATFORM_ENTRY_BY_KEY).sort().join(", ")}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function detectTargetTriple(options) {
|
|
269
|
+
return targetTripleForPlatformKey(detectPlatformKey(options));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Resolve the full platform support policy (runtime → supported? → target triple + asset naming).
|
|
274
|
+
* Consumers should use this as the single source of truth for distinguishing unsupported platforms
|
|
275
|
+
* from missing release artifacts (supported-but-missing).
|
|
276
|
+
*
|
|
277
|
+
* @param {Parameters<typeof detectPlatformKey>[0]} [options]
|
|
278
|
+
*/
|
|
279
|
+
function resolvePlatformPolicy(options) {
|
|
280
|
+
const platform = options?.platform ?? process.platform;
|
|
281
|
+
const arch = options?.arch ?? process.arch;
|
|
282
|
+
const platformKey = detectPlatformKey(options);
|
|
283
|
+
const targetTriple = targetTripleForPlatformKey(platformKey);
|
|
284
|
+
return {
|
|
285
|
+
detected: { platform, arch },
|
|
286
|
+
platformKey,
|
|
287
|
+
targetTriple,
|
|
288
|
+
expectedAssetName: artifactName(platformKey),
|
|
289
|
+
expectedAssetPattern: assetPatternForPlatformKey(platformKey)
|
|
290
|
+
};
|
|
42
291
|
}
|
|
43
292
|
|
|
44
293
|
module.exports = {
|
|
45
294
|
detectLibc,
|
|
295
|
+
detectLibcFromRuntime,
|
|
46
296
|
detectPlatformKey,
|
|
47
|
-
|
|
297
|
+
UnsupportedPlatformError,
|
|
298
|
+
artifactName,
|
|
299
|
+
assetPatternForPlatformKey,
|
|
300
|
+
targetTripleForPlatformKey,
|
|
301
|
+
detectTargetTriple,
|
|
302
|
+
resolvePlatformPolicy
|
|
48
303
|
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single source of truth for:
|
|
5
|
+
* detected runtime (platform/arch/libc) -> platformKey -> Rust target triple -> release asset naming.
|
|
6
|
+
* Contract: docs/contracts/installer_platform_mapping_v1.md
|
|
7
|
+
*
|
|
8
|
+
* Notes:
|
|
9
|
+
* - `platformKey` is the suffix used in release assets: `docdexd-<platformKey>.tar.gz`.
|
|
10
|
+
* - Some mappings are defined but marked `published: false` when the release workflow does not
|
|
11
|
+
* currently publish a matching binary artifact; these MUST be treated as unsupported at install/runtime.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @type {{platform: string, arch: string, libc: (null|"gnu"|"musl"), platformKey: string, targetTriple: string, published: boolean}[]} */
|
|
15
|
+
const PLATFORM_MATRIX = Object.freeze([
|
|
16
|
+
{
|
|
17
|
+
platform: "darwin",
|
|
18
|
+
arch: "arm64",
|
|
19
|
+
libc: null,
|
|
20
|
+
platformKey: "darwin-arm64",
|
|
21
|
+
targetTriple: "aarch64-apple-darwin",
|
|
22
|
+
published: true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
platform: "darwin",
|
|
26
|
+
arch: "x64",
|
|
27
|
+
libc: null,
|
|
28
|
+
platformKey: "darwin-x64",
|
|
29
|
+
targetTriple: "x86_64-apple-darwin",
|
|
30
|
+
published: true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
platform: "linux",
|
|
34
|
+
arch: "x64",
|
|
35
|
+
libc: "gnu",
|
|
36
|
+
platformKey: "linux-x64-gnu",
|
|
37
|
+
targetTriple: "x86_64-unknown-linux-gnu",
|
|
38
|
+
published: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
platform: "linux",
|
|
42
|
+
arch: "x64",
|
|
43
|
+
libc: "musl",
|
|
44
|
+
platformKey: "linux-x64-musl",
|
|
45
|
+
targetTriple: "x86_64-unknown-linux-musl",
|
|
46
|
+
published: true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
platform: "linux",
|
|
50
|
+
arch: "arm64",
|
|
51
|
+
libc: "gnu",
|
|
52
|
+
platformKey: "linux-arm64-gnu",
|
|
53
|
+
targetTriple: "aarch64-unknown-linux-gnu",
|
|
54
|
+
published: true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
platform: "linux",
|
|
58
|
+
arch: "arm64",
|
|
59
|
+
libc: "musl",
|
|
60
|
+
platformKey: "linux-arm64-musl",
|
|
61
|
+
targetTriple: "aarch64-unknown-linux-musl",
|
|
62
|
+
published: false
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
platform: "win32",
|
|
66
|
+
arch: "x64",
|
|
67
|
+
libc: null,
|
|
68
|
+
platformKey: "win32-x64",
|
|
69
|
+
targetTriple: "x86_64-pc-windows-msvc",
|
|
70
|
+
published: true
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
platform: "win32",
|
|
74
|
+
arch: "arm64",
|
|
75
|
+
libc: null,
|
|
76
|
+
platformKey: "win32-arm64",
|
|
77
|
+
targetTriple: "aarch64-pc-windows-msvc",
|
|
78
|
+
published: false
|
|
79
|
+
}
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
const PLATFORM_ENTRY_BY_KEY = Object.freeze(
|
|
83
|
+
PLATFORM_MATRIX.reduce((acc, entry) => {
|
|
84
|
+
acc[entry.platformKey] = entry;
|
|
85
|
+
return acc;
|
|
86
|
+
}, {})
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const PUBLISHED_PLATFORM_KEYS = Object.freeze(
|
|
90
|
+
PLATFORM_MATRIX.filter((e) => e.published)
|
|
91
|
+
.map((e) => e.platformKey)
|
|
92
|
+
.slice()
|
|
93
|
+
.sort()
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const PUBLISHED_TARGET_TRIPLES = Object.freeze(
|
|
97
|
+
[...new Set(PLATFORM_MATRIX.filter((e) => e.published).map((e) => e.targetTriple))].sort()
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
function archiveBaseForPlatformKey(platformKey) {
|
|
101
|
+
return `docdexd-${platformKey}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function assetNameForPlatformKey(platformKey) {
|
|
105
|
+
return `${archiveBaseForPlatformKey(platformKey)}.tar.gz`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Default target list for release-manifest generation (published targets only).
|
|
110
|
+
* Shape matches scripts/generate_release_manifest.cjs `targets` input.
|
|
111
|
+
*/
|
|
112
|
+
const PUBLISHED_RELEASE_TARGETS = Object.freeze(
|
|
113
|
+
PLATFORM_MATRIX.filter((e) => e.published)
|
|
114
|
+
.map((e) => ({ targetTriple: e.targetTriple, archiveBase: archiveBaseForPlatformKey(e.platformKey) }))
|
|
115
|
+
.slice()
|
|
116
|
+
.sort((a, b) => a.targetTriple.localeCompare(b.targetTriple))
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
PLATFORM_MATRIX,
|
|
121
|
+
PLATFORM_ENTRY_BY_KEY,
|
|
122
|
+
PUBLISHED_PLATFORM_KEYS,
|
|
123
|
+
PUBLISHED_TARGET_TRIPLES,
|
|
124
|
+
PUBLISHED_RELEASE_TARGETS,
|
|
125
|
+
archiveBaseForPlatformKey,
|
|
126
|
+
assetNameForPlatformKey
|
|
127
|
+
};
|