pinokiod 7.4.2 → 7.5.0
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-meta.js +13 -13
- package/kernel/bin/conda-pins.js +9 -7
- package/kernel/bin/conda.js +142 -203
- package/kernel/bin/cuda.js +7 -7
- package/kernel/bin/ffmpeg.js +1 -18
- package/kernel/bin/index.js +8 -12
- package/kernel/connect/providers/huggingface/index.js +4 -4
- package/kernel/index.js +0 -7
- package/kernel/shell.js +5 -4
- package/kernel/sysinfo.js +1 -1
- package/kernel/util.js +2 -2
- package/package.json +1 -1
- package/server/index.js +3 -4
- package/system/plugin/claude/pinokio.js +2 -2
- package/system/plugin/claude-auto/pinokio.js +2 -2
- package/system/plugin/codex/pinokio.js +1 -1
- package/system/plugin/codex-auto/pinokio.js +1 -1
- package/test/conda-bin.test.js +254 -0
- package/test/ffmpeg-bin.test.js +2 -3
- package/kernel/legacy_cleanup/ffmpeg.js +0 -167
- package/kernel/legacy_cleanup/index.js +0 -47
- package/test/legacy-cleanup.test.js +0 -161
package/kernel/bin/cuda.js
CHANGED
|
@@ -5,7 +5,7 @@ const { glob } = require('glob')
|
|
|
5
5
|
class Cuda {
|
|
6
6
|
description = "Installs CUDA and cuDNN libraries for NVIDIA GPU workloads."
|
|
7
7
|
async hasNvTargetHeader() {
|
|
8
|
-
const prefix = this.kernel.bin.path("
|
|
8
|
+
const prefix = this.kernel.bin.path("miniforge")
|
|
9
9
|
const patterns = [
|
|
10
10
|
"Library/include/nv/target",
|
|
11
11
|
"include/nv/target",
|
|
@@ -33,7 +33,7 @@ class Cuda {
|
|
|
33
33
|
if (this.kernel.platform !== "win32") {
|
|
34
34
|
return
|
|
35
35
|
}
|
|
36
|
-
const script = this.kernel.bin.path("
|
|
36
|
+
const script = this.kernel.bin.path("miniforge/etc/conda/activate.d/pinokio/~cuda-nvcc_activate.bat")
|
|
37
37
|
let content
|
|
38
38
|
try {
|
|
39
39
|
content = await fs.promises.readFile(script, "utf8")
|
|
@@ -68,7 +68,7 @@ class Cuda {
|
|
|
68
68
|
if (this.kernel.platform !== "win32") {
|
|
69
69
|
return
|
|
70
70
|
}
|
|
71
|
-
const folder = this.kernel.bin.path("
|
|
71
|
+
const folder = this.kernel.bin.path("miniforge/etc/conda/activate.d")
|
|
72
72
|
const stash = path.resolve(folder, "pinokio")
|
|
73
73
|
await fs.promises.mkdir(stash, { recursive: true }).catch(() => {})
|
|
74
74
|
const scripts = [
|
|
@@ -149,10 +149,10 @@ class Cuda {
|
|
|
149
149
|
"vs2019_compiler_vars.bat",
|
|
150
150
|
"vs2022_compiler_vars.bat",
|
|
151
151
|
]
|
|
152
|
-
const folder = this.kernel.bin.path("
|
|
152
|
+
const folder = this.kernel.bin.path("miniforge/etc/conda/activate.d")
|
|
153
153
|
let at_least_one_exists = false
|
|
154
154
|
for(let item of deactivate_list) {
|
|
155
|
-
let exists = await this.kernel.exists("bin/
|
|
155
|
+
let exists = await this.kernel.exists("bin/miniforge/etc/conda/activate.d/" + item)
|
|
156
156
|
if (exists) {
|
|
157
157
|
// break if at least one exists
|
|
158
158
|
at_least_one_exists = true
|
|
@@ -190,11 +190,11 @@ class Cuda {
|
|
|
190
190
|
env() {
|
|
191
191
|
if (this.kernel.platform === 'win32') {
|
|
192
192
|
return {
|
|
193
|
-
CUDA_HOME: this.kernel.bin.path("
|
|
193
|
+
CUDA_HOME: this.kernel.bin.path("miniforge/Library")
|
|
194
194
|
}
|
|
195
195
|
} else {
|
|
196
196
|
return {
|
|
197
|
-
CUDA_HOME: this.kernel.bin.path("
|
|
197
|
+
CUDA_HOME: this.kernel.bin.path("miniforge")
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
package/kernel/bin/ffmpeg.js
CHANGED
|
@@ -9,28 +9,11 @@ class Ffmpeg {
|
|
|
9
9
|
return CONDA_SPEC
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
condaInstallFlags() {
|
|
13
|
-
if (this.kernel.platform === "win32") {
|
|
14
|
-
return `-y --solver=classic ${CONDA_CHANNEL_FLAGS}`
|
|
15
|
-
}
|
|
16
|
-
return `-y ${CONDA_CHANNEL_FLAGS}`
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
solverRepairCommands() {
|
|
20
|
-
if (this.kernel.platform !== "win32") {
|
|
21
|
-
return []
|
|
22
|
-
}
|
|
23
|
-
return [
|
|
24
|
-
`conda install -y --solver=classic ${CONDA_CHANNEL_FLAGS} "conda-libmamba-solver>=25.4.0" libmamba libmambapy libsolv libarchive`
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
12
|
async install(req, ondata) {
|
|
29
13
|
await this.kernel.bin.exec({
|
|
30
14
|
message: [
|
|
31
15
|
"conda clean -y --all",
|
|
32
|
-
|
|
33
|
-
`conda install ${this.condaInstallFlags()} ${this.cmd()}`
|
|
16
|
+
`conda install -y ${CONDA_CHANNEL_FLAGS} ${this.cmd()}`
|
|
34
17
|
]
|
|
35
18
|
}, ondata)
|
|
36
19
|
}
|
package/kernel/bin/index.js
CHANGED
|
@@ -23,8 +23,8 @@ const Cuda = require("./cuda")
|
|
|
23
23
|
const Torch = require("./torch")
|
|
24
24
|
const { buildCondaListFromMeta } = require('./conda-meta')
|
|
25
25
|
const {
|
|
26
|
+
isExpectedPythonPinned,
|
|
26
27
|
isExpectedSqlitePinned,
|
|
27
|
-
isWindowsPythonSslFixed,
|
|
28
28
|
} = require('./conda-pins')
|
|
29
29
|
const { glob } = require('glob')
|
|
30
30
|
const fakeUa = require('fake-useragent');
|
|
@@ -360,9 +360,9 @@ class Bin {
|
|
|
360
360
|
// if importlib_metadata || uvicorn || fastapi exist in the base environment, this.correct_conda = false
|
|
361
361
|
let site_packages_path
|
|
362
362
|
if (this.platform === "win32") {
|
|
363
|
-
site_packages_path = path.resolve(this.kernel.homedir, "bin/
|
|
363
|
+
site_packages_path = path.resolve(this.kernel.homedir, "bin/miniforge/Lib/site-packages")
|
|
364
364
|
} else {
|
|
365
|
-
site_packages_path = path.resolve(this.kernel.homedir, "bin/
|
|
365
|
+
site_packages_path = path.resolve(this.kernel.homedir, "bin/miniforge/lib/python3.10/site-packages")
|
|
366
366
|
}
|
|
367
367
|
// // check if any of 'uvicorn', 'importlib_metadata', 'fastapi' exists
|
|
368
368
|
// let module_paths = ["fastapi", "uvicorn", "importlib_metadata"].map((name) => {
|
|
@@ -384,7 +384,7 @@ class Bin {
|
|
|
384
384
|
if (to_reset_exists) {
|
|
385
385
|
this.correct_conda = false
|
|
386
386
|
} else {
|
|
387
|
-
let res = await buildCondaListFromMeta(this.kernel.bin.path("
|
|
387
|
+
let res = await buildCondaListFromMeta(this.kernel.bin.path("miniforge"))
|
|
388
388
|
let lines = res.response.split(/[\r\n]+/)
|
|
389
389
|
for(let line of lines) {
|
|
390
390
|
if (start) {
|
|
@@ -400,11 +400,8 @@ class Bin {
|
|
|
400
400
|
conda_check.conda = true
|
|
401
401
|
}
|
|
402
402
|
if (name === "conda-libmamba-solver") {
|
|
403
|
-
//if (String(version) === "24.7.0") {
|
|
404
|
-
let channel = chunks[3]
|
|
405
403
|
let coerced = semver.coerce(version)
|
|
406
404
|
let mamba_requirement = ">=25.4.0"
|
|
407
|
-
//if (semver.satisfies(coerced, mamba_requirement) && channel === "conda-forge") {
|
|
408
405
|
if (semver.satisfies(coerced, mamba_requirement)) {
|
|
409
406
|
conda_check.mamba = true
|
|
410
407
|
}
|
|
@@ -417,7 +414,7 @@ class Bin {
|
|
|
417
414
|
}
|
|
418
415
|
}
|
|
419
416
|
if (name === "python") {
|
|
420
|
-
conda_check.python = this.platform
|
|
417
|
+
conda_check.python = isExpectedPythonPinned(this.platform, version, build)
|
|
421
418
|
}
|
|
422
419
|
}
|
|
423
420
|
} else {
|
|
@@ -427,8 +424,7 @@ class Bin {
|
|
|
427
424
|
}
|
|
428
425
|
}
|
|
429
426
|
|
|
430
|
-
if (conda_check.conda && conda_check.mamba && conda_check.sqlite &&
|
|
431
|
-
//if (conda_check.conda && conda_check.mamba) {
|
|
427
|
+
if (conda_check.conda && conda_check.mamba && conda_check.sqlite && conda_check.python) {
|
|
432
428
|
this.correct_conda = true
|
|
433
429
|
}
|
|
434
430
|
}
|
|
@@ -449,7 +445,7 @@ class Bin {
|
|
|
449
445
|
// 1. conda
|
|
450
446
|
|
|
451
447
|
// check conda location and see if it exists. only run if it exists
|
|
452
|
-
let conda_path = path.resolve(this.kernel.homedir, "bin", "
|
|
448
|
+
let conda_path = path.resolve(this.kernel.homedir, "bin", "miniforge")
|
|
453
449
|
let conda_exists = await this.exists(conda_path)
|
|
454
450
|
|
|
455
451
|
let start
|
|
@@ -513,7 +509,7 @@ class Bin {
|
|
|
513
509
|
}
|
|
514
510
|
|
|
515
511
|
// /// B. base path initialization
|
|
516
|
-
// let conda_meta_path = this.kernel.bin.path("
|
|
512
|
+
// let conda_meta_path = this.kernel.bin.path("miniforge", "conda-meta")
|
|
517
513
|
// const metaFiles = await glob("*.json", {
|
|
518
514
|
// cwd: conda_meta_path
|
|
519
515
|
// })
|
|
@@ -39,11 +39,11 @@ class Huggingface {
|
|
|
39
39
|
}
|
|
40
40
|
hfPath() {
|
|
41
41
|
const candidates = this.kernel.platform === "win32" ? [
|
|
42
|
-
this.kernel.path("bin", "
|
|
43
|
-
this.kernel.path("bin", "
|
|
44
|
-
this.kernel.path("bin", "
|
|
42
|
+
this.kernel.path("bin", "miniforge", "Scripts", "hf.exe"),
|
|
43
|
+
this.kernel.path("bin", "miniforge", "Scripts", "hf"),
|
|
44
|
+
this.kernel.path("bin", "miniforge", "bin", "hf"),
|
|
45
45
|
] : [
|
|
46
|
-
this.kernel.path("bin", "
|
|
46
|
+
this.kernel.path("bin", "miniforge", "bin", "hf"),
|
|
47
47
|
]
|
|
48
48
|
for (const candidate of candidates) {
|
|
49
49
|
if (fs.existsSync(candidate)) {
|
package/kernel/index.js
CHANGED
|
@@ -43,7 +43,6 @@ const AppLauncher = require('./app_launcher')
|
|
|
43
43
|
const ReadyState = require('./ready')
|
|
44
44
|
const Autolaunch = require('./autolaunch')
|
|
45
45
|
const LaunchRequirements = require('./launch_requirements')
|
|
46
|
-
const LegacyCleanup = require('./legacy_cleanup')
|
|
47
46
|
const { DownloaderHelper } = require('node-downloader-helper');
|
|
48
47
|
const { ProxyAgent } = require('proxy-agent');
|
|
49
48
|
const fakeUa = require('fake-useragent');
|
|
@@ -101,7 +100,6 @@ class Kernel {
|
|
|
101
100
|
this.launchRequirements = new LaunchRequirements(this)
|
|
102
101
|
this.autolaunch = new Autolaunch(this)
|
|
103
102
|
this.autolaunch_status = this.autolaunch.startupStatus()
|
|
104
|
-
this.legacyCleanup = new LegacyCleanup(this)
|
|
105
103
|
this.sysReady = new Promise((resolve) => {
|
|
106
104
|
this._resolveSysReady = resolve
|
|
107
105
|
})
|
|
@@ -1194,11 +1192,6 @@ class Kernel {
|
|
|
1194
1192
|
await Environment.ensurePinokioCacheDirs(this, {
|
|
1195
1193
|
throwOnFailure: true
|
|
1196
1194
|
})
|
|
1197
|
-
await this.legacyCleanup.runOnce((event) => {
|
|
1198
|
-
if (event && typeof event.raw === "string") {
|
|
1199
|
-
console.log(event.raw.trimEnd())
|
|
1200
|
-
}
|
|
1201
|
-
})
|
|
1202
1195
|
|
|
1203
1196
|
// if key.json doesn't exist, create an empty json file
|
|
1204
1197
|
let ee = await this.exists(this.homedir, "key.json")
|
package/kernel/shell.js
CHANGED
|
@@ -21,6 +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 } = require('./bin/conda-pins')
|
|
24
25
|
const home = os.homedir()
|
|
25
26
|
|
|
26
27
|
function normalizeComparablePath(filePath, platform) {
|
|
@@ -959,7 +960,7 @@ class Shell {
|
|
|
959
960
|
conda_hook () {
|
|
960
961
|
if (this.platform === "win32") {
|
|
961
962
|
if (/bash/i.test(this.shell)) {
|
|
962
|
-
const conda_sh = this.kernel.path("bin/
|
|
963
|
+
const conda_sh = this.kernel.path("bin/miniforge/etc/profile.d/conda.sh")
|
|
963
964
|
return `source ${this.quoteArgForShell(Util.p2u(conda_sh))}`
|
|
964
965
|
} else {
|
|
965
966
|
return "conda_hook"
|
|
@@ -986,7 +987,7 @@ class Shell {
|
|
|
986
987
|
// 1. conda
|
|
987
988
|
let conda_path
|
|
988
989
|
let conda_name
|
|
989
|
-
let conda_python =
|
|
990
|
+
let conda_python = PYTHON_INSTALL_SPEC
|
|
990
991
|
let conda_args
|
|
991
992
|
let conda_activate
|
|
992
993
|
|
|
@@ -1106,7 +1107,7 @@ class Shell {
|
|
|
1106
1107
|
// timeout,
|
|
1107
1108
|
]
|
|
1108
1109
|
} else {
|
|
1109
|
-
let envs_path = this.kernel.bin.path("
|
|
1110
|
+
let envs_path = this.kernel.bin.path("miniforge/envs")
|
|
1110
1111
|
let env_path = path.resolve(envs_path, conda_name)
|
|
1111
1112
|
let env_exists = await this.exists(env_path)
|
|
1112
1113
|
if (env_exists) {
|
|
@@ -1185,7 +1186,7 @@ class Shell {
|
|
|
1185
1186
|
console.log(`Unsupported arch: os.arch()=${architecture}, process.arch=${armArchitecture}`)
|
|
1186
1187
|
}
|
|
1187
1188
|
|
|
1188
|
-
const activate_root = this.kernel.bin.path("
|
|
1189
|
+
const activate_root = this.kernel.bin.path("miniforge/etc/conda/activate.d")
|
|
1189
1190
|
const logDir = this.kernel.path("cache", "logs")
|
|
1190
1191
|
await fs.promises.mkdir(logDir, { recursive: true }).catch(() => {})
|
|
1191
1192
|
const logSuffix = this.id || Date.now()
|
package/kernel/sysinfo.js
CHANGED
|
@@ -206,7 +206,7 @@ class Sysinfo {
|
|
|
206
206
|
// console.log(_res)
|
|
207
207
|
// console.log("############### DEBUGGING ENV FINISH")
|
|
208
208
|
|
|
209
|
-
let conda_path = path.resolve(this.kernel.homedir, "bin", "
|
|
209
|
+
let conda_path = path.resolve(this.kernel.homedir, "bin", "miniforge")
|
|
210
210
|
let conda_exists = await this.exists(conda_path)
|
|
211
211
|
|
|
212
212
|
let res
|
package/kernel/util.js
CHANGED
|
@@ -199,9 +199,9 @@ const filepicker = async(req, ondata, kernel) => {
|
|
|
199
199
|
let picker_path = kernel.path("bin/py/picker.py")
|
|
200
200
|
let python
|
|
201
201
|
if (kernel.platform === "win32") {
|
|
202
|
-
python = kernel.path("bin/
|
|
202
|
+
python = kernel.path("bin/miniforge/python")
|
|
203
203
|
} else {
|
|
204
|
-
python = kernel.path("bin/
|
|
204
|
+
python = kernel.path("bin/miniforge/bin/python")
|
|
205
205
|
}
|
|
206
206
|
const proc = spawn(python, [picker_path])
|
|
207
207
|
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -501,7 +501,7 @@ class Server {
|
|
|
501
501
|
if (this.kernel.platform !== "win32") {
|
|
502
502
|
return null
|
|
503
503
|
}
|
|
504
|
-
const bashPath = this.kernel.path("bin/
|
|
504
|
+
const bashPath = this.kernel.path("bin/miniforge/Library/bin/bash.exe")
|
|
505
505
|
return await this.exists(bashPath) ? bashPath : null
|
|
506
506
|
}
|
|
507
507
|
running_dynamic (name, menu, selected_query) {
|
|
@@ -6013,7 +6013,7 @@ class Server {
|
|
|
6013
6013
|
this.kernel.store.set("version", this.version.pinokiod)
|
|
6014
6014
|
console.log("[DONE] Updating to the new version")
|
|
6015
6015
|
console.log("not up to date. update py.")
|
|
6016
|
-
// remove ~/bin/
|
|
6016
|
+
// remove ~/bin/py
|
|
6017
6017
|
let p = path.resolve(home, "bin/py")
|
|
6018
6018
|
console.log(`[TRY] reset ${p}`)
|
|
6019
6019
|
await fse.remove(p)
|
|
@@ -10034,7 +10034,6 @@ class Server {
|
|
|
10034
10034
|
return
|
|
10035
10035
|
}
|
|
10036
10036
|
// check bin folder
|
|
10037
|
-
// let bin_path = this.kernel.path("bin/miniconda")
|
|
10038
10037
|
// let bin_exists = await this.exists(bin_path)
|
|
10039
10038
|
// if (!bin_exists) {
|
|
10040
10039
|
// res.redirect("/setup")
|
|
@@ -11730,7 +11729,7 @@ class Server {
|
|
|
11730
11729
|
managedLaunchOverrides.conda = { skip: true }
|
|
11731
11730
|
}
|
|
11732
11731
|
if (isWindowsPlatform && (provider.key === "codex" || provider.key === "claude")) {
|
|
11733
|
-
const gitBashPath = this.kernel.path("bin/
|
|
11732
|
+
const gitBashPath = this.kernel.path("bin/miniforge/Library/bin/bash.exe")
|
|
11734
11733
|
const bashExists = await fs.promises.access(gitBashPath, fs.constants.F_OK).then(() => true).catch(() => false)
|
|
11735
11734
|
if (bashExists) {
|
|
11736
11735
|
if (provider.key === "claude") {
|
|
@@ -7,12 +7,12 @@ module.exports = {
|
|
|
7
7
|
id: "run",
|
|
8
8
|
method: "shell.run",
|
|
9
9
|
params: {
|
|
10
|
-
shell: "{{kernel.path('bin/
|
|
10
|
+
shell: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}",
|
|
11
11
|
conda: {
|
|
12
12
|
skip: true
|
|
13
13
|
},
|
|
14
14
|
env: {
|
|
15
|
-
CLAUDE_CODE_GIT_BASH_PATH: "{{kernel.path('bin/
|
|
15
|
+
CLAUDE_CODE_GIT_BASH_PATH: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}"
|
|
16
16
|
},
|
|
17
17
|
message: {
|
|
18
18
|
_: [
|
|
@@ -8,12 +8,12 @@ module.exports = {
|
|
|
8
8
|
id: "run",
|
|
9
9
|
method: "shell.run",
|
|
10
10
|
params: {
|
|
11
|
-
shell: "{{kernel.path('bin/
|
|
11
|
+
shell: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}",
|
|
12
12
|
conda: {
|
|
13
13
|
skip: true
|
|
14
14
|
},
|
|
15
15
|
env: {
|
|
16
|
-
CLAUDE_CODE_GIT_BASH_PATH: "{{kernel.path('bin/
|
|
16
|
+
CLAUDE_CODE_GIT_BASH_PATH: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}",
|
|
17
17
|
CLAUBBIT: "true"
|
|
18
18
|
},
|
|
19
19
|
message: {
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fsModule = require('node:fs')
|
|
3
|
+
const fs = require('node:fs/promises')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const test = require('node:test')
|
|
7
|
+
|
|
8
|
+
const Conda = require('../kernel/bin/conda')
|
|
9
|
+
const {
|
|
10
|
+
PYTHON_INSTALL_SPEC,
|
|
11
|
+
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
12
|
+
isExpectedPythonPinned,
|
|
13
|
+
} = require('../kernel/bin/conda-pins')
|
|
14
|
+
|
|
15
|
+
async function pathExists(target) {
|
|
16
|
+
return fs.access(target).then(() => true).catch(() => false)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createKernel(root, platform = 'win32') {
|
|
20
|
+
return {
|
|
21
|
+
arch: 'x64',
|
|
22
|
+
homedir: root,
|
|
23
|
+
platform,
|
|
24
|
+
path: (...parts) => path.join(root, ...parts),
|
|
25
|
+
exists: async (...parts) => pathExists(path.join(root, ...parts)),
|
|
26
|
+
bin: {
|
|
27
|
+
correct_conda: true,
|
|
28
|
+
path: (...parts) => path.join(root, 'bin', ...parts),
|
|
29
|
+
exists: async (target) => pathExists(path.join(root, 'bin', target)),
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function createCondaRoot(root, name) {
|
|
35
|
+
const condaRoot = path.join(root, 'bin', name)
|
|
36
|
+
await fs.mkdir(path.join(condaRoot, 'Scripts'), { recursive: true })
|
|
37
|
+
await fs.writeFile(path.join(condaRoot, 'Scripts', 'conda.exe'), 'fake conda\n')
|
|
38
|
+
return condaRoot
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createMiniforge(root) {
|
|
42
|
+
return createCondaRoot(root, 'miniforge')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function createLegacyMiniconda(root) {
|
|
46
|
+
return createCondaRoot(root, 'miniconda')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function writeCurrentRuntimeStamp(root) {
|
|
50
|
+
await fs.writeFile(
|
|
51
|
+
path.join(root, 'bin', 'miniforge', '.pinokio-runtime.json'),
|
|
52
|
+
`${JSON.stringify({
|
|
53
|
+
distribution: 'miniforge',
|
|
54
|
+
release: '26.3.2-3',
|
|
55
|
+
epoch: 1,
|
|
56
|
+
}, null, 2)}\n`
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createConda(kernel) {
|
|
61
|
+
const conda = new Conda()
|
|
62
|
+
conda.kernel = kernel
|
|
63
|
+
return conda
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test('Conda uses Miniforge assets and writes conda-forge-only config', async () => {
|
|
67
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-miniforge-config-'))
|
|
68
|
+
const conda = createConda(createKernel(root))
|
|
69
|
+
|
|
70
|
+
assert.equal(
|
|
71
|
+
conda.urls.win32.x64,
|
|
72
|
+
'https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-Windows-x86_64.exe'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
await conda.init()
|
|
76
|
+
const condarc = await fs.readFile(path.join(root, 'condarc'), 'utf8')
|
|
77
|
+
|
|
78
|
+
assert.match(condarc, /^channels:\n - conda-forge\n/m)
|
|
79
|
+
assert.match(condarc, / - python=3\.10\.20\n/)
|
|
80
|
+
assert.match(condarc, /bin\/miniforge\/envs/)
|
|
81
|
+
assert.doesNotMatch(condarc, /bin\/miniconda\/envs/)
|
|
82
|
+
assert.doesNotMatch(condarc, /\bdefaults\b/)
|
|
83
|
+
assert.doesNotMatch(condarc, /auto_accept_tos/)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('Conda pins Python 3.10.20 consistently across platforms', async () => {
|
|
87
|
+
assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
|
|
88
|
+
assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
|
|
89
|
+
|
|
90
|
+
assert.equal(isExpectedPythonPinned('darwin', '3.10.20', 'cpython'), true)
|
|
91
|
+
assert.equal(isExpectedPythonPinned('linux', '3.10.20', 'cpython'), true)
|
|
92
|
+
assert.equal(isExpectedPythonPinned('darwin', '3.10.21', 'cpython'), false)
|
|
93
|
+
assert.equal(isExpectedPythonPinned('darwin', '3.11.0', 'cpython'), false)
|
|
94
|
+
assert.equal(isExpectedPythonPinned('win32', '3.10.20', 'h4de0772_0_cpython'), false)
|
|
95
|
+
assert.equal(isExpectedPythonPinned('win32', '3.10.20', 'h4de0772_1_cpython'), true)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () => {
|
|
99
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-pin-darwin-'))
|
|
100
|
+
const kernel = createKernel(root, 'darwin')
|
|
101
|
+
const calls = []
|
|
102
|
+
|
|
103
|
+
kernel.bin.mods = []
|
|
104
|
+
kernel.bin.download = async () => {}
|
|
105
|
+
kernel.bin.rm = async () => {}
|
|
106
|
+
kernel.bin.exec = async (payload) => {
|
|
107
|
+
calls.push(payload)
|
|
108
|
+
if (payload && payload.conda && payload.conda.skip) {
|
|
109
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await createConda(kernel)._install({ dependencies: [] }, () => {})
|
|
114
|
+
|
|
115
|
+
assert.equal(await pathExists(path.join(root, 'bin', 'miniconda')), true)
|
|
116
|
+
const condaInstall = calls.find((call) => Array.isArray(call.message))
|
|
117
|
+
assert.ok(condaInstall)
|
|
118
|
+
assert.equal(
|
|
119
|
+
condaInstall.message[1],
|
|
120
|
+
'conda install -y --override-channels -c conda-forge "python=3.10.20" "sqlite=3.47.2" "conda-libmamba-solver>=25.4.0"'
|
|
121
|
+
)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async () => {
|
|
125
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-pin-win32-'))
|
|
126
|
+
const kernel = createKernel(root, 'win32')
|
|
127
|
+
const calls = []
|
|
128
|
+
|
|
129
|
+
kernel.bin.mods = []
|
|
130
|
+
kernel.bin.download = async () => {}
|
|
131
|
+
kernel.bin.rm = async () => {}
|
|
132
|
+
kernel.bin.exec = async (payload) => {
|
|
133
|
+
calls.push(payload)
|
|
134
|
+
if (payload && payload.conda && payload.conda.skip) {
|
|
135
|
+
const miniforge = path.join(root, 'bin', 'miniforge')
|
|
136
|
+
await fs.mkdir(path.join(miniforge, 'conda-meta'), { recursive: true })
|
|
137
|
+
await fs.writeFile(path.join(miniforge, 'python.exe'), 'fake python\n')
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await createConda(kernel)._install({ dependencies: [] }, () => {})
|
|
142
|
+
|
|
143
|
+
assert.equal(await pathExists(path.join(root, 'bin', 'miniconda')), true)
|
|
144
|
+
const condaInstall = calls.find((call) => Array.isArray(call.message))
|
|
145
|
+
assert.ok(condaInstall)
|
|
146
|
+
assert.equal(
|
|
147
|
+
condaInstall.message[1],
|
|
148
|
+
'conda install -y --override-channels -c conda-forge "python=3.10.20=*_1_cpython" "sqlite=3.53.2" "conda-libmamba-solver>=25.4.0"'
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
|
|
153
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-miniforge-alias-'))
|
|
154
|
+
const legacy = await createLegacyMiniconda(root)
|
|
155
|
+
await fs.writeFile(path.join(legacy, 'legacy.txt'), 'old runtime\n')
|
|
156
|
+
const kernel = createKernel(root, 'darwin')
|
|
157
|
+
|
|
158
|
+
kernel.bin.mods = []
|
|
159
|
+
kernel.bin.download = async () => {}
|
|
160
|
+
kernel.bin.rm = async () => {}
|
|
161
|
+
kernel.bin.exec = async (payload) => {
|
|
162
|
+
if (payload && payload.conda && payload.conda.skip) {
|
|
163
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await createConda(kernel)._install({ dependencies: [] }, () => {})
|
|
168
|
+
|
|
169
|
+
const miniforge = path.join(root, 'bin', 'miniforge')
|
|
170
|
+
const miniconda = path.join(root, 'bin', 'miniconda')
|
|
171
|
+
assert.equal(await pathExists(path.join(miniconda, 'legacy.txt')), false)
|
|
172
|
+
assert.equal(await fs.realpath(miniconda), await fs.realpath(miniforge))
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('Conda install removes legacy miniconda symlinks without deleting their target', async () => {
|
|
176
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-legacy-symlink-'))
|
|
177
|
+
const external = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-external-'))
|
|
178
|
+
await fs.writeFile(path.join(external, 'keep.txt'), 'do not delete\n')
|
|
179
|
+
await fs.mkdir(path.join(root, 'bin'), { recursive: true })
|
|
180
|
+
await fs.symlink(external, path.join(root, 'bin', 'miniconda'), 'dir')
|
|
181
|
+
const kernel = createKernel(root, 'darwin')
|
|
182
|
+
|
|
183
|
+
kernel.bin.mods = []
|
|
184
|
+
kernel.bin.download = async () => {}
|
|
185
|
+
kernel.bin.rm = async () => {}
|
|
186
|
+
kernel.bin.exec = async (payload) => {
|
|
187
|
+
if (payload && payload.conda && payload.conda.skip) {
|
|
188
|
+
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await createConda(kernel)._install({ dependencies: [] }, () => {})
|
|
193
|
+
|
|
194
|
+
assert.equal(await pathExists(path.join(external, 'keep.txt')), true)
|
|
195
|
+
assert.equal(await fs.realpath(path.join(root, 'bin', 'miniconda')), await fs.realpath(path.join(root, 'bin', 'miniforge')))
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('Conda installed stays false when metadata check already marked Conda invalid', async () => {
|
|
199
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-metadata-invalid-'))
|
|
200
|
+
await createMiniforge(root)
|
|
201
|
+
const kernel = createKernel(root)
|
|
202
|
+
kernel.bin.correct_conda = false
|
|
203
|
+
|
|
204
|
+
assert.equal(await createConda(kernel).installed(), false)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('Conda installed returns false for old Conda runtimes without the Miniforge stamp', async () => {
|
|
208
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-runtime-stale-'))
|
|
209
|
+
await createMiniforge(root)
|
|
210
|
+
const kernel = createKernel(root)
|
|
211
|
+
|
|
212
|
+
assert.equal(await createConda(kernel).installed(), false)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
|
|
216
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-download-fails-'))
|
|
217
|
+
const miniconda = await createLegacyMiniconda(root)
|
|
218
|
+
const kernel = createKernel(root)
|
|
219
|
+
|
|
220
|
+
kernel.bin.download = async () => {
|
|
221
|
+
throw new Error('download failed')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
await assert.rejects(
|
|
225
|
+
createConda(kernel)._install({ dependencies: [] }, () => {}),
|
|
226
|
+
/download failed/
|
|
227
|
+
)
|
|
228
|
+
assert.equal(await pathExists(miniconda), true)
|
|
229
|
+
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge')), false)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
test('Conda install reports manual recovery when the old runtime cannot be removed', async () => {
|
|
233
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-runtime-locked-'))
|
|
234
|
+
await createLegacyMiniconda(root)
|
|
235
|
+
const kernel = createKernel(root)
|
|
236
|
+
kernel.bin.download = async () => {}
|
|
237
|
+
const conda = createConda(kernel)
|
|
238
|
+
const originalRm = fsModule.promises.rm
|
|
239
|
+
|
|
240
|
+
fsModule.promises.rm = async () => {
|
|
241
|
+
const error = new Error('resource busy')
|
|
242
|
+
error.code = 'EBUSY'
|
|
243
|
+
throw error
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
await assert.rejects(
|
|
248
|
+
conda._install({ dependencies: [] }, () => {}),
|
|
249
|
+
/delete that folder manually/
|
|
250
|
+
)
|
|
251
|
+
} finally {
|
|
252
|
+
fsModule.promises.rm = originalRm
|
|
253
|
+
}
|
|
254
|
+
})
|
package/test/ffmpeg-bin.test.js
CHANGED
|
@@ -34,14 +34,13 @@ test('FFmpeg bin installs pinned ffmpeg into base Conda on Windows', async () =>
|
|
|
34
34
|
assert.equal(calls.length, 1)
|
|
35
35
|
assert.deepEqual(calls[0].message, [
|
|
36
36
|
'conda clean -y --all',
|
|
37
|
-
'conda install -y --
|
|
38
|
-
'conda install -y --solver=classic --override-channels -c conda-forge ffmpeg=8.1.2',
|
|
37
|
+
'conda install -y --override-channels -c conda-forge ffmpeg=8.1.2',
|
|
39
38
|
])
|
|
40
39
|
assert.equal(calls[0].env, undefined)
|
|
41
40
|
assert.equal(calls[0].message.some((command) => /ffmpeg-env|conda create|-p /.test(command)), false)
|
|
42
41
|
})
|
|
43
42
|
|
|
44
|
-
test('FFmpeg bin installs pinned ffmpeg into base Conda
|
|
43
|
+
test('FFmpeg bin installs pinned ffmpeg into base Conda consistently across platforms', async () => {
|
|
45
44
|
const calls = []
|
|
46
45
|
const kernel = createKernel('darwin')
|
|
47
46
|
kernel.bin.exec = async (payload) => {
|