pyfig 0.6.3 → 0.7.1
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 +77 -166
- package/SECURITY.md +16 -5
- package/dist/cjs/index.js +471 -980
- package/dist/cjs/index.js.map +6 -15
- package/dist/index.d.ts +2 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/process.d.ts +6 -12
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +35 -54
- package/dist/process.js.map +1 -1
- package/dist/pyfig.d.ts +31 -0
- package/dist/pyfig.d.ts.map +1 -0
- package/dist/pyfig.js +275 -0
- package/dist/pyfig.js.map +1 -0
- package/dist/uv.d.ts +3 -0
- package/dist/uv.d.ts.map +1 -0
- package/dist/uv.js +423 -0
- package/dist/uv.js.map +1 -0
- package/package.json +6 -12
- package/dist/cjs/sync-resolver.js +0 -1217
- package/dist/cjs/sync-resolver.js.map +0 -19
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -48
- package/dist/cli.js.map +0 -1
- package/dist/client.d.ts +0 -15
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -25
- package/dist/client.js.map +0 -1
- package/dist/config.d.ts +0 -62
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -112
- package/dist/config.js.map +0 -1
- package/dist/package-target.d.ts +0 -8
- package/dist/package-target.d.ts.map +0 -1
- package/dist/package-target.js +0 -81
- package/dist/package-target.js.map +0 -1
- package/dist/pep440.d.ts +0 -12
- package/dist/pep440.d.ts.map +0 -1
- package/dist/pep440.js +0 -21
- package/dist/pep440.js.map +0 -1
- package/dist/probes.d.ts +0 -37
- package/dist/probes.d.ts.map +0 -1
- package/dist/probes.js +0 -145
- package/dist/probes.js.map +0 -1
- package/dist/project-root.d.ts +0 -3
- package/dist/project-root.d.ts.map +0 -1
- package/dist/project-root.js +0 -22
- package/dist/project-root.js.map +0 -1
- package/dist/provision-lock.d.ts +0 -10
- package/dist/provision-lock.d.ts.map +0 -1
- package/dist/provision-lock.js +0 -24
- package/dist/provision-lock.js.map +0 -1
- package/dist/provision.d.ts +0 -26
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -685
- package/dist/provision.js.map +0 -1
- package/dist/runners.d.ts +0 -14
- package/dist/runners.d.ts.map +0 -1
- package/dist/runners.js +0 -71
- package/dist/runners.js.map +0 -1
- package/dist/runtime-files.d.ts +0 -3
- package/dist/runtime-files.d.ts.map +0 -1
- package/dist/runtime-files.js +0 -12
- package/dist/runtime-files.js.map +0 -1
- package/dist/sync-resolver.d.ts +0 -2
- package/dist/sync-resolver.d.ts.map +0 -1
- package/dist/sync-resolver.js +0 -15
- package/dist/sync-resolver.js.map +0 -1
- package/dist/virtual-environment.d.ts +0 -11
- package/dist/virtual-environment.d.ts.map +0 -1
- package/dist/virtual-environment.js +0 -49
- package/dist/virtual-environment.js.map +0 -1
|
@@ -1,1217 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
function __accessProp(key) {
|
|
8
|
-
return this[key];
|
|
9
|
-
}
|
|
10
|
-
var __toESMCache_node;
|
|
11
|
-
var __toESMCache_esm;
|
|
12
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
-
var canCache = mod != null && typeof mod === "object";
|
|
14
|
-
if (canCache) {
|
|
15
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
-
var cached = cache.get(mod);
|
|
17
|
-
if (cached)
|
|
18
|
-
return cached;
|
|
19
|
-
}
|
|
20
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
-
const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
-
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
23
|
-
for (let key of __getOwnPropNames(mod))
|
|
24
|
-
if (!__hasOwnProp.call(to, key))
|
|
25
|
-
__defProp(to, key, {
|
|
26
|
-
get: __accessProp.bind(mod, key),
|
|
27
|
-
enumerable: true
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
if (canCache)
|
|
31
|
-
cache.set(mod, to);
|
|
32
|
-
return to;
|
|
33
|
-
};
|
|
34
|
-
var __toCommonJS = (from) => {
|
|
35
|
-
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
36
|
-
if (entry)
|
|
37
|
-
return entry;
|
|
38
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
39
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
40
|
-
for (var key of __getOwnPropNames(from))
|
|
41
|
-
if (!__hasOwnProp.call(entry, key))
|
|
42
|
-
__defProp(entry, key, {
|
|
43
|
-
get: __accessProp.bind(from, key),
|
|
44
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
__moduleCache.set(from, entry);
|
|
48
|
-
return entry;
|
|
49
|
-
};
|
|
50
|
-
var __moduleCache;
|
|
51
|
-
var __returnValue = (v) => v;
|
|
52
|
-
function __exportSetter(name, newValue) {
|
|
53
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
54
|
-
}
|
|
55
|
-
var __export = (target, all) => {
|
|
56
|
-
for (var name in all)
|
|
57
|
-
__defProp(target, name, {
|
|
58
|
-
get: all[name],
|
|
59
|
-
enumerable: true,
|
|
60
|
-
configurable: true,
|
|
61
|
-
set: __exportSetter.bind(all, name)
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// src/sync-resolver.ts
|
|
66
|
-
var import_node_fs5 = require("node:fs");
|
|
67
|
-
|
|
68
|
-
// src/provision.ts
|
|
69
|
-
var import_node_crypto = require("node:crypto");
|
|
70
|
-
var import_node_fs3 = require("node:fs");
|
|
71
|
-
var import_promises = require("node:fs/promises");
|
|
72
|
-
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
73
|
-
var import_node_os = __toESM(require("node:os"), 1);
|
|
74
|
-
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
75
|
-
var import_node_stream = require("node:stream");
|
|
76
|
-
var import_promises2 = require("node:stream/promises");
|
|
77
|
-
var import_fflate = require("fflate");
|
|
78
|
-
var tar = __toESM(require("tar"), 1);
|
|
79
|
-
var import_undici = require("undici");
|
|
80
|
-
|
|
81
|
-
// src/config.ts
|
|
82
|
-
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
83
|
-
var envPaths = __toESM(require("env-paths"), 1);
|
|
84
|
-
|
|
85
|
-
// src/pep440.ts
|
|
86
|
-
var import_pep440 = require("@renovatebot/pep440");
|
|
87
|
-
function isValidPep440Specifier(specifier) {
|
|
88
|
-
return import_pep440.validRange(specifier);
|
|
89
|
-
}
|
|
90
|
-
function normalizePep440Version(version) {
|
|
91
|
-
return import_pep440.valid(version);
|
|
92
|
-
}
|
|
93
|
-
function pep440VersionSatisfies(version, specifier) {
|
|
94
|
-
return import_pep440.filter([version], specifier).length === 1;
|
|
95
|
-
}
|
|
96
|
-
function maxPep440Version(versions, specifier) {
|
|
97
|
-
return import_pep440.maxSatisfying([...versions], specifier);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/project-root.ts
|
|
101
|
-
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
102
|
-
var import_node_path = __toESM(require("node:path"), 1);
|
|
103
|
-
function resolveProjectRoot(startDirectory = process.cwd()) {
|
|
104
|
-
const start = import_node_path.default.resolve(startDirectory);
|
|
105
|
-
for (const directory of ancestors(start)) {
|
|
106
|
-
if (import_node_fs.default.existsSync(import_node_path.default.join(directory, ".git")))
|
|
107
|
-
return directory;
|
|
108
|
-
}
|
|
109
|
-
return start;
|
|
110
|
-
}
|
|
111
|
-
function* ancestors(startDirectory) {
|
|
112
|
-
let directory = startDirectory;
|
|
113
|
-
while (true) {
|
|
114
|
-
yield directory;
|
|
115
|
-
const parent = import_node_path.default.dirname(directory);
|
|
116
|
-
if (parent === directory)
|
|
117
|
-
return;
|
|
118
|
-
directory = parent;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// src/config.ts
|
|
123
|
-
var DEFAULT_REQUIRES_PYTHON = ">=3.10,<4";
|
|
124
|
-
var DEFAULT_MANAGED_PYTHON = "3.12";
|
|
125
|
-
var DEFAULT_REQUIRES_UV = ">=0.10,<1";
|
|
126
|
-
var DEFAULT_PROBE_TIMEOUT_MS = 1e4;
|
|
127
|
-
var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60000;
|
|
128
|
-
var DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60000;
|
|
129
|
-
var envPathsFunction = (() => {
|
|
130
|
-
const imported = envPaths.default;
|
|
131
|
-
const implementation = typeof imported === "function" ? imported : imported.default;
|
|
132
|
-
if (!implementation)
|
|
133
|
-
throw new TypeError("env-paths did not expose its default function.");
|
|
134
|
-
return implementation;
|
|
135
|
-
})();
|
|
136
|
-
function resolveOptions(options = {}) {
|
|
137
|
-
const env = { ...options.env ?? process.env };
|
|
138
|
-
const requiresPython = options.requiresPython ?? env["PYFIG_REQUIRES_PYTHON"] ?? DEFAULT_REQUIRES_PYTHON;
|
|
139
|
-
const requiresUv = options.requiresUv ?? env["PYFIG_REQUIRES_UV"] ?? DEFAULT_REQUIRES_UV;
|
|
140
|
-
assertPep440Specifier("requires-python", requiresPython);
|
|
141
|
-
assertPep440Specifier("requires-uv", requiresUv);
|
|
142
|
-
const managedPython = options.managedPython ?? env["PYFIG_MANAGED_PYTHON"] ?? DEFAULT_MANAGED_PYTHON;
|
|
143
|
-
assertManagedPythonRequest(managedPython, requiresPython);
|
|
144
|
-
const probeTimeoutMs = resolveDuration("probeTimeoutMs", options.probeTimeoutMs, env["PYFIG_PROBE_TIMEOUT_MS"], DEFAULT_PROBE_TIMEOUT_MS);
|
|
145
|
-
const installTimeoutMs = resolveDuration("installTimeoutMs", options.installTimeoutMs, env["PYFIG_INSTALL_TIMEOUT_MS"], DEFAULT_INSTALL_TIMEOUT_MS);
|
|
146
|
-
const downloadTimeoutMs = resolveDuration("downloadTimeoutMs", options.downloadTimeoutMs, env["PYFIG_DOWNLOAD_TIMEOUT_MS"], DEFAULT_DOWNLOAD_TIMEOUT_MS);
|
|
147
|
-
const coordinationTimeoutMs = resolveDuration("coordinationTimeoutMs", options.coordinationTimeoutMs, env["PYFIG_COORDINATION_TIMEOUT_MS"], installTimeoutMs + downloadTimeoutMs + 60000);
|
|
148
|
-
const configuredHome = options.cacheDir ?? env["PYFIG_HOME"];
|
|
149
|
-
const cacheDir = configuredHome ? import_node_path2.default.resolve(configuredHome) : envPathsFunction("pyfig", { suffix: "" }).cache;
|
|
150
|
-
const configuredProjectRoot = options.projectRoot ?? env["PYFIG_PROJECT_ROOT"];
|
|
151
|
-
const projectRoot = configuredProjectRoot ? import_node_path2.default.resolve(configuredProjectRoot) : resolveProjectRoot(env["INIT_CWD"] ?? process.cwd());
|
|
152
|
-
const autoInstall = options.autoInstall ?? parseBoolean(env["PYFIG_AUTO_INSTALL"], true);
|
|
153
|
-
return {
|
|
154
|
-
python: options.python ?? env["PYFIG_PYTHON"] ?? "",
|
|
155
|
-
requiresPython,
|
|
156
|
-
managedPython,
|
|
157
|
-
requiresUv,
|
|
158
|
-
requireEmbedding: options.requireEmbedding ?? parseBoolean(env["PYFIG_REQUIRE_EMBEDDING"], false),
|
|
159
|
-
requireGil: options.requireGil ?? parseBoolean(env["PYFIG_REQUIRE_GIL"], false),
|
|
160
|
-
autoInstall,
|
|
161
|
-
installPython: options.installPython ?? parseBoolean(env["PYFIG_INSTALL_PYTHON"], autoInstall),
|
|
162
|
-
installUv: options.installUv ?? parseBoolean(env["PYFIG_INSTALL_UV"], autoInstall),
|
|
163
|
-
createVirtualEnvironment: options.createVirtualEnvironment ?? parseBoolean(env["PYFIG_CREATE_VIRTUAL_ENVIRONMENT"], autoInstall),
|
|
164
|
-
cacheDir,
|
|
165
|
-
projectRoot,
|
|
166
|
-
probeTimeoutMs,
|
|
167
|
-
installTimeoutMs,
|
|
168
|
-
downloadTimeoutMs,
|
|
169
|
-
coordinationTimeoutMs,
|
|
170
|
-
env
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function uvEnvironment(options) {
|
|
174
|
-
return {
|
|
175
|
-
...options.env,
|
|
176
|
-
UV_PYTHON_INSTALL_DIR: import_node_path2.default.join(options.cacheDir, "python"),
|
|
177
|
-
UV_CACHE_DIR: import_node_path2.default.join(options.cacheDir, "uv-cache"),
|
|
178
|
-
UV_PYTHON_PREFERENCE: "system",
|
|
179
|
-
UV_NO_CONFIG: "1"
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function assertPep440Specifier(name, specifier) {
|
|
183
|
-
if (!isValidPep440Specifier(specifier)) {
|
|
184
|
-
throw new TypeError(`Invalid ${name} PEP 440 specifier: ${specifier}`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function assertManagedPythonRequest(request, requiresPython) {
|
|
188
|
-
if (!request || /[\\/\0\r\n]/u.test(request) || request.includes("..")) {
|
|
189
|
-
throw new TypeError(`Invalid managed Python request: ${request}`);
|
|
190
|
-
}
|
|
191
|
-
if (/^\d+\.\d+(?:\.\d+)?$/u.test(request)) {
|
|
192
|
-
const normalized = request.split(".").length === 2 ? `${request}.0` : request;
|
|
193
|
-
if (!pep440VersionSatisfies(normalized, requiresPython)) {
|
|
194
|
-
throw new TypeError(`Managed Python ${request} cannot satisfy requires-python ${requiresPython}.`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function resolveDuration(name, explicit, environment, fallback, allowZero = false) {
|
|
199
|
-
const value = explicit ?? (environment === undefined ? fallback : Number(environment));
|
|
200
|
-
const minimum = allowZero ? 0 : 1;
|
|
201
|
-
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
202
|
-
throw new TypeError(`${name} must be ${allowZero ? "a non-negative" : "a positive"} integer.`);
|
|
203
|
-
}
|
|
204
|
-
return value;
|
|
205
|
-
}
|
|
206
|
-
function parseBoolean(value, fallback) {
|
|
207
|
-
if (value === undefined)
|
|
208
|
-
return fallback;
|
|
209
|
-
if (/^(1|true|yes|on)$/iu.test(value))
|
|
210
|
-
return true;
|
|
211
|
-
if (/^(0|false|no|off)$/iu.test(value))
|
|
212
|
-
return false;
|
|
213
|
-
throw new TypeError(`Invalid boolean value: ${value}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// src/provision-lock.ts
|
|
217
|
-
var import_loqkit = require("loqkit");
|
|
218
|
-
async function withProvisioningLock(options) {
|
|
219
|
-
let existing;
|
|
220
|
-
const result = await import_loqkit.withLock({ resource: options.key }, options.mutate, {
|
|
221
|
-
key: options.key,
|
|
222
|
-
name: "Pyfig provisioning",
|
|
223
|
-
namespace: "pyfig",
|
|
224
|
-
timeout: options.timeoutMs,
|
|
225
|
-
predicate: async () => {
|
|
226
|
-
const value = await options.check();
|
|
227
|
-
if (value === null)
|
|
228
|
-
return true;
|
|
229
|
-
existing = { value };
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
if (result.context.state === "executed")
|
|
234
|
-
return result.value;
|
|
235
|
-
if (existing)
|
|
236
|
-
return existing.value;
|
|
237
|
-
throw new Error(`Provisioning check for ${options.key} did not produce a resource.`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// src/virtual-environment.ts
|
|
241
|
-
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
242
|
-
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
243
|
-
var VIRTUAL_ENV_DIRECTORY_NAME = import_node_path3.default.join("node_modules", ".pyfig", "venv");
|
|
244
|
-
function virtualEnvironmentDirectory(projectRoot) {
|
|
245
|
-
return import_node_path3.default.join(projectRoot, VIRTUAL_ENV_DIRECTORY_NAME);
|
|
246
|
-
}
|
|
247
|
-
function externalVirtualEnvironmentDirectories(projectRoot, env) {
|
|
248
|
-
const candidates = [env["VIRTUAL_ENV"], import_node_path3.default.join(projectRoot, ".venv")].filter((value) => Boolean(value)).map((value) => import_node_path3.default.resolve(value));
|
|
249
|
-
return [...new Map(candidates.map((value) => [canonicalPath(value), value])).values()];
|
|
250
|
-
}
|
|
251
|
-
function virtualEnvironmentPythonExecutable(virtualEnvironment) {
|
|
252
|
-
return import_node_path3.default.join(virtualEnvironment, process.platform === "win32" ? "Scripts" : "bin", process.platform === "win32" ? "python.exe" : "python");
|
|
253
|
-
}
|
|
254
|
-
function activateVirtualEnvironment(env, virtualEnvironment, pythonExecutable) {
|
|
255
|
-
const activated = { ...env };
|
|
256
|
-
const pathEntry = Object.keys(activated).find((key) => key.toUpperCase() === "PATH");
|
|
257
|
-
const existingPath = pathEntry ? activated[pathEntry] : undefined;
|
|
258
|
-
for (const key of Object.keys(activated)) {
|
|
259
|
-
const normalized = key.toUpperCase();
|
|
260
|
-
if (normalized === "PATH" || normalized === "PYTHONHOME" || normalized === "VIRTUAL_ENV") {
|
|
261
|
-
delete activated[key];
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
activated["PATH"] = [import_node_path3.default.dirname(pythonExecutable), existingPath].filter((value) => Boolean(value)).join(import_node_path3.default.delimiter);
|
|
265
|
-
activated["VIRTUAL_ENV"] = virtualEnvironment;
|
|
266
|
-
return activated;
|
|
267
|
-
}
|
|
268
|
-
function pathsEqual(left, right) {
|
|
269
|
-
return canonicalPath(left) === canonicalPath(right);
|
|
270
|
-
}
|
|
271
|
-
function canonicalPath(value) {
|
|
272
|
-
const resolved = import_node_path3.default.resolve(value);
|
|
273
|
-
let canonical = resolved;
|
|
274
|
-
try {
|
|
275
|
-
canonical = import_node_fs2.default.realpathSync.native(resolved);
|
|
276
|
-
} catch {}
|
|
277
|
-
return process.platform === "win32" ? canonical.toLowerCase() : canonical;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// src/package-target.ts
|
|
281
|
-
var TARGET_OPTIONS = new Set([
|
|
282
|
-
"--active",
|
|
283
|
-
"--config-file",
|
|
284
|
-
"--directory",
|
|
285
|
-
"--isolated",
|
|
286
|
-
"--prefix",
|
|
287
|
-
"--project",
|
|
288
|
-
"--python",
|
|
289
|
-
"--root",
|
|
290
|
-
"--system",
|
|
291
|
-
"--target",
|
|
292
|
-
"--user"
|
|
293
|
-
]);
|
|
294
|
-
var TARGET_ENVIRONMENT_VARIABLES = new Set([
|
|
295
|
-
"CONDA_PREFIX",
|
|
296
|
-
"PIP_PREFIX",
|
|
297
|
-
"PIP_PYTHON",
|
|
298
|
-
"PIP_ROOT",
|
|
299
|
-
"PIP_TARGET",
|
|
300
|
-
"PIP_USER",
|
|
301
|
-
"PYTHONHOME",
|
|
302
|
-
"PYTHONUSERBASE",
|
|
303
|
-
"UV_CONFIG_FILE",
|
|
304
|
-
"UV_PROJECT",
|
|
305
|
-
"UV_PROJECT_ENVIRONMENT",
|
|
306
|
-
"UV_PYTHON",
|
|
307
|
-
"UV_PREFIX",
|
|
308
|
-
"UV_ROOT",
|
|
309
|
-
"UV_SYSTEM_PYTHON",
|
|
310
|
-
"UV_TARGET"
|
|
311
|
-
]);
|
|
312
|
-
function uvCommandNeedsEnvironment(args) {
|
|
313
|
-
return args[0] === "pip" || args[0] === "run";
|
|
314
|
-
}
|
|
315
|
-
function targetUvArguments(args, python) {
|
|
316
|
-
if (!python.virtualEnvironment) {
|
|
317
|
-
throw new Error("Targeted uv commands require a virtual environment.");
|
|
318
|
-
}
|
|
319
|
-
assertNoTargetOverride(args);
|
|
320
|
-
if (args[0] === "run") {
|
|
321
|
-
return ["run", "--no-project", "--python", python.executable, ...args.slice(1)];
|
|
322
|
-
}
|
|
323
|
-
if (args[0] === "pip" && args[1]) {
|
|
324
|
-
return ["pip", args[1], "--python", python.executable, ...args.slice(2)];
|
|
325
|
-
}
|
|
326
|
-
return [...args];
|
|
327
|
-
}
|
|
328
|
-
function uvCommandEnvironment(env, python) {
|
|
329
|
-
const sanitized = sanitizeUvEnvironment(env);
|
|
330
|
-
if (!python?.virtualEnvironment)
|
|
331
|
-
return sanitized;
|
|
332
|
-
return {
|
|
333
|
-
...activateVirtualEnvironment(sanitized, python.virtualEnvironment, python.executable),
|
|
334
|
-
UV_PYTHON: python.executable
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
function sanitizeUvEnvironment(env) {
|
|
338
|
-
const sanitized = { ...env };
|
|
339
|
-
for (const key of Object.keys(sanitized)) {
|
|
340
|
-
if (TARGET_ENVIRONMENT_VARIABLES.has(key.toUpperCase()))
|
|
341
|
-
delete sanitized[key];
|
|
342
|
-
}
|
|
343
|
-
return sanitized;
|
|
344
|
-
}
|
|
345
|
-
function assertNoTargetOverride(args) {
|
|
346
|
-
for (const argument of args.slice(1)) {
|
|
347
|
-
if (argument === "--")
|
|
348
|
-
return;
|
|
349
|
-
const [name] = argument.split("=", 1);
|
|
350
|
-
if (name && TARGET_OPTIONS.has(name)) {
|
|
351
|
-
throw new Error(`${name} cannot override the environment selected by Pyfig.`);
|
|
352
|
-
}
|
|
353
|
-
if (argument === "-p" || argument.startsWith("-p=") || /^-p[^-]/u.test(argument)) {
|
|
354
|
-
throw new Error("-p cannot override the environment selected by Pyfig.");
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/process.ts
|
|
360
|
-
var import_node_child_process = require("node:child_process");
|
|
361
|
-
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
362
|
-
var import_which = __toESM(require("which"), 1);
|
|
363
|
-
async function resolveExecutable(value, env) {
|
|
364
|
-
if (!value)
|
|
365
|
-
return null;
|
|
366
|
-
if (import_node_path4.default.isAbsolute(value)) {
|
|
367
|
-
return await import_which.default(value, { nothrow: true, path: env["PATH"], pathExt: env["PATHEXT"] });
|
|
368
|
-
}
|
|
369
|
-
if (!/^[A-Za-z0-9_.-]+$/u.test(value))
|
|
370
|
-
return null;
|
|
371
|
-
return await import_which.default(value, { nothrow: true, path: env["PATH"], pathExt: env["PATHEXT"] });
|
|
372
|
-
}
|
|
373
|
-
function run(executable, args, env, timeoutMs) {
|
|
374
|
-
const result = import_node_child_process.spawnSync(executable, args, {
|
|
375
|
-
encoding: "utf8",
|
|
376
|
-
env,
|
|
377
|
-
timeout: timeoutMs,
|
|
378
|
-
maxBuffer: 4 * 1024 * 1024,
|
|
379
|
-
windowsHide: true
|
|
380
|
-
});
|
|
381
|
-
if (result.status !== 0 || result.error) {
|
|
382
|
-
const detail = result.stderr?.trim() || result.error?.message || `exit ${result.status}`;
|
|
383
|
-
throw new Error(`${import_node_path4.default.basename(executable)} ${args.slice(0, 3).join(" ")} failed: ${detail}`);
|
|
384
|
-
}
|
|
385
|
-
return { stdout: result.stdout, stderr: result.stderr };
|
|
386
|
-
}
|
|
387
|
-
async function runAsync(executable, args, env, timeoutMs, signal, cwd) {
|
|
388
|
-
return new Promise((resolve, reject) => {
|
|
389
|
-
const child = import_node_child_process.spawn(executable, args, {
|
|
390
|
-
env,
|
|
391
|
-
cwd,
|
|
392
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
393
|
-
windowsHide: true
|
|
394
|
-
});
|
|
395
|
-
let stdout = "";
|
|
396
|
-
let stderr = "";
|
|
397
|
-
let settled = false;
|
|
398
|
-
const finish = (error, result) => {
|
|
399
|
-
if (settled)
|
|
400
|
-
return;
|
|
401
|
-
settled = true;
|
|
402
|
-
clearTimeout(timer);
|
|
403
|
-
signal?.removeEventListener("abort", abort);
|
|
404
|
-
if (error)
|
|
405
|
-
reject(error);
|
|
406
|
-
else if (result)
|
|
407
|
-
resolve(result);
|
|
408
|
-
};
|
|
409
|
-
const abort = () => {
|
|
410
|
-
child.kill();
|
|
411
|
-
const reason = signal?.reason;
|
|
412
|
-
finish(reason instanceof Error ? reason : new Error(`${import_node_path4.default.basename(executable)} was aborted.`));
|
|
413
|
-
};
|
|
414
|
-
const timer = setTimeout(() => {
|
|
415
|
-
child.kill();
|
|
416
|
-
finish(new Error(`${import_node_path4.default.basename(executable)} timed out after ${timeoutMs}ms.`));
|
|
417
|
-
}, timeoutMs);
|
|
418
|
-
child.stdout.setEncoding("utf8");
|
|
419
|
-
child.stderr.setEncoding("utf8");
|
|
420
|
-
child.stdout.on("data", (chunk) => {
|
|
421
|
-
stdout += chunk;
|
|
422
|
-
if (stdout.length > 4 * 1024 * 1024) {
|
|
423
|
-
child.kill();
|
|
424
|
-
finish(new Error(`${import_node_path4.default.basename(executable)} exceeded the output limit.`));
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
child.stderr.on("data", (chunk) => {
|
|
428
|
-
stderr += chunk;
|
|
429
|
-
if (stderr.length > 4 * 1024 * 1024) {
|
|
430
|
-
child.kill();
|
|
431
|
-
finish(new Error(`${import_node_path4.default.basename(executable)} exceeded the error-output limit.`));
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
child.once("error", (error) => finish(error));
|
|
435
|
-
child.once("close", (code, childSignal) => {
|
|
436
|
-
if (code !== 0) {
|
|
437
|
-
const detail = stderr.trim() || (childSignal ? `signal ${childSignal}` : `exit ${code}`);
|
|
438
|
-
finish(new Error(`${import_node_path4.default.basename(executable)} ${args.slice(0, 3).join(" ")} failed: ${detail}`));
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
finish(undefined, { stdout, stderr });
|
|
442
|
-
});
|
|
443
|
-
if (signal?.aborted)
|
|
444
|
-
abort();
|
|
445
|
-
else
|
|
446
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// src/probes.ts
|
|
451
|
-
var PYTHON_PROBE = `
|
|
452
|
-
import json
|
|
453
|
-
import os
|
|
454
|
-
import platform
|
|
455
|
-
import struct
|
|
456
|
-
import sys
|
|
457
|
-
import sysconfig
|
|
458
|
-
|
|
459
|
-
base_prefix = getattr(sys, "base_prefix", sys.prefix)
|
|
460
|
-
include_directory = sysconfig.get_path("include") or ""
|
|
461
|
-
header_path = os.path.join(include_directory, "Python.h") if include_directory else ""
|
|
462
|
-
library_candidates = []
|
|
463
|
-
stable_abi_library_candidates = []
|
|
464
|
-
if os.name == "nt":
|
|
465
|
-
library_directory = sysconfig.get_config_var("LIBDIR") or os.path.join(base_prefix, "libs")
|
|
466
|
-
configured_library = sysconfig.get_config_var("LDLIBRARY") or ""
|
|
467
|
-
if configured_library:
|
|
468
|
-
library_candidates.append(os.path.join(library_directory, os.path.splitext(configured_library)[0] + ".lib"))
|
|
469
|
-
library_candidates.append(os.path.join(library_directory, f"python{sys.version_info.major}{sys.version_info.minor}.lib"))
|
|
470
|
-
stable_abi_library_candidates.append(os.path.join(library_directory, "python3.lib"))
|
|
471
|
-
else:
|
|
472
|
-
library_directories = [
|
|
473
|
-
sysconfig.get_config_var("LIBDIR"),
|
|
474
|
-
sysconfig.get_config_var("LIBPL"),
|
|
475
|
-
os.path.join(base_prefix, "lib"),
|
|
476
|
-
]
|
|
477
|
-
library_names = [
|
|
478
|
-
sysconfig.get_config_var("LDLIBRARY"),
|
|
479
|
-
sysconfig.get_config_var("LIBRARY"),
|
|
480
|
-
]
|
|
481
|
-
for directory in library_directories:
|
|
482
|
-
for name in library_names:
|
|
483
|
-
if directory and name:
|
|
484
|
-
library_candidates.append(os.path.join(directory, name))
|
|
485
|
-
if directory:
|
|
486
|
-
stable_abi_library_candidates.extend([
|
|
487
|
-
os.path.join(directory, "libpython3.so"),
|
|
488
|
-
os.path.join(directory, "libpython3.dylib"),
|
|
489
|
-
])
|
|
490
|
-
|
|
491
|
-
library_path = next((candidate for candidate in library_candidates if os.path.isfile(candidate)), "")
|
|
492
|
-
stable_abi_library_path = next(
|
|
493
|
-
(candidate for candidate in stable_abi_library_candidates if os.path.isfile(candidate)),
|
|
494
|
-
None,
|
|
495
|
-
)
|
|
496
|
-
embedding = None
|
|
497
|
-
if os.path.isfile(header_path) and library_path:
|
|
498
|
-
library_name = os.path.basename(library_path)
|
|
499
|
-
library_kind = "import" if os.name == "nt" else ("static" if library_name.endswith(".a") else "shared")
|
|
500
|
-
embedding = {
|
|
501
|
-
"includeDirectory": include_directory,
|
|
502
|
-
"headerPath": header_path,
|
|
503
|
-
"libraryPath": library_path,
|
|
504
|
-
"libraryDirectory": os.path.dirname(library_path),
|
|
505
|
-
"libraryName": library_name,
|
|
506
|
-
"libraryKind": library_kind,
|
|
507
|
-
"stableAbiLibraryPath": stable_abi_library_path,
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
release_level = sys.version_info.releaselevel
|
|
511
|
-
release_suffixes = {
|
|
512
|
-
"alpha": "a",
|
|
513
|
-
"beta": "b",
|
|
514
|
-
"candidate": "rc",
|
|
515
|
-
"final": "",
|
|
516
|
-
}
|
|
517
|
-
release_suffix = release_suffixes[release_level]
|
|
518
|
-
if release_suffix:
|
|
519
|
-
release_suffix += str(sys.version_info.serial)
|
|
520
|
-
|
|
521
|
-
print(json.dumps({
|
|
522
|
-
"executable": sys.executable,
|
|
523
|
-
"baseExecutable": getattr(sys, "_base_executable", sys.executable),
|
|
524
|
-
"version": ".".join(map(str, sys.version_info[:3])) + release_suffix,
|
|
525
|
-
"major": sys.version_info.major,
|
|
526
|
-
"minor": sys.version_info.minor,
|
|
527
|
-
"patch": sys.version_info.micro,
|
|
528
|
-
"releaseLevel": release_level,
|
|
529
|
-
"implementation": sys.implementation.name,
|
|
530
|
-
"architecture": platform.machine().lower(),
|
|
531
|
-
"pointerBits": struct.calcsize("P") * 8,
|
|
532
|
-
"prefix": sys.prefix,
|
|
533
|
-
"basePrefix": base_prefix,
|
|
534
|
-
"virtualEnvironment": sys.prefix if sys.prefix != base_prefix else None,
|
|
535
|
-
"gilDisabled": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
|
|
536
|
-
"embedding": embedding,
|
|
537
|
-
}))
|
|
538
|
-
`;
|
|
539
|
-
function pythonSatisfiesPolicy(python, options) {
|
|
540
|
-
return pep440VersionSatisfies(python.version, options.requiresPython) && (!options.requireEmbedding || python.implementation === "cpython" && python.embedding !== null && pythonArchitectureMatchesRuntime(python)) && (!options.requireGil || !python.gilDisabled);
|
|
541
|
-
}
|
|
542
|
-
async function probePython(candidate, options) {
|
|
543
|
-
const executable = await resolveExecutable(candidate.executable, options.env);
|
|
544
|
-
if (!executable)
|
|
545
|
-
return null;
|
|
546
|
-
try {
|
|
547
|
-
const { stdout } = run(executable, [...candidate.args, "-c", PYTHON_PROBE], options.env, options.probeTimeoutMs);
|
|
548
|
-
const info = JSON.parse(stdout.trim());
|
|
549
|
-
return normalizePep440Version(info.version) ? info : null;
|
|
550
|
-
} catch {
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
async function findUv(options) {
|
|
555
|
-
const configured = options.env["PYFIG_UV"] ?? "uv";
|
|
556
|
-
const executable = await resolveExecutable(configured, options.env);
|
|
557
|
-
const uv = executable ? probeUv(executable, options) : null;
|
|
558
|
-
return uv && pep440VersionSatisfies(uv.version, options.requiresUv) ? uv : null;
|
|
559
|
-
}
|
|
560
|
-
function probeUv(executable, options) {
|
|
561
|
-
try {
|
|
562
|
-
const { stdout } = run(executable, ["--version"], options.env, options.probeTimeoutMs);
|
|
563
|
-
const version = normalizeVersion(stdout.match(/^uv\s+(\S+)/iu)?.[1]);
|
|
564
|
-
return version ? { executable, version } : null;
|
|
565
|
-
} catch {
|
|
566
|
-
return null;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
function normalizeVersion(value) {
|
|
570
|
-
if (!value)
|
|
571
|
-
return null;
|
|
572
|
-
return normalizePep440Version(value);
|
|
573
|
-
}
|
|
574
|
-
function pythonArchitectureMatchesRuntime(python) {
|
|
575
|
-
const architecture = python.architecture.toLowerCase();
|
|
576
|
-
if (python.pointerBits !== (process.arch === "ia32" ? 32 : 64))
|
|
577
|
-
return false;
|
|
578
|
-
if (process.arch === "x64")
|
|
579
|
-
return architecture === "x86_64" || architecture === "amd64";
|
|
580
|
-
if (process.arch === "arm64")
|
|
581
|
-
return architecture === "aarch64" || architecture === "arm64";
|
|
582
|
-
if (process.arch === "ia32") {
|
|
583
|
-
return architecture === "x86" || architecture === "i386" || architecture === "i686";
|
|
584
|
-
}
|
|
585
|
-
return architecture === process.arch;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// src/provision.ts
|
|
589
|
-
async function ensureUv(options = {}) {
|
|
590
|
-
return ensureUvResolved(resolveOptions(options));
|
|
591
|
-
}
|
|
592
|
-
async function ensureEnvironment(options = {}, dependencies = [], dependencyOptions = {}) {
|
|
593
|
-
const resolved = resolveOptions(options);
|
|
594
|
-
const normalized = normalizeDependencies(dependencies);
|
|
595
|
-
const selections = normalizeDependencyOptions(dependencyOptions);
|
|
596
|
-
const uv = await ensureUvResolved(resolved);
|
|
597
|
-
const python = await ensureVirtualEnvironmentResolved(resolved, uv);
|
|
598
|
-
if (normalized.length > 0 || selections.extras.length > 0 || selections.groups.length > 0 || selections.allExtras) {
|
|
599
|
-
await installRuntimeDependencies(uv, python, normalized, selections, resolved);
|
|
600
|
-
}
|
|
601
|
-
return {
|
|
602
|
-
python,
|
|
603
|
-
uv,
|
|
604
|
-
managed: isManagedEnvironment(python, resolved)
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
async function ensureVirtualEnvironmentResolved(options, uv) {
|
|
608
|
-
const existing = await findReusableEnvironment(options);
|
|
609
|
-
if (existing)
|
|
610
|
-
return existing;
|
|
611
|
-
const directory = virtualEnvironmentDirectory(options.projectRoot);
|
|
612
|
-
const check = async () => {
|
|
613
|
-
const candidate = await inspectVirtualEnvironment(directory, options);
|
|
614
|
-
return candidate && pythonSatisfiesPolicy(candidate, options) && await virtualEnvironmentMatchesConfiguredPython(candidate, options) ? candidate : null;
|
|
615
|
-
};
|
|
616
|
-
if (!options.createVirtualEnvironment) {
|
|
617
|
-
throw new Error(`No compatible environment is available and creation is disabled. ` + `Pyfig would create ${directory}.`);
|
|
618
|
-
}
|
|
619
|
-
const basePython = await ensureBasePythonResolved(uv, options);
|
|
620
|
-
return withProvisioningLock({
|
|
621
|
-
key: `venv:${canonicalPath(directory)}`,
|
|
622
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
623
|
-
check,
|
|
624
|
-
mutate: async () => {
|
|
625
|
-
await import_promises.rm(directory, { recursive: true, force: true });
|
|
626
|
-
await import_promises.mkdir(import_node_path5.default.dirname(directory), { recursive: true });
|
|
627
|
-
await runAsync(uv.executable, [
|
|
628
|
-
"venv",
|
|
629
|
-
"--no-project",
|
|
630
|
-
"--no-config",
|
|
631
|
-
"--no-python-downloads",
|
|
632
|
-
"--python",
|
|
633
|
-
basePython.executable,
|
|
634
|
-
directory
|
|
635
|
-
], isolatedUvEnvironment(options), options.installTimeoutMs);
|
|
636
|
-
const installed = await check();
|
|
637
|
-
if (!installed) {
|
|
638
|
-
throw new Error(`uv created ${directory}, but its Python does not satisfy the requested capabilities.`);
|
|
639
|
-
}
|
|
640
|
-
return installed;
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
async function findReusableEnvironment(options) {
|
|
645
|
-
if (options.python) {
|
|
646
|
-
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
647
|
-
if (!configured || !pythonSatisfiesPolicy(configured, options)) {
|
|
648
|
-
throw incompatibleConfiguredPython(options);
|
|
649
|
-
}
|
|
650
|
-
if (configured.virtualEnvironment)
|
|
651
|
-
return configured;
|
|
652
|
-
}
|
|
653
|
-
for (const directory of [
|
|
654
|
-
...externalVirtualEnvironmentDirectories(options.projectRoot, options.env),
|
|
655
|
-
virtualEnvironmentDirectory(options.projectRoot)
|
|
656
|
-
]) {
|
|
657
|
-
const info = await inspectVirtualEnvironment(directory, options);
|
|
658
|
-
if (info && pythonSatisfiesPolicy(info, options) && await virtualEnvironmentMatchesConfiguredPython(info, options)) {
|
|
659
|
-
return info;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
async function ensureBasePythonResolved(uv, options) {
|
|
665
|
-
if (options.python) {
|
|
666
|
-
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
667
|
-
if (!configured || !pythonSatisfiesPolicy(configured, options)) {
|
|
668
|
-
throw incompatibleConfiguredPython(options);
|
|
669
|
-
}
|
|
670
|
-
if (!configured.virtualEnvironment)
|
|
671
|
-
return configured;
|
|
672
|
-
const base = await probePython({ executable: configured.baseExecutable, args: [] }, options);
|
|
673
|
-
if (base && pythonSatisfiesPolicy(base, options))
|
|
674
|
-
return base;
|
|
675
|
-
throw incompatibleConfiguredPython(options);
|
|
676
|
-
}
|
|
677
|
-
const system = await findPythonWithUv(uv, options, "system");
|
|
678
|
-
if (system)
|
|
679
|
-
return system;
|
|
680
|
-
const cachedManaged = await findPythonWithUv(uv, options, "managed");
|
|
681
|
-
if (cachedManaged)
|
|
682
|
-
return cachedManaged;
|
|
683
|
-
if (!options.installPython) {
|
|
684
|
-
throw new Error(missingPythonMessage(options, "managed Python installation is disabled"));
|
|
685
|
-
}
|
|
686
|
-
return withProvisioningLock({
|
|
687
|
-
key: `python:${canonicalPath(import_node_path5.default.join(options.cacheDir, "python"))}`,
|
|
688
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
689
|
-
check: () => findPythonWithUv(uv, options, "managed"),
|
|
690
|
-
mutate: async () => {
|
|
691
|
-
await runAsync(uv.executable, ["python", "install", "--no-config", options.managedPython], isolatedUvEnvironment(options), options.installTimeoutMs);
|
|
692
|
-
const installed = await findPythonWithUv(uv, options, "managed");
|
|
693
|
-
if (!installed) {
|
|
694
|
-
throw new Error(missingPythonMessage(options, `uv installed ${options.managedPython}, but it remains incompatible`));
|
|
695
|
-
}
|
|
696
|
-
return installed;
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
async function findPythonWithUv(uv, options, source) {
|
|
701
|
-
for (const request of ["", options.managedPython]) {
|
|
702
|
-
const args = [
|
|
703
|
-
"python",
|
|
704
|
-
"find",
|
|
705
|
-
"--no-project",
|
|
706
|
-
"--no-config",
|
|
707
|
-
"--no-python-downloads",
|
|
708
|
-
source === "system" ? "--system" : "--managed-python"
|
|
709
|
-
];
|
|
710
|
-
if (request)
|
|
711
|
-
args.push(request);
|
|
712
|
-
try {
|
|
713
|
-
const { stdout } = await runAsync(uv.executable, args, explicitPythonSourceEnvironment(options), options.probeTimeoutMs);
|
|
714
|
-
const info = await probePython({ executable: stdout.trim(), args: [] }, options);
|
|
715
|
-
if (info && pythonSatisfiesPolicy(info, options))
|
|
716
|
-
return info;
|
|
717
|
-
} catch {}
|
|
718
|
-
}
|
|
719
|
-
try {
|
|
720
|
-
const { stdout } = await runAsync(uv.executable, [
|
|
721
|
-
"python",
|
|
722
|
-
"list",
|
|
723
|
-
"--no-config",
|
|
724
|
-
"--no-python-downloads",
|
|
725
|
-
"--only-installed",
|
|
726
|
-
"--output-format",
|
|
727
|
-
"json",
|
|
728
|
-
source === "system" ? "--no-managed-python" : "--managed-python"
|
|
729
|
-
], explicitPythonSourceEnvironment(options), options.probeTimeoutMs);
|
|
730
|
-
const candidates = installedPythonPaths(stdout);
|
|
731
|
-
for (const executable of candidates) {
|
|
732
|
-
const info = await probePython({ executable, args: [] }, options);
|
|
733
|
-
if (info && !info.virtualEnvironment && pythonSatisfiesPolicy(info, options)) {
|
|
734
|
-
return info;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
} catch {}
|
|
738
|
-
return null;
|
|
739
|
-
}
|
|
740
|
-
function installedPythonPaths(output) {
|
|
741
|
-
const parsed = JSON.parse(output);
|
|
742
|
-
if (!Array.isArray(parsed))
|
|
743
|
-
return [];
|
|
744
|
-
const paths = parsed.flatMap((entry) => {
|
|
745
|
-
if (!entry || typeof entry !== "object" || !("path" in entry))
|
|
746
|
-
return [];
|
|
747
|
-
const candidate = entry.path;
|
|
748
|
-
return typeof candidate === "string" && candidate ? [candidate] : [];
|
|
749
|
-
});
|
|
750
|
-
return [...new Map(paths.map((candidate) => [canonicalPath(candidate), candidate])).values()];
|
|
751
|
-
}
|
|
752
|
-
async function inspectVirtualEnvironment(directory, options) {
|
|
753
|
-
const executable = virtualEnvironmentPythonExecutable(directory);
|
|
754
|
-
const info = await probePython({ executable, args: [] }, options);
|
|
755
|
-
return info?.virtualEnvironment && pathsEqual(info.virtualEnvironment, directory) ? info : null;
|
|
756
|
-
}
|
|
757
|
-
async function installRuntimeDependencies(uv, python, dependencies, dependencyOptions, options) {
|
|
758
|
-
if (!python.virtualEnvironment) {
|
|
759
|
-
throw new Error("Runtime dependency installation requires a virtual environment.");
|
|
760
|
-
}
|
|
761
|
-
await withProvisioningLock({
|
|
762
|
-
key: `dependencies:${canonicalPath(python.virtualEnvironment)}`,
|
|
763
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
764
|
-
check: async () => null,
|
|
765
|
-
mutate: async () => {
|
|
766
|
-
const temporary = await import_promises.mkdtemp(import_node_path5.default.join(import_node_os.default.tmpdir(), "pyfig-dependencies-"));
|
|
767
|
-
try {
|
|
768
|
-
const additions = import_node_path5.default.join(temporary, "runtime-dependencies.txt");
|
|
769
|
-
const resolved = import_node_path5.default.join(temporary, "resolved-dependencies.txt");
|
|
770
|
-
const pyproject = import_node_path5.default.join(options.projectRoot, "pyproject.toml");
|
|
771
|
-
const hasPyproject = import_node_fs4.default.existsSync(pyproject);
|
|
772
|
-
if (!hasPyproject && (dependencyOptions.extras.length > 0 || dependencyOptions.groups.length > 0 || dependencyOptions.allExtras)) {
|
|
773
|
-
throw new Error("Runtime extras and groups require an existing pyproject.toml.");
|
|
774
|
-
}
|
|
775
|
-
const sources = hasPyproject ? [pyproject] : [];
|
|
776
|
-
if (dependencies.length > 0) {
|
|
777
|
-
await import_promises.writeFile(additions, `${dependencies.join(`
|
|
778
|
-
`)}
|
|
779
|
-
`, "utf8");
|
|
780
|
-
sources.push(additions);
|
|
781
|
-
}
|
|
782
|
-
const selectionArgs = [
|
|
783
|
-
...dependencyOptions.extras.flatMap((extra) => ["--extra", extra]),
|
|
784
|
-
...dependencyOptions.groups.flatMap((group) => ["--group", group]),
|
|
785
|
-
...dependencyOptions.allExtras ? ["--all-extras"] : []
|
|
786
|
-
];
|
|
787
|
-
await runAsync(uv.executable, [
|
|
788
|
-
"pip",
|
|
789
|
-
"compile",
|
|
790
|
-
"--no-config",
|
|
791
|
-
"--python",
|
|
792
|
-
python.executable,
|
|
793
|
-
...selectionArgs,
|
|
794
|
-
...sources,
|
|
795
|
-
"--output-file",
|
|
796
|
-
resolved
|
|
797
|
-
], targetUvEnvironment(options, python), options.installTimeoutMs, undefined, options.projectRoot);
|
|
798
|
-
await runAsync(uv.executable, ["pip", "install", "--no-config", "--python", python.executable, "-r", resolved], targetUvEnvironment(options, python), options.installTimeoutMs, undefined, options.projectRoot);
|
|
799
|
-
} finally {
|
|
800
|
-
await import_promises.rm(temporary, { recursive: true, force: true });
|
|
801
|
-
}
|
|
802
|
-
return true;
|
|
803
|
-
}
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
async function ensureUvResolved(options) {
|
|
807
|
-
const existing = await findAvailableUv(options);
|
|
808
|
-
if (existing)
|
|
809
|
-
return existing;
|
|
810
|
-
if (!options.installUv) {
|
|
811
|
-
throw new Error(`Compatible uv is unavailable (requires-uv ${options.requiresUv}), and uv installation is disabled.`);
|
|
812
|
-
}
|
|
813
|
-
const policyKey = import_node_crypto.createHash("sha256").update(options.requiresUv).digest("hex");
|
|
814
|
-
return withProvisioningLock({
|
|
815
|
-
key: `uv-policy:${canonicalPath(uvInstallRoot(options))}:${policyKey}`,
|
|
816
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
817
|
-
check: () => findAvailableUv(options),
|
|
818
|
-
mutate: async () => {
|
|
819
|
-
const release = await resolveUvRelease(options);
|
|
820
|
-
const installDir = uvInstallDirectory(options, release.version);
|
|
821
|
-
const executable = uvExecutable(options, release.version);
|
|
822
|
-
return withProvisioningLock({
|
|
823
|
-
key: `uv-release:${canonicalPath(installDir)}`,
|
|
824
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
825
|
-
check: () => findAvailableUv(options),
|
|
826
|
-
mutate: async () => {
|
|
827
|
-
await installUv(installDir, release, options);
|
|
828
|
-
const result = probeUv(executable, options);
|
|
829
|
-
if (!result || result.version !== release.version) {
|
|
830
|
-
throw new Error(`Expected managed uv ${release.version}, but the installed executable did not match.`);
|
|
831
|
-
}
|
|
832
|
-
return result;
|
|
833
|
-
}
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
async function findAvailableUv(options) {
|
|
839
|
-
const external = await findUv(options);
|
|
840
|
-
if (external)
|
|
841
|
-
return external;
|
|
842
|
-
return findManagedUv(options);
|
|
843
|
-
}
|
|
844
|
-
async function findManagedUv(options) {
|
|
845
|
-
const entries = await import_promises.readdir(uvInstallRoot(options), { withFileTypes: true }).catch(() => []);
|
|
846
|
-
let versions = entries.filter((entry) => entry.isDirectory() && normalizePep440Version(entry.name) === entry.name).map((entry) => entry.name);
|
|
847
|
-
while (versions.length > 0) {
|
|
848
|
-
const selected = maxPep440Version(versions, options.requiresUv);
|
|
849
|
-
if (!selected)
|
|
850
|
-
return null;
|
|
851
|
-
const candidate = probeUv(uvExecutable(options, selected), options);
|
|
852
|
-
if (candidate?.version === selected)
|
|
853
|
-
return candidate;
|
|
854
|
-
versions = versions.filter((version) => version !== selected);
|
|
855
|
-
}
|
|
856
|
-
return null;
|
|
857
|
-
}
|
|
858
|
-
function normalizeDependencies(dependencies) {
|
|
859
|
-
const normalized = dependencies.map((dependency) => dependency.trim());
|
|
860
|
-
for (const dependency of normalized) {
|
|
861
|
-
if (!dependency || /[\0\r\n]/u.test(dependency)) {
|
|
862
|
-
throw new TypeError(`Invalid runtime dependency: ${JSON.stringify(dependency)}`);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
return [...new Set(normalized)];
|
|
866
|
-
}
|
|
867
|
-
function normalizeDependencyOptions(options) {
|
|
868
|
-
const extras = normalizeNames("extra", options.extras ?? []);
|
|
869
|
-
const groups = normalizeNames("dependency group", options.groups ?? []);
|
|
870
|
-
if (options.allExtras && extras.length > 0) {
|
|
871
|
-
throw new TypeError("allExtras cannot be combined with explicit extras.");
|
|
872
|
-
}
|
|
873
|
-
return { extras, groups, allExtras: options.allExtras ?? false };
|
|
874
|
-
}
|
|
875
|
-
function normalizeNames(kind, names) {
|
|
876
|
-
const normalized = names.map((name) => name.trim());
|
|
877
|
-
for (const name of normalized) {
|
|
878
|
-
if (!name || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(name)) {
|
|
879
|
-
throw new TypeError(`Invalid ${kind}: ${JSON.stringify(name)}`);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return [...new Set(normalized)];
|
|
883
|
-
}
|
|
884
|
-
function isManagedEnvironment(python, options) {
|
|
885
|
-
return Boolean(python.virtualEnvironment) && pathsEqual(python.virtualEnvironment, virtualEnvironmentDirectory(options.projectRoot));
|
|
886
|
-
}
|
|
887
|
-
function isolatedUvEnvironment(options) {
|
|
888
|
-
return sanitizeUvEnvironment(uvEnvironment(options));
|
|
889
|
-
}
|
|
890
|
-
function explicitPythonSourceEnvironment(options) {
|
|
891
|
-
const env = isolatedUvEnvironment(options);
|
|
892
|
-
delete env["UV_PYTHON_PREFERENCE"];
|
|
893
|
-
return env;
|
|
894
|
-
}
|
|
895
|
-
function targetUvEnvironment(options, python) {
|
|
896
|
-
return uvCommandEnvironment(uvEnvironment(options), python);
|
|
897
|
-
}
|
|
898
|
-
function incompatibleConfiguredPython(options) {
|
|
899
|
-
return new Error(`Configured Python ${options.python} is unavailable or does not satisfy ` + `requires-python ${options.requiresPython}` + `${options.requireEmbedding ? " with CPython embedding files" : ""}.`);
|
|
900
|
-
}
|
|
901
|
-
function missingPythonMessage(options, reason) {
|
|
902
|
-
const embedding = options.requireEmbedding ? " with compatible CPython headers, library, architecture, and GIL ABI" : "";
|
|
903
|
-
return `No Python satisfies requires-python ${options.requiresPython}${embedding}; ${reason}.`;
|
|
904
|
-
}
|
|
905
|
-
async function virtualEnvironmentMatchesConfiguredPython(virtualEnvironment, options) {
|
|
906
|
-
if (!options.python)
|
|
907
|
-
return true;
|
|
908
|
-
const configured = await probePython({ executable: options.python, args: [] }, options);
|
|
909
|
-
if (!configured || !pythonSatisfiesPolicy(configured, options))
|
|
910
|
-
return false;
|
|
911
|
-
if (configured.virtualEnvironment && pathsEqual(configured.executable, virtualEnvironment.executable)) {
|
|
912
|
-
return true;
|
|
913
|
-
}
|
|
914
|
-
const configuredBase = configured.virtualEnvironment ? configured.baseExecutable : configured.executable;
|
|
915
|
-
return pathsEqual(configuredBase, virtualEnvironment.baseExecutable);
|
|
916
|
-
}
|
|
917
|
-
var UV_RELEASE_PAGE_SIZE = 100;
|
|
918
|
-
var UV_RELEASE_MAX_PAGES = 20;
|
|
919
|
-
var UV_RELEASE_METADATA_TTL_MS = 24 * 60 * 60000;
|
|
920
|
-
async function resolveUvRelease(options) {
|
|
921
|
-
const catalog = await ensureUvReleaseCatalog(options);
|
|
922
|
-
const asset = uvAsset();
|
|
923
|
-
const releases = catalog.releases.filter((release) => release.assets.includes(asset) && release.assets.includes(`${asset}.sha256`));
|
|
924
|
-
const version = maxPep440Version(releases.map((release) => release.version), options.requiresUv);
|
|
925
|
-
const release = releases.find((candidate) => candidate.version === version);
|
|
926
|
-
if (!release) {
|
|
927
|
-
throw new Error(`No published uv release satisfies requires-uv ${options.requiresUv}.`);
|
|
928
|
-
}
|
|
929
|
-
return release;
|
|
930
|
-
}
|
|
931
|
-
async function ensureUvReleaseCatalog(options) {
|
|
932
|
-
const file = uvReleaseMetadataFile(options);
|
|
933
|
-
const stale = await readUvReleaseCatalog(file);
|
|
934
|
-
try {
|
|
935
|
-
return await withProvisioningLock({
|
|
936
|
-
key: `uv-metadata:${canonicalPath(file)}`,
|
|
937
|
-
timeoutMs: options.coordinationTimeoutMs,
|
|
938
|
-
check: () => readFreshUvReleaseCatalog(file),
|
|
939
|
-
mutate: async () => {
|
|
940
|
-
const catalog = await fetchUvReleaseCatalog(options);
|
|
941
|
-
await writeUvReleaseCatalog(file, catalog);
|
|
942
|
-
return catalog;
|
|
943
|
-
}
|
|
944
|
-
});
|
|
945
|
-
} catch (error) {
|
|
946
|
-
const fallback = await readUvReleaseCatalog(file) ?? stale;
|
|
947
|
-
if (fallback)
|
|
948
|
-
return fallback;
|
|
949
|
-
throw error;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
async function fetchUvReleaseCatalog(options) {
|
|
953
|
-
const dispatcher = proxyDispatcher(options.env);
|
|
954
|
-
const releases = new Map;
|
|
955
|
-
let exhausted = false;
|
|
956
|
-
try {
|
|
957
|
-
for (let page = 1;page <= UV_RELEASE_MAX_PAGES; page += 1) {
|
|
958
|
-
const text = await downloadText(`https://api.github.com/repos/astral-sh/uv/releases?per_page=${UV_RELEASE_PAGE_SIZE}&page=${page}`, options.downloadTimeoutMs, dispatcher, githubApiHeaders(options.env));
|
|
959
|
-
const payload = JSON.parse(text);
|
|
960
|
-
if (!Array.isArray(payload)) {
|
|
961
|
-
throw new Error("GitHub returned invalid uv release metadata.");
|
|
962
|
-
}
|
|
963
|
-
for (const candidate of payload) {
|
|
964
|
-
if (!candidate || typeof candidate !== "object")
|
|
965
|
-
continue;
|
|
966
|
-
const release = candidate;
|
|
967
|
-
if (release.draft === true || typeof release.tag_name !== "string")
|
|
968
|
-
continue;
|
|
969
|
-
const assetNames = releaseAssetNames(release.assets);
|
|
970
|
-
const version = normalizePep440Version(release.tag_name);
|
|
971
|
-
if (version) {
|
|
972
|
-
releases.set(version, { assets: assetNames, tag: release.tag_name, version });
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
if (payload.length < UV_RELEASE_PAGE_SIZE) {
|
|
976
|
-
exhausted = true;
|
|
977
|
-
break;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
} finally {
|
|
981
|
-
await dispatcher?.close();
|
|
982
|
-
}
|
|
983
|
-
if (!exhausted) {
|
|
984
|
-
throw new Error(`uv release catalog exceeded ${UV_RELEASE_MAX_PAGES} pages.`);
|
|
985
|
-
}
|
|
986
|
-
if (releases.size === 0) {
|
|
987
|
-
throw new Error("GitHub returned no usable uv releases.");
|
|
988
|
-
}
|
|
989
|
-
return { fetchedAt: Date.now(), releases: [...releases.values()] };
|
|
990
|
-
}
|
|
991
|
-
function githubApiHeaders(env) {
|
|
992
|
-
const token = [env["GITHUB_TOKEN"], env["GH_TOKEN"]].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
993
|
-
if (token && /[\r\n]/u.test(token)) {
|
|
994
|
-
throw new TypeError("GitHub API token must not contain line breaks.");
|
|
995
|
-
}
|
|
996
|
-
return {
|
|
997
|
-
Accept: "application/vnd.github+json",
|
|
998
|
-
"User-Agent": "pyfig",
|
|
999
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
1000
|
-
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
async function readFreshUvReleaseCatalog(file) {
|
|
1004
|
-
const catalog = await readUvReleaseCatalog(file);
|
|
1005
|
-
if (!catalog)
|
|
1006
|
-
return null;
|
|
1007
|
-
const age = Date.now() - catalog.fetchedAt;
|
|
1008
|
-
return age >= 0 && age < UV_RELEASE_METADATA_TTL_MS ? catalog : null;
|
|
1009
|
-
}
|
|
1010
|
-
async function readUvReleaseCatalog(file) {
|
|
1011
|
-
try {
|
|
1012
|
-
return parseUvReleaseCatalog(JSON.parse(await import_promises.readFile(file, "utf8")));
|
|
1013
|
-
} catch {
|
|
1014
|
-
return null;
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
function parseUvReleaseCatalog(value) {
|
|
1018
|
-
if (!value || typeof value !== "object")
|
|
1019
|
-
return null;
|
|
1020
|
-
const catalog = value;
|
|
1021
|
-
if (!Number.isSafeInteger(catalog.fetchedAt) || catalog.fetchedAt < 0 || !Array.isArray(catalog.releases)) {
|
|
1022
|
-
return null;
|
|
1023
|
-
}
|
|
1024
|
-
const releases = [];
|
|
1025
|
-
for (const value of catalog.releases) {
|
|
1026
|
-
if (!value || typeof value !== "object")
|
|
1027
|
-
return null;
|
|
1028
|
-
const release = value;
|
|
1029
|
-
if (!Array.isArray(release.assets) || !release.assets.every((asset) => typeof asset === "string") || typeof release.tag !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._+-]*$/u.test(release.tag) || typeof release.version !== "string" || normalizePep440Version(release.version) !== release.version) {
|
|
1030
|
-
return null;
|
|
1031
|
-
}
|
|
1032
|
-
releases.push({
|
|
1033
|
-
assets: release.assets,
|
|
1034
|
-
tag: release.tag,
|
|
1035
|
-
version: release.version
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
return { fetchedAt: catalog.fetchedAt, releases };
|
|
1039
|
-
}
|
|
1040
|
-
async function writeUvReleaseCatalog(file, catalog) {
|
|
1041
|
-
await import_promises.mkdir(import_node_path5.default.dirname(file), { recursive: true });
|
|
1042
|
-
const staged = import_node_path5.default.join(import_node_path5.default.dirname(file), `.${import_node_path5.default.basename(file)}.${process.pid}.${import_node_crypto.randomUUID()}.tmp`);
|
|
1043
|
-
try {
|
|
1044
|
-
await import_promises.writeFile(staged, `${JSON.stringify(catalog)}
|
|
1045
|
-
`, { encoding: "utf8", flag: "wx" });
|
|
1046
|
-
if (process.platform === "win32")
|
|
1047
|
-
await import_promises.rm(file, { force: true });
|
|
1048
|
-
await import_promises.rename(staged, file);
|
|
1049
|
-
} finally {
|
|
1050
|
-
await import_promises.rm(staged, { force: true });
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
function uvReleaseMetadataFile(options) {
|
|
1054
|
-
return import_node_path5.default.join(options.cacheDir, "metadata", "uv-releases.json");
|
|
1055
|
-
}
|
|
1056
|
-
function releaseAssetNames(value) {
|
|
1057
|
-
if (!Array.isArray(value))
|
|
1058
|
-
return [];
|
|
1059
|
-
return value.flatMap((entry) => {
|
|
1060
|
-
if (!entry || typeof entry !== "object")
|
|
1061
|
-
return [];
|
|
1062
|
-
const name = entry.name;
|
|
1063
|
-
return typeof name === "string" ? [name] : [];
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
function uvInstallRoot(options) {
|
|
1067
|
-
return import_node_path5.default.join(options.cacheDir, "tools", "uv");
|
|
1068
|
-
}
|
|
1069
|
-
function uvInstallDirectory(options, version) {
|
|
1070
|
-
return import_node_path5.default.join(uvInstallRoot(options), version);
|
|
1071
|
-
}
|
|
1072
|
-
function uvExecutable(options, version) {
|
|
1073
|
-
return import_node_path5.default.join(uvInstallDirectory(options, version), process.platform === "win32" ? "uv.exe" : "uv");
|
|
1074
|
-
}
|
|
1075
|
-
async function installUv(installDir, release, options) {
|
|
1076
|
-
const asset = uvAsset();
|
|
1077
|
-
const baseUrl = `https://github.com/astral-sh/uv/releases/download/${release.tag}`;
|
|
1078
|
-
const temporary = await import_promises.mkdtemp(import_node_path5.default.join(import_node_os.default.tmpdir(), "pyfig-uv-"));
|
|
1079
|
-
const dispatcher = proxyDispatcher(options.env);
|
|
1080
|
-
try {
|
|
1081
|
-
const archive = import_node_path5.default.join(temporary, asset);
|
|
1082
|
-
await download(`${baseUrl}/${asset}`, archive, options.downloadTimeoutMs, dispatcher);
|
|
1083
|
-
const expected = (await downloadText(`${baseUrl}/${asset}.sha256`, options.downloadTimeoutMs, dispatcher)).trim().split(/\s+/u)[0];
|
|
1084
|
-
const actual = await sha256File(archive);
|
|
1085
|
-
if (!expected || expected.toLowerCase() !== actual.toLowerCase()) {
|
|
1086
|
-
throw new Error(`uv archive checksum mismatch for ${asset}`);
|
|
1087
|
-
}
|
|
1088
|
-
const extracted = import_node_path5.default.join(temporary, "extracted");
|
|
1089
|
-
await import_promises.mkdir(extracted, { recursive: true });
|
|
1090
|
-
const source = await extractUvExecutable(archive, asset, extracted);
|
|
1091
|
-
if (!source)
|
|
1092
|
-
throw new Error(`uv archive did not contain the expected executable: ${asset}`);
|
|
1093
|
-
await publishUvAtomically(source, installDir, release.version, options);
|
|
1094
|
-
} finally {
|
|
1095
|
-
await dispatcher?.close();
|
|
1096
|
-
await import_promises.rm(temporary, { recursive: true, force: true });
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
async function extractUvExecutable(archive, asset, destination) {
|
|
1100
|
-
const executableName = process.platform === "win32" ? "uv.exe" : "uv";
|
|
1101
|
-
if (!asset.endsWith(".zip")) {
|
|
1102
|
-
await tar.x({ file: archive, cwd: destination, strict: true });
|
|
1103
|
-
return findFile(destination, executableName);
|
|
1104
|
-
}
|
|
1105
|
-
const entries = import_fflate.unzipSync(await import_promises.readFile(archive), {
|
|
1106
|
-
filter: ({ name }) => import_node_path5.default.posix.basename(name.replaceAll("\\", "/")) === executableName
|
|
1107
|
-
});
|
|
1108
|
-
const matches = Object.entries(entries).filter(([name]) => import_node_path5.default.posix.basename(name.replaceAll("\\", "/")) === executableName);
|
|
1109
|
-
if (matches.length !== 1 || !matches[0])
|
|
1110
|
-
return null;
|
|
1111
|
-
const source = import_node_path5.default.join(destination, executableName);
|
|
1112
|
-
await import_promises.writeFile(source, matches[0][1], { flag: "wx" });
|
|
1113
|
-
return source;
|
|
1114
|
-
}
|
|
1115
|
-
async function publishUvAtomically(source, installDir, version, options) {
|
|
1116
|
-
await import_promises.mkdir(installDir, { recursive: true });
|
|
1117
|
-
const executable = uvExecutable(options, version);
|
|
1118
|
-
const staged = import_node_path5.default.join(installDir, `.${import_node_path5.default.basename(executable)}.${process.pid}.${import_node_crypto.randomUUID()}.tmp`);
|
|
1119
|
-
try {
|
|
1120
|
-
await import_promises.copyFile(source, staged);
|
|
1121
|
-
if (process.platform !== "win32")
|
|
1122
|
-
await import_promises.chmod(staged, 493);
|
|
1123
|
-
const stagedUv = probeUv(staged, options);
|
|
1124
|
-
if (!stagedUv || stagedUv.version !== version) {
|
|
1125
|
-
throw new Error(`Downloaded uv executable is not version ${version}.`);
|
|
1126
|
-
}
|
|
1127
|
-
if (process.platform === "win32")
|
|
1128
|
-
await import_promises.rm(executable, { force: true });
|
|
1129
|
-
await import_promises.rename(staged, executable);
|
|
1130
|
-
} finally {
|
|
1131
|
-
await import_promises.rm(staged, { force: true });
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
function uvAsset() {
|
|
1135
|
-
const architecture = process.arch === "x64" ? "x86_64" : process.arch === "arm64" ? "aarch64" : null;
|
|
1136
|
-
if (!architecture)
|
|
1137
|
-
throw new Error(`uv bootstrap does not support architecture ${process.arch}.`);
|
|
1138
|
-
if (process.platform === "win32")
|
|
1139
|
-
return `uv-${architecture}-pc-windows-msvc.zip`;
|
|
1140
|
-
if (process.platform === "darwin")
|
|
1141
|
-
return `uv-${architecture}-apple-darwin.tar.gz`;
|
|
1142
|
-
if (process.platform === "linux")
|
|
1143
|
-
return `uv-${architecture}-unknown-linux-musl.tar.gz`;
|
|
1144
|
-
throw new Error(`uv bootstrap does not support platform ${process.platform}.`);
|
|
1145
|
-
}
|
|
1146
|
-
async function download(url, destination, timeoutMs, dispatcher) {
|
|
1147
|
-
const response = await import_undici.fetch(url, {
|
|
1148
|
-
dispatcher,
|
|
1149
|
-
redirect: "follow",
|
|
1150
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
1151
|
-
});
|
|
1152
|
-
if (!response.ok || !response.body) {
|
|
1153
|
-
await response.body?.cancel();
|
|
1154
|
-
throw new Error(`Could not download ${url}: HTTP ${response.status}`);
|
|
1155
|
-
}
|
|
1156
|
-
await import_promises2.pipeline(import_node_stream.Readable.fromWeb(response.body), import_node_fs3.createWriteStream(destination, { flags: "wx" }));
|
|
1157
|
-
}
|
|
1158
|
-
async function downloadText(url, timeoutMs, dispatcher, headers) {
|
|
1159
|
-
const response = await import_undici.fetch(url, {
|
|
1160
|
-
dispatcher,
|
|
1161
|
-
headers,
|
|
1162
|
-
redirect: "follow",
|
|
1163
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
1164
|
-
});
|
|
1165
|
-
if (!response.ok) {
|
|
1166
|
-
await response.body?.cancel();
|
|
1167
|
-
throw new Error(`Could not download ${url}: HTTP ${response.status}`);
|
|
1168
|
-
}
|
|
1169
|
-
return response.text();
|
|
1170
|
-
}
|
|
1171
|
-
function proxyDispatcher(env) {
|
|
1172
|
-
const httpProxy = environmentValue(env, "http_proxy", "HTTP_PROXY");
|
|
1173
|
-
const httpsProxy = environmentValue(env, "https_proxy", "HTTPS_PROXY");
|
|
1174
|
-
if (!httpProxy && !httpsProxy)
|
|
1175
|
-
return;
|
|
1176
|
-
return new import_undici.EnvHttpProxyAgent({
|
|
1177
|
-
httpProxy,
|
|
1178
|
-
httpsProxy,
|
|
1179
|
-
noProxy: environmentValue(env, "no_proxy", "NO_PROXY")
|
|
1180
|
-
});
|
|
1181
|
-
}
|
|
1182
|
-
function environmentValue(env, lowercase, uppercase) {
|
|
1183
|
-
return env[lowercase] ?? env[uppercase];
|
|
1184
|
-
}
|
|
1185
|
-
async function sha256File(file) {
|
|
1186
|
-
const hash = import_node_crypto.createHash("sha256");
|
|
1187
|
-
for await (const chunk of import_node_fs3.createReadStream(file))
|
|
1188
|
-
hash.update(chunk);
|
|
1189
|
-
return hash.digest("hex");
|
|
1190
|
-
}
|
|
1191
|
-
async function findFile(directory, name) {
|
|
1192
|
-
for (const entry of await import_promises.readdir(directory, { withFileTypes: true })) {
|
|
1193
|
-
const target = import_node_path5.default.join(directory, entry.name);
|
|
1194
|
-
if (entry.isFile() && entry.name === name)
|
|
1195
|
-
return target;
|
|
1196
|
-
if (entry.isDirectory()) {
|
|
1197
|
-
const nested = await findFile(target, name);
|
|
1198
|
-
if (nested)
|
|
1199
|
-
return nested;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return null;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
// src/sync-resolver.ts
|
|
1206
|
-
async function main() {
|
|
1207
|
-
const request = JSON.parse(import_node_fs5.readFileSync(0, "utf8"));
|
|
1208
|
-
const result = request.target === "uv" ? await ensureUv(request.options) : await ensureEnvironment(request.options);
|
|
1209
|
-
process.stdout.write(JSON.stringify(result));
|
|
1210
|
-
}
|
|
1211
|
-
main().catch((error) => {
|
|
1212
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1213
|
-
process.stderr.write(message);
|
|
1214
|
-
process.exitCode = 1;
|
|
1215
|
-
});
|
|
1216
|
-
|
|
1217
|
-
//# debugId=DFB6475FEB013D6C64756E2164756E21
|