pinokiod 7.5.32 → 7.5.34
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/bin/conda-pins.js +1 -1
- package/kernel/bin/conda.js +109 -3
- package/package.json +1 -1
- package/test/conda-bin.test.js +110 -15
package/kernel/bin/conda-pins.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const semver = require('semver')
|
|
2
2
|
|
|
3
|
-
const CONDA_PIN_VERSION = "26.
|
|
3
|
+
const CONDA_PIN_VERSION = "26.3.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`
|
package/kernel/bin/conda.js
CHANGED
|
@@ -69,6 +69,76 @@ const LEGACY_SSL_CERT_DIR_HOOK_FILES = [
|
|
|
69
69
|
"zz_pinokio_unset_ssl_cert_dir-win.ps1",
|
|
70
70
|
"zz_pinokio_unset_ssl_cert_dir-win.sh",
|
|
71
71
|
]
|
|
72
|
+
const WINDOWS_CONDA_ACTIVATE_RELATIVE_PATH = path.join(
|
|
73
|
+
"Lib",
|
|
74
|
+
"site-packages",
|
|
75
|
+
"conda",
|
|
76
|
+
"shell",
|
|
77
|
+
"condabin",
|
|
78
|
+
"_conda_activate.bat"
|
|
79
|
+
)
|
|
80
|
+
const WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER = ":PINOKIO_APPLY_CONDA_ENV"
|
|
81
|
+
|
|
82
|
+
function windowsCondaActivateOriginalBlock(eol) {
|
|
83
|
+
return [
|
|
84
|
+
" ) ELSE ENDLOCAL & (",
|
|
85
|
+
' FOR /F "tokens=1,* delims==" %%A IN (%%T) DO (',
|
|
86
|
+
' :: A, B = "key", "value"',
|
|
87
|
+
' IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
|
|
88
|
+
' IF "%%A"=="_CONDA_SCRIPT" (',
|
|
89
|
+
" :: Script execution, fast exit if activation scripts fail",
|
|
90
|
+
' CALL "%%B" || (',
|
|
91
|
+
' ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
|
|
92
|
+
' CALL :DELETE "%%T"',
|
|
93
|
+
' CALL :DELETE "%__conda_tmp%"',
|
|
94
|
+
" EXIT /B 4",
|
|
95
|
+
" )",
|
|
96
|
+
' ) ELSE IF "%%B"=="" (',
|
|
97
|
+
" :: Unset variable",
|
|
98
|
+
' SET "%%A="',
|
|
99
|
+
" ) ELSE (",
|
|
100
|
+
" :: Set variable",
|
|
101
|
+
' SET "%%A=%%B"',
|
|
102
|
+
" )",
|
|
103
|
+
" )",
|
|
104
|
+
" :: Clean up",
|
|
105
|
+
' CALL :DELETE "%%T"',
|
|
106
|
+
' CALL :DELETE "%__conda_tmp%"',
|
|
107
|
+
" EXIT /B 0",
|
|
108
|
+
" )",
|
|
109
|
+
].join(eol)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function windowsCondaActivatePatchedBlock(eol) {
|
|
113
|
+
return [
|
|
114
|
+
' ) ELSE ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF',
|
|
115
|
+
].join(eol)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function windowsCondaActivatePatchRoutines(eol) {
|
|
119
|
+
return [
|
|
120
|
+
":PINOKIO_APPLY_CONDA_ENV",
|
|
121
|
+
'@FOR /F "tokens=1,* delims==" %%A IN (%~1) DO (',
|
|
122
|
+
'@IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
|
|
123
|
+
'@IF "%%A"=="_CONDA_SCRIPT" (',
|
|
124
|
+
'@CALL "%%B" || (',
|
|
125
|
+
'@ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
|
|
126
|
+
'@CALL :DELETE "%~1"',
|
|
127
|
+
'@CALL :DELETE "%~2"',
|
|
128
|
+
"@EXIT /B 4",
|
|
129
|
+
")",
|
|
130
|
+
') ELSE IF "%%B"=="" (',
|
|
131
|
+
'@SET "%%A="',
|
|
132
|
+
") ELSE (",
|
|
133
|
+
'@SET "%%A=%%B"',
|
|
134
|
+
")",
|
|
135
|
+
")",
|
|
136
|
+
'@CALL :DELETE "%~1"',
|
|
137
|
+
'@CALL :DELETE "%~2"',
|
|
138
|
+
"@EXIT /B 0",
|
|
139
|
+
"",
|
|
140
|
+
].join(eol)
|
|
141
|
+
}
|
|
72
142
|
|
|
73
143
|
class Conda {
|
|
74
144
|
description = "Pinokio uses Conda to install various useful programs in an isolated manner."
|
|
@@ -161,6 +231,42 @@ class Conda {
|
|
|
161
231
|
}
|
|
162
232
|
}
|
|
163
233
|
}
|
|
234
|
+
async ensureWindowsCondaActivateEchoPatch() {
|
|
235
|
+
if (this.kernel.platform !== "win32" || !(await this.hasCondaMeta())) {
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
const activatePath = this.kernel.bin.path(CONDA_ROOT_DIR, WINDOWS_CONDA_ACTIVATE_RELATIVE_PATH)
|
|
239
|
+
let content
|
|
240
|
+
try {
|
|
241
|
+
content = await fs.promises.readFile(activatePath, "utf8")
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (error && error.code === "ENOENT") {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
throw error
|
|
247
|
+
}
|
|
248
|
+
if (content.includes(WINDOWS_CONDA_ACTIVATE_PINOKIO_MARKER)) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
const eol = content.includes("\r\n") ? "\r\n" : "\n"
|
|
252
|
+
const originalBlock = windowsCondaActivateOriginalBlock(eol)
|
|
253
|
+
if (!content.includes(originalBlock)) {
|
|
254
|
+
console.warn("Could not patch Conda CMD activation echo leak; unexpected _conda_activate.bat")
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
const routineAnchor = `${eol}:: Routine to delete a temp file if CONDA_DEBUG is not set`
|
|
258
|
+
if (!content.includes(routineAnchor)) {
|
|
259
|
+
console.warn("Could not patch Conda CMD activation echo leak; missing _conda_activate.bat routine anchor")
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
const patched = content
|
|
263
|
+
.replace(originalBlock, windowsCondaActivatePatchedBlock(eol))
|
|
264
|
+
.replace(
|
|
265
|
+
routineAnchor,
|
|
266
|
+
`${eol}${windowsCondaActivatePatchRoutines(eol)}:: Routine to delete a temp file if CONDA_DEBUG is not set`
|
|
267
|
+
)
|
|
268
|
+
await fs.promises.writeFile(activatePath, patched)
|
|
269
|
+
}
|
|
164
270
|
async init() {
|
|
165
271
|
if (this.kernel.homedir) {
|
|
166
272
|
console.log("condarc init")
|
|
@@ -183,6 +289,7 @@ report_errors: false`)
|
|
|
183
289
|
await this.ensureCompatibilityAlias()
|
|
184
290
|
}
|
|
185
291
|
await this.ensureWindowsOpenSslHooks()
|
|
292
|
+
await this.ensureWindowsCondaActivateEchoPatch()
|
|
186
293
|
}
|
|
187
294
|
}
|
|
188
295
|
async check() {
|
|
@@ -205,8 +312,7 @@ report_errors: false`)
|
|
|
205
312
|
conda_versions[name] = version
|
|
206
313
|
conda_builds[name] = build
|
|
207
314
|
if (name === "conda") {
|
|
208
|
-
|
|
209
|
-
conda_check.conda = coerced && semver.eq(coerced, CONDA_PIN_VERSION)
|
|
315
|
+
conda_check.conda = true
|
|
210
316
|
}
|
|
211
317
|
// check conda-libmamba-solver is up to date
|
|
212
318
|
// sometimes it just fails silently so need to check
|
|
@@ -309,7 +415,6 @@ report_errors: false`)
|
|
|
309
415
|
|
|
310
416
|
let condaPackages = [
|
|
311
417
|
this.kernel.platform === "win32" ? `"${WINDOWS_PYTHON_SSL_FIX_SPEC}"` : `"${PYTHON_INSTALL_SPEC}"`,
|
|
312
|
-
`"conda=${CONDA_PIN_VERSION}"`,
|
|
313
418
|
`"conda-libmamba-solver>=25.4.0"`,
|
|
314
419
|
]
|
|
315
420
|
|
|
@@ -333,6 +438,7 @@ report_errors: false`)
|
|
|
333
438
|
)
|
|
334
439
|
}
|
|
335
440
|
await this.ensureWindowsOpenSslHooks()
|
|
441
|
+
await this.ensureWindowsCondaActivateEchoPatch()
|
|
336
442
|
await this.ensureCompatibilityAlias()
|
|
337
443
|
ondata({ raw: `Install finished\r\n` })
|
|
338
444
|
await this.kernel.bin.rm(installer, ondata)
|
package/package.json
CHANGED
package/test/conda-bin.test.js
CHANGED
|
@@ -7,7 +7,6 @@ const test = require('node:test')
|
|
|
7
7
|
|
|
8
8
|
const Conda = require('../kernel/bin/conda')
|
|
9
9
|
const {
|
|
10
|
-
CONDA_PIN_VERSION,
|
|
11
10
|
PYTHON_INSTALL_SPEC,
|
|
12
11
|
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
13
12
|
isExpectedPythonPinned,
|
|
@@ -43,7 +42,7 @@ async function writeCondaMeta(root, name, version, build = 'py_0') {
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
async function writeHealthyCondaMeta(root, platform = 'win32') {
|
|
46
|
-
await writeCondaMeta(root, 'conda',
|
|
45
|
+
await writeCondaMeta(root, 'conda', '26.3.2', 'py310')
|
|
47
46
|
await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
|
|
48
47
|
await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
|
|
49
48
|
}
|
|
@@ -126,6 +125,58 @@ const EXPECTED_WINDOWS_OPENSSL_HOOKS = {
|
|
|
126
125
|
].join('\n'),
|
|
127
126
|
}
|
|
128
127
|
|
|
128
|
+
const WINDOWS_CONDA_ACTIVATE_PATH = path.join(
|
|
129
|
+
'bin',
|
|
130
|
+
'miniforge',
|
|
131
|
+
'Lib',
|
|
132
|
+
'site-packages',
|
|
133
|
+
'conda',
|
|
134
|
+
'shell',
|
|
135
|
+
'condabin',
|
|
136
|
+
'_conda_activate.bat'
|
|
137
|
+
)
|
|
138
|
+
const WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK = [
|
|
139
|
+
' ) ELSE ENDLOCAL & (',
|
|
140
|
+
' FOR /F "tokens=1,* delims==" %%A IN (%%T) DO (',
|
|
141
|
+
' :: A, B = "key", "value"',
|
|
142
|
+
' IF "%CONDA_DEBUG%" == "1" ECHO DEBUG: "%%A=%%B">&2',
|
|
143
|
+
' IF "%%A"=="_CONDA_SCRIPT" (',
|
|
144
|
+
' :: Script execution, fast exit if activation scripts fail',
|
|
145
|
+
' CALL "%%B" || (',
|
|
146
|
+
' ECHO ERROR: Activation script "%%B" failed with code %ERRORLEVEL%.>&2',
|
|
147
|
+
' CALL :DELETE "%%T"',
|
|
148
|
+
' CALL :DELETE "%__conda_tmp%"',
|
|
149
|
+
' EXIT /B 4',
|
|
150
|
+
' )',
|
|
151
|
+
' ) ELSE IF "%%B"=="" (',
|
|
152
|
+
' :: Unset variable',
|
|
153
|
+
' SET "%%A="',
|
|
154
|
+
' ) ELSE (',
|
|
155
|
+
' :: Set variable',
|
|
156
|
+
' SET "%%A=%%B"',
|
|
157
|
+
' )',
|
|
158
|
+
' )',
|
|
159
|
+
' :: Clean up',
|
|
160
|
+
' CALL :DELETE "%%T"',
|
|
161
|
+
' CALL :DELETE "%__conda_tmp%"',
|
|
162
|
+
' EXIT /B 0',
|
|
163
|
+
' )',
|
|
164
|
+
].join('\r\n')
|
|
165
|
+
const WINDOWS_CONDA_ACTIVATE_SCRIPT = [
|
|
166
|
+
'@ECHO OFF',
|
|
167
|
+
'SETLOCAL',
|
|
168
|
+
'FOR /F "delims=" %%T IN (%__conda_tmp%) DO (',
|
|
169
|
+
' IF NOT EXIST "%%T" (',
|
|
170
|
+
' ENDLOCAL & EXIT /B 3',
|
|
171
|
+
WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK,
|
|
172
|
+
')',
|
|
173
|
+
'',
|
|
174
|
+
':: Routine to delete a temp file if CONDA_DEBUG is not set',
|
|
175
|
+
':DELETE',
|
|
176
|
+
'GOTO :EOF',
|
|
177
|
+
'',
|
|
178
|
+
].join('\r\n')
|
|
179
|
+
|
|
129
180
|
async function seedWindowsOpenSslHooks(root, condaRootName) {
|
|
130
181
|
const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
|
|
131
182
|
for (const hookRelativePath of Object.keys(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
|
|
@@ -144,6 +195,13 @@ async function seedWindowsOpenSslHooks(root, condaRootName) {
|
|
|
144
195
|
}
|
|
145
196
|
}
|
|
146
197
|
|
|
198
|
+
async function seedWindowsCondaActivateScript(root, content = WINDOWS_CONDA_ACTIVATE_SCRIPT) {
|
|
199
|
+
const activatePath = path.join(root, WINDOWS_CONDA_ACTIVATE_PATH)
|
|
200
|
+
await fs.mkdir(path.dirname(activatePath), { recursive: true })
|
|
201
|
+
await fs.writeFile(activatePath, content)
|
|
202
|
+
return activatePath
|
|
203
|
+
}
|
|
204
|
+
|
|
147
205
|
async function assertWindowsOpenSslHooksPatched(root, condaRootName) {
|
|
148
206
|
const condaHookRoot = path.join(root, 'bin', condaRootName, 'etc', 'conda')
|
|
149
207
|
for (const [hookRelativePath, expectedContent] of Object.entries(EXPECTED_WINDOWS_OPENSSL_HOOKS)) {
|
|
@@ -204,8 +262,52 @@ test('Conda init patches Windows OpenSSL hooks for legacy Miniconda roots', asyn
|
|
|
204
262
|
await assertWindowsOpenSslHooksPatched(root, 'miniconda')
|
|
205
263
|
})
|
|
206
264
|
|
|
265
|
+
test('Conda init patches Windows CMD activation echo leak idempotently', async () => {
|
|
266
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-patch-'))
|
|
267
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
268
|
+
const activatePath = await seedWindowsCondaActivateScript(root)
|
|
269
|
+
const conda = createConda(createKernel(root))
|
|
270
|
+
|
|
271
|
+
await conda.init()
|
|
272
|
+
const patched = await fs.readFile(activatePath, 'utf8')
|
|
273
|
+
await conda.init()
|
|
274
|
+
|
|
275
|
+
assert.equal(await fs.readFile(activatePath, 'utf8'), patched)
|
|
276
|
+
assert.equal(patched.includes(WINDOWS_CONDA_ACTIVATE_ORIGINAL_BLOCK), false)
|
|
277
|
+
assert.match(patched, /ENDLOCAL & @CALL :PINOKIO_APPLY_CONDA_ENV "%%T" "%__conda_tmp%" & @GOTO :EOF/)
|
|
278
|
+
assert.match(patched, /:PINOKIO_APPLY_CONDA_ENV/)
|
|
279
|
+
assert.match(patched, /@CALL "%%B" \|\| \(/)
|
|
280
|
+
assert.match(patched, /:: Routine to delete a temp file if CONDA_DEBUG is not set/)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('Conda init leaves unexpected Windows CMD activation script unchanged', async () => {
|
|
284
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-unexpected-'))
|
|
285
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
286
|
+
const unexpected = '@ECHO OFF\r\nREM unexpected upstream script\r\n'
|
|
287
|
+
const activatePath = await seedWindowsCondaActivateScript(root, unexpected)
|
|
288
|
+
const conda = createConda(createKernel(root))
|
|
289
|
+
|
|
290
|
+
await conda.init()
|
|
291
|
+
|
|
292
|
+
assert.equal(await fs.readFile(activatePath, 'utf8'), unexpected)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test('Conda init leaves Windows CMD activation script unchanged when patch anchor is missing', async () => {
|
|
296
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-cmd-activate-missing-anchor-'))
|
|
297
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
298
|
+
const missingAnchor = WINDOWS_CONDA_ACTIVATE_SCRIPT.replace(
|
|
299
|
+
'\r\n:: Routine to delete a temp file if CONDA_DEBUG is not set',
|
|
300
|
+
'\r\n:: unexpected routine header'
|
|
301
|
+
)
|
|
302
|
+
const activatePath = await seedWindowsCondaActivateScript(root, missingAnchor)
|
|
303
|
+
const conda = createConda(createKernel(root))
|
|
304
|
+
|
|
305
|
+
await conda.init()
|
|
306
|
+
|
|
307
|
+
assert.equal(await fs.readFile(activatePath, 'utf8'), missingAnchor)
|
|
308
|
+
})
|
|
309
|
+
|
|
207
310
|
test('Conda pins Python 3.10.20 consistently across platforms', async () => {
|
|
208
|
-
assert.equal(CONDA_PIN_VERSION, '26.5.2')
|
|
209
311
|
assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
|
|
210
312
|
assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
|
|
211
313
|
|
|
@@ -239,7 +341,7 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
|
|
|
239
341
|
assert.ok(condaInstall)
|
|
240
342
|
assert.equal(
|
|
241
343
|
condaInstall.message[1],
|
|
242
|
-
'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda
|
|
344
|
+
'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda-libmamba-solver>=25.4.0"'
|
|
243
345
|
)
|
|
244
346
|
})
|
|
245
347
|
|
|
@@ -247,6 +349,7 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
|
|
|
247
349
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-pin-win32-'))
|
|
248
350
|
const kernel = createKernel(root, 'win32')
|
|
249
351
|
const calls = []
|
|
352
|
+
let activatePath
|
|
250
353
|
|
|
251
354
|
kernel.bin.mods = []
|
|
252
355
|
kernel.bin.download = async () => {}
|
|
@@ -257,6 +360,7 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
|
|
|
257
360
|
const miniforge = path.join(root, 'bin', 'miniforge')
|
|
258
361
|
await fs.mkdir(path.join(miniforge, 'conda-meta'), { recursive: true })
|
|
259
362
|
await fs.writeFile(path.join(miniforge, 'python.exe'), 'fake python\n')
|
|
363
|
+
activatePath = await seedWindowsCondaActivateScript(root)
|
|
260
364
|
}
|
|
261
365
|
}
|
|
262
366
|
|
|
@@ -267,8 +371,9 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
|
|
|
267
371
|
assert.ok(condaInstall)
|
|
268
372
|
assert.equal(
|
|
269
373
|
condaInstall.message[1],
|
|
270
|
-
'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda
|
|
374
|
+
'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "conda-libmamba-solver>=25.4.0"'
|
|
271
375
|
)
|
|
376
|
+
assert.match(await fs.readFile(activatePath, 'utf8'), /:PINOKIO_APPLY_CONDA_ENV/)
|
|
272
377
|
})
|
|
273
378
|
|
|
274
379
|
test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
|
|
@@ -341,16 +446,6 @@ test('Conda check accepts a runnable managed Windows conda executable', async ()
|
|
|
341
446
|
assert.equal(await createConda(createKernel(root, 'win32')).check(), true)
|
|
342
447
|
})
|
|
343
448
|
|
|
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
|
-
|
|
354
449
|
test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
|
|
355
450
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-download-fails-'))
|
|
356
451
|
const miniconda = await createLegacyMiniconda(root)
|