pinokiod 8.0.2 → 8.0.4
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/caddy.js +30 -0
- package/kernel/bin/conda-pins.js +4 -3
- package/kernel/bin/conda.js +23 -11
- package/kernel/bin/index.js +3 -7
- package/package.json +1 -1
- package/test/caddy-install.test.js +65 -0
- package/test/conda-bin.test.js +85 -41
package/kernel/bin/caddy.js
CHANGED
|
@@ -8,6 +8,7 @@ const Util = require('../util')
|
|
|
8
8
|
const ADMIN_READY_TIMEOUT_MS = 30000
|
|
9
9
|
const ADMIN_START_TIMEOUT_MS = 5000
|
|
10
10
|
const ADMIN_POLL_INTERVAL_MS = 250
|
|
11
|
+
const ADMIN_STOP_TIMEOUT_MS = 5000
|
|
11
12
|
|
|
12
13
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
13
14
|
|
|
@@ -29,6 +30,35 @@ class Caddy {
|
|
|
29
30
|
return false
|
|
30
31
|
}
|
|
31
32
|
}
|
|
33
|
+
async waitForStopped(timeout = ADMIN_STOP_TIMEOUT_MS, interval = ADMIN_POLL_INTERVAL_MS) {
|
|
34
|
+
const started = Date.now()
|
|
35
|
+
while ((Date.now() - started) < timeout) {
|
|
36
|
+
if (!(await this.running())) {
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
await sleep(interval)
|
|
40
|
+
}
|
|
41
|
+
return !(await this.running())
|
|
42
|
+
}
|
|
43
|
+
async stop() {
|
|
44
|
+
if (!(await this.running())) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await axios.post('http://127.0.0.1:2019/stop', null, {
|
|
49
|
+
timeout: ADMIN_STOP_TIMEOUT_MS
|
|
50
|
+
})
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (await this.running()) {
|
|
53
|
+
throw error
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const stopped = await this.waitForStopped()
|
|
57
|
+
if (!stopped) {
|
|
58
|
+
throw new Error("Pinokio could not stop Caddy before replacing the managed Conda runtime.")
|
|
59
|
+
}
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
32
62
|
async start() {
|
|
33
63
|
// if peer.https_active is true,
|
|
34
64
|
// 1. kill existing caddy
|
package/kernel/bin/conda-pins.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const semver = require('semver')
|
|
2
2
|
|
|
3
|
-
const CONDA_PIN_VERSION = "26.3
|
|
4
|
-
const CONDA_RUNTIME_MARKER = ".pinokio-runtime-v1"
|
|
3
|
+
const CONDA_PIN_VERSION = "26.5.3"
|
|
5
4
|
const PYTHON_PIN_VERSION = "3.10.20"
|
|
6
5
|
const PYTHON_INSTALL_SPEC = `python=${PYTHON_PIN_VERSION}`
|
|
7
6
|
const WINDOWS_PYTHON_SSL_FIX_SPEC = `${PYTHON_INSTALL_SPEC}=*_1_cpython`
|
|
8
7
|
|
|
8
|
+
const isExpectedCondaPinned = (version) => String(version) === CONDA_PIN_VERSION
|
|
9
|
+
|
|
9
10
|
const condaBuildNumber = (build) => {
|
|
10
11
|
const chunks = String(build || "").split("_").reverse()
|
|
11
12
|
const buildNumber = chunks.find((chunk) => /^\d+$/.test(chunk))
|
|
@@ -29,8 +30,8 @@ const isExpectedPythonPinned = (platform, version, build) => {
|
|
|
29
30
|
|
|
30
31
|
module.exports = {
|
|
31
32
|
CONDA_PIN_VERSION,
|
|
32
|
-
CONDA_RUNTIME_MARKER,
|
|
33
33
|
PYTHON_INSTALL_SPEC,
|
|
34
34
|
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
35
|
+
isExpectedCondaPinned,
|
|
35
36
|
isExpectedPythonPinned,
|
|
36
37
|
}
|
package/kernel/bin/conda.js
CHANGED
|
@@ -4,9 +4,9 @@ const semver = require('semver')
|
|
|
4
4
|
const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
|
|
5
5
|
const {
|
|
6
6
|
CONDA_PIN_VERSION,
|
|
7
|
-
CONDA_RUNTIME_MARKER,
|
|
8
7
|
PYTHON_INSTALL_SPEC,
|
|
9
8
|
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
9
|
+
isExpectedCondaPinned,
|
|
10
10
|
isExpectedPythonPinned,
|
|
11
11
|
} = require('./conda-pins')
|
|
12
12
|
|
|
@@ -48,9 +48,6 @@ class Conda {
|
|
|
48
48
|
async hasCondaMeta() {
|
|
49
49
|
return await this.kernel.exists(`bin/${CONDA_ROOT_DIR}/conda-meta`)
|
|
50
50
|
}
|
|
51
|
-
async hasRuntimeMarker() {
|
|
52
|
-
return await this.kernel.exists(`bin/${CONDA_ROOT_DIR}/${CONDA_RUNTIME_MARKER}`)
|
|
53
|
-
}
|
|
54
51
|
env() {
|
|
55
52
|
let base = {
|
|
56
53
|
CONDA_PREFIX: this.kernel.bin.path(CONDA_ROOT_DIR),
|
|
@@ -96,9 +93,6 @@ report_errors: false`)
|
|
|
96
93
|
}
|
|
97
94
|
}
|
|
98
95
|
async check() {
|
|
99
|
-
if (!(await this.hasRuntimeMarker())) {
|
|
100
|
-
return false
|
|
101
|
-
}
|
|
102
96
|
let res = await buildCondaListFromMeta(this.kernel.bin.path(CONDA_ROOT_DIR))
|
|
103
97
|
|
|
104
98
|
let lines = res.response.split(/[\r\n]+/)
|
|
@@ -118,7 +112,7 @@ report_errors: false`)
|
|
|
118
112
|
conda_versions[name] = version
|
|
119
113
|
conda_builds[name] = build
|
|
120
114
|
if (name === "conda") {
|
|
121
|
-
conda_check.conda =
|
|
115
|
+
conda_check.conda = isExpectedCondaPinned(version)
|
|
122
116
|
}
|
|
123
117
|
// check conda-libmamba-solver is up to date
|
|
124
118
|
// sometimes it just fails silently so need to check
|
|
@@ -176,6 +170,16 @@ report_errors: false`)
|
|
|
176
170
|
ondata({ raw: `downloading installer: ${installer_url}...\r\n` })
|
|
177
171
|
await this.kernel.bin.download(installer_url, installer, ondata)
|
|
178
172
|
|
|
173
|
+
const caddy = this.kernel.bin.mod && this.kernel.bin.mod.caddy
|
|
174
|
+
let caddyWasRunning = false
|
|
175
|
+
if (caddy && typeof caddy.running === "function") {
|
|
176
|
+
caddyWasRunning = await caddy.running()
|
|
177
|
+
}
|
|
178
|
+
if (caddyWasRunning) {
|
|
179
|
+
ondata({ raw: `stopping Home Server before replacing Miniforge...\r\n` })
|
|
180
|
+
await caddy.stop()
|
|
181
|
+
}
|
|
182
|
+
|
|
179
183
|
legacy_path_exists = await this.kernel.exists(`bin/${LEGACY_CONDA_ROOT_DIR}`)
|
|
180
184
|
if (legacy_path_exists) {
|
|
181
185
|
console.log("Removing legacy install path...", legacy_path)
|
|
@@ -208,8 +212,12 @@ report_errors: false`)
|
|
|
208
212
|
await fs.promises.writeFile(this.kernel.path(`bin/${CONDA_ROOT_DIR}/conda-meta/pinned`), this.pinnedPackages())
|
|
209
213
|
}
|
|
210
214
|
|
|
215
|
+
const dependencies = new Set(Array.isArray(req.dependencies) ? req.dependencies : [])
|
|
216
|
+
if (caddyWasRunning) {
|
|
217
|
+
dependencies.add("caddy")
|
|
218
|
+
}
|
|
211
219
|
let mods = this.kernel.bin.mods.filter((m) => {
|
|
212
|
-
return
|
|
220
|
+
return dependencies.has(m.name)
|
|
213
221
|
}).map((m) => {
|
|
214
222
|
if (m.mod.cmd) {
|
|
215
223
|
return m.mod.cmd()
|
|
@@ -220,6 +228,7 @@ report_errors: false`)
|
|
|
220
228
|
console.log("Conda dependencies to install", { mods })
|
|
221
229
|
|
|
222
230
|
let condaPackages = [
|
|
231
|
+
`"conda=${CONDA_PIN_VERSION}"`,
|
|
223
232
|
this.kernel.platform === "win32" ? `"${WINDOWS_PYTHON_SSL_FIX_SPEC}"` : `"${PYTHON_INSTALL_SPEC}"`,
|
|
224
233
|
`"conda-libmamba-solver>=25.4.0"`,
|
|
225
234
|
]
|
|
@@ -244,9 +253,12 @@ report_errors: false`)
|
|
|
244
253
|
)
|
|
245
254
|
}
|
|
246
255
|
await this.ensureCompatibilityAlias()
|
|
247
|
-
await this.kernel.bin.rm(installer, ondata)
|
|
248
|
-
await fs.promises.writeFile(this.kernel.bin.path(CONDA_ROOT_DIR, CONDA_RUNTIME_MARKER), "")
|
|
249
256
|
ondata({ raw: `Install finished\r\n` })
|
|
257
|
+
await this.kernel.bin.rm(installer, ondata)
|
|
258
|
+
if (caddyWasRunning) {
|
|
259
|
+
ondata({ raw: `restarting Home Server after replacing Miniforge...\r\n` })
|
|
260
|
+
await caddy.start()
|
|
261
|
+
}
|
|
250
262
|
}
|
|
251
263
|
async removeInstallPath(target) {
|
|
252
264
|
try {
|
package/kernel/bin/index.js
CHANGED
|
@@ -23,7 +23,7 @@ const Cuda = require("./cuda")
|
|
|
23
23
|
const Torch = require("./torch")
|
|
24
24
|
const { buildCondaListFromMeta, managedCondaRuns } = require('./conda-meta')
|
|
25
25
|
const {
|
|
26
|
-
|
|
26
|
+
isExpectedCondaPinned,
|
|
27
27
|
isExpectedPythonPinned,
|
|
28
28
|
} = require('./conda-pins')
|
|
29
29
|
const { glob } = require('glob')
|
|
@@ -350,10 +350,6 @@ class Bin {
|
|
|
350
350
|
let conda_builds = {}
|
|
351
351
|
this.correct_conda = false
|
|
352
352
|
|
|
353
|
-
const runtimeMarkerExists = await fs.promises.access(
|
|
354
|
-
this.kernel.bin.path("miniforge", CONDA_RUNTIME_MARKER)
|
|
355
|
-
).then(() => true).catch(() => false)
|
|
356
|
-
|
|
357
353
|
//////////////////////////////////////////////////////////////////
|
|
358
354
|
// exception handling
|
|
359
355
|
// if importlib_metadata || uvicorn || fastapi exist in the base environment, this.correct_conda = false
|
|
@@ -380,7 +376,7 @@ class Bin {
|
|
|
380
376
|
|
|
381
377
|
let to_reset_exists = false
|
|
382
378
|
|
|
383
|
-
if (
|
|
379
|
+
if (to_reset_exists) {
|
|
384
380
|
this.correct_conda = false
|
|
385
381
|
} else {
|
|
386
382
|
let res = await buildCondaListFromMeta(this.kernel.bin.path("miniforge"))
|
|
@@ -396,7 +392,7 @@ class Bin {
|
|
|
396
392
|
conda_versions[name] = version
|
|
397
393
|
conda_builds[name] = build
|
|
398
394
|
if (name === "conda") {
|
|
399
|
-
conda_check.conda =
|
|
395
|
+
conda_check.conda = isExpectedCondaPinned(version)
|
|
400
396
|
}
|
|
401
397
|
if (name === "conda-libmamba-solver") {
|
|
402
398
|
let coerced = semver.coerce(version)
|
package/package.json
CHANGED
|
@@ -51,3 +51,68 @@ test('caddy admin wait returns true when admin responds', async () => {
|
|
|
51
51
|
assert.equal(ready, true)
|
|
52
52
|
assert.equal(checks, 2)
|
|
53
53
|
})
|
|
54
|
+
|
|
55
|
+
test('caddy start uses the normal shell execution contract', async () => {
|
|
56
|
+
const caddy = new Caddy()
|
|
57
|
+
let running = false
|
|
58
|
+
let launch
|
|
59
|
+
caddy.kernel = {
|
|
60
|
+
homedir: '/tmp/pinokio',
|
|
61
|
+
peer: {
|
|
62
|
+
https_active: true,
|
|
63
|
+
announce() {}
|
|
64
|
+
},
|
|
65
|
+
processes: {},
|
|
66
|
+
exec(params, ondata) {
|
|
67
|
+
launch = params
|
|
68
|
+
running = true
|
|
69
|
+
ondata({ raw: '', cleaned: 'admin endpoint started' })
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
caddy.running = async () => running
|
|
73
|
+
caddy.installed = async () => true
|
|
74
|
+
|
|
75
|
+
await caddy.start()
|
|
76
|
+
|
|
77
|
+
assert.equal(launch.message, 'caddy run --watch')
|
|
78
|
+
assert.deepEqual(Object.keys(launch).sort(), ['message', 'path'])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('caddy stop uses the native admin API only when Caddy is running', async () => {
|
|
82
|
+
const caddy = new Caddy()
|
|
83
|
+
let running = true
|
|
84
|
+
caddy.running = async () => running
|
|
85
|
+
const originalPost = require('axios').post
|
|
86
|
+
const calls = []
|
|
87
|
+
require('axios').post = async (...args) => {
|
|
88
|
+
calls.push(args)
|
|
89
|
+
running = false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
assert.equal(await caddy.stop(), true)
|
|
94
|
+
assert.equal(await caddy.stop(), false)
|
|
95
|
+
} finally {
|
|
96
|
+
require('axios').post = originalPost
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
assert.equal(calls.length, 1)
|
|
100
|
+
assert.equal(calls[0][0], 'http://127.0.0.1:2019/stop')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('caddy stop refuses to continue when native shutdown is not confirmed', async () => {
|
|
104
|
+
const caddy = new Caddy()
|
|
105
|
+
caddy.running = async () => true
|
|
106
|
+
caddy.waitForStopped = async () => false
|
|
107
|
+
const originalPost = require('axios').post
|
|
108
|
+
require('axios').post = async () => {}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await assert.rejects(
|
|
112
|
+
caddy.stop(),
|
|
113
|
+
/could not stop Caddy/
|
|
114
|
+
)
|
|
115
|
+
} finally {
|
|
116
|
+
require('axios').post = originalPost
|
|
117
|
+
}
|
|
118
|
+
})
|
package/test/conda-bin.test.js
CHANGED
|
@@ -9,9 +9,9 @@ const Bin = require('../kernel/bin')
|
|
|
9
9
|
const Conda = require('../kernel/bin/conda')
|
|
10
10
|
const {
|
|
11
11
|
CONDA_PIN_VERSION,
|
|
12
|
-
CONDA_RUNTIME_MARKER,
|
|
13
12
|
PYTHON_INSTALL_SPEC,
|
|
14
13
|
WINDOWS_PYTHON_SSL_FIX_SPEC,
|
|
14
|
+
isExpectedCondaPinned,
|
|
15
15
|
isExpectedPythonPinned,
|
|
16
16
|
} = require('../kernel/bin/conda-pins')
|
|
17
17
|
|
|
@@ -44,19 +44,12 @@ async function writeCondaMeta(root, name, version, build = 'py_0') {
|
|
|
44
44
|
)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async function writeHealthyCondaMeta(root, platform = 'win32') {
|
|
48
|
-
await writeCondaMeta(root, 'conda',
|
|
47
|
+
async function writeHealthyCondaMeta(root, platform = 'win32', condaVersion = CONDA_PIN_VERSION) {
|
|
48
|
+
await writeCondaMeta(root, 'conda', condaVersion, 'py310')
|
|
49
49
|
await writeCondaMeta(root, 'conda-libmamba-solver', '25.4.0', 'pyhd3eb1b0_0')
|
|
50
50
|
await writeCondaMeta(root, 'python', '3.10.20', platform === 'win32' ? 'h4de0772_1_cpython' : 'cpython')
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
async function writeRuntimeMarker(root) {
|
|
54
|
-
const marker = path.join(root, 'bin', 'miniforge', CONDA_RUNTIME_MARKER)
|
|
55
|
-
await fs.mkdir(path.dirname(marker), { recursive: true })
|
|
56
|
-
await fs.writeFile(marker, '')
|
|
57
|
-
return marker
|
|
58
|
-
}
|
|
59
|
-
|
|
60
53
|
async function writeFakeManagedConda(root, platform = 'win32') {
|
|
61
54
|
const condaPath = platform === 'win32'
|
|
62
55
|
? path.join(root, 'bin', 'miniforge', 'Scripts', 'conda.exe')
|
|
@@ -123,11 +116,13 @@ test('Conda uses Miniforge assets and writes conda-forge-only config', async ()
|
|
|
123
116
|
})
|
|
124
117
|
|
|
125
118
|
test('Conda pins the managed Conda and Python versions consistently', async () => {
|
|
126
|
-
assert.equal(CONDA_PIN_VERSION, '26.3
|
|
127
|
-
assert.equal(CONDA_RUNTIME_MARKER, '.pinokio-runtime-v1')
|
|
119
|
+
assert.equal(CONDA_PIN_VERSION, '26.5.3')
|
|
128
120
|
assert.equal(PYTHON_INSTALL_SPEC, 'python=3.10.20')
|
|
129
121
|
assert.equal(WINDOWS_PYTHON_SSL_FIX_SPEC, 'python=3.10.20=*_1_cpython')
|
|
130
122
|
|
|
123
|
+
assert.equal(isExpectedCondaPinned('26.5.3'), true)
|
|
124
|
+
assert.equal(isExpectedCondaPinned('26.3.2'), false)
|
|
125
|
+
|
|
131
126
|
assert.equal(isExpectedPythonPinned('darwin', '3.10.20', 'cpython'), true)
|
|
132
127
|
assert.equal(isExpectedPythonPinned('linux', '3.10.20', 'cpython'), true)
|
|
133
128
|
assert.equal(isExpectedPythonPinned('darwin', '3.10.21', 'cpython'), false)
|
|
@@ -136,17 +131,16 @@ test('Conda pins the managed Conda and Python versions consistently', async () =
|
|
|
136
131
|
assert.equal(isExpectedPythonPinned('win32', '3.10.20', 'h4de0772_1_cpython'), true)
|
|
137
132
|
})
|
|
138
133
|
|
|
139
|
-
test('Conda init
|
|
140
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-
|
|
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-'))
|
|
141
136
|
await createMiniforge(root)
|
|
142
137
|
await fs.mkdir(path.join(root, 'bin', 'miniforge', 'conda-meta'), { recursive: true })
|
|
143
138
|
|
|
144
139
|
await createConda(createKernel(root, 'darwin')).init()
|
|
145
140
|
|
|
146
|
-
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge', CONDA_RUNTIME_MARKER)), false)
|
|
147
141
|
assert.equal(
|
|
148
142
|
await fs.readFile(path.join(root, 'bin', 'miniforge', 'conda-meta', 'pinned'), 'utf8'),
|
|
149
|
-
'conda ==26.3
|
|
143
|
+
'conda ==26.5.3'
|
|
150
144
|
)
|
|
151
145
|
})
|
|
152
146
|
|
|
@@ -200,13 +194,12 @@ test('Conda install requests the Python 3.10.20 pin on macOS/Linux', async () =>
|
|
|
200
194
|
assert.ok(condaInstall)
|
|
201
195
|
assert.equal(
|
|
202
196
|
condaInstall.message[1],
|
|
203
|
-
'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"'
|
|
204
198
|
)
|
|
205
199
|
assert.equal(
|
|
206
200
|
await fs.readFile(path.join(root, 'bin', 'miniforge', 'conda-meta', 'pinned'), 'utf8'),
|
|
207
|
-
'conda ==26.3
|
|
201
|
+
'conda ==26.5.3'
|
|
208
202
|
)
|
|
209
|
-
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge', CONDA_RUNTIME_MARKER)), true)
|
|
210
203
|
})
|
|
211
204
|
|
|
212
205
|
test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async () => {
|
|
@@ -233,9 +226,8 @@ test('Conda install keeps the Windows Python 3.10.20 SSL-fixed build pin', async
|
|
|
233
226
|
assert.ok(condaInstall)
|
|
234
227
|
assert.equal(
|
|
235
228
|
condaInstall.message[1],
|
|
236
|
-
'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"'
|
|
237
230
|
)
|
|
238
|
-
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge', CONDA_RUNTIME_MARKER)), true)
|
|
239
231
|
})
|
|
240
232
|
|
|
241
233
|
test('Conda install replaces the stale runtime and rebuilds declared Conda modules', async () => {
|
|
@@ -266,9 +258,62 @@ test('Conda install replaces the stale runtime and rebuilds declared Conda modul
|
|
|
266
258
|
assert.ok(condaInstall)
|
|
267
259
|
assert.equal(
|
|
268
260
|
condaInstall.message[1],
|
|
269
|
-
'conda install -y --override-channels -c conda-forge "python=3.10.20" "conda-libmamba-solver>=25.4.0" uv=0.11.23 nodejs=22.21.1 pnpm'
|
|
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'
|
|
270
262
|
)
|
|
271
|
-
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('Conda replacement stops, preserves, and restarts a running managed Caddy', async () => {
|
|
266
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-caddy-lifecycle-'))
|
|
267
|
+
await createMiniforge(root)
|
|
268
|
+
const kernel = createKernel(root, 'win32')
|
|
269
|
+
const events = []
|
|
270
|
+
let caddyRunning = true
|
|
271
|
+
let condaInstall
|
|
272
|
+
|
|
273
|
+
const caddy = {
|
|
274
|
+
cmd: () => 'caddy=2.9.1',
|
|
275
|
+
running: async () => caddyRunning,
|
|
276
|
+
stop: async () => {
|
|
277
|
+
events.push('caddy-stop')
|
|
278
|
+
caddyRunning = false
|
|
279
|
+
},
|
|
280
|
+
start: async () => {
|
|
281
|
+
events.push('caddy-start')
|
|
282
|
+
caddyRunning = true
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
kernel.bin.installed.conda = new Set(['caddy'])
|
|
286
|
+
kernel.bin.mod = { caddy }
|
|
287
|
+
kernel.bin.mods = [{ name: 'caddy', mod: caddy }]
|
|
288
|
+
kernel.bin.download = async () => events.push('download')
|
|
289
|
+
kernel.bin.rm = async () => events.push('installer-cleanup')
|
|
290
|
+
kernel.bin.exec = async (payload) => {
|
|
291
|
+
if (payload && payload.conda && payload.conda.skip) {
|
|
292
|
+
events.push('bootstrap')
|
|
293
|
+
const miniforge = path.join(root, 'bin', 'miniforge')
|
|
294
|
+
await fs.mkdir(path.join(miniforge, 'conda-meta'), { recursive: true })
|
|
295
|
+
await fs.writeFile(path.join(miniforge, 'python.exe'), 'fake python\n')
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
events.push('conda-install')
|
|
299
|
+
condaInstall = payload
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const conda = createConda(kernel)
|
|
303
|
+
const removeInstallPath = conda.removeInstallPath.bind(conda)
|
|
304
|
+
conda.removeInstallPath = async (target) => {
|
|
305
|
+
assert.equal(caddyRunning, false)
|
|
306
|
+
events.push('remove-miniforge')
|
|
307
|
+
await removeInstallPath(target)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
await conda._install({ dependencies: [] }, () => {})
|
|
311
|
+
|
|
312
|
+
assert.ok(events.indexOf('caddy-stop') < events.indexOf('remove-miniforge'))
|
|
313
|
+
assert.ok(events.indexOf('remove-miniforge') < events.indexOf('bootstrap'))
|
|
314
|
+
assert.ok(events.indexOf('conda-install') < events.indexOf('caddy-start'))
|
|
315
|
+
assert.match(condaInstall.message[1], / caddy=2\.9\.1$/)
|
|
316
|
+
assert.equal(caddyRunning, true)
|
|
272
317
|
})
|
|
273
318
|
|
|
274
319
|
test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
|
|
@@ -329,14 +374,13 @@ test('Conda installed stays false when metadata check already marked Conda inval
|
|
|
329
374
|
test('Conda check rejects metadata-only Windows installs', async () => {
|
|
330
375
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-missing-'))
|
|
331
376
|
await writeHealthyCondaMeta(root, 'win32')
|
|
332
|
-
await writeRuntimeMarker(root)
|
|
333
377
|
|
|
334
378
|
assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
|
|
335
379
|
})
|
|
336
380
|
|
|
337
|
-
test('Conda check rejects
|
|
338
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-
|
|
339
|
-
await writeHealthyCondaMeta(root, 'win32')
|
|
381
|
+
test('Conda check rejects a runnable runtime with the previous Conda version', async () => {
|
|
382
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-version-stale-'))
|
|
383
|
+
await writeHealthyCondaMeta(root, 'win32', '26.3.2')
|
|
340
384
|
await writeFakeManagedConda(root, 'win32')
|
|
341
385
|
|
|
342
386
|
assert.equal(await createConda(createKernel(root, 'win32')).check(), false)
|
|
@@ -346,24 +390,26 @@ test('Conda check accepts a runnable managed Windows conda executable', async ()
|
|
|
346
390
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-smoke-runnable-'))
|
|
347
391
|
await writeHealthyCondaMeta(root, 'win32')
|
|
348
392
|
await writeFakeManagedConda(root, 'win32')
|
|
349
|
-
await writeRuntimeMarker(root)
|
|
350
393
|
|
|
351
394
|
assert.equal(await createConda(createKernel(root, 'win32')).check(), true)
|
|
352
395
|
})
|
|
353
396
|
|
|
354
|
-
test('Bin readiness rejects
|
|
355
|
-
const
|
|
356
|
-
await writeHealthyCondaMeta(
|
|
357
|
-
await writeFakeManagedConda(
|
|
358
|
-
const
|
|
397
|
+
test('Bin readiness rejects the previous Conda version and accepts the pinned version', async () => {
|
|
398
|
+
const staleRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-bin-conda-version-stale-'))
|
|
399
|
+
await writeHealthyCondaMeta(staleRoot, 'win32', '26.3.2')
|
|
400
|
+
await writeFakeManagedConda(staleRoot, 'win32')
|
|
401
|
+
const staleBin = createBin(staleRoot, 'win32')
|
|
359
402
|
|
|
360
|
-
await
|
|
361
|
-
assert.equal(
|
|
403
|
+
await staleBin.tryList()
|
|
404
|
+
assert.equal(staleBin.correct_conda, false)
|
|
362
405
|
|
|
363
|
-
await
|
|
406
|
+
const currentRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-bin-conda-version-current-'))
|
|
407
|
+
await writeHealthyCondaMeta(currentRoot, 'win32')
|
|
408
|
+
await writeFakeManagedConda(currentRoot, 'win32')
|
|
409
|
+
const currentBin = createBin(currentRoot, 'win32')
|
|
364
410
|
|
|
365
|
-
await
|
|
366
|
-
assert.equal(
|
|
411
|
+
await currentBin.tryList()
|
|
412
|
+
assert.equal(currentBin.correct_conda, true)
|
|
367
413
|
})
|
|
368
414
|
|
|
369
415
|
test('Conda install keeps the old runtime when the replacement installer download fails', async () => {
|
|
@@ -382,10 +428,9 @@ test('Conda install keeps the old runtime when the replacement installer downloa
|
|
|
382
428
|
/download failed/
|
|
383
429
|
)
|
|
384
430
|
assert.equal(await pathExists(oldRuntimeFile), true)
|
|
385
|
-
assert.equal(await pathExists(path.join(miniforge, CONDA_RUNTIME_MARKER)), false)
|
|
386
431
|
})
|
|
387
432
|
|
|
388
|
-
test('Conda install
|
|
433
|
+
test('Conda install propagates bootstrap failure after replacing the runtime', async () => {
|
|
389
434
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-bootstrap-fails-'))
|
|
390
435
|
await createMiniforge(root)
|
|
391
436
|
const kernel = createKernel(root, 'darwin')
|
|
@@ -405,7 +450,6 @@ test('Conda install leaves the runtime unmarked when bootstrap fails after repla
|
|
|
405
450
|
createConda(kernel)._install({ dependencies: [] }, () => {}),
|
|
406
451
|
/bootstrap failed/
|
|
407
452
|
)
|
|
408
|
-
assert.equal(await pathExists(path.join(root, 'bin', 'miniforge', CONDA_RUNTIME_MARKER)), false)
|
|
409
453
|
})
|
|
410
454
|
|
|
411
455
|
test('Conda install reports manual recovery when the old runtime cannot be removed', async () => {
|