pinokiod 8.0.1 → 8.0.3

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.
@@ -1,10 +1,12 @@
1
1
  const semver = require('semver')
2
2
 
3
- const CONDA_PIN_VERSION = "26.3.2"
3
+ const CONDA_PIN_VERSION = "26.5.3"
4
4
  const PYTHON_PIN_VERSION = "3.10.20"
5
5
  const PYTHON_INSTALL_SPEC = `python=${PYTHON_PIN_VERSION}`
6
6
  const WINDOWS_PYTHON_SSL_FIX_SPEC = `${PYTHON_INSTALL_SPEC}=*_1_cpython`
7
7
 
8
+ const isExpectedCondaPinned = (version) => String(version) === CONDA_PIN_VERSION
9
+
8
10
  const condaBuildNumber = (build) => {
9
11
  const chunks = String(build || "").split("_").reverse()
10
12
  const buildNumber = chunks.find((chunk) => /^\d+$/.test(chunk))
@@ -30,5 +32,6 @@ module.exports = {
30
32
  CONDA_PIN_VERSION,
31
33
  PYTHON_INSTALL_SPEC,
32
34
  WINDOWS_PYTHON_SSL_FIX_SPEC,
35
+ isExpectedCondaPinned,
33
36
  isExpectedPythonPinned,
34
37
  }
@@ -1,5 +1,4 @@
1
1
  const fs = require('fs')
2
- const path = require('path')
3
2
  const { glob } = require('glob')
4
3
  const semver = require('semver')
5
4
  const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
@@ -7,6 +6,7 @@ const {
7
6
  CONDA_PIN_VERSION,
8
7
  PYTHON_INSTALL_SPEC,
9
8
  WINDOWS_PYTHON_SSL_FIX_SPEC,
9
+ isExpectedCondaPinned,
10
10
  isExpectedPythonPinned,
11
11
  } = require('./conda-pins')
12
12
 
@@ -14,61 +14,6 @@ const MINIFORGE_RELEASE = "26.3.2-3"
14
14
  const MINIFORGE_BASE_URL = `https://github.com/conda-forge/miniforge/releases/download/${MINIFORGE_RELEASE}`
15
15
  const CONDA_ROOT_DIR = "miniforge"
16
16
  const LEGACY_CONDA_ROOT_DIR = "miniconda"
17
- const WINDOWS_OPENSSL_HOOKS = {
18
- activate: {
19
- "openssl_activate-win.bat": [
20
- "@echo off",
21
- 'if "%SSL_CERT_FILE%"=="" (',
22
- ' set "SSL_CERT_FILE=%CONDA_PREFIX%\\Library\\ssl\\cacert.pem"',
23
- ' set "__CONDA_OPENSSL_CERT_FILE_SET=1"',
24
- ")",
25
- "",
26
- ].join("\r\n"),
27
- "openssl_activate-win.ps1": [
28
- "if (-not $Env:SSL_CERT_FILE) {",
29
- ' $Env:SSL_CERT_FILE = "$Env:CONDA_PREFIX\\Library\\ssl\\cacert.pem"',
30
- ' $Env:__CONDA_OPENSSL_CERT_FILE_SET = "1"',
31
- "}",
32
- "",
33
- ].join("\r\n"),
34
- "openssl_activate-win.sh": [
35
- 'if [[ "${SSL_CERT_FILE:-}" == "" ]]; then',
36
- ' export SSL_CERT_FILE="${CONDA_PREFIX}\\\\Library\\\\ssl\\\\cacert.pem"',
37
- ' export __CONDA_OPENSSL_CERT_FILE_SET="1"',
38
- "fi",
39
- "",
40
- ].join("\n"),
41
- },
42
- deactivate: {
43
- "openssl_deactivate-win.bat": [
44
- "@echo off",
45
- 'if "%__CONDA_OPENSSL_CERT_FILE_SET%" == "1" (',
46
- " set SSL_CERT_FILE=",
47
- " set __CONDA_OPENSSL_CERT_FILE_SET=",
48
- ")",
49
- "",
50
- ].join("\r\n"),
51
- "openssl_deactivate-win.ps1": [
52
- 'if ($Env:__CONDA_OPENSSL_CERT_FILE_SET -eq "1") {',
53
- " Remove-Item -Path Env:\\SSL_CERT_FILE",
54
- " Remove-Item -Path Env:\\__CONDA_OPENSSL_CERT_FILE_SET",
55
- "}",
56
- "",
57
- ].join("\r\n"),
58
- "openssl_deactivate-win.sh": [
59
- 'if [[ "${__CONDA_OPENSSL_CERT_FILE_SET:-}" == "1" ]]; then',
60
- " unset SSL_CERT_FILE",
61
- " unset __CONDA_OPENSSL_CERT_FILE_SET",
62
- "fi",
63
- "",
64
- ].join("\n"),
65
- },
66
- }
67
- const LEGACY_SSL_CERT_DIR_HOOK_FILES = [
68
- "zz_pinokio_unset_ssl_cert_dir-win.bat",
69
- "zz_pinokio_unset_ssl_cert_dir-win.ps1",
70
- "zz_pinokio_unset_ssl_cert_dir-win.sh",
71
- ]
72
17
 
73
18
  class Conda {
74
19
  description = "Pinokio uses Conda to install various useful programs in an isolated manner."
@@ -124,43 +69,6 @@ class Conda {
124
69
  }
125
70
  return base
126
71
  }
127
- async ensureWindowsOpenSslHooks() {
128
- if (this.kernel.platform !== "win32") {
129
- return
130
- }
131
- const condaRootDirs = []
132
- for (const rootDir of [CONDA_ROOT_DIR, LEGACY_CONDA_ROOT_DIR]) {
133
- if (await this.kernel.exists(`bin/${rootDir}/conda-meta`)) {
134
- condaRootDirs.push(rootDir)
135
- }
136
- }
137
- if (condaRootDirs.length === 0) {
138
- return
139
- }
140
- for (const rootDir of condaRootDirs) {
141
- const activateDir = this.kernel.bin.path(`${rootDir}/etc/conda/activate.d`)
142
- const deactivateDir = this.kernel.bin.path(`${rootDir}/etc/conda/deactivate.d`)
143
- for (const hookDir of [activateDir, deactivateDir]) {
144
- for (const filename of LEGACY_SSL_CERT_DIR_HOOK_FILES) {
145
- await fs.promises.rm(path.resolve(hookDir, filename), { force: true }).catch(() => {})
146
- }
147
- }
148
- for (const [filename, content] of Object.entries(WINDOWS_OPENSSL_HOOKS.activate)) {
149
- const hookPath = path.resolve(activateDir, filename)
150
- const exists = await fs.promises.access(hookPath).then(() => true).catch(() => false)
151
- if (exists) {
152
- await fs.promises.writeFile(hookPath, content)
153
- }
154
- }
155
- for (const [filename, content] of Object.entries(WINDOWS_OPENSSL_HOOKS.deactivate)) {
156
- const hookPath = path.resolve(deactivateDir, filename)
157
- const exists = await fs.promises.access(hookPath).then(() => true).catch(() => false)
158
- if (exists) {
159
- await fs.promises.writeFile(hookPath, content)
160
- }
161
- }
162
- }
163
- }
164
72
  async init() {
165
73
  if (this.kernel.homedir) {
166
74
  console.log("condarc init")
@@ -182,7 +90,6 @@ report_errors: false`)
182
90
  await fs.promises.writeFile(this.kernel.path(`bin/${CONDA_ROOT_DIR}/conda-meta/pinned`), this.pinnedPackages())
183
91
  await this.ensureCompatibilityAlias()
184
92
  }
185
- await this.ensureWindowsOpenSslHooks()
186
93
  }
187
94
  }
188
95
  async check() {
@@ -205,7 +112,7 @@ report_errors: false`)
205
112
  conda_versions[name] = version
206
113
  conda_builds[name] = build
207
114
  if (name === "conda") {
208
- conda_check.conda = true
115
+ conda_check.conda = isExpectedCondaPinned(version)
209
116
  }
210
117
  // check conda-libmamba-solver is up to date
211
118
  // sometimes it just fails silently so need to check
@@ -307,6 +214,7 @@ report_errors: false`)
307
214
  console.log("Conda dependencies to install", { mods })
308
215
 
309
216
  let condaPackages = [
217
+ `"conda=${CONDA_PIN_VERSION}"`,
310
218
  this.kernel.platform === "win32" ? `"${WINDOWS_PYTHON_SSL_FIX_SPEC}"` : `"${PYTHON_INSTALL_SPEC}"`,
311
219
  `"conda-libmamba-solver>=25.4.0"`,
312
220
  ]
@@ -330,7 +238,6 @@ report_errors: false`)
330
238
  this.kernel.bin.path(CONDA_ROOT_DIR, "python3.exe"),
331
239
  )
332
240
  }
333
- await this.ensureWindowsOpenSslHooks()
334
241
  await this.ensureCompatibilityAlias()
335
242
  ondata({ raw: `Install finished\r\n` })
336
243
  await this.kernel.bin.rm(installer, ondata)
@@ -23,6 +23,7 @@ const Cuda = require("./cuda")
23
23
  const Torch = require("./torch")
24
24
  const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
25
25
  const {
26
+ isExpectedCondaPinned,
26
27
  isExpectedPythonPinned,
27
28
  } = require('./conda-pins')
28
29
  const { glob } = require('glob')
@@ -391,7 +392,7 @@ class Bin {
391
392
  conda_versions[name] = version
392
393
  conda_builds[name] = build
393
394
  if (name === "conda") {
394
- conda_check.conda = true
395
+ conda_check.conda = isExpectedCondaPinned(version)
395
396
  }
396
397
  if (name === "conda-libmamba-solver") {
397
398
  let coerced = semver.coerce(version)
@@ -448,9 +449,6 @@ class Bin {
448
449
  break
449
450
  }
450
451
  }
451
- if (this.mod && this.mod.conda && this.mod.conda.ensureWindowsOpenSslHooks) {
452
- await this.mod.conda.ensureWindowsOpenSslHooks()
453
- }
454
452
  }
455
453
 
456
454
  if (this.platform === "darwin") {
package/kernel/shell.js CHANGED
@@ -294,6 +294,14 @@ class Shell {
294
294
  setDefaultEnvValue(this.env, "HF_TOKEN_PATH", path.resolve(this.kernel.homedir, "cache", "HF_AUTH", "token"))
295
295
 
296
296
  if (this.platform === "win32") {
297
+ const managedSslDir = path.resolve(this.kernel.homedir, "bin", "miniforge", "Library", "ssl")
298
+ const managedCertFile = path.resolve(managedSslDir, "cacert.pem")
299
+ const managedCertFileExists = await fs.promises.access(managedCertFile).then(() => true).catch(() => false)
300
+ if (managedCertFileExists) {
301
+ setDefaultEnvValue(this.env, "SSL_CERT_FILE", managedCertFile)
302
+ setDefaultEnvValue(this.env, "SSL_CERT_DIR", managedSslDir)
303
+ }
304
+
297
305
  // Hugging Face file symlinks regularly fail on non-admin Windows setups.
298
306
  // Default to no-symlink cache mode unless the user/app explicitly overrides it.
299
307
  setDefaultEnvValue(this.env, "HF_HUB_DISABLE_SYMLINKS", "1")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "8.0.1",
3
+ "version": "8.0.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -5,10 +5,13 @@ const os = require('node:os')
5
5
  const path = require('node:path')
6
6
  const test = require('node:test')
7
7
 
8
+ const Bin = require('../kernel/bin')
8
9
  const Conda = require('../kernel/bin/conda')
9
10
  const {
11
+ CONDA_PIN_VERSION,
10
12
  PYTHON_INSTALL_SPEC,
11
13
  WINDOWS_PYTHON_SSL_FIX_SPEC,
14
+ isExpectedCondaPinned,
12
15
  isExpectedPythonPinned,
13
16
  } = require('../kernel/bin/conda-pins')
14
17
 
@@ -41,8 +44,8 @@ async function writeCondaMeta(root, name, version, build = 'py_0') {
41
44
  )
42
45
  }
43
46
 
44
- async function writeHealthyCondaMeta(root, platform = 'win32') {
45
- await writeCondaMeta(root, 'conda', '26.3.2', 'py310')
47
+ async function writeHealthyCondaMeta(root, platform = 'win32', condaVersion = CONDA_PIN_VERSION) {
48
+ await writeCondaMeta(root, 'conda', condaVersion, 'py310')
46
49
  await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
47
50
  await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
48
51
  }
@@ -78,86 +81,17 @@ function createConda(kernel) {
78
81
  return conda
79
82
  }
80
83
 
81
- const EXPECTED_WINDOWS_OPENSSL_HOOKS = {
82
- [path.join('activate.d', 'openssl_activate-win.bat')]: [
83
- '@echo off',
84
- 'if "%SSL_CERT_FILE%"=="" (',
85
- ' set "SSL_CERT_FILE=%CONDA_PREFIX%\\Library\\ssl\\cacert.pem"',
86
- ' set "__CONDA_OPENSSL_CERT_FILE_SET=1"',
87
- ')',
88
- '',
89
- ].join('\r\n'),
90
- [path.join('activate.d', 'openssl_activate-win.ps1')]: [
91
- 'if (-not $Env:SSL_CERT_FILE) {',
92
- ' $Env:SSL_CERT_FILE = "$Env:CONDA_PREFIX\\Library\\ssl\\cacert.pem"',
93
- ' $Env:__CONDA_OPENSSL_CERT_FILE_SET = "1"',
94
- '}',
95
- '',
96
- ].join('\r\n'),
97
- [path.join('activate.d', 'openssl_activate-win.sh')]: [
98
- 'if [[ "${SSL_CERT_FILE:-}" == "" ]]; then',
99
- ' export SSL_CERT_FILE="${CONDA_PREFIX}\\\\Library\\\\ssl\\\\cacert.pem"',
100
- ' export __CONDA_OPENSSL_CERT_FILE_SET="1"',
101
- 'fi',
102
- '',
103
- ].join('\n'),
104
- [path.join('deactivate.d', 'openssl_deactivate-win.bat')]: [
105
- '@echo off',
106
- 'if "%__CONDA_OPENSSL_CERT_FILE_SET%" == "1" (',
107
- ' set SSL_CERT_FILE=',
108
- ' set __CONDA_OPENSSL_CERT_FILE_SET=',
109
- ')',
110
- '',
111
- ].join('\r\n'),
112
- [path.join('deactivate.d', 'openssl_deactivate-win.ps1')]: [
113
- 'if ($Env:__CONDA_OPENSSL_CERT_FILE_SET -eq "1") {',
114
- ' Remove-Item -Path Env:\\SSL_CERT_FILE',
115
- ' Remove-Item -Path Env:\\__CONDA_OPENSSL_CERT_FILE_SET',
116
- '}',
117
- '',
118
- ].join('\r\n'),
119
- [path.join('deactivate.d', 'openssl_deactivate-win.sh')]: [
120
- 'if [[ "${__CONDA_OPENSSL_CERT_FILE_SET:-}" == "1" ]]; then',
121
- ' unset SSL_CERT_FILE',
122
- ' unset __CONDA_OPENSSL_CERT_FILE_SET',
123
- 'fi',
124
- '',
125
- ].join('\n'),
126
- }
127
-
128
- async function seedWindowsOpenSslHooks(root, condaRootName) {
129
- const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
130
- for (const hookRelativePath of Object.keys(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
131
- const hookPath = path.join(condaHookRoot, hookRelativePath)
132
- await fs.mkdir(path.dirname(hookPath), { recursive: true })
133
- await fs.writeFile(hookPath, 'upstream hook with SSL_CERT_DIR\n')
134
- }
135
- for (const hookDirName of ['activate.d', 'deactivate.d']) {
136
- for (const filename of [
137
- 'zz_pinokio_unset_ssl_cert_dir-win.bat',
138
- 'zz_pinokio_unset_ssl_cert_dir-win.ps1',
139
- 'zz_pinokio_unset_ssl_cert_dir-win.sh',
140
- ]) {
141
- await fs.writeFile(path.join(condaHookRoot, hookDirName, filename), 'legacy hook\n')
142
- }
143
- }
144
- }
145
-
146
- async function assertWindowsOpenSslHooksPatched(root, condaRootName) {
147
- const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
148
- for (const [hookRelativePath, expectedContent] of Object.entries(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
149
- const hookPath = path.join(condaHookRoot, hookRelativePath)
150
- assert.equal(await fs.readFile(hookPath, 'utf8'), expectedContent)
151
- }
152
- for (const hookDirName of ['activate.d', 'deactivate.d']) {
153
- for (const filename of [
154
- 'zz_pinokio_unset_ssl_cert_dir-win.bat',
155
- 'zz_pinokio_unset_ssl_cert_dir-win.ps1',
156
- 'zz_pinokio_unset_ssl_cert_dir-win.sh',
157
- ]) {
158
- assert.equal(await pathExists(path.join(condaHookRoot, hookDirName, filename)), false)
159
- }
84
+ function createBin(root, platform = 'win32') {
85
+ const kernel = {
86
+ homedir: root,
87
+ platform,
88
+ path: (...parts) => path.join(root, ...parts),
160
89
  }
90
+ const bin = new Bin(kernel)
91
+ bin.platform = platform
92
+ bin.installed = {}
93
+ kernel.bin = bin
94
+ return bin
161
95
  }
162
96
 
163
97
  test('Conda uses Miniforge assets and writes conda-forge-only config', async () => {
@@ -181,32 +115,14 @@ test('Conda uses Miniforge assets and writes conda-forge-only config', async ()
181
115
  assert.equal(await pathExists(path.join(root, 'bin', 'miniforge')), false)
182
116
  })
183
117
 
184
- test('Conda init patches Windows OpenSSL hooks and removes legacy SSL_CERT_DIR hooks', async () => {
185
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-openssl-hooks-'))
186
- await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
187
- await seedWindowsOpenSslHooks(root, 'miniforge')
188
- const conda = createConda(createKernel(root))
189
-
190
- await conda.init()
191
-
192
- await assertWindowsOpenSslHooksPatched(root, 'miniforge')
193
- })
194
-
195
- test('Conda init patches Windows OpenSSL hooks for legacy Miniconda roots', async () => {
196
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-legacy-openssl-hooks-'))
197
- await fs.mkdir(path.join(root, 'bin', 'miniconda', 'conda-meta'), { recursive: true })
198
- await seedWindowsOpenSslHooks(root, 'miniconda')
199
- const conda = createConda(createKernel(root))
200
-
201
- await conda.init()
202
-
203
- await assertWindowsOpenSslHooksPatched(root, 'miniconda')
204
- })
205
-
206
- test('Conda pins Python 3.10.20 consistently across platforms', async () => {
118
+ test('Conda pins the managed Conda and Python versions consistently', async () => {
119
+ assert.equal(CONDA_PIN_VERSION, '26.5.3')
207
120
  assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
208
121
  assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
209
122
 
123
+ assert.equal(isExpectedCondaPinned('26.5.3'), true)
124
+ assert.equal(isExpectedCondaPinned('26.3.2'), false)
125
+
210
126
  assert.equal(isExpectedPythonPinned('darwin', '3.10.20', 'cpython'), true)
211
127
  assert.equal(isExpectedPythonPinned('linux', '3.10.20', 'cpython'), true)
212
128
  assert.equal(isExpectedPythonPinned('darwin', '3.10.21', 'cpython'), false)
@@ -215,6 +131,47 @@ test('Conda pins Python 3.10.20 consistently across platforms', async () => {
215
131
  assert.equal(isExpectedPythonPinned('win32', '3.10.20', 'h4de0772_1_cpython'), true)
216
132
  })
217
133
 
134
+ test('Conda init writes the current Conda pin into an existing runtime', async () => {
135
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-pin-init-'))
136
+ await createMiniforge(root)
137
+ await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
138
+
139
+ await createConda(createKernel(root, 'darwin')).init()
140
+
141
+ assert.equal(
142
+ await fs.readFile(path.join(root, 'bin', 'miniforge', 'conda-meta', 'pinned'), 'utf8'),
143
+ 'conda ==26.5.3'
144
+ )
145
+ })
146
+
147
+ test('Conda init leaves Windows OpenSSL activation hooks untouched', async () => {
148
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-hooks-untouched-'))
149
+ const hookRoot = path.join(root, 'bin', 'miniforge', 'etc', 'conda')
150
+ const hookFiles = [
151
+ path.join('activate.d', 'openssl_activate-win.bat'),
152
+ path.join('activate.d', 'openssl_activate-win.ps1'),
153
+ path.join('activate.d', 'openssl_activate-win.sh'),
154
+ path.join('deactivate.d', 'openssl_deactivate-win.bat'),
155
+ path.join('deactivate.d', 'openssl_deactivate-win.ps1'),
156
+ path.join('deactivate.d', 'openssl_deactivate-win.sh'),
157
+ ]
158
+ await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
159
+ for (const relativePath of hookFiles) {
160
+ const hookPath = path.join(hookRoot, relativePath)
161
+ await fs.mkdir(path.dirname(hookPath), { recursive: true })
162
+ await fs.writeFile(hookPath, `upstream ${relativePath}\n`)
163
+ }
164
+
165
+ await createConda(createKernel(root, 'win32')).init()
166
+
167
+ for (const relativePath of hookFiles) {
168
+ assert.equal(
169
+ await fs.readFile(path.join(hookRoot, relativePath), 'utf8'),
170
+ `upstream ${relativePath}\n`
171
+ )
172
+ }
173
+ })
174
+
218
175
  test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () => {
219
176
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-pin-darwin-'))
220
177
  const kernel = createKernel(root, 'darwin')
@@ -237,7 +194,11 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
237
194
  assert.ok(condaInstall)
238
195
  assert.equal(
239
196
  condaInstall.message[1],
240
- 'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda-libmamba-solver>=25.4.0"'
197
+ 'conda install -y --override-channels -c conda-forge "conda=26.5.3" "python=3.10.20" "conda-libmamba-solver>=25.4.0"'
198
+ )
199
+ assert.equal(
200
+ await fs.readFile(path.join(root, 'bin', 'miniforge', 'conda-meta', 'pinned'), 'utf8'),
201
+ 'conda ==26.5.3'
241
202
  )
242
203
  })
243
204
 
@@ -265,7 +226,39 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
265
226
  assert.ok(condaInstall)
266
227
  assert.equal(
267
228
  condaInstall.message[1],
268
- 'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
229
+ 'conda install -y --override-channels -c conda-forge "conda=26.5.3" "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
230
+ )
231
+ })
232
+
233
+ test('Conda install replaces the stale runtime and rebuilds declared Conda modules', async () => {
234
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-full-reset-'))
235
+ const staleRoot = await createMiniforge(root)
236
+ const staleMarker = path.join(staleRoot, 'stale-runtime.txt')
237
+ await fs.writeFile(staleMarker, 'remove me\n')
238
+ const kernel = createKernel(root, 'darwin')
239
+ const calls = []
240
+
241
+ kernel.bin.mods = [
242
+ { name: 'uv', mod: { cmd: () => 'uv=0.11.23' } },
243
+ { name: 'node', mod: { cmd: () => 'nodejs=22.21.1 pnpm' } },
244
+ ]
245
+ kernel.bin.download = async () => {}
246
+ kernel.bin.rm = async () => {}
247
+ kernel.bin.exec = async (payload) => {
248
+ calls.push(payload)
249
+ if (payload && payload.conda && payload.conda.skip) {
250
+ await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
251
+ }
252
+ }
253
+
254
+ await createConda(kernel)._install({ dependencies: ['uv', 'node'] }, () => {})
255
+
256
+ assert.equal(await pathExists(staleMarker), false)
257
+ const condaInstall = calls.find((call) => Array.isArray(call.message))
258
+ assert.ok(condaInstall)
259
+ assert.equal(
260
+ condaInstall.message[1],
261
+ 'conda install -y --override-channels -c conda-forge "conda=26.5.3" "python=3.10.20" "conda-libmamba-solver>=25.4.0" uv=0.11.23 nodejs=22.21.1 pnpm'
269
262
  )
270
263
  })
271
264
 
@@ -331,6 +324,14 @@ test('Conda check rejects metadata-only Windows installs', async () => {
331
324
  assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
332
325
  })
333
326
 
327
+ test('Conda check rejects a runnable runtime with the previous Conda version', async () => {
328
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-version-stale-'))
329
+ await writeHealthyCondaMeta(root, 'win32', '26.3.2')
330
+ await writeFakeManagedConda(root, 'win32')
331
+
332
+ assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
333
+ })
334
+
334
335
  test('Conda check accepts a runnable managed Windows conda executable', async () => {
335
336
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-runnable-'))
336
337
  await writeHealthyCondaMeta(root, 'win32')
@@ -339,9 +340,29 @@ test('Conda check accepts a runnable managed Windows conda executable', async ()
339
340
  assert.equal(await createConda(createKernel(root, 'win32')).check(), true)
340
341
  })
341
342
 
343
+ test('Bin readiness rejects the previous Conda version and accepts the pinned version', async () => {
344
+ const staleRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-bin-conda-version-stale-'))
345
+ await writeHealthyCondaMeta(staleRoot, 'win32', '26.3.2')
346
+ await writeFakeManagedConda(staleRoot, 'win32')
347
+ const staleBin = createBin(staleRoot, 'win32')
348
+
349
+ await staleBin.tryList()
350
+ assert.equal(staleBin.correct_conda, false)
351
+
352
+ const currentRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-bin-conda-version-current-'))
353
+ await writeHealthyCondaMeta(currentRoot, 'win32')
354
+ await writeFakeManagedConda(currentRoot, 'win32')
355
+ const currentBin = createBin(currentRoot, 'win32')
356
+
357
+ await currentBin.tryList()
358
+ assert.equal(currentBin.correct_conda, true)
359
+ })
360
+
342
361
  test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
343
362
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-download-fails-'))
344
- const miniconda = await createLegacyMiniconda(root)
363
+ const miniforge = await createMiniforge(root)
364
+ const oldRuntimeFile = path.join(miniforge, 'old-runtime.txt')
365
+ await fs.writeFile(oldRuntimeFile, 'keep me\n')
345
366
  const kernel = createKernel(root)
346
367
 
347
368
  kernel.bin.download = async () => {
@@ -352,8 +373,29 @@ test('Conda install keeps the old runtime when the replacement installer downloa
352
373
  createConda(kernel)._install({ dependencies: [] }, () => {}),
353
374
  /download failed/
354
375
  )
355
- assert.equal(await pathExists(miniconda), true)
356
- assert.equal(await pathExists(path.join(root, 'bin', 'miniforge')), false)
376
+ assert.equal(await pathExists(oldRuntimeFile), true)
377
+ })
378
+
379
+ test('Conda install propagates bootstrap failure after replacing the runtime', async () => {
380
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-bootstrap-fails-'))
381
+ await createMiniforge(root)
382
+ const kernel = createKernel(root, 'darwin')
383
+
384
+ kernel.bin.mods = []
385
+ kernel.bin.download = async () => {}
386
+ kernel.bin.rm = async () => {}
387
+ kernel.bin.exec = async (payload) => {
388
+ if (payload && payload.conda && payload.conda.skip) {
389
+ await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
390
+ return
391
+ }
392
+ throw new Error('bootstrap failed')
393
+ }
394
+
395
+ await assert.rejects(
396
+ createConda(kernel)._install({ dependencies: [] }, () => {}),
397
+ /bootstrap failed/
398
+ )
357
399
  })
358
400
 
359
401
  test('Conda install reports manual recovery when the old runtime cannot be removed', async () => {
@@ -197,6 +197,48 @@ test('Shell.init_env keeps Windows Hugging Face symlink defaults scoped to win32
197
197
  assert.equal(winShell.env.HF_HUB_DISABLE_SYMLINKS_WARNING, '1')
198
198
  })
199
199
 
200
+ test('Shell.init_env gives Windows uv a valid managed certificate directory without overriding apps', async () => {
201
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-shell-ssl-win-'))
202
+ const managedSslDir = path.join(root, 'bin', 'miniforge', 'Library', 'ssl')
203
+ const managedCertFile = path.join(managedSslDir, 'cacert.pem')
204
+ await fs.mkdir(path.join(root, 'api'), { recursive: true })
205
+ await fs.mkdir(managedSslDir, { recursive: true })
206
+ await fs.writeFile(path.join(root, 'ENVIRONMENT'), 'PINOKIO_TEST_ENV=1\n')
207
+ await fs.writeFile(managedCertFile, 'test certificate bundle\n')
208
+
209
+ const defaultShell = createShell(createKernel(root))
210
+ defaultShell.platform = 'win32'
211
+ await defaultShell.init_env({
212
+ path: process.cwd(),
213
+ env: {}
214
+ })
215
+ assert.equal(defaultShell.env.SSL_CERT_FILE, managedCertFile)
216
+ assert.equal(defaultShell.env.SSL_CERT_DIR, managedSslDir)
217
+
218
+ const customCertFile = path.join(root, 'custom', 'certs.pem')
219
+ const customCertDir = path.join(root, 'custom', 'certs')
220
+ const overrideShell = createShell(createKernel(root))
221
+ overrideShell.platform = 'win32'
222
+ await overrideShell.init_env({
223
+ path: process.cwd(),
224
+ env: {
225
+ SSL_CERT_FILE: customCertFile,
226
+ SSL_CERT_DIR: customCertDir
227
+ }
228
+ })
229
+ assert.equal(overrideShell.env.SSL_CERT_FILE, customCertFile)
230
+ assert.equal(overrideShell.env.SSL_CERT_DIR, customCertDir)
231
+
232
+ const darwinShell = createShell(createKernel(root))
233
+ darwinShell.platform = 'darwin'
234
+ await darwinShell.init_env({
235
+ path: process.cwd(),
236
+ env: {}
237
+ })
238
+ assert.equal(darwinShell.env.SSL_CERT_FILE, undefined)
239
+ assert.equal(darwinShell.env.SSL_CERT_DIR, undefined)
240
+ })
241
+
200
242
  test('redactEnvArgs summarizes protected argv env values', () => {
201
243
  const redacted = ShellRunTemplate.redactEnvArgs({
202
244
  PINOKIO_ARG_0: 'line one\nline two',