@dbx-tools/core 0.9.11 → 0.9.17
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/README.md +54 -10
- package/index.ts +5 -3
- package/lib/index.d.ts +5 -3
- package/lib/index.js +3 -2
- package/lib/src/bin.d.ts +12 -1
- package/lib/src/bin.js +36 -8
- package/lib/src/dependency-resolver.d.ts +83 -0
- package/lib/src/dependency-resolver.js +515 -0
- package/lib/src/project-utils.d.ts +18 -9
- package/lib/src/project-utils.js +217 -32
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -4
- package/src/bin.ts +52 -8
- package/src/dependency-resolver.ts +654 -0
- package/src/project-utils.ts +233 -38
package/src/bin.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "node:fs/promises";
|
|
19
19
|
import { homedir, tmpdir } from "node:os";
|
|
20
20
|
import { basename, join } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
21
22
|
import { promisify } from "node:util";
|
|
22
23
|
|
|
23
24
|
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
@@ -81,16 +82,31 @@ export interface BinOptions {
|
|
|
81
82
|
versionArgument?: string;
|
|
82
83
|
/** Version output parser. Defaults to {@link parseVersion}. */
|
|
83
84
|
versionParser?: BinVersionParser;
|
|
85
|
+
/** Skip `--version` and treat an executable file as acceptable. */
|
|
86
|
+
skipVersionCheck?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Temporary directory created for one `ensure` attempt after the install lock. */
|
|
90
|
+
export interface BinUrlResolveContext {
|
|
91
|
+
readonly tempDir: string;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
/** Download source resolved only when the executable is not already installed. */
|
|
87
95
|
export interface BinSource {
|
|
88
96
|
url: string;
|
|
89
97
|
sha256?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Skip `--version` on this candidate. Use when the source already identified
|
|
100
|
+
* the requested version, such as `cargo install --version`.
|
|
101
|
+
*/
|
|
102
|
+
trustVersion?: boolean;
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
/** A download source resolved only when the executable is not already installed. */
|
|
93
|
-
export type BinUrl =
|
|
106
|
+
export type BinUrl =
|
|
107
|
+
| string
|
|
108
|
+
| BinSource
|
|
109
|
+
| ((context: BinUrlResolveContext) => string | BinSource | Promise<string | BinSource>);
|
|
94
110
|
|
|
95
111
|
function context(name: string, homeDir: string, destination?: BinContext): BinContext {
|
|
96
112
|
if (!name || basename(name) !== name || name === "." || name === "..") {
|
|
@@ -188,6 +204,10 @@ async function isValidBin(path: string, options: BinOptions): Promise<boolean> {
|
|
|
188
204
|
logger.debug("binary access check failed", { path, ...access });
|
|
189
205
|
return false;
|
|
190
206
|
}
|
|
207
|
+
if (options.skipVersionCheck) {
|
|
208
|
+
logger.debug("binary version check skipped", { path });
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
191
211
|
let stdout: string;
|
|
192
212
|
let stderr: string;
|
|
193
213
|
try {
|
|
@@ -262,6 +282,16 @@ async function selectSingleFile(source: string): Promise<string> {
|
|
|
262
282
|
return selected;
|
|
263
283
|
}
|
|
264
284
|
|
|
285
|
+
function fileUrlPath(url: string): string | undefined {
|
|
286
|
+
try {
|
|
287
|
+
const parsed = new URL(url);
|
|
288
|
+
if (parsed.protocol !== "file:") return undefined;
|
|
289
|
+
return fileURLToPath(parsed);
|
|
290
|
+
} catch {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
265
295
|
async function selectedBin(
|
|
266
296
|
destination: BinContext,
|
|
267
297
|
source: BinSource,
|
|
@@ -269,6 +299,15 @@ async function selectedBin(
|
|
|
269
299
|
options: BinOptions,
|
|
270
300
|
): Promise<string> {
|
|
271
301
|
const { url } = source;
|
|
302
|
+
const localPath = fileUrlPath(url);
|
|
303
|
+
if (localPath) {
|
|
304
|
+
const access = await accessContext(localPath);
|
|
305
|
+
if (!access.file) {
|
|
306
|
+
throw new Error(`binary file URL does not exist: ${displayUrl(url)}`);
|
|
307
|
+
}
|
|
308
|
+
logger.debug("using local binary", { from: displayUrl(url), path: localPath });
|
|
309
|
+
return localPath;
|
|
310
|
+
}
|
|
272
311
|
const name = downloadName(url, basename(destination.path));
|
|
273
312
|
const downloadPath = join(temp, name);
|
|
274
313
|
logger.debug("downloading binary", {
|
|
@@ -347,22 +386,27 @@ export async function ensure(
|
|
|
347
386
|
return destination;
|
|
348
387
|
}
|
|
349
388
|
|
|
350
|
-
const resolved = typeof url === "function" ? await url() : url;
|
|
351
|
-
const source = typeof resolved === "string" ? { url: resolved } : resolved;
|
|
352
|
-
const from = displayUrl(source.url);
|
|
353
389
|
logger.debug("installing binary", {
|
|
354
390
|
name,
|
|
355
|
-
from,
|
|
356
391
|
to: destination.path,
|
|
357
392
|
minVersion: options.minVersion,
|
|
358
393
|
});
|
|
359
394
|
const temp = await mkdtemp(join(tmpdir(), `${name}-`));
|
|
360
395
|
let staged: string | undefined;
|
|
361
396
|
try {
|
|
397
|
+
const resolved = typeof url === "function" ? await url({ tempDir: temp }) : url;
|
|
398
|
+
const source = typeof resolved === "string" ? { url: resolved } : resolved;
|
|
399
|
+
const from = displayUrl(source.url);
|
|
400
|
+
logger.debug("resolved binary source", { name, from });
|
|
362
401
|
const selected = await selectedBin(destination, source, temp, options);
|
|
363
402
|
await chmod(selected, 0o755);
|
|
364
|
-
|
|
365
|
-
|
|
403
|
+
const accepted = source.trustVersion ? { ...options, skipVersionCheck: true } : options;
|
|
404
|
+
if (!(await isValidBin(selected, accepted))) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
source.trustVersion
|
|
407
|
+
? `selected binary is not executable: ${selected}`
|
|
408
|
+
: `selected binary has no acceptable version: ${selected}`,
|
|
409
|
+
);
|
|
366
410
|
}
|
|
367
411
|
|
|
368
412
|
await mkdir(destination.binDir, { recursive: true });
|
|
@@ -371,7 +415,7 @@ export async function ensure(
|
|
|
371
415
|
await chmod(staged, 0o755);
|
|
372
416
|
await rename(staged, destination.path);
|
|
373
417
|
staged = undefined;
|
|
374
|
-
if (!(await isValidBin(destination.path,
|
|
418
|
+
if (!(await isValidBin(destination.path, accepted))) {
|
|
375
419
|
throw new Error(`installed binary is invalid after rename: ${destination.path}`);
|
|
376
420
|
}
|
|
377
421
|
logger.info("installed binary", {
|