pinokiod 7.5.30 → 7.5.32

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,6 +1,6 @@
1
1
  const semver = require('semver')
2
2
 
3
- const CONDA_PIN_VERSION = "26.3.2"
3
+ const CONDA_PIN_VERSION = "26.5.2"
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`
@@ -14,6 +14,61 @@ 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
+ ]
17
72
 
18
73
  class Conda {
19
74
  description = "Pinokio uses Conda to install various useful programs in an isolated manner."
@@ -69,7 +124,7 @@ class Conda {
69
124
  }
70
125
  return base
71
126
  }
72
- async ensureSslCertDirOverride() {
127
+ async ensureWindowsOpenSslHooks() {
73
128
  if (this.kernel.platform !== "win32") {
74
129
  return
75
130
  }
@@ -82,33 +137,26 @@ class Conda {
82
137
  if (condaRootDirs.length === 0) {
83
138
  return
84
139
  }
85
- const hookFiles = {
86
- "zz_pinokio_unset_ssl_cert_dir-win.bat": `@echo off
87
- if "%__CONDA_OPENSSL_CERT_DIR_SET%"=="1" (
88
- set "SSL_CERT_DIR="
89
- set "__CONDA_OPENSSL_CERT_DIR_SET="
90
- )
91
- `,
92
- "zz_pinokio_unset_ssl_cert_dir-win.ps1": `if ($Env:__CONDA_OPENSSL_CERT_DIR_SET -eq "1") {
93
- Remove-Item -Path Env:\\SSL_CERT_DIR -ErrorAction SilentlyContinue
94
- Remove-Item -Path Env:\\__CONDA_OPENSSL_CERT_DIR_SET -ErrorAction SilentlyContinue
95
- }
96
- `,
97
- "zz_pinokio_unset_ssl_cert_dir-win.sh": `if [[ "\${__CONDA_OPENSSL_CERT_DIR_SET:-}" == "1" ]]; then
98
- unset SSL_CERT_DIR
99
- unset __CONDA_OPENSSL_CERT_DIR_SET
100
- fi
101
- `,
102
- }
103
140
  for (const rootDir of condaRootDirs) {
104
- const hookDirs = [
105
- this.kernel.bin.path(`${rootDir}/etc/conda/activate.d`),
106
- this.kernel.bin.path(`${rootDir}/etc/conda/deactivate.d`),
107
- ]
108
- for (const hookDir of hookDirs) {
109
- await fs.promises.mkdir(hookDir, { recursive: true }).catch(() => {})
110
- for (const [filename, content] of Object.entries(hookFiles)) {
111
- await fs.promises.writeFile(path.resolve(hookDir, filename), content)
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)
112
160
  }
113
161
  }
114
162
  }
@@ -134,7 +182,7 @@ report_errors: false`)
134
182
  await fs.promises.writeFile(this.kernel.path(`bin/${CONDA_ROOT_DIR}/conda-meta/pinned`), this.pinnedPackages())
135
183
  await this.ensureCompatibilityAlias()
136
184
  }
137
- await this.ensureSslCertDirOverride()
185
+ await this.ensureWindowsOpenSslHooks()
138
186
  }
139
187
  }
140
188
  async check() {
@@ -157,7 +205,8 @@ report_errors: false`)
157
205
  conda_versions[name] = version
158
206
  conda_builds[name] = build
159
207
  if (name === "conda") {
160
- conda_check.conda = true
208
+ const coerced = semver.coerce(version)
209
+ conda_check.conda = coerced && semver.eq(coerced, CONDA_PIN_VERSION)
161
210
  }
162
211
  // check conda-libmamba-solver is up to date
163
212
  // sometimes it just fails silently so need to check
@@ -260,6 +309,7 @@ report_errors: false`)
260
309
 
261
310
  let condaPackages = [
262
311
  this.kernel.platform === "win32" ? `"${WINDOWS_PYTHON_SSL_FIX_SPEC}"` : `"${PYTHON_INSTALL_SPEC}"`,
312
+ `"conda=${CONDA_PIN_VERSION}"`,
263
313
  `"conda-libmamba-solver>=25.4.0"`,
264
314
  ]
265
315
 
@@ -282,7 +332,7 @@ report_errors: false`)
282
332
  this.kernel.bin.path(CONDA_ROOT_DIR, "python3.exe"),
283
333
  )
284
334
  }
285
- await this.ensureSslCertDirOverride()
335
+ await this.ensureWindowsOpenSslHooks()
286
336
  await this.ensureCompatibilityAlias()
287
337
  ondata({ raw: `Install finished\r\n` })
288
338
  await this.kernel.bin.rm(installer, ondata)
@@ -448,6 +448,9 @@ class Bin {
448
448
  break
449
449
  }
450
450
  }
451
+ if (this.mod && this.mod.conda && this.mod.conda.ensureWindowsOpenSslHooks) {
452
+ await this.mod.conda.ensureWindowsOpenSslHooks()
453
+ }
451
454
  }
452
455
 
453
456
  if (this.platform === "darwin") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.5.30",
3
+ "version": "7.5.32",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -6041,6 +6041,13 @@ class Server {
6041
6041
  if (version === this.version.pinokiod) {
6042
6042
  console.log("version up to date")
6043
6043
  } else if (!(await fse.pathExists(path.resolve(home, "bin/miniforge/conda-meta")))) {
6044
+ // if miniconda => miniforge migration hasn't happened yet
6045
+ if (!(await fse.pathExists(path.resolve(home, "bin/miniconda/conda-meta")))) {
6046
+ // if miniconda folder does NOT exist (new user), set version
6047
+ // if miniconda folder exists (existing user), don't set version yet
6048
+ // => version only set after miniconda => miniforge migration has happened
6049
+ this.kernel.store.set("version", this.version.pinokiod)
6050
+ }
6044
6051
  console.warn("[WARN] Managed home refresh deferred until Miniforge is installed")
6045
6052
  } else {
6046
6053
  // For every update, this gets triggered exactly once.
@@ -7,6 +7,7 @@ const test = require('node:test')
7
7
 
8
8
  const Conda = require('../kernel/bin/conda')
9
9
  const {
10
+ CONDA_PIN_VERSION,
10
11
  PYTHON_INSTALL_SPEC,
11
12
  WINDOWS_PYTHON_SSL_FIX_SPEC,
12
13
  isExpectedPythonPinned,
@@ -42,7 +43,7 @@ async function writeCondaMeta(root, name, version, build = 'py_0') {
42
43
  }
43
44
 
44
45
  async function writeHealthyCondaMeta(root, platform = 'win32') {
45
- await writeCondaMeta(root, 'conda', '26.3.2', 'py310')
46
+ await writeCondaMeta(root, 'conda', CONDA_PIN_VERSION, 'py310')
46
47
  await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
47
48
  await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
48
49
  }
@@ -78,6 +79,88 @@ function createConda(kernel) {
78
79
  return conda
79
80
  }
80
81
 
82
+ const EXPECTED_WINDOWS_OPENSSL_HOOKS = {
83
+ [path.join('activate.d', 'openssl_activate-win.bat')]: [
84
+ '@echo off',
85
+ 'if "%SSL_CERT_FILE%"=="" (',
86
+ ' set "SSL_CERT_FILE=%CONDA_PREFIX%\\Library\\ssl\\cacert.pem"',
87
+ ' set "__CONDA_OPENSSL_CERT_FILE_SET=1"',
88
+ ')',
89
+ '',
90
+ ].join('\r\n'),
91
+ [path.join('activate.d', 'openssl_activate-win.ps1')]: [
92
+ 'if (-not $Env:SSL_CERT_FILE) {',
93
+ ' $Env:SSL_CERT_FILE = "$Env:CONDA_PREFIX\\Library\\ssl\\cacert.pem"',
94
+ ' $Env:__CONDA_OPENSSL_CERT_FILE_SET = "1"',
95
+ '}',
96
+ '',
97
+ ].join('\r\n'),
98
+ [path.join('activate.d', 'openssl_activate-win.sh')]: [
99
+ 'if [[ "${SSL_CERT_FILE:-}" == "" ]]; then',
100
+ ' export SSL_CERT_FILE="${CONDA_PREFIX}\\\\Library\\\\ssl\\\\cacert.pem"',
101
+ ' export __CONDA_OPENSSL_CERT_FILE_SET="1"',
102
+ 'fi',
103
+ '',
104
+ ].join('\n'),
105
+ [path.join('deactivate.d', 'openssl_deactivate-win.bat')]: [
106
+ '@echo off',
107
+ 'if "%__CONDA_OPENSSL_CERT_FILE_SET%" == "1" (',
108
+ ' set SSL_CERT_FILE=',
109
+ ' set __CONDA_OPENSSL_CERT_FILE_SET=',
110
+ ')',
111
+ '',
112
+ ].join('\r\n'),
113
+ [path.join('deactivate.d', 'openssl_deactivate-win.ps1')]: [
114
+ 'if ($Env:__CONDA_OPENSSL_CERT_FILE_SET -eq "1") {',
115
+ ' Remove-Item -Path Env:\\SSL_CERT_FILE',
116
+ ' Remove-Item -Path Env:\\__CONDA_OPENSSL_CERT_FILE_SET',
117
+ '}',
118
+ '',
119
+ ].join('\r\n'),
120
+ [path.join('deactivate.d', 'openssl_deactivate-win.sh')]: [
121
+ 'if [[ "${__CONDA_OPENSSL_CERT_FILE_SET:-}" == "1" ]]; then',
122
+ ' unset SSL_CERT_FILE',
123
+ ' unset __CONDA_OPENSSL_CERT_FILE_SET',
124
+ 'fi',
125
+ '',
126
+ ].join('\n'),
127
+ }
128
+
129
+ async function seedWindowsOpenSslHooks(root, condaRootName) {
130
+ const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
131
+ for (const hookRelativePath of Object.keys(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
132
+ const hookPath = path.join(condaHookRoot, hookRelativePath)
133
+ await fs.mkdir(path.dirname(hookPath), { recursive: true })
134
+ await fs.writeFile(hookPath, 'upstream hook with SSL_CERT_DIR\n')
135
+ }
136
+ for (const hookDirName of ['activate.d', 'deactivate.d']) {
137
+ for (const filename of [
138
+ 'zz_pinokio_unset_ssl_cert_dir-win.bat',
139
+ 'zz_pinokio_unset_ssl_cert_dir-win.ps1',
140
+ 'zz_pinokio_unset_ssl_cert_dir-win.sh',
141
+ ]) {
142
+ await fs.writeFile(path.join(condaHookRoot, hookDirName, filename), 'legacy hook\n')
143
+ }
144
+ }
145
+ }
146
+
147
+ async function assertWindowsOpenSslHooksPatched(root, condaRootName) {
148
+ const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
149
+ for (const [hookRelativePath, expectedContent] of Object.entries(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
150
+ const hookPath = path.join(condaHookRoot, hookRelativePath)
151
+ assert.equal(await fs.readFile(hookPath, 'utf8'), expectedContent)
152
+ }
153
+ for (const hookDirName of ['activate.d', 'deactivate.d']) {
154
+ for (const filename of [
155
+ 'zz_pinokio_unset_ssl_cert_dir-win.bat',
156
+ 'zz_pinokio_unset_ssl_cert_dir-win.ps1',
157
+ 'zz_pinokio_unset_ssl_cert_dir-win.sh',
158
+ ]) {
159
+ assert.equal(await pathExists(path.join(condaHookRoot, hookDirName, filename)), false)
160
+ }
161
+ }
162
+ }
163
+
81
164
  test('Conda uses Miniforge assets and writes conda-forge-only config', async () => {
82
165
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-miniforge-config-'))
83
166
  const conda = createConda(createKernel(root))
@@ -99,62 +182,30 @@ test('Conda uses Miniforge assets and writes conda-forge-only config', async ()
99
182
  assert.equal(await pathExists(path.join(root, 'bin', 'miniforge')), false)
100
183
  })
101
184
 
102
- test('Conda init writes SSL_CERT_DIR neutralizing hooks only after Miniforge exists', async () => {
103
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-init-hooks-'))
185
+ test('Conda init patches Windows OpenSSL hooks and removes legacy SSL_CERT_DIR hooks', async () => {
186
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-openssl-hooks-'))
104
187
  await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
188
+ await seedWindowsOpenSslHooks(root, 'miniforge')
105
189
  const conda = createConda(createKernel(root))
106
190
 
107
191
  await conda.init()
108
192
 
109
- const hookRelativePaths = [
110
- path.join('activate.d', 'zz_pinokio_unset_ssl_cert_dir-win.bat'),
111
- path.join('activate.d', 'zz_pinokio_unset_ssl_cert_dir-win.ps1'),
112
- path.join('activate.d', 'zz_pinokio_unset_ssl_cert_dir-win.sh'),
113
- path.join('deactivate.d', 'zz_pinokio_unset_ssl_cert_dir-win.bat'),
114
- path.join('deactivate.d', 'zz_pinokio_unset_ssl_cert_dir-win.ps1'),
115
- path.join('deactivate.d', 'zz_pinokio_unset_ssl_cert_dir-win.sh'),
116
- ]
117
- for (const hookRelativePath of hookRelativePaths) {
118
- assert.equal(
119
- await pathExists(path.join(root, 'bin', 'miniforge', 'etc', 'conda', hookRelativePath)),
120
- true
121
- )
122
- }
123
-
124
- const batHook = await fs.readFile(
125
- path.join(root, 'bin', 'miniforge', 'etc', 'conda', 'activate.d', 'zz_pinokio_unset_ssl_cert_dir-win.bat'),
126
- 'utf8'
127
- )
128
- assert.match(batHook, /set "SSL_CERT_DIR="/)
129
- assert.match(batHook, /set "__CONDA_OPENSSL_CERT_DIR_SET="/)
130
- assert.doesNotMatch(batHook, /SSL_CERT_FILE/)
193
+ await assertWindowsOpenSslHooksPatched(root, 'miniforge')
131
194
  })
132
195
 
133
- test('Conda init writes SSL_CERT_DIR neutralizing hooks for legacy Miniconda roots', async () => {
134
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-legacy-init-hooks-'))
196
+ test('Conda init patches Windows OpenSSL hooks for legacy Miniconda roots', async () => {
197
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-legacy-openssl-hooks-'))
135
198
  await fs.mkdir(path.join(root, 'bin', 'miniconda', 'conda-meta'), { recursive: true })
199
+ await seedWindowsOpenSslHooks(root, 'miniconda')
136
200
  const conda = createConda(createKernel(root))
137
201
 
138
202
  await conda.init()
139
203
 
140
- const batHookPath = path.join(
141
- root,
142
- 'bin',
143
- 'miniconda',
144
- 'etc',
145
- 'conda',
146
- 'deactivate.d',
147
- 'zz_pinokio_unset_ssl_cert_dir-win.bat'
148
- )
149
- assert.equal(await pathExists(batHookPath), true)
150
-
151
- const batHook = await fs.readFile(batHookPath, 'utf8')
152
- assert.match(batHook, /set "SSL_CERT_DIR="/)
153
- assert.match(batHook, /set "__CONDA_OPENSSL_CERT_DIR_SET="/)
154
- assert.doesNotMatch(batHook, /SSL_CERT_FILE/)
204
+ await assertWindowsOpenSslHooksPatched(root, 'miniconda')
155
205
  })
156
206
 
157
207
  test('Conda pins Python 3.10.20 consistently across platforms', async () => {
208
+ assert.equal(CONDA_PIN_VERSION, '26.5.2')
158
209
  assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
159
210
  assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
160
211
 
@@ -188,7 +239,7 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
188
239
  assert.ok(condaInstall)
189
240
  assert.equal(
190
241
  condaInstall.message[1],
191
- 'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda-libmamba-solver>=25.4.0"'
242
+ 'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda=26.5.2" "conda-libmamba-solver>=25.4.0"'
192
243
  )
193
244
  })
194
245
 
@@ -216,7 +267,7 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
216
267
  assert.ok(condaInstall)
217
268
  assert.equal(
218
269
  condaInstall.message[1],
219
- 'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
270
+ 'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda=26.5.2" "conda-libmamba-solver>=25.4.0"'
220
271
  )
221
272
  })
222
273
 
@@ -290,6 +341,16 @@ test('Conda check accepts a runnable managed Windows conda executable', async ()
290
341
  assert.equal(await createConda(createKernel(root, 'win32')).check(), true)
291
342
  })
292
343
 
344
+ test('Conda check rejects runnable installs with the wrong Conda version', async () => {
345
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-wrong-version-'))
346
+ await writeCondaMeta(root, 'conda', '26.3.2', 'py310')
347
+ await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
348
+ await writeCondaMeta(root, 'python', '3.10.20', 'h4de0772_1_cpython')
349
+ await writeFakeManagedConda(root, 'win32')
350
+
351
+ assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
352
+ })
353
+
293
354
  test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
294
355
  const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-download-fails-'))
295
356
  const miniconda = await createLegacyMiniconda(root)