@rudderhq/cli 0.7.5 → 0.7.7
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/index.js +416 -198
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3460,17 +3460,20 @@ var init_resource = __esm({
|
|
|
3460
3460
|
resourceId: z19.string().uuid(),
|
|
3461
3461
|
role: projectResourceAttachmentRoleSchema.optional(),
|
|
3462
3462
|
note: z19.string().optional().nullable(),
|
|
3463
|
-
sortOrder: z19.number().int().nonnegative().optional()
|
|
3463
|
+
sortOrder: z19.number().int().nonnegative().optional(),
|
|
3464
|
+
isPrimary: z19.boolean().optional()
|
|
3464
3465
|
}).strict();
|
|
3465
3466
|
updateProjectResourceAttachmentSchema = z19.object({
|
|
3466
3467
|
role: projectResourceAttachmentRoleSchema.optional(),
|
|
3467
3468
|
note: z19.string().optional().nullable(),
|
|
3468
|
-
sortOrder: z19.number().int().nonnegative().optional()
|
|
3469
|
+
sortOrder: z19.number().int().nonnegative().optional(),
|
|
3470
|
+
isPrimary: z19.boolean().optional()
|
|
3469
3471
|
}).strict();
|
|
3470
3472
|
createProjectInlineResourceSchema = createOrganizationResourceBaseSchema.extend({
|
|
3471
3473
|
role: projectResourceAttachmentRoleSchema.optional(),
|
|
3472
3474
|
note: z19.string().optional().nullable(),
|
|
3473
|
-
sortOrder: z19.number().int().nonnegative().optional()
|
|
3475
|
+
sortOrder: z19.number().int().nonnegative().optional(),
|
|
3476
|
+
isPrimary: z19.boolean().optional()
|
|
3474
3477
|
}).strict().superRefine(validateLibraryResourceContract);
|
|
3475
3478
|
}
|
|
3476
3479
|
});
|
|
@@ -5413,9 +5416,13 @@ var init_postgres_payload = __esm({
|
|
|
5413
5416
|
|
|
5414
5417
|
// src/runtime/install.ts
|
|
5415
5418
|
import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
5419
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5420
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
5416
5421
|
import { copyFile, mkdir as mkdir2, mkdtemp, readFile, readdir, realpath, rename, rm, stat as stat2, symlink, writeFile } from "node:fs/promises";
|
|
5417
5422
|
import { createRequire } from "node:module";
|
|
5418
5423
|
import path7 from "node:path";
|
|
5424
|
+
import { Readable, Transform } from "node:stream";
|
|
5425
|
+
import { pipeline } from "node:stream/promises";
|
|
5419
5426
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
5420
5427
|
function sanitizeRuntimeCacheSegment(value) {
|
|
5421
5428
|
return encodeURIComponent(value.trim() || "latest").replaceAll("%", "_");
|
|
@@ -6137,8 +6144,29 @@ function runtimePostgresArchiveUrl(platform = process.platform, arch = process.a
|
|
|
6137
6144
|
return null;
|
|
6138
6145
|
}
|
|
6139
6146
|
async function downloadRuntimePostgresArchive(url, targetPath) {
|
|
6147
|
+
const expectedSha256 = process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV]?.trim().toLowerCase() || null;
|
|
6148
|
+
if (expectedSha256 && !/^[a-f0-9]{64}$/.test(expectedSha256)) {
|
|
6149
|
+
throw new Error(`${RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV} must be a 64-character SHA-256 digest`);
|
|
6150
|
+
}
|
|
6151
|
+
const configuredMaxBytes = Number.parseInt(process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV] ?? "", 10);
|
|
6152
|
+
const maxBytes = Number.isSafeInteger(configuredMaxBytes) && configuredMaxBytes > 0 ? configuredMaxBytes : DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES;
|
|
6153
|
+
async function verifyFile(filePath) {
|
|
6154
|
+
if (!expectedSha256) return;
|
|
6155
|
+
const hash = createHash2("sha256");
|
|
6156
|
+
await new Promise((resolve, reject) => {
|
|
6157
|
+
const stream = createReadStream(filePath);
|
|
6158
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
6159
|
+
stream.on("error", reject);
|
|
6160
|
+
stream.on("end", resolve);
|
|
6161
|
+
});
|
|
6162
|
+
const actual = hash.digest("hex");
|
|
6163
|
+
if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
|
|
6164
|
+
}
|
|
6140
6165
|
if (url.startsWith("file://")) {
|
|
6141
6166
|
await copyFile(fileURLToPath3(url), targetPath);
|
|
6167
|
+
const archiveStat = await stat2(targetPath);
|
|
6168
|
+
if (archiveStat.size > maxBytes) throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
|
|
6169
|
+
await verifyFile(targetPath);
|
|
6142
6170
|
return;
|
|
6143
6171
|
}
|
|
6144
6172
|
const parsedTimeout = Number.parseInt(
|
|
@@ -6152,7 +6180,33 @@ async function downloadRuntimePostgresArchive(url, targetPath) {
|
|
|
6152
6180
|
if (!response.ok) {
|
|
6153
6181
|
throw new Error(`failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
6154
6182
|
}
|
|
6155
|
-
|
|
6183
|
+
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
6184
|
+
if (Number.isSafeInteger(contentLength) && contentLength > maxBytes) {
|
|
6185
|
+
throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
|
|
6186
|
+
}
|
|
6187
|
+
if (!response.body) throw new Error("PostgreSQL runtime archive response has no body");
|
|
6188
|
+
const hash = createHash2("sha256");
|
|
6189
|
+
let bytes = 0;
|
|
6190
|
+
const monitor = new Transform({
|
|
6191
|
+
transform(chunk, _encoding, callback) {
|
|
6192
|
+
bytes += chunk.byteLength;
|
|
6193
|
+
if (bytes > maxBytes) {
|
|
6194
|
+
callback(new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`));
|
|
6195
|
+
return;
|
|
6196
|
+
}
|
|
6197
|
+
hash.update(chunk);
|
|
6198
|
+
callback(null, chunk);
|
|
6199
|
+
}
|
|
6200
|
+
});
|
|
6201
|
+
await pipeline(
|
|
6202
|
+
Readable.fromWeb(response.body),
|
|
6203
|
+
monitor,
|
|
6204
|
+
createWriteStream(targetPath, { flags: "wx" })
|
|
6205
|
+
);
|
|
6206
|
+
if (expectedSha256) {
|
|
6207
|
+
const actual = hash.digest("hex");
|
|
6208
|
+
if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
|
|
6209
|
+
}
|
|
6156
6210
|
} finally {
|
|
6157
6211
|
if (timeout) clearTimeout(timeout);
|
|
6158
6212
|
}
|
|
@@ -6672,7 +6726,7 @@ function parseRuntimeVersion(version) {
|
|
|
6672
6726
|
canaryNumber: canaryMatch ? Number(canaryMatch[1]) : null
|
|
6673
6727
|
};
|
|
6674
6728
|
}
|
|
6675
|
-
var RUNTIME_NPM_PACKAGE_NAME, NPM_PUBLIC_REGISTRY_URL, RUNTIME_METADATA_FILE, RUNTIME_POSTGRES_PAYLOAD_DIR, DEFAULT_RUNTIME_CACHE_MAX_ENTRIES, DEFAULT_RUNTIME_CACHE_MAX_AGE_MS, DEFAULT_RUNTIME_CACHE_MAX_BYTES, DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS, RUNTIME_NPM_INSTALL_FLAGS, RUNTIME_NPM_PACK_FLAGS, EMBEDDED_POSTGRES_PACKAGE_NAME, RUDDER_DESKTOP_MANAGED_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV, RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV, RUNTIME_CACHE_PACKAGE_JSON, NPM_PLATFORM_REPAIR_ENV, RuntimeInstallError;
|
|
6729
|
+
var RUNTIME_NPM_PACKAGE_NAME, NPM_PUBLIC_REGISTRY_URL, RUNTIME_METADATA_FILE, RUNTIME_POSTGRES_PAYLOAD_DIR, DEFAULT_RUNTIME_CACHE_MAX_ENTRIES, DEFAULT_RUNTIME_CACHE_MAX_AGE_MS, DEFAULT_RUNTIME_CACHE_MAX_BYTES, DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS, RUNTIME_NPM_INSTALL_FLAGS, RUNTIME_NPM_PACK_FLAGS, EMBEDDED_POSTGRES_PACKAGE_NAME, RUDDER_DESKTOP_MANAGED_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV, RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV, DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES, RUNTIME_CACHE_PACKAGE_JSON, NPM_PLATFORM_REPAIR_ENV, RuntimeInstallError;
|
|
6676
6730
|
var init_install = __esm({
|
|
6677
6731
|
"src/runtime/install.ts"() {
|
|
6678
6732
|
"use strict";
|
|
@@ -6693,6 +6747,9 @@ var init_install = __esm({
|
|
|
6693
6747
|
RUDDER_POSTGRES_BIN_DIR_ENV = "RUDDER_POSTGRES_BIN_DIR";
|
|
6694
6748
|
RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL";
|
|
6695
6749
|
RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV = "RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS";
|
|
6750
|
+
RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256";
|
|
6751
|
+
RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES";
|
|
6752
|
+
DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES = 1024 * 1024 * 1024;
|
|
6696
6753
|
RUNTIME_CACHE_PACKAGE_JSON = {
|
|
6697
6754
|
name: "rudder-runtime-cache",
|
|
6698
6755
|
version: "0.0.0",
|
|
@@ -12384,41 +12441,41 @@ var RudderApiClient = class {
|
|
|
12384
12441
|
this.signal = opts.signal;
|
|
12385
12442
|
this.recoverAuth = opts.recoverAuth;
|
|
12386
12443
|
}
|
|
12387
|
-
get(
|
|
12388
|
-
return this.request(
|
|
12444
|
+
get(path25, opts) {
|
|
12445
|
+
return this.request(path25, { method: "GET" }, opts);
|
|
12389
12446
|
}
|
|
12390
|
-
post(
|
|
12391
|
-
return this.request(
|
|
12447
|
+
post(path25, body, opts) {
|
|
12448
|
+
return this.request(path25, {
|
|
12392
12449
|
method: "POST",
|
|
12393
12450
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
12394
12451
|
}, opts);
|
|
12395
12452
|
}
|
|
12396
|
-
postForm(
|
|
12397
|
-
return this.request(
|
|
12453
|
+
postForm(path25, form, opts) {
|
|
12454
|
+
return this.request(path25, {
|
|
12398
12455
|
method: "POST",
|
|
12399
12456
|
body: form
|
|
12400
12457
|
}, opts);
|
|
12401
12458
|
}
|
|
12402
|
-
patch(
|
|
12403
|
-
return this.request(
|
|
12459
|
+
patch(path25, body, opts) {
|
|
12460
|
+
return this.request(path25, {
|
|
12404
12461
|
method: "PATCH",
|
|
12405
12462
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
12406
12463
|
}, opts);
|
|
12407
12464
|
}
|
|
12408
|
-
put(
|
|
12409
|
-
return this.request(
|
|
12465
|
+
put(path25, body, opts) {
|
|
12466
|
+
return this.request(path25, {
|
|
12410
12467
|
method: "PUT",
|
|
12411
12468
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
12412
12469
|
}, opts);
|
|
12413
12470
|
}
|
|
12414
|
-
delete(
|
|
12415
|
-
return this.request(
|
|
12471
|
+
delete(path25, opts) {
|
|
12472
|
+
return this.request(path25, { method: "DELETE" }, opts);
|
|
12416
12473
|
}
|
|
12417
12474
|
setApiKey(apiKey) {
|
|
12418
12475
|
this.apiKey = apiKey?.trim() || void 0;
|
|
12419
12476
|
}
|
|
12420
|
-
async request(
|
|
12421
|
-
const url = buildUrl(this.apiBase,
|
|
12477
|
+
async request(path25, init, opts, hasRetriedAuth = false) {
|
|
12478
|
+
const url = buildUrl(this.apiBase, path25);
|
|
12422
12479
|
const headers = {
|
|
12423
12480
|
accept: "application/json",
|
|
12424
12481
|
...toStringRecord(init.headers)
|
|
@@ -12449,13 +12506,13 @@ var RudderApiClient = class {
|
|
|
12449
12506
|
const apiError = await toApiError(response);
|
|
12450
12507
|
if (!hasRetriedAuth && this.recoverAuth) {
|
|
12451
12508
|
const recoveredToken = await this.recoverAuth({
|
|
12452
|
-
path:
|
|
12509
|
+
path: path25,
|
|
12453
12510
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
12454
12511
|
error: apiError
|
|
12455
12512
|
});
|
|
12456
12513
|
if (recoveredToken) {
|
|
12457
12514
|
this.setApiKey(recoveredToken);
|
|
12458
|
-
return this.request(
|
|
12515
|
+
return this.request(path25, init, opts, true);
|
|
12459
12516
|
}
|
|
12460
12517
|
}
|
|
12461
12518
|
throw apiError;
|
|
@@ -12474,8 +12531,8 @@ function shouldAttachAgentContext(method) {
|
|
|
12474
12531
|
const normalized = String(method ?? "GET").toUpperCase();
|
|
12475
12532
|
return normalized !== "GET" && normalized !== "HEAD";
|
|
12476
12533
|
}
|
|
12477
|
-
function buildUrl(apiBase,
|
|
12478
|
-
const normalizedPath =
|
|
12534
|
+
function buildUrl(apiBase, path25) {
|
|
12535
|
+
const normalizedPath = path25.startsWith("/") ? path25 : `/${path25}`;
|
|
12479
12536
|
const [pathname, query] = normalizedPath.split("?");
|
|
12480
12537
|
const url = new URL2(apiBase);
|
|
12481
12538
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -14934,8 +14991,8 @@ function registerActivityCommands(program) {
|
|
|
14934
14991
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
14935
14992
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
14936
14993
|
const query = params.toString();
|
|
14937
|
-
const
|
|
14938
|
-
const rows = await ctx.api.get(
|
|
14994
|
+
const path25 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
|
|
14995
|
+
const rows = await ctx.api.get(path25) ?? [];
|
|
14939
14996
|
if (ctx.json) {
|
|
14940
14997
|
printOutput(rows, { json: true });
|
|
14941
14998
|
return;
|
|
@@ -19868,8 +19925,8 @@ function registerUserCommands(program) {
|
|
|
19868
19925
|
appendParam(params, "limit", opts.limit);
|
|
19869
19926
|
appendParam(params, "cursor", opts.cursor);
|
|
19870
19927
|
const query = params.toString();
|
|
19871
|
-
const
|
|
19872
|
-
const result = await ctx.api.get(
|
|
19928
|
+
const path25 = `/api/orgs/${ctx.orgId}/users/${encodeURIComponent(userId)}/activity-ledger${query ? `?${query}` : ""}`;
|
|
19929
|
+
const result = await ctx.api.get(path25);
|
|
19873
19930
|
if (ctx.json) {
|
|
19874
19931
|
printOutput(result, { json: true });
|
|
19875
19932
|
return;
|
|
@@ -20681,22 +20738,48 @@ init_onboard();
|
|
|
20681
20738
|
init_run();
|
|
20682
20739
|
|
|
20683
20740
|
// src/commands/start.ts
|
|
20684
|
-
init_home();
|
|
20685
|
-
init_install2();
|
|
20686
|
-
init_install();
|
|
20687
20741
|
import * as p15 from "@clack/prompts";
|
|
20688
20742
|
import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
|
|
20689
|
-
import { createHash as
|
|
20690
|
-
import {
|
|
20691
|
-
import { access, chmod, copyFile as copyFile2, cp as cp2, lstat, mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile6, rm as
|
|
20743
|
+
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
20744
|
+
import { constants as fsConstants, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
20745
|
+
import { access, chmod, copyFile as copyFile2, cp as cp2, lstat, mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile6, rm as rm3, stat as stat5, utimes, writeFile as writeFile3 } from "node:fs/promises";
|
|
20692
20746
|
import { homedir, tmpdir } from "node:os";
|
|
20693
|
-
import
|
|
20694
|
-
import {
|
|
20695
|
-
import { pipeline } from "node:stream/promises";
|
|
20696
|
-
import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers";
|
|
20747
|
+
import path23 from "node:path";
|
|
20748
|
+
import { clearTimeout as clearTimeout3, setTimeout as setTimeout3 } from "node:timers";
|
|
20697
20749
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
20698
20750
|
import pc14 from "picocolors";
|
|
20699
20751
|
|
|
20752
|
+
// src/checksum-manifest.ts
|
|
20753
|
+
function parseChecksumFile(contents) {
|
|
20754
|
+
const checksums = /* @__PURE__ */ new Map();
|
|
20755
|
+
for (const [index, line] of contents.split(/\r?\n/).entries()) {
|
|
20756
|
+
if (!line.trim()) continue;
|
|
20757
|
+
const match = line.match(/^([a-fA-F0-9]{64})[ \t]+\*?(\S+)[ \t]*$/);
|
|
20758
|
+
if (!match) {
|
|
20759
|
+
throw new Error(`Invalid SHA-256 checksum manifest line ${index + 1}.`);
|
|
20760
|
+
}
|
|
20761
|
+
const assetName = match[2];
|
|
20762
|
+
if (checksums.has(assetName)) {
|
|
20763
|
+
throw new Error(`Duplicate SHA-256 checksum manifest entry for ${assetName}.`);
|
|
20764
|
+
}
|
|
20765
|
+
checksums.set(assetName, match[1].toLowerCase());
|
|
20766
|
+
}
|
|
20767
|
+
if (checksums.size === 0) throw new Error("Desktop release checksum manifest is empty.");
|
|
20768
|
+
return checksums;
|
|
20769
|
+
}
|
|
20770
|
+
|
|
20771
|
+
// src/commands/start.ts
|
|
20772
|
+
init_home();
|
|
20773
|
+
|
|
20774
|
+
// src/desktop-download.ts
|
|
20775
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
20776
|
+
import { createWriteStream as createWriteStream2, mkdirSync } from "node:fs";
|
|
20777
|
+
import { rm as rm2 } from "node:fs/promises";
|
|
20778
|
+
import path22 from "node:path";
|
|
20779
|
+
import { Readable as Readable2, Transform as Transform2 } from "node:stream";
|
|
20780
|
+
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
20781
|
+
import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers";
|
|
20782
|
+
|
|
20700
20783
|
// src/utils/progress.ts
|
|
20701
20784
|
var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
20702
20785
|
function formatBytes(bytes) {
|
|
@@ -20804,9 +20887,201 @@ function createByteProgress(label, options = {}) {
|
|
|
20804
20887
|
};
|
|
20805
20888
|
}
|
|
20806
20889
|
|
|
20890
|
+
// src/desktop-download.ts
|
|
20891
|
+
var DEFAULT_DESKTOP_RELEASE_REPO = "Undertone0809/rudder";
|
|
20892
|
+
var DEFAULT_DESKTOP_RELEASE_MIRROR_BASE_URL = "https://rudder-releases-cn-1302936001.cos.ap-shanghai.myqcloud.com";
|
|
20893
|
+
var GITHUB_ASSET_DOWNLOAD_ACCEPT = "application/octet-stream";
|
|
20894
|
+
var DESKTOP_DOWNLOAD_SOURCE_PROBE_TIMEOUT_MS = 3e3;
|
|
20895
|
+
var DESKTOP_ASSET_RESPONSE_TIMEOUT_MS = 3e4;
|
|
20896
|
+
var DESKTOP_ASSET_IDLE_TIMEOUT_MS = 3e4;
|
|
20897
|
+
function resolveDesktopDownloadSource(optionValue, env = process.env) {
|
|
20898
|
+
const value = optionValue?.trim() || env.RUDDER_DOWNLOAD_SOURCE?.trim() || "auto";
|
|
20899
|
+
if (value === "auto" || value === "cn" || value === "global") return value;
|
|
20900
|
+
throw new Error(`Desktop download source must be auto, cn, or global. Received ${value}.`);
|
|
20901
|
+
}
|
|
20902
|
+
function normalizeReleaseMirrorBaseUrl(value) {
|
|
20903
|
+
let parsed;
|
|
20904
|
+
try {
|
|
20905
|
+
parsed = new URL(value);
|
|
20906
|
+
} catch {
|
|
20907
|
+
throw new Error(`Invalid Rudder release mirror base URL: ${value}`);
|
|
20908
|
+
}
|
|
20909
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
20910
|
+
throw new Error(`Rudder release mirror base URL must use HTTP or HTTPS: ${value}`);
|
|
20911
|
+
}
|
|
20912
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
20913
|
+
parsed.search = "";
|
|
20914
|
+
parsed.hash = "";
|
|
20915
|
+
return parsed.toString().replace(/\/$/, "");
|
|
20916
|
+
}
|
|
20917
|
+
function resolveDesktopReleaseMirrorBaseUrl(repo, env = process.env) {
|
|
20918
|
+
const override = env.RUDDER_RELEASE_MIRROR_BASE_URL?.trim();
|
|
20919
|
+
if (override) return normalizeReleaseMirrorBaseUrl(override);
|
|
20920
|
+
return repo === DEFAULT_DESKTOP_RELEASE_REPO ? DEFAULT_DESKTOP_RELEASE_MIRROR_BASE_URL : null;
|
|
20921
|
+
}
|
|
20922
|
+
function encodeReleaseTagForDownloadUrl(tag) {
|
|
20923
|
+
return tag.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
20924
|
+
}
|
|
20925
|
+
function buildReleaseMirrorAssetDownloadUrl(baseUrl, tag, assetName) {
|
|
20926
|
+
const encodedTag = encodeReleaseTagForDownloadUrl(tag);
|
|
20927
|
+
return `${normalizeReleaseMirrorBaseUrl(baseUrl)}/releases/${encodedTag}/${encodeURIComponent(assetName)}`;
|
|
20928
|
+
}
|
|
20929
|
+
function githubAssetDownloadUrls(asset) {
|
|
20930
|
+
return [asset.browser_download_url, asset.url].filter((url) => Boolean(url));
|
|
20931
|
+
}
|
|
20932
|
+
function uniqueAssetDownloadUrls(asset) {
|
|
20933
|
+
const urls = asset.download_urls ?? githubAssetDownloadUrls(asset);
|
|
20934
|
+
return Array.from(new Set(urls));
|
|
20935
|
+
}
|
|
20936
|
+
function withDesktopDownloadOrigins(asset, origins, options) {
|
|
20937
|
+
const githubUrls = githubAssetDownloadUrls(asset);
|
|
20938
|
+
const urls = origins.flatMap((origin) => {
|
|
20939
|
+
if (origin === "github") return githubUrls;
|
|
20940
|
+
if (!options.mirrorBaseUrl) return [];
|
|
20941
|
+
return [buildReleaseMirrorAssetDownloadUrl(options.mirrorBaseUrl, options.tag, asset.name)];
|
|
20942
|
+
});
|
|
20943
|
+
return { ...asset, download_urls: Array.from(new Set(urls)) };
|
|
20944
|
+
}
|
|
20945
|
+
function downloadHeadersForAssetUrl(asset, url) {
|
|
20946
|
+
return {
|
|
20947
|
+
Accept: url === asset.url ? GITHUB_ASSET_DOWNLOAD_ACCEPT : "*/*",
|
|
20948
|
+
"User-Agent": "rudder-cli-installer"
|
|
20949
|
+
};
|
|
20950
|
+
}
|
|
20951
|
+
function formatFetchError(error) {
|
|
20952
|
+
if (!(error instanceof Error)) return String(error);
|
|
20953
|
+
const cause = error.cause;
|
|
20954
|
+
if (cause instanceof Error) {
|
|
20955
|
+
const code = cause.code;
|
|
20956
|
+
const suffix = typeof code === "string" ? ` [${code}]` : "";
|
|
20957
|
+
return `${error.message}: ${cause.message}${suffix}`;
|
|
20958
|
+
}
|
|
20959
|
+
return error.message;
|
|
20960
|
+
}
|
|
20961
|
+
function contentLengthFromHeaders(headers) {
|
|
20962
|
+
const raw = headers.get("content-length");
|
|
20963
|
+
if (!raw) return null;
|
|
20964
|
+
const value = Number(raw);
|
|
20965
|
+
return Number.isFinite(value) && value > 0 ? value : null;
|
|
20966
|
+
}
|
|
20967
|
+
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
20968
|
+
const controller = new AbortController();
|
|
20969
|
+
const timeout = setTimeout2(() => controller.abort(), timeoutMs);
|
|
20970
|
+
try {
|
|
20971
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
20972
|
+
} finally {
|
|
20973
|
+
clearTimeout2(timeout);
|
|
20974
|
+
}
|
|
20975
|
+
}
|
|
20976
|
+
async function probeDesktopDownloadUrl(url, timeoutMs = DESKTOP_DOWNLOAD_SOURCE_PROBE_TIMEOUT_MS) {
|
|
20977
|
+
const startedAt = Date.now();
|
|
20978
|
+
try {
|
|
20979
|
+
const response = await fetchWithTimeout(
|
|
20980
|
+
url,
|
|
20981
|
+
{
|
|
20982
|
+
headers: {
|
|
20983
|
+
Accept: "*/*",
|
|
20984
|
+
Range: "bytes=0-0",
|
|
20985
|
+
"User-Agent": "rudder-cli-installer"
|
|
20986
|
+
}
|
|
20987
|
+
},
|
|
20988
|
+
timeoutMs
|
|
20989
|
+
);
|
|
20990
|
+
if (!response.ok) return null;
|
|
20991
|
+
await response.body?.cancel();
|
|
20992
|
+
return Date.now() - startedAt;
|
|
20993
|
+
} catch {
|
|
20994
|
+
return null;
|
|
20995
|
+
}
|
|
20996
|
+
}
|
|
20997
|
+
async function resolveDesktopDownloadOrigins(options) {
|
|
20998
|
+
if (!options.mirrorBaseUrl) return ["github"];
|
|
20999
|
+
if (options.source === "cn") return ["mirror", "github"];
|
|
21000
|
+
if (options.source === "global") return ["github", "mirror"];
|
|
21001
|
+
const mirrorUrl = buildReleaseMirrorAssetDownloadUrl(
|
|
21002
|
+
options.mirrorBaseUrl,
|
|
21003
|
+
options.tag,
|
|
21004
|
+
options.checksumAsset.name
|
|
21005
|
+
);
|
|
21006
|
+
const githubUrl = options.checksumAsset.browser_download_url;
|
|
21007
|
+
const [mirrorElapsed, githubElapsed] = await Promise.all([
|
|
21008
|
+
probeDesktopDownloadUrl(mirrorUrl, options.probeTimeoutMs),
|
|
21009
|
+
probeDesktopDownloadUrl(githubUrl, options.probeTimeoutMs)
|
|
21010
|
+
]);
|
|
21011
|
+
if (mirrorElapsed !== null && (githubElapsed === null || mirrorElapsed <= githubElapsed)) {
|
|
21012
|
+
return ["mirror", "github"];
|
|
21013
|
+
}
|
|
21014
|
+
return mirrorElapsed === null ? ["github"] : ["github", "mirror"];
|
|
21015
|
+
}
|
|
21016
|
+
async function downloadAsset(asset, outputDir, progressFactory = createByteProgress, expectedChecksum, timeouts = {}) {
|
|
21017
|
+
mkdirSync(outputDir, { recursive: true });
|
|
21018
|
+
const outputPath = path22.join(outputDir, path22.basename(asset.name));
|
|
21019
|
+
const idleTimeoutMs = timeouts.idleMs ?? DESKTOP_ASSET_IDLE_TIMEOUT_MS;
|
|
21020
|
+
const responseTimeoutMs = timeouts.responseMs ?? DESKTOP_ASSET_RESPONSE_TIMEOUT_MS;
|
|
21021
|
+
const failures = [];
|
|
21022
|
+
for (const url of uniqueAssetDownloadUrls(asset)) {
|
|
21023
|
+
let progress = null;
|
|
21024
|
+
let idleTimeout = null;
|
|
21025
|
+
try {
|
|
21026
|
+
const response = await fetchWithTimeout(
|
|
21027
|
+
url,
|
|
21028
|
+
{ headers: downloadHeadersForAssetUrl(asset, url) },
|
|
21029
|
+
responseTimeoutMs
|
|
21030
|
+
);
|
|
21031
|
+
if (!response.ok || !response.body) {
|
|
21032
|
+
failures.push(`Failed to download ${asset.name} from ${url} (${response.status}).`);
|
|
21033
|
+
continue;
|
|
21034
|
+
}
|
|
21035
|
+
const totalBytes = contentLengthFromHeaders(response.headers);
|
|
21036
|
+
progress = progressFactory(`Downloading ${asset.name}`);
|
|
21037
|
+
let receivedBytes = 0;
|
|
21038
|
+
const hash = createHash3("sha256");
|
|
21039
|
+
let monitor;
|
|
21040
|
+
const armIdleTimeout = () => {
|
|
21041
|
+
if (idleTimeout) clearTimeout2(idleTimeout);
|
|
21042
|
+
idleTimeout = setTimeout2(() => {
|
|
21043
|
+
monitor.destroy(new Error(`Download from ${url} made no progress for ${idleTimeoutMs}ms.`));
|
|
21044
|
+
}, idleTimeoutMs);
|
|
21045
|
+
};
|
|
21046
|
+
monitor = new Transform2({
|
|
21047
|
+
transform(chunk, encoding, callback) {
|
|
21048
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk, encoding) : chunk;
|
|
21049
|
+
receivedBytes += bytes.length;
|
|
21050
|
+
hash.update(bytes);
|
|
21051
|
+
progress?.update(receivedBytes, totalBytes);
|
|
21052
|
+
armIdleTimeout();
|
|
21053
|
+
callback(null, chunk);
|
|
21054
|
+
}
|
|
21055
|
+
});
|
|
21056
|
+
progress.start(totalBytes);
|
|
21057
|
+
armIdleTimeout();
|
|
21058
|
+
await pipeline2(Readable2.fromWeb(response.body), monitor, createWriteStream2(outputPath));
|
|
21059
|
+
if (idleTimeout) clearTimeout2(idleTimeout);
|
|
21060
|
+
idleTimeout = null;
|
|
21061
|
+
const actualChecksum = hash.digest("hex");
|
|
21062
|
+
if (expectedChecksum && actualChecksum !== expectedChecksum.toLowerCase()) {
|
|
21063
|
+
progress.fail();
|
|
21064
|
+
progress = null;
|
|
21065
|
+
await rm2(outputPath, { force: true });
|
|
21066
|
+
failures.push(`Checksum mismatch for ${asset.name} from ${url}.`);
|
|
21067
|
+
continue;
|
|
21068
|
+
}
|
|
21069
|
+
progress.finish(receivedBytes, totalBytes);
|
|
21070
|
+
return outputPath;
|
|
21071
|
+
} catch (error) {
|
|
21072
|
+
if (idleTimeout) clearTimeout2(idleTimeout);
|
|
21073
|
+
progress?.fail();
|
|
21074
|
+
await rm2(outputPath, { force: true });
|
|
21075
|
+
failures.push(`Failed to download ${asset.name} from ${url}: ${formatFetchError(error)}.`);
|
|
21076
|
+
}
|
|
21077
|
+
}
|
|
21078
|
+
throw new Error(failures.join("\n"));
|
|
21079
|
+
}
|
|
21080
|
+
|
|
20807
21081
|
// src/commands/start.ts
|
|
21082
|
+
init_install2();
|
|
21083
|
+
init_install();
|
|
20808
21084
|
init_version();
|
|
20809
|
-
var DEFAULT_DESKTOP_RELEASE_REPO = "Undertone0809/rudder";
|
|
20810
21085
|
var DESKTOP_UPDATE_QUIT_ARG = "--rudder-update-quit";
|
|
20811
21086
|
var DESKTOP_UPDATE_FORCE_ARG = "--rudder-update-force";
|
|
20812
21087
|
var STABLE_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+$/;
|
|
@@ -20818,7 +21093,6 @@ var DESKTOP_APP_NAME = "Rudder";
|
|
|
20818
21093
|
var DESKTOP_METADATA_FILE = ".rudder-desktop-install.json";
|
|
20819
21094
|
var DESKTOP_CHECKSUM_ASSET_NAME = "SHASUMS256.txt";
|
|
20820
21095
|
var DESKTOP_ASSET_CACHE_DIR = "desktop-assets";
|
|
20821
|
-
var GITHUB_ASSET_DOWNLOAD_ACCEPT = "application/octet-stream";
|
|
20822
21096
|
var DEFAULT_DESKTOP_ASSET_CACHE_MAX_ENTRIES = 2;
|
|
20823
21097
|
var DEFAULT_DESKTOP_ASSET_CACHE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
20824
21098
|
var DEFAULT_DESKTOP_ASSET_CACHE_MAX_BYTES = 768 * 1024 * 1024;
|
|
@@ -20961,10 +21235,10 @@ function createDesktopApplySignalController() {
|
|
|
20961
21235
|
if (settled) return;
|
|
20962
21236
|
settled = true;
|
|
20963
21237
|
forceWaiters.delete(finish);
|
|
20964
|
-
|
|
21238
|
+
clearTimeout3(timer);
|
|
20965
21239
|
resolve(force);
|
|
20966
21240
|
};
|
|
20967
|
-
const timer =
|
|
21241
|
+
const timer = setTimeout3(() => finish(false), timeoutMs);
|
|
20968
21242
|
timer.unref?.();
|
|
20969
21243
|
forceWaiters.add(finish);
|
|
20970
21244
|
});
|
|
@@ -20995,7 +21269,7 @@ function compareStableSemver(a, b) {
|
|
|
20995
21269
|
}
|
|
20996
21270
|
async function fetchLatestCliVersion() {
|
|
20997
21271
|
const controller = new AbortController();
|
|
20998
|
-
const timeout =
|
|
21272
|
+
const timeout = setTimeout3(() => controller.abort(), 2e3);
|
|
20999
21273
|
try {
|
|
21000
21274
|
const response = await fetch(CLI_REGISTRY_LATEST_URL, {
|
|
21001
21275
|
signal: controller.signal,
|
|
@@ -21007,7 +21281,7 @@ async function fetchLatestCliVersion() {
|
|
|
21007
21281
|
} catch {
|
|
21008
21282
|
return null;
|
|
21009
21283
|
} finally {
|
|
21010
|
-
|
|
21284
|
+
clearTimeout3(timeout);
|
|
21011
21285
|
}
|
|
21012
21286
|
}
|
|
21013
21287
|
async function getCliUpdateNotice(currentVersion) {
|
|
@@ -21048,38 +21322,38 @@ function resolveDesktopAssetTarget(platform = process.platform, arch = process.a
|
|
|
21048
21322
|
throw new Error(`Rudder Desktop does not publish portable assets for ${platform}.`);
|
|
21049
21323
|
}
|
|
21050
21324
|
function resolveDefaultDesktopInstallRoot(target, env = process.env, homeDir = homedir()) {
|
|
21051
|
-
if (target.platform === "macos") return
|
|
21325
|
+
if (target.platform === "macos") return path23.join(homeDir, "Applications");
|
|
21052
21326
|
if (target.platform === "windows") {
|
|
21053
|
-
const localAppData = env.LOCALAPPDATA?.trim() ||
|
|
21054
|
-
return
|
|
21327
|
+
const localAppData = env.LOCALAPPDATA?.trim() || path23.join(homeDir, "AppData", "Local");
|
|
21328
|
+
return path23.join(localAppData, "Programs", DESKTOP_APP_NAME);
|
|
21055
21329
|
}
|
|
21056
|
-
return
|
|
21330
|
+
return path23.join(homeDir, ".local", "share", "rudder");
|
|
21057
21331
|
}
|
|
21058
21332
|
function resolveDesktopInstallPaths(target, installRoot) {
|
|
21059
|
-
const root =
|
|
21333
|
+
const root = path23.resolve(installRoot);
|
|
21060
21334
|
if (target.platform === "macos") {
|
|
21061
|
-
const appPath2 =
|
|
21335
|
+
const appPath2 = path23.join(root, `${DESKTOP_APP_NAME}.app`);
|
|
21062
21336
|
return {
|
|
21063
21337
|
installRoot: root,
|
|
21064
21338
|
appPath: appPath2,
|
|
21065
|
-
executablePath:
|
|
21066
|
-
metadataPath:
|
|
21339
|
+
executablePath: path23.join(appPath2, "Contents", "MacOS", DESKTOP_APP_NAME),
|
|
21340
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
21067
21341
|
};
|
|
21068
21342
|
}
|
|
21069
21343
|
if (target.platform === "windows") {
|
|
21070
21344
|
return {
|
|
21071
21345
|
installRoot: root,
|
|
21072
21346
|
appPath: root,
|
|
21073
|
-
executablePath:
|
|
21074
|
-
metadataPath:
|
|
21347
|
+
executablePath: path23.join(root, `${DESKTOP_APP_NAME}.exe`),
|
|
21348
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
21075
21349
|
};
|
|
21076
21350
|
}
|
|
21077
|
-
const appPath =
|
|
21351
|
+
const appPath = path23.join(root, `${DESKTOP_APP_NAME}.AppImage`);
|
|
21078
21352
|
return {
|
|
21079
21353
|
installRoot: root,
|
|
21080
21354
|
appPath,
|
|
21081
21355
|
executablePath: appPath,
|
|
21082
|
-
metadataPath:
|
|
21356
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
21083
21357
|
};
|
|
21084
21358
|
}
|
|
21085
21359
|
function normalizeAssetName(name) {
|
|
@@ -21169,13 +21443,13 @@ function selectChecksummedDesktopAssetCandidate(candidates, checksums) {
|
|
|
21169
21443
|
function selectChecksumAsset(assets) {
|
|
21170
21444
|
return assets.find((asset) => asset.name.toLowerCase() === DESKTOP_CHECKSUM_ASSET_NAME.toLowerCase()) ?? null;
|
|
21171
21445
|
}
|
|
21172
|
-
async function
|
|
21446
|
+
async function fetchWithTimeout2(url, init, timeoutMs) {
|
|
21173
21447
|
const controller = new AbortController();
|
|
21174
|
-
const timeout =
|
|
21448
|
+
const timeout = setTimeout3(() => controller.abort(), timeoutMs);
|
|
21175
21449
|
try {
|
|
21176
21450
|
return await fetch(url, { ...init, signal: controller.signal });
|
|
21177
21451
|
} finally {
|
|
21178
|
-
|
|
21452
|
+
clearTimeout3(timeout);
|
|
21179
21453
|
}
|
|
21180
21454
|
}
|
|
21181
21455
|
function githubApiHeaders() {
|
|
@@ -21187,7 +21461,7 @@ function githubApiHeaders() {
|
|
|
21187
21461
|
var GITHUB_API_TIMEOUT_MS = 15e3;
|
|
21188
21462
|
async function fetchGithubRelease(repo, tag) {
|
|
21189
21463
|
const endpoint = tag === "latest" ? `https://api.github.com/repos/${repo}/releases/latest` : `https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`;
|
|
21190
|
-
const response = await
|
|
21464
|
+
const response = await fetchWithTimeout2(endpoint, { headers: githubApiHeaders() }, GITHUB_API_TIMEOUT_MS);
|
|
21191
21465
|
if (!response.ok) {
|
|
21192
21466
|
throw new Error(`GitHub Release ${tag} was not found in ${repo} (${response.status}).`);
|
|
21193
21467
|
}
|
|
@@ -21211,11 +21485,11 @@ function resolveDesktopShellAssetName(version, target) {
|
|
|
21211
21485
|
if (target.platform === "windows") return `${DESKTOP_APP_NAME}-${version}-windows-x64-shell.zip`;
|
|
21212
21486
|
return null;
|
|
21213
21487
|
}
|
|
21214
|
-
function
|
|
21488
|
+
function encodeReleaseTagForDownloadUrl2(tag) {
|
|
21215
21489
|
return tag.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
21216
21490
|
}
|
|
21217
21491
|
function buildGithubReleaseAssetDownloadUrl(repo, tag, assetName) {
|
|
21218
|
-
const encodedTag =
|
|
21492
|
+
const encodedTag = encodeReleaseTagForDownloadUrl2(tag);
|
|
21219
21493
|
return `https://github.com/${repo}/releases/download/${encodedTag}/${encodeURIComponent(assetName)}`;
|
|
21220
21494
|
}
|
|
21221
21495
|
function buildGithubReleaseAsset(repo, tag, assetName) {
|
|
@@ -21224,102 +21498,22 @@ function buildGithubReleaseAsset(repo, tag, assetName) {
|
|
|
21224
21498
|
browser_download_url: buildGithubReleaseAssetDownloadUrl(repo, tag, assetName)
|
|
21225
21499
|
};
|
|
21226
21500
|
}
|
|
21227
|
-
function uniqueAssetDownloadUrls(asset) {
|
|
21228
|
-
const urls = [asset.browser_download_url, asset.url].filter((url) => Boolean(url));
|
|
21229
|
-
return Array.from(new Set(urls));
|
|
21230
|
-
}
|
|
21231
|
-
function downloadHeadersForAssetUrl(asset, url) {
|
|
21232
|
-
return {
|
|
21233
|
-
Accept: url === asset.url ? GITHUB_ASSET_DOWNLOAD_ACCEPT : "*/*",
|
|
21234
|
-
"User-Agent": "rudder-cli-installer"
|
|
21235
|
-
};
|
|
21236
|
-
}
|
|
21237
|
-
function formatFetchError(error) {
|
|
21238
|
-
if (!(error instanceof Error)) return String(error);
|
|
21239
|
-
const cause = error.cause;
|
|
21240
|
-
if (cause instanceof Error) {
|
|
21241
|
-
const code = cause.code;
|
|
21242
|
-
const suffix = typeof code === "string" ? ` [${code}]` : "";
|
|
21243
|
-
return `${error.message}: ${cause.message}${suffix}`;
|
|
21244
|
-
}
|
|
21245
|
-
return error.message;
|
|
21246
|
-
}
|
|
21247
|
-
function contentLengthFromHeaders(headers) {
|
|
21248
|
-
const raw = headers.get("content-length");
|
|
21249
|
-
if (!raw) return null;
|
|
21250
|
-
const value = Number(raw);
|
|
21251
|
-
return Number.isFinite(value) && value > 0 ? value : null;
|
|
21252
|
-
}
|
|
21253
|
-
async function downloadAsset(asset, outputDir, progressFactory = createByteProgress) {
|
|
21254
|
-
mkdirSync(outputDir, { recursive: true });
|
|
21255
|
-
const outputPath = path22.join(outputDir, path22.basename(asset.name));
|
|
21256
|
-
const ASSET_DOWNLOAD_TIMEOUT_MS = 6e5;
|
|
21257
|
-
let response = null;
|
|
21258
|
-
const failures = [];
|
|
21259
|
-
for (const url of uniqueAssetDownloadUrls(asset)) {
|
|
21260
|
-
try {
|
|
21261
|
-
const candidate = await fetchWithTimeout(
|
|
21262
|
-
url,
|
|
21263
|
-
{ headers: downloadHeadersForAssetUrl(asset, url) },
|
|
21264
|
-
ASSET_DOWNLOAD_TIMEOUT_MS
|
|
21265
|
-
);
|
|
21266
|
-
if (candidate.ok && candidate.body) {
|
|
21267
|
-
response = candidate;
|
|
21268
|
-
break;
|
|
21269
|
-
}
|
|
21270
|
-
failures.push(`Failed to download ${asset.name} from ${url} (${candidate.status}).`);
|
|
21271
|
-
} catch (error) {
|
|
21272
|
-
failures.push(`Failed to download ${asset.name} from ${url}: ${formatFetchError(error)}.`);
|
|
21273
|
-
}
|
|
21274
|
-
}
|
|
21275
|
-
if (!response) {
|
|
21276
|
-
throw new Error(failures.join("\n"));
|
|
21277
|
-
}
|
|
21278
|
-
const totalBytes = contentLengthFromHeaders(response.headers);
|
|
21279
|
-
const progress = progressFactory(`Downloading ${asset.name}`);
|
|
21280
|
-
let receivedBytes = 0;
|
|
21281
|
-
const monitor = new Transform({
|
|
21282
|
-
transform(chunk, _encoding, callback) {
|
|
21283
|
-
receivedBytes += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
21284
|
-
progress.update(receivedBytes, totalBytes);
|
|
21285
|
-
callback(null, chunk);
|
|
21286
|
-
}
|
|
21287
|
-
});
|
|
21288
|
-
progress.start(totalBytes);
|
|
21289
|
-
try {
|
|
21290
|
-
await pipeline(Readable.fromWeb(response.body), monitor, createWriteStream(outputPath));
|
|
21291
|
-
progress.finish(receivedBytes, totalBytes);
|
|
21292
|
-
} catch (error) {
|
|
21293
|
-
progress.fail();
|
|
21294
|
-
throw error;
|
|
21295
|
-
}
|
|
21296
|
-
return outputPath;
|
|
21297
|
-
}
|
|
21298
21501
|
function checksumForFile(filePath) {
|
|
21299
|
-
const hash =
|
|
21502
|
+
const hash = createHash4("sha256");
|
|
21300
21503
|
hash.update(readFileSync2(filePath));
|
|
21301
21504
|
return hash.digest("hex");
|
|
21302
21505
|
}
|
|
21303
|
-
function parseChecksumFile(contents) {
|
|
21304
|
-
const checksums = /* @__PURE__ */ new Map();
|
|
21305
|
-
for (const line of contents.split(/\r?\n/)) {
|
|
21306
|
-
const match = line.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
|
|
21307
|
-
if (!match) continue;
|
|
21308
|
-
checksums.set(match[2].trim(), match[1].toLowerCase());
|
|
21309
|
-
}
|
|
21310
|
-
return checksums;
|
|
21311
|
-
}
|
|
21312
21506
|
function resolveAssetChecksum(checksums, assetName) {
|
|
21313
|
-
const expected = checksums.get(
|
|
21507
|
+
const expected = checksums.get(path23.basename(assetName));
|
|
21314
21508
|
if (!expected) {
|
|
21315
|
-
throw new Error(`Desktop release checksums do not include ${
|
|
21509
|
+
throw new Error(`Desktop release checksums do not include ${path23.basename(assetName)}.`);
|
|
21316
21510
|
}
|
|
21317
21511
|
return expected;
|
|
21318
21512
|
}
|
|
21319
21513
|
function assertChecksumMatch(filePath, expected) {
|
|
21320
21514
|
const actual = checksumForFile(filePath);
|
|
21321
21515
|
if (actual !== expected.toLowerCase()) {
|
|
21322
|
-
throw new Error(`Checksum mismatch for ${
|
|
21516
|
+
throw new Error(`Checksum mismatch for ${path23.basename(filePath)}.`);
|
|
21323
21517
|
}
|
|
21324
21518
|
return actual;
|
|
21325
21519
|
}
|
|
@@ -21338,10 +21532,10 @@ function normalizeDesktopAssetChecksum(checksum) {
|
|
|
21338
21532
|
return normalized;
|
|
21339
21533
|
}
|
|
21340
21534
|
function resolveDesktopAssetCacheDir(assetChecksum, homeDir = resolveRudderHomeDir()) {
|
|
21341
|
-
return
|
|
21535
|
+
return path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR, normalizeDesktopAssetChecksum(assetChecksum));
|
|
21342
21536
|
}
|
|
21343
21537
|
function resolveDesktopCachedAssetPath(assetName, assetChecksum, homeDir = resolveRudderHomeDir()) {
|
|
21344
|
-
return
|
|
21538
|
+
return path23.join(resolveDesktopAssetCacheDir(assetChecksum, homeDir), path23.basename(assetName));
|
|
21345
21539
|
}
|
|
21346
21540
|
async function pruneDesktopAssetCache(options = {}) {
|
|
21347
21541
|
const homeDir = options.homeDir ?? resolveRudderHomeDir();
|
|
@@ -21362,7 +21556,7 @@ async function pruneDesktopAssetCache(options = {}) {
|
|
|
21362
21556
|
const warnings = [];
|
|
21363
21557
|
for (const entry of deletions) {
|
|
21364
21558
|
try {
|
|
21365
|
-
await
|
|
21559
|
+
await rm3(entry.cacheDir, { recursive: true, force: true });
|
|
21366
21560
|
deleted.push({
|
|
21367
21561
|
cacheDir: entry.cacheDir,
|
|
21368
21562
|
checksum: entry.checksum,
|
|
@@ -21387,7 +21581,7 @@ async function maybePruneDesktopAssetCache(options) {
|
|
|
21387
21581
|
return result.deleted.length > 0 || result.warnings.length > 0 ? result : null;
|
|
21388
21582
|
}
|
|
21389
21583
|
async function scanDesktopAssetCacheEntries(homeDir) {
|
|
21390
|
-
const cacheRoot =
|
|
21584
|
+
const cacheRoot = path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR);
|
|
21391
21585
|
const dirents = await readdir3(cacheRoot, { withFileTypes: true }).catch(() => null);
|
|
21392
21586
|
if (!dirents) return [];
|
|
21393
21587
|
const entries = [];
|
|
@@ -21399,7 +21593,7 @@ async function scanDesktopAssetCacheEntries(homeDir) {
|
|
|
21399
21593
|
} catch {
|
|
21400
21594
|
continue;
|
|
21401
21595
|
}
|
|
21402
|
-
const cacheDir =
|
|
21596
|
+
const cacheDir = path23.join(cacheRoot, dirent.name);
|
|
21403
21597
|
const stats = await desktopCacheDirectoryStats(cacheDir);
|
|
21404
21598
|
entries.push({
|
|
21405
21599
|
cacheDir,
|
|
@@ -21423,7 +21617,7 @@ async function desktopCacheDirectoryStats(targetPath) {
|
|
|
21423
21617
|
let lastUsedAtMs = Number(fallbackStat?.mtimeMs ?? 0);
|
|
21424
21618
|
for (const dirent of dirents) {
|
|
21425
21619
|
if (dirent.isSymbolicLink()) continue;
|
|
21426
|
-
const entryPath =
|
|
21620
|
+
const entryPath = path23.join(targetPath, dirent.name);
|
|
21427
21621
|
const entryStat = await stat5(entryPath).catch(() => null);
|
|
21428
21622
|
if (!entryStat) continue;
|
|
21429
21623
|
lastUsedAtMs = Math.max(lastUsedAtMs, Number(entryStat.mtimeMs ?? 0));
|
|
@@ -21497,21 +21691,21 @@ async function downloadDesktopAssetWithCache(asset, expectedChecksum, options =
|
|
|
21497
21691
|
await touchDesktopCachedAsset(cachePath);
|
|
21498
21692
|
return { path: cachePath, checksum, cacheStatus: "hit" };
|
|
21499
21693
|
} catch {
|
|
21500
|
-
await
|
|
21694
|
+
await rm3(cachePath, { force: true });
|
|
21501
21695
|
}
|
|
21502
21696
|
}
|
|
21503
|
-
const outputDir = options.outputDir ?? await mkdtemp2(
|
|
21697
|
+
const outputDir = options.outputDir ?? await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
|
|
21504
21698
|
const removeOutputDir = options.outputDir ? false : true;
|
|
21505
21699
|
try {
|
|
21506
|
-
const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory);
|
|
21700
|
+
const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory, normalizedChecksum);
|
|
21507
21701
|
const checksum = assertChecksumMatch(downloadedPath, normalizedChecksum);
|
|
21508
|
-
await mkdir4(
|
|
21509
|
-
if (
|
|
21702
|
+
await mkdir4(path23.dirname(cachePath), { recursive: true });
|
|
21703
|
+
if (path23.resolve(downloadedPath) !== path23.resolve(cachePath)) {
|
|
21510
21704
|
await copyFile2(downloadedPath, cachePath);
|
|
21511
21705
|
}
|
|
21512
21706
|
return { path: cachePath, checksum, cacheStatus: "miss" };
|
|
21513
21707
|
} finally {
|
|
21514
|
-
if (removeOutputDir) await
|
|
21708
|
+
if (removeOutputDir) await rm3(outputDir, { recursive: true, force: true });
|
|
21515
21709
|
}
|
|
21516
21710
|
}
|
|
21517
21711
|
async function pathExists2(targetPath) {
|
|
@@ -21523,8 +21717,8 @@ async function pathExists2(targetPath) {
|
|
|
21523
21717
|
}
|
|
21524
21718
|
}
|
|
21525
21719
|
function resolveDesktopInstallLockPath(paths) {
|
|
21526
|
-
const installRootHash =
|
|
21527
|
-
return
|
|
21720
|
+
const installRootHash = createHash4("sha256").update(path23.resolve(paths.installRoot)).digest("hex").slice(0, 16);
|
|
21721
|
+
return path23.join(path23.dirname(paths.appPath), `.rudder-desktop-install-${installRootHash}.lock`);
|
|
21528
21722
|
}
|
|
21529
21723
|
async function readDesktopInstallLock(lockPath) {
|
|
21530
21724
|
try {
|
|
@@ -21544,14 +21738,14 @@ async function readDesktopInstallLock(lockPath) {
|
|
|
21544
21738
|
}
|
|
21545
21739
|
async function withDesktopInstallLock(paths, fn, options = {}) {
|
|
21546
21740
|
const lockPath = resolveDesktopInstallLockPath(paths);
|
|
21547
|
-
const lockDir =
|
|
21741
|
+
const lockDir = path23.dirname(lockPath);
|
|
21548
21742
|
const timeoutMs = options.timeoutMs ?? DESKTOP_INSTALL_LOCK_TIMEOUT_MS;
|
|
21549
21743
|
const pollMs = options.pollMs ?? DESKTOP_INSTALL_LOCK_POLL_MS;
|
|
21550
21744
|
const startedAt = Date.now();
|
|
21551
21745
|
const payload = {
|
|
21552
21746
|
lockId: randomUUID(),
|
|
21553
21747
|
pid: process.pid,
|
|
21554
|
-
installRoot:
|
|
21748
|
+
installRoot: path23.resolve(paths.installRoot),
|
|
21555
21749
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21556
21750
|
};
|
|
21557
21751
|
await mkdir4(lockDir, { recursive: true });
|
|
@@ -21566,7 +21760,7 @@ async function withDesktopInstallLock(paths, fn, options = {}) {
|
|
|
21566
21760
|
const existing = await readDesktopInstallLock(lockPath);
|
|
21567
21761
|
const stale = !existing || !processExists(existing.pid);
|
|
21568
21762
|
if (stale) {
|
|
21569
|
-
await
|
|
21763
|
+
await rm3(lockPath, { force: true });
|
|
21570
21764
|
continue;
|
|
21571
21765
|
}
|
|
21572
21766
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
@@ -21582,7 +21776,7 @@ async function withDesktopInstallLock(paths, fn, options = {}) {
|
|
|
21582
21776
|
} finally {
|
|
21583
21777
|
const existing = await readDesktopInstallLock(lockPath);
|
|
21584
21778
|
if (existing?.lockId === payload.lockId) {
|
|
21585
|
-
await
|
|
21779
|
+
await rm3(lockPath, { force: true });
|
|
21586
21780
|
}
|
|
21587
21781
|
}
|
|
21588
21782
|
}
|
|
@@ -21616,7 +21810,7 @@ function isSuccessfulRobocopyExitCode(status) {
|
|
|
21616
21810
|
return typeof status === "number" && status >= 0 && status <= 7;
|
|
21617
21811
|
}
|
|
21618
21812
|
async function extractZip(zipPath, outputDir, target) {
|
|
21619
|
-
await
|
|
21813
|
+
await rm3(outputDir, { recursive: true, force: true });
|
|
21620
21814
|
await mkdir4(outputDir, { recursive: true });
|
|
21621
21815
|
if (target.platform === "macos") {
|
|
21622
21816
|
runChecked("ditto", ["-x", "-k", zipPath, outputDir]);
|
|
@@ -21633,7 +21827,7 @@ async function findPath(root, predicate, maxDepth = 5) {
|
|
|
21633
21827
|
async function visit(dir, depth) {
|
|
21634
21828
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
21635
21829
|
for (const entry of entries) {
|
|
21636
|
-
const fullPath =
|
|
21830
|
+
const fullPath = path23.join(dir, entry.name);
|
|
21637
21831
|
if (predicate(fullPath, entry.isDirectory())) return fullPath;
|
|
21638
21832
|
if (entry.isDirectory() && depth < maxDepth) {
|
|
21639
21833
|
const nested = await visit(fullPath, depth + 1);
|
|
@@ -21645,18 +21839,18 @@ async function findPath(root, predicate, maxDepth = 5) {
|
|
|
21645
21839
|
return await visit(root, 0);
|
|
21646
21840
|
}
|
|
21647
21841
|
async function findMacApp(extractDir) {
|
|
21648
|
-
const direct =
|
|
21842
|
+
const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.app`);
|
|
21649
21843
|
if (await pathExists2(direct)) return direct;
|
|
21650
|
-
const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory &&
|
|
21844
|
+
const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory && path23.basename(filePath) === `${DESKTOP_APP_NAME}.app`);
|
|
21651
21845
|
if (!found) throw new Error(`Portable macOS archive did not contain ${DESKTOP_APP_NAME}.app.`);
|
|
21652
21846
|
return found;
|
|
21653
21847
|
}
|
|
21654
21848
|
async function findWindowsAppDir(extractDir) {
|
|
21655
|
-
const direct =
|
|
21849
|
+
const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.exe`);
|
|
21656
21850
|
if (await pathExists2(direct)) return extractDir;
|
|
21657
|
-
const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory &&
|
|
21851
|
+
const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory && path23.basename(filePath).toLowerCase() === `${DESKTOP_APP_NAME.toLowerCase()}.exe`);
|
|
21658
21852
|
if (!executable) throw new Error(`Portable Windows archive did not contain ${DESKTOP_APP_NAME}.exe.`);
|
|
21659
|
-
return
|
|
21853
|
+
return path23.dirname(executable);
|
|
21660
21854
|
}
|
|
21661
21855
|
async function readInstallMetadata(metadataPath) {
|
|
21662
21856
|
try {
|
|
@@ -21724,7 +21918,7 @@ async function waitForUpdateQuitResponse(responsePath, timeoutMs = 8e3) {
|
|
|
21724
21918
|
}
|
|
21725
21919
|
async function requestDesktopQuit(executablePath, target, options = {}) {
|
|
21726
21920
|
if (!await pathExists2(executablePath)) return { ok: true, status: "not_running" };
|
|
21727
|
-
const responsePath =
|
|
21921
|
+
const responsePath = path23.join(tmpdir(), `rudder-update-quit-${process.pid}-${Date.now()}.json`);
|
|
21728
21922
|
const result = spawnSync4(executablePath, [
|
|
21729
21923
|
`${DESKTOP_UPDATE_QUIT_ARG}=${responsePath}`,
|
|
21730
21924
|
...options.forceUpdate ? [DESKTOP_UPDATE_FORCE_ARG] : []
|
|
@@ -21738,7 +21932,7 @@ async function requestDesktopQuit(executablePath, target, options = {}) {
|
|
|
21738
21932
|
try {
|
|
21739
21933
|
return await waitForUpdateQuitResponse(responsePath, options.responseTimeoutMs);
|
|
21740
21934
|
} finally {
|
|
21741
|
-
await
|
|
21935
|
+
await rm3(responsePath, { force: true });
|
|
21742
21936
|
}
|
|
21743
21937
|
}
|
|
21744
21938
|
function processExists(pid) {
|
|
@@ -21774,7 +21968,7 @@ async function waitForProcessesExit(pids, waitForExit) {
|
|
|
21774
21968
|
async function removePathWithRetry(targetPath, attempts = 5) {
|
|
21775
21969
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
21776
21970
|
try {
|
|
21777
|
-
await
|
|
21971
|
+
await rm3(targetPath, { recursive: true, force: true });
|
|
21778
21972
|
if (!await pathExists2(targetPath)) return true;
|
|
21779
21973
|
} catch {
|
|
21780
21974
|
}
|
|
@@ -21888,7 +22082,7 @@ async function installPortableDesktop(installerPath, paths, target) {
|
|
|
21888
22082
|
await chmod(paths.appPath, 493);
|
|
21889
22083
|
return;
|
|
21890
22084
|
}
|
|
21891
|
-
const extractDir = await mkdtemp2(
|
|
22085
|
+
const extractDir = await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-extract."));
|
|
21892
22086
|
try {
|
|
21893
22087
|
await extractZip(installerPath, extractDir, target);
|
|
21894
22088
|
if (target.platform === "macos") {
|
|
@@ -21897,10 +22091,10 @@ async function installPortableDesktop(installerPath, paths, target) {
|
|
|
21897
22091
|
return;
|
|
21898
22092
|
}
|
|
21899
22093
|
const appSource = await findWindowsAppDir(extractDir);
|
|
21900
|
-
await mkdir4(
|
|
22094
|
+
await mkdir4(path23.dirname(paths.installRoot), { recursive: true });
|
|
21901
22095
|
await copyPortableAppBundle(appSource, paths.installRoot);
|
|
21902
22096
|
} finally {
|
|
21903
|
-
await
|
|
22097
|
+
await rm3(extractDir, { recursive: true, force: true });
|
|
21904
22098
|
}
|
|
21905
22099
|
}
|
|
21906
22100
|
async function copyPortableAppBundle(sourcePath, destinationPath) {
|
|
@@ -21938,12 +22132,12 @@ function buildLinuxDesktopEntry(executablePath) {
|
|
|
21938
22132
|
].join("\n");
|
|
21939
22133
|
}
|
|
21940
22134
|
async function writeLinuxLaunchers(paths) {
|
|
21941
|
-
const desktopDir =
|
|
22135
|
+
const desktopDir = path23.join(homedir(), ".local", "share", "applications");
|
|
21942
22136
|
await mkdir4(desktopDir, { recursive: true });
|
|
21943
|
-
await writeFile3(
|
|
21944
|
-
const binDir =
|
|
22137
|
+
await writeFile3(path23.join(desktopDir, "rudder.desktop"), buildLinuxDesktopEntry(paths.executablePath), "utf8");
|
|
22138
|
+
const binDir = path23.join(homedir(), ".local", "bin");
|
|
21945
22139
|
await mkdir4(binDir, { recursive: true });
|
|
21946
|
-
const wrapperPath =
|
|
22140
|
+
const wrapperPath = path23.join(binDir, "rudder-desktop");
|
|
21947
22141
|
const escaped = paths.executablePath.replaceAll("'", `'"'"'`);
|
|
21948
22142
|
await writeFile3(wrapperPath, `#!/bin/sh
|
|
21949
22143
|
exec '${escaped}' "$@"
|
|
@@ -21951,13 +22145,13 @@ exec '${escaped}' "$@"
|
|
|
21951
22145
|
await chmod(wrapperPath, 493);
|
|
21952
22146
|
}
|
|
21953
22147
|
function buildWindowsShortcutScript(executablePath) {
|
|
21954
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
21955
|
-
const shortcutPath =
|
|
22148
|
+
const appData = process.env.APPDATA?.trim() || path23.join(homedir(), "AppData", "Roaming");
|
|
22149
|
+
const shortcutPath = path23.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Rudder.lnk");
|
|
21956
22150
|
return [
|
|
21957
22151
|
"$shell = New-Object -ComObject WScript.Shell",
|
|
21958
22152
|
`$shortcut = $shell.CreateShortcut(${powershellQuote(shortcutPath)})`,
|
|
21959
22153
|
`$shortcut.TargetPath = ${powershellQuote(executablePath)}`,
|
|
21960
|
-
`$shortcut.WorkingDirectory = ${powershellQuote(
|
|
22154
|
+
`$shortcut.WorkingDirectory = ${powershellQuote(path23.dirname(executablePath))}`,
|
|
21961
22155
|
"$shortcut.Save()"
|
|
21962
22156
|
].join("; ");
|
|
21963
22157
|
}
|
|
@@ -21989,7 +22183,7 @@ function launchDesktop(paths, target) {
|
|
|
21989
22183
|
spawn3(paths.executablePath, [], { detached: true, stdio: "ignore" }).unref();
|
|
21990
22184
|
}
|
|
21991
22185
|
async function writeInstallMetadata(paths, releaseTag, assetName, assetChecksum, assetKind = "full") {
|
|
21992
|
-
|
|
22186
|
+
mkdirSync2(path23.dirname(paths.metadataPath), { recursive: true });
|
|
21993
22187
|
const metadata = {
|
|
21994
22188
|
version: 1,
|
|
21995
22189
|
releaseTag,
|
|
@@ -21998,7 +22192,7 @@ async function writeInstallMetadata(paths, releaseTag, assetName, assetChecksum,
|
|
|
21998
22192
|
assetKind,
|
|
21999
22193
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22000
22194
|
};
|
|
22001
|
-
|
|
22195
|
+
mkdirSync2(paths.installRoot, { recursive: true });
|
|
22002
22196
|
await writeFile3(paths.metadataPath, `${JSON.stringify(metadata, null, 2)}
|
|
22003
22197
|
`, "utf8");
|
|
22004
22198
|
}
|
|
@@ -22044,7 +22238,7 @@ async function startCommand(opts) {
|
|
|
22044
22238
|
if (!exactDesktopAssetPath || !exactDesktopAssetChecksum || !exactDesktopAssetName || !exactDesktopReleaseDigest) {
|
|
22045
22239
|
throw new Error("Exact Desktop asset mode requires path, checksum, asset name, and release digest.");
|
|
22046
22240
|
}
|
|
22047
|
-
if (!
|
|
22241
|
+
if (!path23.isAbsolute(exactDesktopAssetPath) || exactDesktopAssetName.includes("/") || !/^[a-f0-9]{64}$/iu.test(exactDesktopAssetChecksum) || !/^[a-f0-9]{64}$/iu.test(exactDesktopReleaseDigest)) {
|
|
22048
22242
|
throw new Error("Exact Desktop asset mode received invalid candidate identity.");
|
|
22049
22243
|
}
|
|
22050
22244
|
if (opts.desktopAssetKind && opts.desktopAssetKind !== "full" && opts.desktopAssetKind !== "shell") {
|
|
@@ -22122,15 +22316,18 @@ async function startCommand(opts) {
|
|
|
22122
22316
|
}
|
|
22123
22317
|
}
|
|
22124
22318
|
if (installDesktop) {
|
|
22319
|
+
const downloadSource = resolveDesktopDownloadSource(opts.downloadSource);
|
|
22320
|
+
const mirrorBaseUrl = resolveDesktopReleaseMirrorBaseUrl(repo);
|
|
22125
22321
|
const target = resolveDesktopAssetTarget();
|
|
22126
22322
|
const tag = resolveDesktopReleaseTag(version);
|
|
22127
|
-
const installRoot = opts.desktopInstallDir ?
|
|
22323
|
+
const installRoot = opts.desktopInstallDir ? path23.resolve(opts.desktopInstallDir) : resolveDefaultDesktopInstallRoot(target);
|
|
22128
22324
|
const installPaths = resolveDesktopInstallPaths(target, installRoot);
|
|
22129
|
-
const outputDir = opts.outputDir ?
|
|
22325
|
+
const outputDir = opts.outputDir ? path23.resolve(opts.outputDir) : await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
|
|
22130
22326
|
p15.log.step("Installing desktop app");
|
|
22131
22327
|
p15.log.message(`Release: ${pc14.cyan(`${repo}@${tag}`)}`);
|
|
22132
22328
|
p15.log.message(`Target: ${pc14.cyan(`${target.platform}/${target.arch}`)}`);
|
|
22133
22329
|
p15.log.message(`Install: ${pc14.cyan(installPaths.appPath)}`);
|
|
22330
|
+
p15.log.message(`Download source: ${pc14.cyan(downloadSource)}`);
|
|
22134
22331
|
if (dryRun) {
|
|
22135
22332
|
p15.log.message(`[dry-run] Would resolve, download, verify, install, and ${opts.open === false ? "not launch" : "launch"} Rudder Desktop.`);
|
|
22136
22333
|
p15.outro(pc14.green("Dry run complete."));
|
|
@@ -22145,6 +22342,7 @@ async function startCommand(opts) {
|
|
|
22145
22342
|
let cachedAsset = null;
|
|
22146
22343
|
let assetCandidates = [];
|
|
22147
22344
|
let checksums = /* @__PURE__ */ new Map();
|
|
22345
|
+
let downloadOrigins = ["github"];
|
|
22148
22346
|
if (exactDesktopAssetPath) {
|
|
22149
22347
|
releaseTag = tag;
|
|
22150
22348
|
selectedAsset = {
|
|
@@ -22153,7 +22351,7 @@ async function startCommand(opts) {
|
|
|
22153
22351
|
};
|
|
22154
22352
|
selectedAssetKind = opts.desktopAssetKind ?? "full";
|
|
22155
22353
|
expectedChecksum = normalizeDesktopAssetChecksum(exactDesktopAssetChecksum);
|
|
22156
|
-
const computedReleaseDigest =
|
|
22354
|
+
const computedReleaseDigest = createHash4("sha256").update(JSON.stringify({
|
|
22157
22355
|
releaseTag,
|
|
22158
22356
|
assetName: selectedAsset.name,
|
|
22159
22357
|
assetChecksum: expectedChecksum,
|
|
@@ -22170,7 +22368,7 @@ async function startCommand(opts) {
|
|
|
22170
22368
|
if (linkDescriptor.isSymbolicLink()) throw new Error("Exact Desktop asset must not be a symbolic link.");
|
|
22171
22369
|
const checksum = await runStartPhase(
|
|
22172
22370
|
"Verifying staged Desktop checksum...",
|
|
22173
|
-
`Verified ${pc14.cyan(
|
|
22371
|
+
`Verified ${pc14.cyan(path23.basename(exactDesktopAssetPath))}.`,
|
|
22174
22372
|
() => assertChecksumMatch(exactDesktopAssetPath, expectedChecksum),
|
|
22175
22373
|
desktopProgressJson ? "verifying_checksum" : null
|
|
22176
22374
|
);
|
|
@@ -22205,6 +22403,20 @@ async function startCommand(opts) {
|
|
|
22205
22403
|
throw new Error(`No Rudder Desktop portable asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
|
|
22206
22404
|
}
|
|
22207
22405
|
const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(repo, tag, DESKTOP_CHECKSUM_ASSET_NAME) : null);
|
|
22406
|
+
if (!checksumAsset) {
|
|
22407
|
+
throw new Error("Desktop release is missing SHASUMS256.txt.");
|
|
22408
|
+
}
|
|
22409
|
+
downloadOrigins = await resolveDesktopDownloadOrigins({
|
|
22410
|
+
source: downloadSource,
|
|
22411
|
+
checksumAsset,
|
|
22412
|
+
tag: releaseTag,
|
|
22413
|
+
mirrorBaseUrl
|
|
22414
|
+
});
|
|
22415
|
+
if (downloadOrigins[0] === "mirror") {
|
|
22416
|
+
p15.log.message("Using the China release mirror with GitHub fallback.");
|
|
22417
|
+
} else if (downloadSource !== "global" && mirrorBaseUrl) {
|
|
22418
|
+
p15.log.message("Using GitHub for this network path.");
|
|
22419
|
+
}
|
|
22208
22420
|
checksums = await downloadChecksums(checksumAsset, outputDir, progressFactory);
|
|
22209
22421
|
let selectedCandidate;
|
|
22210
22422
|
try {
|
|
@@ -22213,7 +22425,10 @@ async function startCommand(opts) {
|
|
|
22213
22425
|
throw new Error(`No checksummed Rudder Desktop asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
|
|
22214
22426
|
}
|
|
22215
22427
|
for (const warning of selectedCandidate.warnings) p15.log.warn(warning);
|
|
22216
|
-
selectedAsset = selectedCandidate.asset
|
|
22428
|
+
selectedAsset = withDesktopDownloadOrigins(selectedCandidate.asset, downloadOrigins, {
|
|
22429
|
+
mirrorBaseUrl,
|
|
22430
|
+
tag: releaseTag
|
|
22431
|
+
});
|
|
22217
22432
|
selectedAssetKind = selectedCandidate.kind;
|
|
22218
22433
|
expectedChecksum = selectedCandidate.expectedChecksum;
|
|
22219
22434
|
}
|
|
@@ -22241,7 +22456,10 @@ async function startCommand(opts) {
|
|
|
22241
22456
|
p15.log.warn(
|
|
22242
22457
|
`Layered Desktop shell asset download failed; falling back to the full portable asset. ${formatFetchError(error)}`
|
|
22243
22458
|
);
|
|
22244
|
-
selectedAsset = fullCandidate.asset
|
|
22459
|
+
selectedAsset = withDesktopDownloadOrigins(fullCandidate.asset, downloadOrigins, {
|
|
22460
|
+
mirrorBaseUrl,
|
|
22461
|
+
tag: releaseTag
|
|
22462
|
+
});
|
|
22245
22463
|
selectedAssetKind = fullCandidate.kind;
|
|
22246
22464
|
expectedChecksum = resolveAssetChecksum(checksums, selectedAsset.name);
|
|
22247
22465
|
cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
|
|
@@ -22263,7 +22481,7 @@ async function startCommand(opts) {
|
|
|
22263
22481
|
}
|
|
22264
22482
|
const checksum = await runStartPhase(
|
|
22265
22483
|
"Verifying Desktop checksum...",
|
|
22266
|
-
`Verified ${pc14.cyan(
|
|
22484
|
+
`Verified ${pc14.cyan(path23.basename(verifiedAsset.path))}.`,
|
|
22267
22485
|
() => assertChecksumMatch(verifiedAsset.path, expectedChecksum),
|
|
22268
22486
|
desktopProgressJson ? "verifying_checksum" : null
|
|
22269
22487
|
);
|
|
@@ -22274,9 +22492,9 @@ async function startCommand(opts) {
|
|
|
22274
22492
|
percent: 100,
|
|
22275
22493
|
assetName: selectedAsset.name,
|
|
22276
22494
|
assetChecksum: checksum,
|
|
22277
|
-
stagedArtifactPath:
|
|
22495
|
+
stagedArtifactPath: path23.resolve(verifiedAsset.path),
|
|
22278
22496
|
stagedArtifactDigest: checksum,
|
|
22279
|
-
releaseDigest:
|
|
22497
|
+
releaseDigest: createHash4("sha256").update(JSON.stringify({
|
|
22280
22498
|
releaseTag,
|
|
22281
22499
|
assetName: selectedAsset.name,
|
|
22282
22500
|
assetChecksum: checksum,
|
|
@@ -22367,11 +22585,11 @@ async function startCommand(opts) {
|
|
|
22367
22585
|
|
|
22368
22586
|
// src/config/data-dir.ts
|
|
22369
22587
|
init_home();
|
|
22370
|
-
import
|
|
22588
|
+
import path24 from "node:path";
|
|
22371
22589
|
function applyDataDirOverride(options, support = {}) {
|
|
22372
22590
|
const rawDataDir = options.dataDir?.trim();
|
|
22373
22591
|
if (!rawDataDir) return null;
|
|
22374
|
-
const resolvedDataDir =
|
|
22592
|
+
const resolvedDataDir = path24.resolve(expandHomePrefix(rawDataDir));
|
|
22375
22593
|
process.env.RUDDER_HOME = resolvedDataDir;
|
|
22376
22594
|
if (support.hasConfigOption) {
|
|
22377
22595
|
const hasConfigOverride = Boolean(options.config?.trim()) || Boolean(process.env.RUDDER_CONFIG?.trim());
|
|
@@ -22431,7 +22649,7 @@ function createProgram() {
|
|
|
22431
22649
|
});
|
|
22432
22650
|
loadRudderEnvFile(options.config);
|
|
22433
22651
|
});
|
|
22434
|
-
program.command("start").description("Start Rudder Desktop and prepare the matching persistent CLI").option("--server-only", "Prepare the Rudder server runtime and persistent CLI without installing Desktop").option("--no-cli", "Skip persistent CLI installation").option("--no-runtime", "Skip Rudder runtime installation").option("--no-desktop", "Skip desktop app installation").option("--version <version>", "Rudder version to start (default: current CLI version)").option("--target-version <version>", "Rudder version to start; avoids the root CLI version flag").option("--repo <owner/repo>", "GitHub repository that hosts desktop releases").option("--output-dir <path>", "Directory for downloaded desktop release assets").option("--desktop-install-dir <path>", "Directory for the portable Desktop install").option("--no-open", "Install Desktop without launching it").option("--wait-for-active-runs", "Wait for active Rudder runs to finish before replacing Desktop", false).option("--desktop-progress-json", "Emit newline-delimited Desktop update progress events").option("--desktop-wait-for-apply", "Wait for an apply signal after downloading and verifying the Desktop update", false).option("--desktop-prepare-only", "Download and verify the Desktop update without installing or launching it", false).option("--desktop-asset-path <path>", "Use one previously staged, exact Desktop asset path").option("--desktop-asset-checksum <sha256>", "SHA-256 for the exact staged Desktop asset").option("--desktop-asset-name <name>", "Asset name bound to the exact staged Desktop candidate").option("--desktop-asset-kind <kind>", "Asset kind bound to the exact staged Desktop candidate (full or shell)").option("--desktop-release-digest <sha256>", "Release digest bound to the exact staged Desktop candidate").option("--no-version-check", "Skip checking npm for a newer Rudder CLI version").option("--dry-run", "Print the start actions without changing the machine", false).action(startCommand);
|
|
22652
|
+
program.command("start").description("Start Rudder Desktop and prepare the matching persistent CLI").option("--server-only", "Prepare the Rudder server runtime and persistent CLI without installing Desktop").option("--no-cli", "Skip persistent CLI installation").option("--no-runtime", "Skip Rudder runtime installation").option("--no-desktop", "Skip desktop app installation").option("--version <version>", "Rudder version to start (default: current CLI version)").option("--target-version <version>", "Rudder version to start; avoids the root CLI version flag").option("--repo <owner/repo>", "GitHub repository that hosts desktop releases").option("--download-source <source>", "Desktop download source: auto, cn, or global").option("--output-dir <path>", "Directory for downloaded desktop release assets").option("--desktop-install-dir <path>", "Directory for the portable Desktop install").option("--no-open", "Install Desktop without launching it").option("--wait-for-active-runs", "Wait for active Rudder runs to finish before replacing Desktop", false).option("--desktop-progress-json", "Emit newline-delimited Desktop update progress events").option("--desktop-wait-for-apply", "Wait for an apply signal after downloading and verifying the Desktop update", false).option("--desktop-prepare-only", "Download and verify the Desktop update without installing or launching it", false).option("--desktop-asset-path <path>", "Use one previously staged, exact Desktop asset path").option("--desktop-asset-checksum <sha256>", "SHA-256 for the exact staged Desktop asset").option("--desktop-asset-name <name>", "Asset name bound to the exact staged Desktop candidate").option("--desktop-asset-kind <kind>", "Asset kind bound to the exact staged Desktop candidate (full or shell)").option("--desktop-release-digest <sha256>", "Release digest bound to the exact staged Desktop candidate").option("--no-version-check", "Skip checking npm for a newer Rudder CLI version").option("--dry-run", "Print the start actions without changing the machine", false).action(startCommand);
|
|
22435
22653
|
program.command("onboard").description("Interactive first-run setup wizard").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("-y, --yes", "Accept defaults (quickstart + start immediately)", false).option("--run", "Start Rudder immediately after saving config", false).action(onboard);
|
|
22436
22654
|
program.command("doctor").description("Run diagnostic checks on your Rudder setup").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--repair", "Attempt to repair issues automatically").alias("--fix").option("-y, --yes", "Skip repair confirmation prompts").action(async (opts) => {
|
|
22437
22655
|
await doctor(opts);
|