pinokiod 7.5.28 → 7.5.30
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 +18 -5
- package/kernel/bin/conda.js +36 -21
- package/kernel/bin/index.js +2 -2
- package/package.json +1 -1
- package/server/public/style.css +4 -0
- package/server/views/partials/home_run_menu.ejs +1 -1
- package/test/conda-bin.test.js +87 -4
- package/test/launch-settings-ui.test.js +7 -0
package/kernel/bin/conda-meta.js
CHANGED
|
@@ -69,15 +69,27 @@ async function runCondaList(minicondaPath) {
|
|
|
69
69
|
})
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function
|
|
73
|
-
|
|
72
|
+
async function managedCondaRuns(minicondaPath, platform = process.platform) {
|
|
73
|
+
const condaBinary = resolveCondaBinary(minicondaPath, platform, false)
|
|
74
|
+
if (!condaBinary) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
return await new Promise((resolve) => {
|
|
78
|
+
execFile(condaBinary, ['--version'], { windowsHide: true, timeout: 15000 }, (err) => {
|
|
79
|
+
resolve(!err)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveCondaBinary(minicondaPath, platform = process.platform, allowFallback = true) {
|
|
85
|
+
if (platform === 'win32') {
|
|
74
86
|
if (minicondaPath) {
|
|
75
87
|
const scriptPath = path.join(minicondaPath, 'Scripts', 'conda.exe')
|
|
76
88
|
if (fs.existsSync(scriptPath)) {
|
|
77
89
|
return scriptPath
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
|
-
return 'conda'
|
|
92
|
+
return allowFallback ? 'conda' : null
|
|
81
93
|
}
|
|
82
94
|
if (minicondaPath) {
|
|
83
95
|
const binPath = path.join(minicondaPath, 'bin', 'conda')
|
|
@@ -85,9 +97,10 @@ function resolveCondaBinary(minicondaPath) {
|
|
|
85
97
|
return binPath
|
|
86
98
|
}
|
|
87
99
|
}
|
|
88
|
-
return 'conda'
|
|
100
|
+
return allowFallback ? 'conda' : null
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
module.exports = {
|
|
92
|
-
buildCondaListFromMeta
|
|
104
|
+
buildCondaListFromMeta,
|
|
105
|
+
managedCondaRuns,
|
|
93
106
|
}
|
package/kernel/bin/conda.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const { glob } = require('glob')
|
|
4
4
|
const semver = require('semver')
|
|
5
|
-
const { buildCondaListFromMeta } = require('./conda-meta')
|
|
5
|
+
const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
|
|
6
6
|
const {
|
|
7
7
|
CONDA_PIN_VERSION,
|
|
8
8
|
PYTHON_INSTALL_SPEC,
|
|
@@ -73,33 +73,45 @@ class Conda {
|
|
|
73
73
|
if (this.kernel.platform !== "win32") {
|
|
74
74
|
return
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
const condaRootDirs = []
|
|
77
|
+
for (const rootDir of [CONDA_ROOT_DIR, LEGACY_CONDA_ROOT_DIR]) {
|
|
78
|
+
if (await this.kernel.exists(`bin/${rootDir}/conda-meta`)) {
|
|
79
|
+
condaRootDirs.push(rootDir)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (condaRootDirs.length === 0) {
|
|
77
83
|
return
|
|
78
84
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
await fs.promises.writeFile(
|
|
82
|
-
path.resolve(activateDir, "zz_pinokio_unset_ssl_cert_dir-win.bat"),
|
|
83
|
-
`@echo off
|
|
85
|
+
const hookFiles = {
|
|
86
|
+
"zz_pinokio_unset_ssl_cert_dir-win.bat": `@echo off
|
|
84
87
|
if "%__CONDA_OPENSSL_CERT_DIR_SET%"=="1" (
|
|
85
88
|
set "SSL_CERT_DIR="
|
|
89
|
+
set "__CONDA_OPENSSL_CERT_DIR_SET="
|
|
86
90
|
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await fs.promises.writeFile(
|
|
90
|
-
path.resolve(activateDir, "zz_pinokio_unset_ssl_cert_dir-win.ps1"),
|
|
91
|
-
`if ($Env:__CONDA_OPENSSL_CERT_DIR_SET -eq "1") {
|
|
91
|
+
`,
|
|
92
|
+
"zz_pinokio_unset_ssl_cert_dir-win.ps1": `if ($Env:__CONDA_OPENSSL_CERT_DIR_SET -eq "1") {
|
|
92
93
|
Remove-Item -Path Env:\\SSL_CERT_DIR -ErrorAction SilentlyContinue
|
|
94
|
+
Remove-Item -Path Env:\\__CONDA_OPENSSL_CERT_DIR_SET -ErrorAction SilentlyContinue
|
|
93
95
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
await fs.promises.writeFile(
|
|
97
|
-
path.resolve(activateDir, "zz_pinokio_unset_ssl_cert_dir-win.sh"),
|
|
98
|
-
`if [[ "\${__CONDA_OPENSSL_CERT_DIR_SET:-}" == "1" ]]; then
|
|
96
|
+
`,
|
|
97
|
+
"zz_pinokio_unset_ssl_cert_dir-win.sh": `if [[ "\${__CONDA_OPENSSL_CERT_DIR_SET:-}" == "1" ]]; then
|
|
99
98
|
unset SSL_CERT_DIR
|
|
99
|
+
unset __CONDA_OPENSSL_CERT_DIR_SET
|
|
100
100
|
fi
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
`,
|
|
102
|
+
}
|
|
103
|
+
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)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
103
115
|
}
|
|
104
116
|
async init() {
|
|
105
117
|
if (this.kernel.homedir) {
|
|
@@ -120,9 +132,9 @@ report_errors: false`)
|
|
|
120
132
|
let pinned_exists = await this.hasCondaMeta()
|
|
121
133
|
if (pinned_exists) {
|
|
122
134
|
await fs.promises.writeFile(this.kernel.path(`bin/${CONDA_ROOT_DIR}/conda-meta/pinned`), this.pinnedPackages())
|
|
123
|
-
await this.ensureSslCertDirOverride()
|
|
124
135
|
await this.ensureCompatibilityAlias()
|
|
125
136
|
}
|
|
137
|
+
await this.ensureSslCertDirOverride()
|
|
126
138
|
}
|
|
127
139
|
}
|
|
128
140
|
async check() {
|
|
@@ -170,7 +182,10 @@ report_errors: false`)
|
|
|
170
182
|
this.kernel.bin.installed.conda = conda
|
|
171
183
|
this.kernel.bin.installed.conda_versions = conda_versions
|
|
172
184
|
this.kernel.bin.installed.conda_builds = conda_builds
|
|
173
|
-
|
|
185
|
+
if (!(conda_check.conda && conda_check.mamba && conda_check.python)) {
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
return await managedCondaRuns(this.kernel.bin.path(CONDA_ROOT_DIR), this.kernel.platform)
|
|
174
189
|
}
|
|
175
190
|
async install(req, ondata) {
|
|
176
191
|
for(let i=0; i<5; i++) {
|
package/kernel/bin/index.js
CHANGED
|
@@ -21,7 +21,7 @@ const LLVM = require('./llvm')
|
|
|
21
21
|
const VS = require("./vs")
|
|
22
22
|
const Cuda = require("./cuda")
|
|
23
23
|
const Torch = require("./torch")
|
|
24
|
-
const { buildCondaListFromMeta } = require('./conda-meta')
|
|
24
|
+
const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
|
|
25
25
|
const {
|
|
26
26
|
isExpectedPythonPinned,
|
|
27
27
|
} = require('./conda-pins')
|
|
@@ -412,7 +412,7 @@ class Bin {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
if (conda_check.conda && conda_check.mamba && conda_check.python) {
|
|
415
|
-
this.correct_conda =
|
|
415
|
+
this.correct_conda = await managedCondaRuns(this.kernel.bin.path("miniforge"), this.platform)
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
this.installed.conda = conda
|
package/package.json
CHANGED
package/server/public/style.css
CHANGED
|
@@ -1284,6 +1284,10 @@ body.main-sidebar-page .navheader {
|
|
|
1284
1284
|
box-shadow: none;
|
|
1285
1285
|
isolation: isolate;
|
|
1286
1286
|
}
|
|
1287
|
+
body.main-sidebar-page > header.navheader h1,
|
|
1288
|
+
body.app-page > header.navheader h1 {
|
|
1289
|
+
gap: 6px;
|
|
1290
|
+
}
|
|
1287
1291
|
body.main-sidebar-page .navheader .sidebar-toggle {
|
|
1288
1292
|
color: #0f172a;
|
|
1289
1293
|
position: relative;
|
|
@@ -33,7 +33,7 @@ const buildHomeSelectionPayload = (menuItem, index) => {
|
|
|
33
33
|
return payload
|
|
34
34
|
}
|
|
35
35
|
const buildHomeSelectionUrl = (menuItem, index) => {
|
|
36
|
-
const base = homeRunApp && homeRunApp.browser_url ? homeRunApp.browser_url : ""
|
|
36
|
+
const base = homeRunApp && homeRunApp.view_url ? homeRunApp.view_url : (homeRunApp && homeRunApp.browser_url ? homeRunApp.browser_url : "")
|
|
37
37
|
if (!base) {
|
|
38
38
|
return menuItem.href || "#"
|
|
39
39
|
}
|
package/test/conda-bin.test.js
CHANGED
|
@@ -25,12 +25,38 @@ function createKernel(root, platform = 'win32') {
|
|
|
25
25
|
exists: async (...parts) => pathExists(path.join(root, ...parts)),
|
|
26
26
|
bin: {
|
|
27
27
|
correct_conda: true,
|
|
28
|
+
installed: {},
|
|
28
29
|
path: (...parts) => path.join(root, 'bin', ...parts),
|
|
29
30
|
exists: async (target) => pathExists(path.join(root, 'bin', target)),
|
|
30
31
|
},
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
async function writeCondaMeta(root, name, version, build = 'py_0') {
|
|
36
|
+
const metaDir = path.join(root, 'bin', 'miniforge', 'conda-meta')
|
|
37
|
+
await fs.mkdir(metaDir, { recursive: true })
|
|
38
|
+
await fs.writeFile(
|
|
39
|
+
path.join(metaDir, `${name}-${version}.json`),
|
|
40
|
+
JSON.stringify({ name, version, build_string: build, channel: 'conda-forge' })
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function writeHealthyCondaMeta(root, platform = 'win32') {
|
|
45
|
+
await writeCondaMeta(root, 'conda', '26.3.2', 'py310')
|
|
46
|
+
await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
|
|
47
|
+
await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function writeFakeManagedConda(root, platform = 'win32') {
|
|
51
|
+
const condaPath = platform === 'win32'
|
|
52
|
+
? path.join(root, 'bin', 'miniforge', 'Scripts', 'conda.exe')
|
|
53
|
+
: path.join(root, 'bin', 'miniforge', 'bin', 'conda')
|
|
54
|
+
await fs.mkdir(path.dirname(condaPath), { recursive: true })
|
|
55
|
+
await fs.copyFile(process.execPath, condaPath)
|
|
56
|
+
await fs.chmod(condaPath, 0o755)
|
|
57
|
+
return condaPath
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
async function createCondaRoot(root, name) {
|
|
35
61
|
const condaRoot = path.join(root, 'bin', name)
|
|
36
62
|
await fs.mkdir(path.join(condaRoot, 'Scripts'), { recursive: true })
|
|
@@ -73,17 +99,59 @@ test('Conda uses Miniforge assets and writes conda-forge-only config', async ()
|
|
|
73
99
|
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge')), false)
|
|
74
100
|
})
|
|
75
101
|
|
|
76
|
-
test('Conda init writes
|
|
102
|
+
test('Conda init writes SSL_CERT_DIR neutralizing hooks only after Miniforge exists', async () => {
|
|
77
103
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-init-hooks-'))
|
|
78
104
|
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
79
105
|
const conda = createConda(createKernel(root))
|
|
80
106
|
|
|
81
107
|
await conda.init()
|
|
82
108
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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'
|
|
86
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/)
|
|
131
|
+
})
|
|
132
|
+
|
|
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-'))
|
|
135
|
+
await fs.mkdir(path.join(root, 'bin', 'miniconda', 'conda-meta'), { recursive: true })
|
|
136
|
+
const conda = createConda(createKernel(root))
|
|
137
|
+
|
|
138
|
+
await conda.init()
|
|
139
|
+
|
|
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/)
|
|
87
155
|
})
|
|
88
156
|
|
|
89
157
|
test('Conda pins Python 3.10.20 consistently across platforms', async () => {
|
|
@@ -207,6 +275,21 @@ test('Conda installed stays false when metadata check already marked Conda inval
|
|
|
207
275
|
assert.equal(await createConda(kernel).installed(), false)
|
|
208
276
|
})
|
|
209
277
|
|
|
278
|
+
test('Conda check rejects metadata-only Windows installs', async () => {
|
|
279
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-missing-'))
|
|
280
|
+
await writeHealthyCondaMeta(root, 'win32')
|
|
281
|
+
|
|
282
|
+
assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test('Conda check accepts a runnable managed Windows conda executable', async () => {
|
|
286
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-runnable-'))
|
|
287
|
+
await writeHealthyCondaMeta(root, 'win32')
|
|
288
|
+
await writeFakeManagedConda(root, 'win32')
|
|
289
|
+
|
|
290
|
+
assert.equal(await createConda(createKernel(root, 'win32')).check(), true)
|
|
291
|
+
})
|
|
292
|
+
|
|
210
293
|
test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
|
|
211
294
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-download-fails-'))
|
|
212
295
|
const miniconda = await createLegacyMiniconda(root)
|
|
@@ -391,3 +391,10 @@ test("static guard: open without launching disables page-load script frame selec
|
|
|
391
391
|
assert.match(appView, /if \(!target && !automaticSelectionDisabled && followCurrentDefault\)/)
|
|
392
392
|
assert.match(appView, /if \(!target && !automaticSelectionDisabled && preselected && preselected !== devTab\)/)
|
|
393
393
|
})
|
|
394
|
+
|
|
395
|
+
test("static guard: home run command selection opens without launching before selecting command", async () => {
|
|
396
|
+
const homeRunMenu = await fs.readFile(path.resolve(root, "server/views/partials/home_run_menu.ejs"), "utf8")
|
|
397
|
+
|
|
398
|
+
assert.match(homeRunMenu, /homeRunApp && homeRunApp\.view_url \? homeRunApp\.view_url :/)
|
|
399
|
+
assert.match(homeRunMenu, /pinokio_home_select: JSON\.stringify\(buildHomeSelectionPayload\(menuItem, index\)\)/)
|
|
400
|
+
})
|