pinokiod 8.0.3 → 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.js +19 -1
- package/package.json +1 -1
- package/test/caddy-install.test.js +65 -0
- package/test/conda-bin.test.js +54 -0
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.js
CHANGED
|
@@ -170,6 +170,16 @@ report_errors: false`)
|
|
|
170
170
|
ondata({ raw: `downloading installer: ${installer_url}...\r\n` })
|
|
171
171
|
await this.kernel.bin.download(installer_url, installer, ondata)
|
|
172
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
|
+
|
|
173
183
|
legacy_path_exists = await this.kernel.exists(`bin/${LEGACY_CONDA_ROOT_DIR}`)
|
|
174
184
|
if (legacy_path_exists) {
|
|
175
185
|
console.log("Removing legacy install path...", legacy_path)
|
|
@@ -202,8 +212,12 @@ report_errors: false`)
|
|
|
202
212
|
await fs.promises.writeFile(this.kernel.path(`bin/${CONDA_ROOT_DIR}/conda-meta/pinned`), this.pinnedPackages())
|
|
203
213
|
}
|
|
204
214
|
|
|
215
|
+
const dependencies = new Set(Array.isArray(req.dependencies) ? req.dependencies : [])
|
|
216
|
+
if (caddyWasRunning) {
|
|
217
|
+
dependencies.add("caddy")
|
|
218
|
+
}
|
|
205
219
|
let mods = this.kernel.bin.mods.filter((m) => {
|
|
206
|
-
return
|
|
220
|
+
return dependencies.has(m.name)
|
|
207
221
|
}).map((m) => {
|
|
208
222
|
if (m.mod.cmd) {
|
|
209
223
|
return m.mod.cmd()
|
|
@@ -241,6 +255,10 @@ report_errors: false`)
|
|
|
241
255
|
await this.ensureCompatibilityAlias()
|
|
242
256
|
ondata({ raw: `Install finished\r\n` })
|
|
243
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
|
+
}
|
|
244
262
|
}
|
|
245
263
|
async removeInstallPath(target) {
|
|
246
264
|
try {
|
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
|
@@ -262,6 +262,60 @@ test('Conda install replaces the stale runtime and rebuilds declared Conda modul
|
|
|
262
262
|
)
|
|
263
263
|
})
|
|
264
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)
|
|
317
|
+
})
|
|
318
|
+
|
|
265
319
|
test('Conda install replaces legacy miniconda with a compatibility alias to miniforge', async () => {
|
|
266
320
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-miniforge-alias-'))
|
|
267
321
|
const legacy = await createLegacyMiniconda(root)
|