pyfig 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/SECURITY.md +1 -1
- package/dist/cjs/index.js +221 -73
- package/dist/cjs/index.js.map +7 -7
- package/dist/cjs/sync-resolver.js +220 -71
- package/dist/cjs/sync-resolver.js.map +8 -8
- package/dist/cli.js +2 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -29
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pep440.d.ts +12 -0
- package/dist/pep440.d.ts.map +1 -0
- package/dist/pep440.js +21 -0
- package/dist/pep440.js.map +1 -0
- package/dist/probes.d.ts.map +1 -1
- package/dist/probes.js +5 -6
- package/dist/probes.js.map +1 -1
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +203 -24
- package/dist/provision.js.map +1 -1
- package/package.json +3 -3
- package/dist/python-version.d.ts +0 -10
- package/dist/python-version.d.ts.map +0 -1
- package/dist/python-version.js +0 -17
- package/dist/python-version.js.map +0 -1
package/dist/config.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import * as envPaths from 'env-paths';
|
|
3
|
-
import
|
|
3
|
+
import { isValidPep440Specifier, pep440VersionSatisfies, } from './pep440.js';
|
|
4
4
|
import { resolveProjectRoot } from './project-root.js';
|
|
5
|
-
import { isValidPythonSpecifier, pythonVersionSatisfies } from './python-version.js';
|
|
6
5
|
export const DEFAULT_REQUIRES_PYTHON = '>=3.10,<4';
|
|
7
6
|
export const DEFAULT_MANAGED_PYTHON = '3.12';
|
|
8
|
-
export const DEFAULT_UV_VERSION = '0.13.6';
|
|
9
7
|
// External uv only needs to support the command and isolation flags Pyfig uses.
|
|
10
|
-
//
|
|
11
|
-
export const
|
|
8
|
+
// The installer resolves the greatest published release allowed by this policy.
|
|
9
|
+
export const DEFAULT_REQUIRES_UV = '>=0.10,<1';
|
|
12
10
|
const DEFAULT_PROBE_TIMEOUT_MS = 10_000;
|
|
13
11
|
const DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60_000;
|
|
14
12
|
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60_000;
|
|
@@ -28,14 +26,12 @@ export function resolveOptions(options = {}) {
|
|
|
28
26
|
const env = { ...(options.env ?? process.env) };
|
|
29
27
|
const requiresPython = options.requiresPython ??
|
|
30
28
|
env['PYFIG_REQUIRES_PYTHON'] ?? DEFAULT_REQUIRES_PYTHON;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const requiresUv = options.requiresUv ?? env['PYFIG_REQUIRES_UV'] ?? DEFAULT_REQUIRES_UV;
|
|
30
|
+
assertPep440Specifier('requires-python', requiresPython);
|
|
31
|
+
assertPep440Specifier('requires-uv', requiresUv);
|
|
34
32
|
const managedPython = options.managedPython ??
|
|
35
33
|
env['PYFIG_MANAGED_PYTHON'] ?? DEFAULT_MANAGED_PYTHON;
|
|
36
|
-
const uvVersion = options.uvVersion ?? env['PYFIG_UV_VERSION'] ?? DEFAULT_UV_VERSION;
|
|
37
34
|
assertManagedPythonRequest(managedPython, requiresPython);
|
|
38
|
-
assertUvVersion(uvVersion, uvRange);
|
|
39
35
|
const probeTimeoutMs = resolveDuration('probeTimeoutMs', options.probeTimeoutMs, env['PYFIG_PROBE_TIMEOUT_MS'], DEFAULT_PROBE_TIMEOUT_MS);
|
|
40
36
|
const installTimeoutMs = resolveDuration('installTimeoutMs', options.installTimeoutMs, env['PYFIG_INSTALL_TIMEOUT_MS'], DEFAULT_INSTALL_TIMEOUT_MS);
|
|
41
37
|
const downloadTimeoutMs = resolveDuration('downloadTimeoutMs', options.downloadTimeoutMs, env['PYFIG_DOWNLOAD_TIMEOUT_MS'], DEFAULT_DOWNLOAD_TIMEOUT_MS);
|
|
@@ -53,8 +49,7 @@ export function resolveOptions(options = {}) {
|
|
|
53
49
|
python: options.python ?? env['PYFIG_PYTHON'] ?? '',
|
|
54
50
|
requiresPython,
|
|
55
51
|
managedPython,
|
|
56
|
-
|
|
57
|
-
uvRange,
|
|
52
|
+
requiresUv,
|
|
58
53
|
requireEmbedding: options.requireEmbedding ?? parseBoolean(env['PYFIG_REQUIRE_EMBEDDING'], false),
|
|
59
54
|
requireGil: options.requireGil ?? parseBoolean(env['PYFIG_REQUIRE_GIL'], false),
|
|
60
55
|
autoInstall,
|
|
@@ -81,22 +76,9 @@ export function uvEnvironment(options) {
|
|
|
81
76
|
UV_NO_CONFIG: '1',
|
|
82
77
|
};
|
|
83
78
|
}
|
|
84
|
-
function
|
|
85
|
-
if (!
|
|
86
|
-
throw new TypeError(`Invalid ${
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function assertUvVersion(version, range) {
|
|
90
|
-
if (!semver.valid(version)) {
|
|
91
|
-
throw new TypeError(`Invalid exact uv version: ${version}`);
|
|
92
|
-
}
|
|
93
|
-
if (!semver.satisfies(version, range)) {
|
|
94
|
-
throw new TypeError(`Pinned uv ${version} does not satisfy supported range ${range}.`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function assertPythonSpecifier(specifier) {
|
|
98
|
-
if (!isValidPythonSpecifier(specifier)) {
|
|
99
|
-
throw new TypeError(`Invalid requires-python PEP 440 specifier: ${specifier}`);
|
|
79
|
+
function assertPep440Specifier(name, specifier) {
|
|
80
|
+
if (!isValidPep440Specifier(specifier)) {
|
|
81
|
+
throw new TypeError(`Invalid ${name} PEP 440 specifier: ${specifier}`);
|
|
100
82
|
}
|
|
101
83
|
}
|
|
102
84
|
function assertManagedPythonRequest(request, requiresPython) {
|
|
@@ -105,7 +87,7 @@ function assertManagedPythonRequest(request, requiresPython) {
|
|
|
105
87
|
}
|
|
106
88
|
if (/^\d+\.\d+(?:\.\d+)?$/u.test(request)) {
|
|
107
89
|
const normalized = request.split('.').length === 2 ? `${request}.0` : request;
|
|
108
|
-
if (!
|
|
90
|
+
if (!pep440VersionSatisfies(normalized, requiresPython)) {
|
|
109
91
|
throw new TypeError(`Managed Python ${request} cannot satisfy requires-python ${requiresPython}.`);
|
|
110
92
|
}
|
|
111
93
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_MANAGED_PYTHON, DEFAULT_REQUIRES_PYTHON,
|
|
1
|
+
export { DEFAULT_MANAGED_PYTHON, DEFAULT_REQUIRES_PYTHON, DEFAULT_REQUIRES_UV, } from './config.js';
|
|
2
2
|
export type { PyfigOptions } from './config.js';
|
|
3
3
|
export { createPyfig } from './client.js';
|
|
4
4
|
export type { PyfigClient } from './client.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,mBAAmB,EACnB,UAAU,EACV,MAAM,GACP,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,EACL,OAAO,EACP,WAAW,GACZ,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_MANAGED_PYTHON, DEFAULT_REQUIRES_PYTHON,
|
|
1
|
+
export { DEFAULT_MANAGED_PYTHON, DEFAULT_REQUIRES_PYTHON, DEFAULT_REQUIRES_UV, } from './config.js';
|
|
2
2
|
export { createPyfig } from './client.js';
|
|
3
3
|
export { resolveProjectRoot } from './project-root.js';
|
|
4
4
|
export { ensureEnvironment, ensureUv, } from './provision.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAMvD,OAAO,EACL,iBAAiB,EACjB,QAAQ,GACT,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,OAAO,EACP,WAAW,GACZ,MAAM,cAAc,CAAC"}
|
package/dist/pep440.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Validate a PEP 440 version specifier used by a runtime policy. */
|
|
2
|
+
export declare function isValidPep440Specifier(specifier: string): boolean;
|
|
3
|
+
/** Normalize a PEP 440 version, or reject invalid input. */
|
|
4
|
+
export declare function normalizePep440Version(version: string): string | null;
|
|
5
|
+
/**
|
|
6
|
+
* Match a version using PEP 440 specifier-set behavior, including its
|
|
7
|
+
* pre-release fallback when the candidate set has no matching final release.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pep440VersionSatisfies(version: string, specifier: string): boolean;
|
|
10
|
+
/** Select the greatest PEP 440 version allowed by a specifier. */
|
|
11
|
+
export declare function maxPep440Version(versions: readonly string[], specifier: string): string | null;
|
|
12
|
+
//# sourceMappingURL=pep440.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pep440.d.ts","sourceRoot":"","sources":["../src/pep440.ts"],"names":[],"mappings":"AAOA,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,4DAA4D;AAC5D,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAElF;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAEf"}
|
package/dist/pep440.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { filter, maxSatisfying, valid as validPep440Version, validRange as validPep440Specifier, } from '@renovatebot/pep440';
|
|
2
|
+
/** Validate a PEP 440 version specifier used by a runtime policy. */
|
|
3
|
+
export function isValidPep440Specifier(specifier) {
|
|
4
|
+
return validPep440Specifier(specifier);
|
|
5
|
+
}
|
|
6
|
+
/** Normalize a PEP 440 version, or reject invalid input. */
|
|
7
|
+
export function normalizePep440Version(version) {
|
|
8
|
+
return validPep440Version(version);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Match a version using PEP 440 specifier-set behavior, including its
|
|
12
|
+
* pre-release fallback when the candidate set has no matching final release.
|
|
13
|
+
*/
|
|
14
|
+
export function pep440VersionSatisfies(version, specifier) {
|
|
15
|
+
return filter([version], specifier).length === 1;
|
|
16
|
+
}
|
|
17
|
+
/** Select the greatest PEP 440 version allowed by a specifier. */
|
|
18
|
+
export function maxPep440Version(versions, specifier) {
|
|
19
|
+
return maxSatisfying([...versions], specifier);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=pep440.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pep440.js","sourceRoot":"","sources":["../src/pep440.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,aAAa,EACb,KAAK,IAAI,kBAAkB,EAC3B,UAAU,IAAI,oBAAoB,GACnC,MAAM,qBAAqB,CAAC;AAE7B,qEAAqE;AACrE,MAAM,UAAU,sBAAsB,CAAC,SAAiB;IACtD,OAAO,oBAAoB,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe,EAAE,SAAiB;IACvE,OAAO,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAC9B,QAA2B,EAC3B,SAAiB;IAEjB,OAAO,aAAa,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/probes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;CAC7C;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AA+ED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,YAAY,CAAC,GACxF,OAAO,CAQT;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAe5B;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKlF;AAED,wBAAgB,OAAO,CACrB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,GAC5B,MAAM,GAAG,IAAI,CAQf"}
|
package/dist/probes.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { normalizePep440Version, pep440VersionSatisfies } from './pep440.js';
|
|
2
2
|
import { resolveExecutable, run } from './process.js';
|
|
3
|
-
import { isValidPythonVersion, pythonVersionSatisfies } from './python-version.js';
|
|
4
3
|
const PYTHON_PROBE = `
|
|
5
4
|
import json
|
|
6
5
|
import os
|
|
@@ -78,7 +77,7 @@ print(json.dumps({
|
|
|
78
77
|
}))
|
|
79
78
|
`;
|
|
80
79
|
export function pythonSatisfiesPolicy(python, options) {
|
|
81
|
-
return
|
|
80
|
+
return pep440VersionSatisfies(python.version, options.requiresPython) &&
|
|
82
81
|
(!options.requireEmbedding || (python.implementation === 'cpython' &&
|
|
83
82
|
python.embedding !== null &&
|
|
84
83
|
pythonArchitectureMatchesRuntime(python))) &&
|
|
@@ -91,7 +90,7 @@ export async function probePython(candidate, options) {
|
|
|
91
90
|
try {
|
|
92
91
|
const { stdout } = run(executable, [...candidate.args, '-c', PYTHON_PROBE], options.env, options.probeTimeoutMs);
|
|
93
92
|
const info = JSON.parse(stdout.trim());
|
|
94
|
-
return
|
|
93
|
+
return normalizePep440Version(info.version) ? info : null;
|
|
95
94
|
}
|
|
96
95
|
catch {
|
|
97
96
|
return null;
|
|
@@ -101,7 +100,7 @@ export async function findUv(options) {
|
|
|
101
100
|
const configured = options.env['PYFIG_UV'] ?? 'uv';
|
|
102
101
|
const executable = await resolveExecutable(configured, options.env);
|
|
103
102
|
const uv = executable ? probeUv(executable, options) : null;
|
|
104
|
-
return uv &&
|
|
103
|
+
return uv && pep440VersionSatisfies(uv.version, options.requiresUv) ? uv : null;
|
|
105
104
|
}
|
|
106
105
|
export function probeUv(executable, options) {
|
|
107
106
|
try {
|
|
@@ -116,7 +115,7 @@ export function probeUv(executable, options) {
|
|
|
116
115
|
function normalizeVersion(value) {
|
|
117
116
|
if (!value)
|
|
118
117
|
return null;
|
|
119
|
-
return
|
|
118
|
+
return normalizePep440Version(value);
|
|
120
119
|
}
|
|
121
120
|
function pythonArchitectureMatchesRuntime(python) {
|
|
122
121
|
const architecture = python.architecture.toLowerCase();
|
package/dist/probes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probes.js","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"probes.js","sourceRoot":"","sources":["../src/probes.ts"],"names":[],"mappings":"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"}
|
package/dist/provision.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"AAqBA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"AAqBA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAOrB,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,MAAM,EACZ,MAAM,aAAa,CAAC;AAUrB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,uEAAuE;IACvE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,8EAA8E;AAC9E,wBAAsB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,YAAiB,EAC1B,YAAY,GAAE,SAAS,MAAM,EAAO,EACpC,iBAAiB,GAAE,wBAA6B,GAC/C,OAAO,CAAC,iBAAiB,CAAC,CAe5B"}
|
package/dist/provision.js
CHANGED
|
@@ -12,6 +12,7 @@ import { EnvHttpProxyAgent, fetch } from 'undici';
|
|
|
12
12
|
import { resolveOptions, uvEnvironment, } from './config.js';
|
|
13
13
|
import { withProvisioningLock } from './provision-lock.js';
|
|
14
14
|
import { sanitizeUvEnvironment, uvCommandEnvironment } from './package-target.js';
|
|
15
|
+
import { maxPep440Version, normalizePep440Version, } from './pep440.js';
|
|
15
16
|
import { findUv, probePython, probeUv, pythonSatisfiesPolicy, } from './probes.js';
|
|
16
17
|
import { runAsync } from './process.js';
|
|
17
18
|
import { canonicalPath, externalVirtualEnvironmentDirectories, pathsEqual, virtualEnvironmentDirectory, virtualEnvironmentPythonExecutable, } from './virtual-environment.js';
|
|
@@ -258,21 +259,30 @@ async function ensureUvResolved(options) {
|
|
|
258
259
|
if (existing)
|
|
259
260
|
return existing;
|
|
260
261
|
if (!options.installUv) {
|
|
261
|
-
throw new Error(`Compatible uv is unavailable (
|
|
262
|
+
throw new Error(`Compatible uv is unavailable (requires-uv ${options.requiresUv}), and uv installation is disabled.`);
|
|
262
263
|
}
|
|
263
|
-
const
|
|
264
|
-
const executable = uvExecutable(options);
|
|
264
|
+
const policyKey = createHash('sha256').update(options.requiresUv).digest('hex');
|
|
265
265
|
return withProvisioningLock({
|
|
266
|
-
key: `uv:${canonicalPath(
|
|
266
|
+
key: `uv-policy:${canonicalPath(uvInstallRoot(options))}:${policyKey}`,
|
|
267
267
|
timeoutMs: options.coordinationTimeoutMs,
|
|
268
268
|
check: () => findAvailableUv(options),
|
|
269
269
|
mutate: async () => {
|
|
270
|
-
await
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
270
|
+
const release = await resolveUvRelease(options);
|
|
271
|
+
const installDir = uvInstallDirectory(options, release.version);
|
|
272
|
+
const executable = uvExecutable(options, release.version);
|
|
273
|
+
return withProvisioningLock({
|
|
274
|
+
key: `uv-release:${canonicalPath(installDir)}`,
|
|
275
|
+
timeoutMs: options.coordinationTimeoutMs,
|
|
276
|
+
check: () => findAvailableUv(options),
|
|
277
|
+
mutate: async () => {
|
|
278
|
+
await installUv(installDir, release, options);
|
|
279
|
+
const result = probeUv(executable, options);
|
|
280
|
+
if (!result || result.version !== release.version) {
|
|
281
|
+
throw new Error(`Expected managed uv ${release.version}, but the installed executable did not match.`);
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
},
|
|
285
|
+
});
|
|
276
286
|
},
|
|
277
287
|
});
|
|
278
288
|
}
|
|
@@ -280,8 +290,23 @@ async function findAvailableUv(options) {
|
|
|
280
290
|
const external = await findUv(options);
|
|
281
291
|
if (external)
|
|
282
292
|
return external;
|
|
283
|
-
|
|
284
|
-
|
|
293
|
+
return findManagedUv(options);
|
|
294
|
+
}
|
|
295
|
+
async function findManagedUv(options) {
|
|
296
|
+
const entries = await readdir(uvInstallRoot(options), { withFileTypes: true }).catch(() => []);
|
|
297
|
+
let versions = entries
|
|
298
|
+
.filter(entry => entry.isDirectory() && normalizePep440Version(entry.name) === entry.name)
|
|
299
|
+
.map(entry => entry.name);
|
|
300
|
+
while (versions.length > 0) {
|
|
301
|
+
const selected = maxPep440Version(versions, options.requiresUv);
|
|
302
|
+
if (!selected)
|
|
303
|
+
return null;
|
|
304
|
+
const candidate = probeUv(uvExecutable(options, selected), options);
|
|
305
|
+
if (candidate?.version === selected)
|
|
306
|
+
return candidate;
|
|
307
|
+
versions = versions.filter(version => version !== selected);
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
285
310
|
}
|
|
286
311
|
function normalizeDependencies(dependencies) {
|
|
287
312
|
const normalized = dependencies.map((dependency) => dependency.trim());
|
|
@@ -349,15 +374,168 @@ async function virtualEnvironmentMatchesConfiguredPython(virtualEnvironment, opt
|
|
|
349
374
|
: configured.executable;
|
|
350
375
|
return pathsEqual(configuredBase, virtualEnvironment.baseExecutable);
|
|
351
376
|
}
|
|
352
|
-
|
|
353
|
-
|
|
377
|
+
const UV_RELEASE_PAGE_SIZE = 100;
|
|
378
|
+
const UV_RELEASE_MAX_PAGES = 20;
|
|
379
|
+
const UV_RELEASE_METADATA_TTL_MS = 24 * 60 * 60_000;
|
|
380
|
+
async function resolveUvRelease(options) {
|
|
381
|
+
const catalog = await ensureUvReleaseCatalog(options);
|
|
382
|
+
const asset = uvAsset();
|
|
383
|
+
const releases = catalog.releases.filter(release => release.assets.includes(asset) && release.assets.includes(`${asset}.sha256`));
|
|
384
|
+
const version = maxPep440Version(releases.map(release => release.version), options.requiresUv);
|
|
385
|
+
const release = releases.find(candidate => candidate.version === version);
|
|
386
|
+
if (!release) {
|
|
387
|
+
throw new Error(`No published uv release satisfies requires-uv ${options.requiresUv}.`);
|
|
388
|
+
}
|
|
389
|
+
return release;
|
|
390
|
+
}
|
|
391
|
+
async function ensureUvReleaseCatalog(options) {
|
|
392
|
+
const file = uvReleaseMetadataFile(options);
|
|
393
|
+
const stale = await readUvReleaseCatalog(file);
|
|
394
|
+
try {
|
|
395
|
+
return await withProvisioningLock({
|
|
396
|
+
key: `uv-metadata:${canonicalPath(file)}`,
|
|
397
|
+
timeoutMs: options.coordinationTimeoutMs,
|
|
398
|
+
check: () => readFreshUvReleaseCatalog(file),
|
|
399
|
+
mutate: async () => {
|
|
400
|
+
const catalog = await fetchUvReleaseCatalog(options);
|
|
401
|
+
await writeUvReleaseCatalog(file, catalog);
|
|
402
|
+
return catalog;
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
const fallback = await readUvReleaseCatalog(file) ?? stale;
|
|
408
|
+
if (fallback)
|
|
409
|
+
return fallback;
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async function fetchUvReleaseCatalog(options) {
|
|
414
|
+
const dispatcher = proxyDispatcher(options.env);
|
|
415
|
+
const releases = new Map();
|
|
416
|
+
let exhausted = false;
|
|
417
|
+
try {
|
|
418
|
+
for (let page = 1; page <= UV_RELEASE_MAX_PAGES; page += 1) {
|
|
419
|
+
const text = await downloadText(`https://api.github.com/repos/astral-sh/uv/releases?per_page=${UV_RELEASE_PAGE_SIZE}&page=${page}`, options.downloadTimeoutMs, dispatcher, {
|
|
420
|
+
Accept: 'application/vnd.github+json',
|
|
421
|
+
'User-Agent': 'pyfig',
|
|
422
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
423
|
+
});
|
|
424
|
+
const payload = JSON.parse(text);
|
|
425
|
+
if (!Array.isArray(payload)) {
|
|
426
|
+
throw new Error('GitHub returned invalid uv release metadata.');
|
|
427
|
+
}
|
|
428
|
+
for (const candidate of payload) {
|
|
429
|
+
if (!candidate || typeof candidate !== 'object')
|
|
430
|
+
continue;
|
|
431
|
+
const release = candidate;
|
|
432
|
+
if (release.draft === true || typeof release.tag_name !== 'string')
|
|
433
|
+
continue;
|
|
434
|
+
const assetNames = releaseAssetNames(release.assets);
|
|
435
|
+
const version = normalizePep440Version(release.tag_name);
|
|
436
|
+
if (version) {
|
|
437
|
+
releases.set(version, { assets: assetNames, tag: release.tag_name, version });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (payload.length < UV_RELEASE_PAGE_SIZE) {
|
|
441
|
+
exhausted = true;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
await dispatcher?.close();
|
|
448
|
+
}
|
|
449
|
+
if (!exhausted) {
|
|
450
|
+
throw new Error(`uv release catalog exceeded ${UV_RELEASE_MAX_PAGES} pages.`);
|
|
451
|
+
}
|
|
452
|
+
if (releases.size === 0) {
|
|
453
|
+
throw new Error('GitHub returned no usable uv releases.');
|
|
454
|
+
}
|
|
455
|
+
return { fetchedAt: Date.now(), releases: [...releases.values()] };
|
|
456
|
+
}
|
|
457
|
+
async function readFreshUvReleaseCatalog(file) {
|
|
458
|
+
const catalog = await readUvReleaseCatalog(file);
|
|
459
|
+
if (!catalog)
|
|
460
|
+
return null;
|
|
461
|
+
const age = Date.now() - catalog.fetchedAt;
|
|
462
|
+
return age >= 0 && age < UV_RELEASE_METADATA_TTL_MS ? catalog : null;
|
|
463
|
+
}
|
|
464
|
+
async function readUvReleaseCatalog(file) {
|
|
465
|
+
try {
|
|
466
|
+
return parseUvReleaseCatalog(JSON.parse(await readFile(file, 'utf8')));
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function parseUvReleaseCatalog(value) {
|
|
473
|
+
if (!value || typeof value !== 'object')
|
|
474
|
+
return null;
|
|
475
|
+
const catalog = value;
|
|
476
|
+
if (!Number.isSafeInteger(catalog.fetchedAt) || catalog.fetchedAt < 0 ||
|
|
477
|
+
!Array.isArray(catalog.releases)) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
const releases = [];
|
|
481
|
+
for (const value of catalog.releases) {
|
|
482
|
+
if (!value || typeof value !== 'object')
|
|
483
|
+
return null;
|
|
484
|
+
const release = value;
|
|
485
|
+
if (!Array.isArray(release.assets) ||
|
|
486
|
+
!release.assets.every(asset => typeof asset === 'string') ||
|
|
487
|
+
typeof release.tag !== 'string' ||
|
|
488
|
+
!/^[A-Za-z0-9][A-Za-z0-9._+-]*$/u.test(release.tag) ||
|
|
489
|
+
typeof release.version !== 'string' ||
|
|
490
|
+
normalizePep440Version(release.version) !== release.version) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
releases.push({
|
|
494
|
+
assets: release.assets,
|
|
495
|
+
tag: release.tag,
|
|
496
|
+
version: release.version,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return { fetchedAt: catalog.fetchedAt, releases };
|
|
500
|
+
}
|
|
501
|
+
async function writeUvReleaseCatalog(file, catalog) {
|
|
502
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
503
|
+
const staged = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
|
|
504
|
+
try {
|
|
505
|
+
await writeFile(staged, `${JSON.stringify(catalog)}\n`, { encoding: 'utf8', flag: 'wx' });
|
|
506
|
+
if (process.platform === 'win32')
|
|
507
|
+
await rm(file, { force: true });
|
|
508
|
+
await rename(staged, file);
|
|
509
|
+
}
|
|
510
|
+
finally {
|
|
511
|
+
await rm(staged, { force: true });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function uvReleaseMetadataFile(options) {
|
|
515
|
+
return path.join(options.cacheDir, 'metadata', 'uv-releases.json');
|
|
516
|
+
}
|
|
517
|
+
function releaseAssetNames(value) {
|
|
518
|
+
if (!Array.isArray(value))
|
|
519
|
+
return [];
|
|
520
|
+
return value.flatMap(entry => {
|
|
521
|
+
if (!entry || typeof entry !== 'object')
|
|
522
|
+
return [];
|
|
523
|
+
const name = entry.name;
|
|
524
|
+
return typeof name === 'string' ? [name] : [];
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
function uvInstallRoot(options) {
|
|
528
|
+
return path.join(options.cacheDir, 'tools', 'uv');
|
|
529
|
+
}
|
|
530
|
+
function uvInstallDirectory(options, version) {
|
|
531
|
+
return path.join(uvInstallRoot(options), version);
|
|
354
532
|
}
|
|
355
|
-
function uvExecutable(options) {
|
|
356
|
-
return path.join(uvInstallDirectory(options), process.platform === 'win32' ? 'uv.exe' : 'uv');
|
|
533
|
+
function uvExecutable(options, version) {
|
|
534
|
+
return path.join(uvInstallDirectory(options, version), process.platform === 'win32' ? 'uv.exe' : 'uv');
|
|
357
535
|
}
|
|
358
|
-
async function installUv(installDir, options) {
|
|
536
|
+
async function installUv(installDir, release, options) {
|
|
359
537
|
const asset = uvAsset();
|
|
360
|
-
const baseUrl = `https://github.com/astral-sh/uv/releases/download/${
|
|
538
|
+
const baseUrl = `https://github.com/astral-sh/uv/releases/download/${release.tag}`;
|
|
361
539
|
const temporary = await mkdtemp(path.join(os.tmpdir(), 'pyfig-uv-'));
|
|
362
540
|
const dispatcher = proxyDispatcher(options.env);
|
|
363
541
|
try {
|
|
@@ -373,7 +551,7 @@ async function installUv(installDir, options) {
|
|
|
373
551
|
const source = await extractUvExecutable(archive, asset, extracted);
|
|
374
552
|
if (!source)
|
|
375
553
|
throw new Error(`uv archive did not contain the expected executable: ${asset}`);
|
|
376
|
-
await publishUvAtomically(source, installDir, options);
|
|
554
|
+
await publishUvAtomically(source, installDir, release.version, options);
|
|
377
555
|
}
|
|
378
556
|
finally {
|
|
379
557
|
await dispatcher?.close();
|
|
@@ -400,17 +578,17 @@ async function extractUvExecutable(archive, asset, destination) {
|
|
|
400
578
|
await writeFile(source, matches[0][1], { flag: 'wx' });
|
|
401
579
|
return source;
|
|
402
580
|
}
|
|
403
|
-
async function publishUvAtomically(source, installDir, options) {
|
|
581
|
+
async function publishUvAtomically(source, installDir, version, options) {
|
|
404
582
|
await mkdir(installDir, { recursive: true });
|
|
405
|
-
const executable = uvExecutable(options);
|
|
583
|
+
const executable = uvExecutable(options, version);
|
|
406
584
|
const staged = path.join(installDir, `.${path.basename(executable)}.${process.pid}.${randomUUID()}.tmp`);
|
|
407
585
|
try {
|
|
408
586
|
await copyFile(source, staged);
|
|
409
587
|
if (process.platform !== 'win32')
|
|
410
588
|
await chmod(staged, 0o755);
|
|
411
589
|
const stagedUv = probeUv(staged, options);
|
|
412
|
-
if (!stagedUv || stagedUv.version !==
|
|
413
|
-
throw new Error(`Downloaded uv executable is not version ${
|
|
590
|
+
if (!stagedUv || stagedUv.version !== version) {
|
|
591
|
+
throw new Error(`Downloaded uv executable is not version ${version}.`);
|
|
414
592
|
}
|
|
415
593
|
if (process.platform === 'win32')
|
|
416
594
|
await rm(executable, { force: true });
|
|
@@ -448,9 +626,10 @@ async function download(url, destination, timeoutMs, dispatcher) {
|
|
|
448
626
|
}
|
|
449
627
|
await pipeline(Readable.fromWeb(response.body), createWriteStream(destination, { flags: 'wx' }));
|
|
450
628
|
}
|
|
451
|
-
async function downloadText(url, timeoutMs, dispatcher) {
|
|
629
|
+
async function downloadText(url, timeoutMs, dispatcher, headers) {
|
|
452
630
|
const response = await fetch(url, {
|
|
453
631
|
dispatcher,
|
|
632
|
+
headers,
|
|
454
633
|
redirect: 'follow',
|
|
455
634
|
signal: AbortSignal.timeout(timeoutMs),
|
|
456
635
|
});
|