pinokiod 8.2.5 → 8.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +0 -1
- package/kernel/shell.js +3 -6
- package/package.json +1 -1
- package/test/anywhere.test.js +0 -15
- package/test/conda-bin.test.js +14 -11
- package/test/shell-run-template.test.js +77 -36
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
|
@@ -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, PIP_MIN_VERSION } = 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
|
|
@@ -1399,16 +1399,13 @@ class Shell {
|
|
|
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
1406
|
if (!use_uv) {
|
|
1410
|
-
const
|
|
1411
|
-
venv_activation.push(this.isPowerShell() ? `${
|
|
1407
|
+
const updatePip = `uv pip install --exclude-newer-package pip=false "pip>=${PIP_MIN_VERSION}"`
|
|
1408
|
+
venv_activation.push(this.isPowerShell() ? `${updatePip}; if ($LASTEXITCODE -ne 0) { exit 1 }` : `${updatePip} || exit 1`)
|
|
1412
1409
|
}
|
|
1413
1410
|
} else {
|
|
1414
1411
|
venv_activation = []
|
package/package.json
CHANGED
package/test/anywhere.test.js
CHANGED
|
@@ -441,18 +441,3 @@ test('sign-out resets the login state, and a link the server rejected is replace
|
|
|
441
441
|
fs.rmSync(tmp, { recursive: true, force: true })
|
|
442
442
|
}
|
|
443
443
|
})
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
test('old Tailscale daemons are stopped and cannot be adopted through direct start', async t => {
|
|
447
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-anywhere-version-'))
|
|
448
|
-
t.after(() => fs.rmSync(root, { recursive: true, force: true }))
|
|
449
|
-
const anywhere = new Anywhere(fakeKernel(root, { installed: false }))
|
|
450
|
-
let stopped = 0
|
|
451
|
-
anywhere.daemonAlive = async () => true
|
|
452
|
-
anywhere.enabled = async () => true
|
|
453
|
-
anywhere.stop = async () => { stopped++ }
|
|
454
|
-
assert.equal(await anywhere.autostart(), false)
|
|
455
|
-
assert.equal(stopped, 1)
|
|
456
|
-
await assert.rejects(anywhere.start(), /Update the tailscaled tool/)
|
|
457
|
-
assert.equal(stopped, 2)
|
|
458
|
-
})
|
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
|
|
|
@@ -3,9 +3,11 @@ const fs = require('node:fs/promises')
|
|
|
3
3
|
const os = require('node:os')
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
const test = require('node:test')
|
|
6
|
+
const { spawnSync } = require('node:child_process')
|
|
6
7
|
|
|
7
8
|
const ShellRunTemplate = require('../kernel/api/shell_run_template')
|
|
8
9
|
const Shell = require('../kernel/shell')
|
|
10
|
+
const { PIP_MIN_VERSION } = require('../kernel/bin/conda-pins')
|
|
9
11
|
|
|
10
12
|
function createKernel(root = process.cwd()) {
|
|
11
13
|
return {
|
|
@@ -379,47 +381,86 @@ test('redactEnvArgs summarizes protected argv env values', () => {
|
|
|
379
381
|
})
|
|
380
382
|
|
|
381
383
|
|
|
382
|
-
test('
|
|
383
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-
|
|
384
|
+
test('venvs preserve creation behavior and enforce the pip minimum across shells', async t => {
|
|
385
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-venv-selection-'))
|
|
384
386
|
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
|
-
|
|
387
|
+
const cases = [
|
|
388
|
+
{ venv: 'default-env' },
|
|
389
|
+
{ venv: { path: 'object-default-env' } },
|
|
390
|
+
{ venv: 'explicit-env', venv_python: '3.10', version: '3.10' },
|
|
391
|
+
{ venv: { path: 'object-env', python: '3.11' }, version: '3.11' },
|
|
392
|
+
{ venv: 'existing-env', existing: true },
|
|
393
|
+
{ venv: 'existing-env', venv_python: '3.10', version: '3.10', existing: true },
|
|
394
|
+
{},
|
|
395
|
+
]
|
|
396
|
+
await fs.mkdir(path.join(root, 'existing-env'))
|
|
397
|
+
for (const [platform, shellName] of [
|
|
398
|
+
['linux', 'bash'],
|
|
399
|
+
['darwin', 'bash'],
|
|
400
|
+
['win32', 'cmd.exe'],
|
|
401
|
+
['win32', 'powershell'],
|
|
402
|
+
['win32', 'bash'],
|
|
403
|
+
]) {
|
|
404
|
+
for (const scenario of cases) {
|
|
405
|
+
const shell = createShell(createKernel(root))
|
|
406
|
+
shell.platform = platform
|
|
407
|
+
shell.shell = shellName
|
|
408
|
+
const params = await shell.activate({
|
|
409
|
+
path: root,
|
|
410
|
+
conda: { skip: true },
|
|
411
|
+
venv: scenario.venv,
|
|
412
|
+
venv_python: scenario.venv_python,
|
|
413
|
+
message: 'echo PINOKIO_REQUIREMENTS_RAN',
|
|
414
|
+
})
|
|
415
|
+
const command = shell.build(params)
|
|
416
|
+
assert.doesNotMatch(command, /--seed|--no-config|--python-preference|python -I|files\.pythonhosted\.org/)
|
|
417
|
+
assert.match(command, /echo PINOKIO_REQUIREMENTS_RAN$/)
|
|
418
|
+
if (!scenario.venv || scenario.existing) {
|
|
419
|
+
assert.doesNotMatch(command, /python -m venv|uv venv/)
|
|
420
|
+
} else if (scenario.version) {
|
|
421
|
+
assert.ok(command.includes(` --python ${scenario.version}`), command)
|
|
422
|
+
assert.doesNotMatch(command, /python -m venv/)
|
|
423
|
+
} else {
|
|
424
|
+
assert.match(command, /python -m venv /)
|
|
425
|
+
}
|
|
426
|
+
const updatePip = `uv pip install --exclude-newer-package pip=false "pip>=${PIP_MIN_VERSION}"`
|
|
427
|
+
if (scenario.venv && !scenario.version) {
|
|
428
|
+
assert.equal(command.split(updatePip).length - 1, 1)
|
|
429
|
+
assert.ok(command.indexOf(updatePip) > command.lastIndexOf('activate'))
|
|
430
|
+
assert.ok(command.includes(shell.isPowerShell()
|
|
431
|
+
? `${updatePip}; if ($LASTEXITCODE -ne 0) { exit 1 }`
|
|
432
|
+
: `${updatePip} || exit 1`))
|
|
433
|
+
} else {
|
|
434
|
+
assert.doesNotMatch(command, /uv pip install/)
|
|
435
|
+
}
|
|
436
|
+
assert.equal(shell.env.UV_PYTHON_PREFERENCE, 'only-managed')
|
|
437
|
+
}
|
|
438
|
+
}
|
|
408
439
|
})
|
|
409
440
|
|
|
410
441
|
|
|
411
|
-
test('
|
|
412
|
-
|
|
442
|
+
test('failed pip updates stop the application even with an unconditional command chain', async t => {
|
|
443
|
+
if (process.platform === 'win32') return t.skip('executes the POSIX shell failure guard')
|
|
444
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-pip-failure-'))
|
|
413
445
|
t.after(() => fs.rm(root, { recursive: true, force: true }))
|
|
414
|
-
|
|
415
|
-
await fs.
|
|
416
|
-
|
|
417
|
-
await fs.
|
|
418
|
-
await fs.writeFile(path.join(
|
|
446
|
+
const binDir = path.join(root, 'bin')
|
|
447
|
+
await fs.mkdir(path.join(root, 'env', 'bin'), { recursive: true })
|
|
448
|
+
await fs.mkdir(binDir)
|
|
449
|
+
await fs.writeFile(path.join(root, 'env', 'bin', 'activate'), 'true\n')
|
|
450
|
+
await fs.writeFile(path.join(binDir, 'uv'), '#!/bin/sh\nexit "$PINOKIO_TEST_UV_EXIT_CODE"\n', { mode: 0o755 })
|
|
419
451
|
const shell = createShell(createKernel(root))
|
|
420
452
|
shell.shell = 'bash'
|
|
421
|
-
const params = await shell.activate({
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
453
|
+
const params = await shell.activate({
|
|
454
|
+
path: root, conda: { skip: true }, venv: 'env',
|
|
455
|
+
chain: '*', message: 'echo PINOKIO_APPLICATION_RAN',
|
|
456
|
+
})
|
|
457
|
+
const command = shell.build(params)
|
|
458
|
+
for (const exitCode of [0, 7]) {
|
|
459
|
+
const result = spawnSync('/bin/bash', ['-c', command], {
|
|
460
|
+
encoding: 'utf8',
|
|
461
|
+
env: { ...process.env, PATH: `${binDir}${path.delimiter}${process.env.PATH}`, PINOKIO_TEST_UV_EXIT_CODE: String(exitCode) },
|
|
462
|
+
})
|
|
463
|
+
assert.equal(result.status, exitCode ? 1 : 0, result.stderr)
|
|
464
|
+
assert.equal(result.stdout.includes('PINOKIO_APPLICATION_RAN'), exitCode === 0)
|
|
465
|
+
}
|
|
425
466
|
})
|