pinokiod 8.2.4 → 8.2.6
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/kernel/anywhere/index.js +1 -9
- package/kernel/bin/conda-pins.js +1 -3
- package/kernel/bin/conda.js +4 -7
- package/kernel/bin/tailscaled.js +2 -3
- package/kernel/shell.js +3 -10
- package/package.json +1 -1
- package/test/anywhere.test.js +3 -21
- package/test/conda-bin.test.js +14 -11
- package/test/shell-run-template.test.js +41 -42
package/kernel/anywhere/index.js
CHANGED
|
@@ -187,11 +187,8 @@ class Anywhere {
|
|
|
187
187
|
async autostart() {
|
|
188
188
|
// a pid file left behind by a crash or reboot must never point at an unrelated process
|
|
189
189
|
if (!(await this.daemonAlive())) fs.rmSync(this.pidPath(), { force: true })
|
|
190
|
-
if (!(await this.installed())) {
|
|
191
|
-
await this.stop()
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
190
|
if (!(await this.enabled())) return false
|
|
191
|
+
if (!(await this.installed())) return false
|
|
195
192
|
await this.start()
|
|
196
193
|
return true
|
|
197
194
|
}
|
|
@@ -202,11 +199,6 @@ class Anywhere {
|
|
|
202
199
|
}
|
|
203
200
|
async _start() {
|
|
204
201
|
this.error = null
|
|
205
|
-
if (!(await this.installed())) {
|
|
206
|
-
await this.stop()
|
|
207
|
-
this.error = "Update the tailscaled tool before starting Pinokio Anywhere."
|
|
208
|
-
throw new Error(this.error)
|
|
209
|
-
}
|
|
210
202
|
await this.loadConfig()
|
|
211
203
|
await fs.promises.mkdir(this.stateDir(), { recursive: true })
|
|
212
204
|
const bin = this.binPath()
|
package/kernel/bin/conda-pins.js
CHANGED
|
@@ -11,7 +11,7 @@ const TLS_PACKAGE_MINIMUMS = {
|
|
|
11
11
|
// Bootstrap URL and hash: https://discuss.python.org/t/109576
|
|
12
12
|
const PIP_MIN_VERSION = '26.2.2'
|
|
13
13
|
const PIP_INSTALL_SPEC = 'pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70'
|
|
14
|
-
const PIP_INSTALL_COMMAND = `python -I -m pip --isolated
|
|
14
|
+
const PIP_INSTALL_COMMAND = `python -I -m pip --isolated install --no-index "${PIP_INSTALL_SPEC}"`
|
|
15
15
|
const TLS_PACKAGE_SPECS = Object.entries(TLS_PACKAGE_MINIMUMS).map(([name, version]) => `${name}>=${version}`)
|
|
16
16
|
const hasSecureTlsPackages = (versions = {}) => Object.entries(TLS_PACKAGE_MINIMUMS).every(([name, minimum]) => {
|
|
17
17
|
const version = semver.coerce(versions[name])
|
|
@@ -46,9 +46,7 @@ const isExpectedPythonPinned = (platform, version, build) => {
|
|
|
46
46
|
|
|
47
47
|
module.exports = {
|
|
48
48
|
CONDA_PIN_VERSION,
|
|
49
|
-
TLS_PACKAGE_MINIMUMS,
|
|
50
49
|
PIP_MIN_VERSION,
|
|
51
|
-
PIP_INSTALL_SPEC,
|
|
52
50
|
PIP_INSTALL_COMMAND,
|
|
53
51
|
TLS_PACKAGE_SPECS,
|
|
54
52
|
hasSecureTlsPackages,
|
package/kernel/bin/conda.js
CHANGED
|
@@ -9,7 +9,6 @@ const { buildCondaListFromMeta, managedCondaRuns, hasSecureManagedPip } = requir
|
|
|
9
9
|
const {
|
|
10
10
|
CONDA_PIN_VERSION,
|
|
11
11
|
PIP_INSTALL_COMMAND,
|
|
12
|
-
TLS_PACKAGE_MINIMUMS,
|
|
13
12
|
TLS_PACKAGE_SPECS,
|
|
14
13
|
hasSecureTlsPackages,
|
|
15
14
|
PYTHON_INSTALL_SPEC,
|
|
@@ -183,16 +182,14 @@ report_errors: false`)
|
|
|
183
182
|
// Refresh a working runtime in place, preserving named app environments.
|
|
184
183
|
await this.init()
|
|
185
184
|
const bootstrap = await this.kernel.bin.exec({
|
|
186
|
-
message:
|
|
185
|
+
message: PIP_INSTALL_COMMAND,
|
|
187
186
|
}, ondata)
|
|
188
|
-
if (bootstrap?.error?.length) throw new Error("Conda certificate bootstrap failed; existing environments were preserved.")
|
|
187
|
+
if (bootstrap?.error?.length || !(await hasSecureManagedPip(this.kernel.bin.path(CONDA_ROOT_DIR), this.kernel.platform))) throw new Error("Conda certificate bootstrap failed; existing environments were preserved.")
|
|
189
188
|
const python = this.kernel.bin.path(CONDA_ROOT_DIR, this.kernel.platform === 'win32' ? 'python.exe' : 'bin/python')
|
|
190
|
-
const
|
|
191
|
-
['-I', '-c', '
|
|
189
|
+
const caFile = await new Promise((resolve, reject) => execFile(python,
|
|
190
|
+
['-I', '-c', 'from pip._vendor import certifi; print(certifi.where())'],
|
|
192
191
|
{ env: managedPythonEnv(), windowsHide: true, timeout: 15000 },
|
|
193
192
|
(error, stdout) => error ? reject(error) : resolve(stdout.trim())))
|
|
194
|
-
const [caFile, certifiVersion] = output.split(/[\r\n]+/)
|
|
195
|
-
if (!semver.valid(certifiVersion) || !semver.gte(certifiVersion, TLS_PACKAGE_MINIMUMS.certifi)) throw new Error("The certificate bootstrap did not complete.")
|
|
196
193
|
const result = await this.kernel.bin.exec({
|
|
197
194
|
message: [`conda install -y --override-channels -c conda-forge "${this.kernel.platform === "win32" ? WINDOWS_PYTHON_SSL_FIX_SPEC : PYTHON_INSTALL_SPEC}" "conda-libmamba-solver>=25.4.0" pip ${TLS_PACKAGE_SPECS.map(spec => `"${spec}"`).join(" ")}`, PIP_INSTALL_COMMAND],
|
|
198
195
|
env: { CONDA_SSL_VERIFY: caFile },
|
package/kernel/bin/tailscaled.js
CHANGED
|
@@ -3,8 +3,8 @@ const semver = require('semver')
|
|
|
3
3
|
|
|
4
4
|
// conda-forge ships only the daemon binary (no `tailscale` CLI); Pinokio
|
|
5
5
|
// drives it over its LocalAPI (see kernel/anywhere).
|
|
6
|
-
const TAILSCALED_SPEC = "tailscaled=1.104.
|
|
7
|
-
const TAILSCALED_REQUIREMENT = ">=1.104.
|
|
6
|
+
const TAILSCALED_SPEC = "tailscaled=1.104.1"
|
|
7
|
+
const TAILSCALED_REQUIREMENT = ">=1.104.0" // 1.104 added --windows-mode=dev
|
|
8
8
|
|
|
9
9
|
class Tailscaled {
|
|
10
10
|
description = "Installs the Tailscale daemon for Pinokio Anywhere: reach this Pinokio and its apps from your phone or another computer over a private network."
|
|
@@ -18,7 +18,6 @@ class Tailscaled {
|
|
|
18
18
|
return this.kernel.bin.path("miniforge", "bin", "tailscaled")
|
|
19
19
|
}
|
|
20
20
|
async install(req, ondata) {
|
|
21
|
-
if (this.kernel.anywhere) await this.kernel.anywhere.stop()
|
|
22
21
|
await this.kernel.bin.exec({
|
|
23
22
|
message: [
|
|
24
23
|
// a stale libmamba index cache reports the package as missing even
|
package/kernel/shell.js
CHANGED
|
@@ -21,7 +21,7 @@ const ShellParser = require('./shell_parser')
|
|
|
21
21
|
const AnsiStreamTracker = require('./ansi_stream_tracker')
|
|
22
22
|
const ShellStateSync = require('./shell_state_sync')
|
|
23
23
|
const ShellRunTemplate = require('./api/shell_run_template')
|
|
24
|
-
const { PYTHON_INSTALL_SPEC
|
|
24
|
+
const { PYTHON_INSTALL_SPEC } = require('./bin/conda-pins')
|
|
25
25
|
const CondaRuntimeGuard = require('./shell_conda_runtime_guard')
|
|
26
26
|
const home = os.homedir()
|
|
27
27
|
const WINDOWS_CMD_CONDA_COMMAND_PATTERN = /(?:^|[&|()]\s*)@?\s*(?:call\s+)?(?:"[^"]*[\\/]conda(?:\.bat|\.exe)?"|(?:[a-z]:)?[^&|()\s"]*[\\/]conda(?:\.bat|\.exe)?|conda(?:\.bat|\.exe)?)\s+/i
|
|
@@ -1391,25 +1391,18 @@ class Shell {
|
|
|
1391
1391
|
// timeout,
|
|
1392
1392
|
]
|
|
1393
1393
|
} else {
|
|
1394
|
-
// when python version is not specified,
|
|
1394
|
+
// when python version is not specified, seed pip using the activated Python
|
|
1395
1395
|
venv_activation = [
|
|
1396
|
-
`python -
|
|
1396
|
+
`uv venv --seed --python-preference only-system --python python ${shellEnvPath}`,
|
|
1397
1397
|
activate_command,
|
|
1398
1398
|
// `python -m pip install --upgrade pip setuptools wheel`,
|
|
1399
1399
|
deactivate_path,
|
|
1400
1400
|
// timeout,
|
|
1401
1401
|
activate_command,
|
|
1402
|
-
this.isPowerShell()
|
|
1403
|
-
? `${PIP_INSTALL_COMMAND}; if ($LASTEXITCODE -ne 0) { exit 1 }`
|
|
1404
|
-
: `${PIP_INSTALL_COMMAND} || exit 1`,
|
|
1405
1402
|
// timeout,
|
|
1406
1403
|
]
|
|
1407
1404
|
}
|
|
1408
1405
|
}
|
|
1409
|
-
if (!use_uv) {
|
|
1410
|
-
const checkPip = `python -I -c "from importlib.metadata import distributions; assert all(tuple(map(int, d.version.split('.')[:3])) >= (${PIP_MIN_VERSION.split(".").join(", ")}) for d in distributions(name='pip')), 'Update pip in this app environment before running it.'"`
|
|
1411
|
-
venv_activation.push(this.isPowerShell() ? `${checkPip}; if ($LASTEXITCODE -ne 0) { exit 1 }` : `${checkPip} || exit 1`)
|
|
1412
|
-
}
|
|
1413
1406
|
} else {
|
|
1414
1407
|
venv_activation = []
|
|
1415
1408
|
}
|
package/package.json
CHANGED
package/test/anywhere.test.js
CHANGED
|
@@ -59,20 +59,17 @@ test('anywhere preset requires tailscaled but not caddy', () => {
|
|
|
59
59
|
assert.ok(!preset.conda_requirements.includes('caddy'))
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
test('tailscaled bin module pins 1.104.
|
|
62
|
+
test('tailscaled bin module pins 1.104.1 and checks the installed version', async () => {
|
|
63
63
|
const mod = new Tailscaled()
|
|
64
|
-
assert.equal(mod.cmd(), 'tailscaled=1.104.
|
|
64
|
+
assert.equal(mod.cmd(), 'tailscaled=1.104.1')
|
|
65
65
|
mod.kernel = {
|
|
66
66
|
platform: 'darwin',
|
|
67
67
|
bin: {
|
|
68
|
-
installed: { conda: new Set(['tailscaled']), conda_versions: { tailscaled: '1.104.
|
|
68
|
+
installed: { conda: new Set(['tailscaled']), conda_versions: { tailscaled: '1.104.1' } },
|
|
69
69
|
path: (...segments) => segments.join('/'),
|
|
70
70
|
},
|
|
71
71
|
}
|
|
72
72
|
assert.equal(await mod.installed(), true)
|
|
73
|
-
mod.kernel.bin.installed.conda_versions.tailscaled = '1.104.1'
|
|
74
|
-
assert.equal(await mod.installed(), false)
|
|
75
|
-
mod.kernel.bin.installed.conda_versions.tailscaled = '1.104.2'
|
|
76
73
|
assert.equal(mod.binPath(), 'miniforge/bin/tailscaled')
|
|
77
74
|
mod.kernel.bin.installed.conda_versions.tailscaled = '1.98.5'
|
|
78
75
|
assert.equal(await mod.installed(), false, 'older than 1.104 (no --windows-mode=dev) is not accepted')
|
|
@@ -444,18 +441,3 @@ test('sign-out resets the login state, and a link the server rejected is replace
|
|
|
444
441
|
fs.rmSync(tmp, { recursive: true, force: true })
|
|
445
442
|
}
|
|
446
443
|
})
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
test('old Tailscale daemons are stopped and cannot be adopted through direct start', async t => {
|
|
450
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-anywhere-version-'))
|
|
451
|
-
t.after(() => fs.rmSync(root, { recursive: true, force: true }))
|
|
452
|
-
const anywhere = new Anywhere(fakeKernel(root, { installed: false }))
|
|
453
|
-
let stopped = 0
|
|
454
|
-
anywhere.daemonAlive = async () => true
|
|
455
|
-
anywhere.enabled = async () => true
|
|
456
|
-
anywhere.stop = async () => { stopped++ }
|
|
457
|
-
assert.equal(await anywhere.autostart(), false)
|
|
458
|
-
assert.equal(stopped, 1)
|
|
459
|
-
await assert.rejects(anywhere.start(), /Update the tailscaled tool/)
|
|
460
|
-
assert.equal(stopped, 2)
|
|
461
|
-
})
|
package/test/conda-bin.test.js
CHANGED
|
@@ -14,9 +14,7 @@ const {
|
|
|
14
14
|
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
15
15
|
isExpectedCondaPinned,
|
|
16
16
|
isExpectedPythonPinned,
|
|
17
|
-
TLS_PACKAGE_MINIMUMS,
|
|
18
17
|
TLS_PACKAGE_SPECS,
|
|
19
|
-
PIP_INSTALL_SPEC,
|
|
20
18
|
PIP_MIN_VERSION,
|
|
21
19
|
} = require('../kernel/bin/conda-pins')
|
|
22
20
|
|
|
@@ -26,7 +24,8 @@ test.beforeEach(t => {
|
|
|
26
24
|
if (args[0] !== '-I' || args[1] !== '-c') return realExecFile(binary, args, options, done)
|
|
27
25
|
const prefix = path.basename(binary) === 'python.exe' ? path.dirname(binary) : path.dirname(path.dirname(binary))
|
|
28
26
|
if (args[2].includes('certifi.where()')) {
|
|
29
|
-
|
|
27
|
+
assert.match(args[2], /^from pip\._vendor import certifi;/)
|
|
28
|
+
done(null, path.join(prefix, 'certificates.pem') + '\n')
|
|
30
29
|
return
|
|
31
30
|
}
|
|
32
31
|
fs.readFile(path.join(prefix, 'test-pip-version'), 'utf8').then(
|
|
@@ -73,8 +72,8 @@ async function writeHealthyCondaMeta(root, platform = 'win32', condaVersion = CO
|
|
|
73
72
|
await writeCondaMeta(root, 'conda', condaVersion, 'py310')
|
|
74
73
|
await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
|
|
75
74
|
await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
|
|
76
|
-
for (const [
|
|
77
|
-
await writeCondaMeta(root, name,
|
|
75
|
+
for (const name of ['ca-certificates', 'certifi']) {
|
|
76
|
+
await writeCondaMeta(root, name, '2026.10.19')
|
|
78
77
|
}
|
|
79
78
|
await fs.writeFile(path.join(root, 'bin/miniforge/test-pip-version'), PIP_MIN_VERSION)
|
|
80
79
|
}
|
|
@@ -282,7 +281,7 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
|
|
|
282
281
|
assert.equal(await pathExists(path.join(root, 'bin', 'miniconda')), true)
|
|
283
282
|
const condaInstall = calls.find((call) => Array.isArray(call.message))
|
|
284
283
|
assert.ok(condaInstall)
|
|
285
|
-
assert.
|
|
284
|
+
assert.match(condaInstall.message[2], /^python -I -m pip --isolated install --no-index "pip @ https:\/\/files\.pythonhosted\.org\/packages\/[a-f0-9/]+\/pip-26\.2\.2-py3-none-any\.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"$/)
|
|
286
285
|
assert.equal(
|
|
287
286
|
condaInstall.message[1],
|
|
288
287
|
'conda install -y --override-channels -c conda-forge "conda=26.5.3" "python=3.10.20" "conda-libmamba-solver>=25.4.0" pip "ca-certificates>=2026.10.19" "certifi>=2026.10.19"'
|
|
@@ -620,8 +619,8 @@ test('stale certificate packages trigger the existing Conda setup checks', async
|
|
|
620
619
|
t.after(() => fs.rm(root, { recursive: true, force: true }))
|
|
621
620
|
await writeHealthyCondaMeta(root, 'darwin')
|
|
622
621
|
await writeFakeManagedConda(root, 'darwin')
|
|
623
|
-
for (const [
|
|
624
|
-
await fs.rm(path.join(root, 'bin/miniforge/conda-meta', `${name}
|
|
622
|
+
for (const name of ['ca-certificates', 'certifi']) {
|
|
623
|
+
await fs.rm(path.join(root, 'bin/miniforge/conda-meta', `${name}-2026.10.19.json`))
|
|
625
624
|
await writeCondaMeta(root, name, '2026.4.22')
|
|
626
625
|
}
|
|
627
626
|
assert.equal(await createConda(createKernel(root, 'darwin')).check(), false)
|
|
@@ -655,7 +654,7 @@ test('old pip cannot pass Conda readiness even with current CA packages', async
|
|
|
655
654
|
assert.equal(bin.correct_conda, false)
|
|
656
655
|
})
|
|
657
656
|
|
|
658
|
-
test(
|
|
657
|
+
test("certificate refresh preserves named environments and uses pip's bundled CA", async t => {
|
|
659
658
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-ca-refresh-'))
|
|
660
659
|
t.after(() => fs.rm(root, { recursive: true, force: true }))
|
|
661
660
|
await writeHealthyCondaMeta(root, 'darwin')
|
|
@@ -670,6 +669,7 @@ test('certificate refresh preserves named environments and uses the new CA bundl
|
|
|
670
669
|
await createConda(kernel).install({}, () => {})
|
|
671
670
|
assert.equal(await fs.readFile(marker, 'utf8'), 'existing app')
|
|
672
671
|
assert.equal(calls.length, 2)
|
|
672
|
+
assert.match(calls[0].message, /^python -I -m pip --isolated install --no-index /)
|
|
673
673
|
assert.equal(calls[1].env.CONDA_SSL_VERIFY, path.join(root, 'bin/miniforge/certificates.pem'))
|
|
674
674
|
})
|
|
675
675
|
|
|
@@ -679,9 +679,12 @@ test('failed pip bootstrap stops certificate refresh without replacing environme
|
|
|
679
679
|
await writeHealthyCondaMeta(root, 'darwin')
|
|
680
680
|
await writeFakeManagedConda(root, 'darwin')
|
|
681
681
|
const kernel = createKernel(root, 'darwin')
|
|
682
|
-
kernel.bin.exec = async () => ({ error: ['ERROR: hash verification failed'] })
|
|
683
682
|
kernel.bin.download = async () => assert.fail('must preserve the runtime')
|
|
684
|
-
await
|
|
683
|
+
await fs.writeFile(path.join(root, 'bin/miniforge/test-pip-version'), '26.2.1')
|
|
684
|
+
for (const response of [{ error: ['ERROR: hash verification failed'] }, {}]) {
|
|
685
|
+
kernel.bin.exec = async () => response
|
|
686
|
+
await assert.rejects(createConda(kernel).install({}, () => {}), /bootstrap failed/)
|
|
687
|
+
}
|
|
685
688
|
assert.equal(await pathExists(path.join(root, 'bin/miniforge/conda-meta')), true)
|
|
686
689
|
})
|
|
687
690
|
|
|
@@ -379,47 +379,46 @@ test('redactEnvArgs summarizes protected argv env values', () => {
|
|
|
379
379
|
})
|
|
380
380
|
|
|
381
381
|
|
|
382
|
-
test('
|
|
383
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-
|
|
382
|
+
test('venv creation preserves interpreter selection and existing environments across shells', async t => {
|
|
383
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-selection-'))
|
|
384
384
|
t.after(() => fs.rm(root, { recursive: true, force: true }))
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
assert.equal(await fs.access(path.join(root, 'app-ran')).then(() => true, () => false), false)
|
|
385
|
+
const cases = [
|
|
386
|
+
{ venv: 'default-env', seeded: true },
|
|
387
|
+
{ venv: 'explicit-env', venv_python: '3.10', version: '3.10' },
|
|
388
|
+
{ venv: { path: 'object-env', python: '3.11' }, version: '3.11' },
|
|
389
|
+
{ venv: 'existing-env', existing: true },
|
|
390
|
+
]
|
|
391
|
+
await fs.mkdir(path.join(root, 'existing-env'))
|
|
392
|
+
for (const [platform, shellName] of [
|
|
393
|
+
['linux', 'bash'],
|
|
394
|
+
['darwin', 'bash'],
|
|
395
|
+
['win32', 'cmd.exe'],
|
|
396
|
+
['win32', 'powershell'],
|
|
397
|
+
['win32', 'bash'],
|
|
398
|
+
]) {
|
|
399
|
+
for (const scenario of cases) {
|
|
400
|
+
const shell = createShell(createKernel(root))
|
|
401
|
+
shell.platform = platform
|
|
402
|
+
shell.shell = shellName
|
|
403
|
+
const params = await shell.activate({
|
|
404
|
+
path: root,
|
|
405
|
+
conda: { skip: true },
|
|
406
|
+
venv: scenario.venv,
|
|
407
|
+
venv_python: scenario.venv_python,
|
|
408
|
+
message: 'echo PINOKIO_REQUIREMENTS_RAN',
|
|
409
|
+
})
|
|
410
|
+
const command = shell.build(params)
|
|
411
|
+
assert.doesNotMatch(command, /python -m venv|python -I|files\.pythonhosted\.org|\|\| exit 1/)
|
|
412
|
+
assert.match(command, /echo PINOKIO_REQUIREMENTS_RAN$/)
|
|
413
|
+
if (scenario.existing) {
|
|
414
|
+
assert.doesNotMatch(command, /uv venv|--seed/)
|
|
415
|
+
} else if (scenario.seeded) {
|
|
416
|
+
assert.match(command, /uv venv --seed --python-preference only-system --python python /)
|
|
417
|
+
} else {
|
|
418
|
+
assert.ok(command.includes(` --python ${scenario.version}`), command)
|
|
419
|
+
assert.doesNotMatch(command, /--seed|--python-preference only-system/)
|
|
420
|
+
}
|
|
421
|
+
assert.equal(shell.env.UV_PYTHON_PREFERENCE, 'only-managed')
|
|
422
|
+
}
|
|
423
|
+
}
|
|
425
424
|
})
|