pinokiod 7.3.14 → 7.4.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/api/index.js +111 -15
- package/kernel/api/script/index.js +10 -0
- package/kernel/autolaunch.js +111 -0
- package/kernel/environment.js +89 -1
- package/kernel/git.js +9 -19
- package/kernel/index.js +142 -43
- package/kernel/launch_requirements.js +1115 -0
- package/kernel/ready.js +231 -0
- package/kernel/script.js +16 -0
- package/kernel/shells.js +9 -1
- package/kernel/workspace_status.js +111 -45
- package/package.json +1 -1
- package/server/autolaunch.js +725 -0
- package/server/index.js +244 -411
- package/server/public/logs.js +15 -1
- package/server/public/style.css +99 -31
- package/server/views/app.ejs +263 -159
- package/server/views/autolaunch.ejs +363 -75
- package/server/views/index.ejs +550 -26
- package/server/views/logs.ejs +14 -12
- package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
- package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
- package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
- package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
- package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
- package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
- package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
- package/server/views/partials/home_action_modal.ejs +4 -1
- package/server/views/partials/launch_requirements_status_client.ejs +271 -0
- package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
- package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
- package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
- package/server/views/terminal.ejs +196 -2
- package/test/home-autolaunch-live-ui.test.js +455 -0
- package/test/launch-requirements-browser.test.js +579 -0
- package/test/launch-requirements-contract-coverage.test.js +627 -0
- package/test/launch-requirements-real-browser.js +625 -0
- package/test/launch-requirements-status-client.test.js +132 -0
- package/test/launch-requirements.test.js +1806 -0
- package/test/launch-settings-ui.test.js +370 -0
- package/test/ready-state.test.js +49 -0
- package/test/server-autolaunch.test.js +1052 -0
- package/test/startup-git-index-benchmark.js +409 -0
- package/test/startup-git-index-browser.js +320 -0
- package/test/startup-git-index-performance.test.js +380 -0
- package/test/startup-git-index-refactor.test.js +450 -0
- package/test/startup-git-index-route.test.js +588 -0
- package/test/universal-launcher.smoke.spec.js +10 -9
- package/test/workspace-gitignore-benchmark.js +815 -0
- package/test/workspace-gitignore-path-scoped.test.js +256 -0
- package/spec/INSTRUCTION_SYNC.md +0 -432
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const { execFileSync } = require('node:child_process')
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const fsp = require('node:fs/promises')
|
|
5
|
+
const os = require('node:os')
|
|
6
|
+
const path = require('node:path')
|
|
7
|
+
const test = require('node:test')
|
|
8
|
+
|
|
9
|
+
const Api = require('../kernel/api')
|
|
10
|
+
const Git = require('../kernel/git')
|
|
11
|
+
const Shell = require('../kernel/shell')
|
|
12
|
+
const Shells = require('../kernel/shells')
|
|
13
|
+
const WorkspaceStatusManager = require('../kernel/workspace_status')
|
|
14
|
+
|
|
15
|
+
function run(cmd, args, cwd, options = {}) {
|
|
16
|
+
return execFileSync(cmd, args, {
|
|
17
|
+
cwd,
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
env: {
|
|
20
|
+
...process.env,
|
|
21
|
+
GIT_AUTHOR_NAME: 'Codex Test',
|
|
22
|
+
GIT_AUTHOR_EMAIL: 'codex-test@example.com',
|
|
23
|
+
GIT_COMMITTER_NAME: 'Codex Test',
|
|
24
|
+
GIT_COMMITTER_EMAIL: 'codex-test@example.com',
|
|
25
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
26
|
+
...options.env,
|
|
27
|
+
},
|
|
28
|
+
stdio: options.stdio || ['ignore', 'pipe', 'pipe'],
|
|
29
|
+
}).trim()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function git(args, cwd) {
|
|
33
|
+
return run('git', args, cwd)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function fileUrl(targetPath) {
|
|
37
|
+
return `file://${targetPath}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function makeTempRoot(prefix) {
|
|
41
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeLowercaseTmpRoot(name) {
|
|
45
|
+
const root = path.join('/tmp', `${name}-${process.pid}-${Date.now()}`)
|
|
46
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
47
|
+
fs.mkdirSync(root, { recursive: true })
|
|
48
|
+
return root
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createKernel(root) {
|
|
52
|
+
const kernel = {
|
|
53
|
+
homedir: root,
|
|
54
|
+
platform: 'test',
|
|
55
|
+
arch: 'test',
|
|
56
|
+
gpu_model: 'test-gpu',
|
|
57
|
+
ram: 1,
|
|
58
|
+
vram: 0,
|
|
59
|
+
envs: { ...process.env },
|
|
60
|
+
path: (...parts) => path.join(root, ...parts),
|
|
61
|
+
api: {
|
|
62
|
+
exists: async (targetPath) => {
|
|
63
|
+
try {
|
|
64
|
+
await fsp.access(targetPath)
|
|
65
|
+
return true
|
|
66
|
+
} catch (_) {
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
exec: async (params) => {
|
|
72
|
+
const messages = Array.isArray(params.message) ? params.message : [params.message]
|
|
73
|
+
for (const message of messages) {
|
|
74
|
+
run('/bin/sh', ['-lc', message], params.path || root)
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
kernel.git = new Git(kernel)
|
|
79
|
+
return kernel
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function initRepo(repoPath, { remote, file = 'tracked.txt', content = 'baseline\n', message = 'baseline' } = {}) {
|
|
83
|
+
fs.mkdirSync(repoPath, { recursive: true })
|
|
84
|
+
git(['init'], repoPath)
|
|
85
|
+
git(['config', 'user.name', 'Codex Test'], repoPath)
|
|
86
|
+
git(['config', 'user.email', 'codex-test@example.com'], repoPath)
|
|
87
|
+
fs.writeFileSync(path.join(repoPath, file), content)
|
|
88
|
+
git(['add', '.'], repoPath)
|
|
89
|
+
git(['commit', '-m', message], repoPath)
|
|
90
|
+
git(['branch', '-M', 'main'], repoPath)
|
|
91
|
+
if (remote) {
|
|
92
|
+
git(['remote', 'add', 'origin', remote], repoPath)
|
|
93
|
+
}
|
|
94
|
+
return git(['rev-parse', 'HEAD'], repoPath)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function pushWorkRepoToBare({ root, bareName, workName, file, firstContent, firstMessage, secondContent, secondMessage }) {
|
|
98
|
+
const bare = path.join(root, 'remotes', bareName)
|
|
99
|
+
const work = path.join(root, 'work', workName)
|
|
100
|
+
fs.mkdirSync(path.dirname(bare), { recursive: true })
|
|
101
|
+
fs.mkdirSync(path.dirname(work), { recursive: true })
|
|
102
|
+
git(['init', '--bare', bare], root)
|
|
103
|
+
fs.mkdirSync(work, { recursive: true })
|
|
104
|
+
git(['init'], work)
|
|
105
|
+
git(['config', 'user.name', 'Codex Test'], work)
|
|
106
|
+
git(['config', 'user.email', 'codex-test@example.com'], work)
|
|
107
|
+
fs.writeFileSync(path.join(work, file), firstContent)
|
|
108
|
+
git(['add', '.'], work)
|
|
109
|
+
git(['commit', '-m', firstMessage], work)
|
|
110
|
+
git(['branch', '-M', 'main'], work)
|
|
111
|
+
git(['remote', 'add', 'origin', fileUrl(bare)], work)
|
|
112
|
+
git(['push', '-u', 'origin', 'main'], work)
|
|
113
|
+
const firstHead = git(['rev-parse', 'HEAD'], work)
|
|
114
|
+
|
|
115
|
+
let secondHead = firstHead
|
|
116
|
+
if (secondContent != null) {
|
|
117
|
+
fs.writeFileSync(path.join(work, file), secondContent)
|
|
118
|
+
git(['commit', '-am', secondMessage || 'second'], work)
|
|
119
|
+
git(['push'], work)
|
|
120
|
+
secondHead = git(['rev-parse', 'HEAD'], work)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
bare,
|
|
125
|
+
work,
|
|
126
|
+
remote: fileUrl(bare),
|
|
127
|
+
firstHead,
|
|
128
|
+
secondHead,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function readCheckpointPayload(gitApi, saved) {
|
|
133
|
+
const payloadPath = gitApi.checkpointFilePath(saved.hash)
|
|
134
|
+
assert.ok(payloadPath, 'checkpoint hash should map to a payload path')
|
|
135
|
+
return JSON.parse(fs.readFileSync(payloadPath, 'utf8'))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
test('recursive git repo scan preserves /info/api?git compatibility for top-level, nested, and missing remotes', async () => {
|
|
139
|
+
const root = makeTempRoot('pinokio-startup-info-api-')
|
|
140
|
+
try {
|
|
141
|
+
const apiRoot = path.join(root, 'api')
|
|
142
|
+
const workspace = path.join(apiRoot, 'fixture')
|
|
143
|
+
const nested = path.join(workspace, 'nested')
|
|
144
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
145
|
+
|
|
146
|
+
const topRemote = 'https://example.com/pinokio-startup-fixture.git'
|
|
147
|
+
const nestedRemote = 'https://example.com/pinokio-startup-fixture-nested.git'
|
|
148
|
+
initRepo(workspace, { remote: topRemote, content: 'top\n', message: 'top baseline' })
|
|
149
|
+
initRepo(nested, { remote: nestedRemote, file: 'nested.txt', content: 'nested\n', message: 'nested baseline' })
|
|
150
|
+
|
|
151
|
+
const kernel = createKernel(root)
|
|
152
|
+
await kernel.git.repos(apiRoot)
|
|
153
|
+
|
|
154
|
+
const topHit = kernel.git.find(topRemote)
|
|
155
|
+
assert.ok(topHit)
|
|
156
|
+
assert.equal(topHit.path, workspace)
|
|
157
|
+
const topRepos = (await kernel.git.repos(topHit.path)).filter((repo) => repo.main)
|
|
158
|
+
assert.equal(topRepos.length, 1)
|
|
159
|
+
assert.equal(topRepos[0].url, topRemote)
|
|
160
|
+
|
|
161
|
+
const nestedHit = kernel.git.find(nestedRemote)
|
|
162
|
+
assert.ok(nestedHit)
|
|
163
|
+
assert.equal(nestedHit.path, nested)
|
|
164
|
+
const nestedRepos = (await kernel.git.repos(nestedHit.path)).filter((repo) => repo.main)
|
|
165
|
+
assert.equal(nestedRepos.length, 1)
|
|
166
|
+
assert.equal(nestedRepos[0].url, nestedRemote)
|
|
167
|
+
|
|
168
|
+
assert.equal(kernel.git.find('https://example.com/missing.git'), undefined)
|
|
169
|
+
} finally {
|
|
170
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('workspace repo scans clear stale git index locks without a startup API-root scan', async () => {
|
|
175
|
+
const root = makeTempRoot('pinokio-startup-stale-lock-')
|
|
176
|
+
try {
|
|
177
|
+
const apiRoot = path.join(root, 'api')
|
|
178
|
+
const workspace = path.join(apiRoot, 'fixture')
|
|
179
|
+
const nested = path.join(workspace, 'nested')
|
|
180
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
181
|
+
initRepo(workspace, { remote: 'https://example.com/stale-lock-root.git', content: 'top\n' })
|
|
182
|
+
initRepo(nested, { remote: 'https://example.com/stale-lock-nested.git', file: 'nested.txt', content: 'nested\n' })
|
|
183
|
+
|
|
184
|
+
const topLock = path.join(workspace, '.git', 'index.lock')
|
|
185
|
+
const nestedLock = path.join(nested, '.git', 'index.lock')
|
|
186
|
+
fs.writeFileSync(topLock, '')
|
|
187
|
+
fs.writeFileSync(nestedLock, '')
|
|
188
|
+
|
|
189
|
+
const kernel = createKernel(root)
|
|
190
|
+
await kernel.git.repos(workspace)
|
|
191
|
+
|
|
192
|
+
assert.equal(fs.existsSync(topLock), false)
|
|
193
|
+
assert.equal(fs.existsSync(nestedLock), false)
|
|
194
|
+
} finally {
|
|
195
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test('findRepoRootForPath resolves top-level and nested repo roots without prefilled git dirs', async () => {
|
|
200
|
+
const root = makeTempRoot('pinokio-startup-diff-root-')
|
|
201
|
+
try {
|
|
202
|
+
const apiRoot = path.join(root, 'api')
|
|
203
|
+
const workspace = path.join(apiRoot, 'fixture')
|
|
204
|
+
const nested = path.join(workspace, 'nested')
|
|
205
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
206
|
+
initRepo(workspace, { remote: 'https://example.com/root.git', content: 'top\n' })
|
|
207
|
+
initRepo(nested, { remote: 'https://example.com/nested.git', file: 'nested.txt', content: 'nested\n' })
|
|
208
|
+
|
|
209
|
+
const kernel = createKernel(root)
|
|
210
|
+
assert.equal(kernel.git.dirs.size, 0)
|
|
211
|
+
|
|
212
|
+
assert.equal(
|
|
213
|
+
await kernel.git.findRepoRootForPath(path.join(workspace, 'tracked.txt')),
|
|
214
|
+
workspace
|
|
215
|
+
)
|
|
216
|
+
assert.equal(
|
|
217
|
+
await kernel.git.findRepoRootForPath(path.join(nested, 'nested.txt')),
|
|
218
|
+
nested
|
|
219
|
+
)
|
|
220
|
+
} finally {
|
|
221
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('workspace status cache returns cached data, marks dirty, then refreshes the selected workspace', async () => {
|
|
226
|
+
const manager = new WorkspaceStatusManager({
|
|
227
|
+
enableWatchers: true,
|
|
228
|
+
fallbackIntervalMs: 60000,
|
|
229
|
+
})
|
|
230
|
+
let computeCount = 0
|
|
231
|
+
const computeStatus = async () => ({
|
|
232
|
+
totalChanges: computeCount++,
|
|
233
|
+
repos: [{ name: 'fixture', changeCount: computeCount }],
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
const first = await manager.getStatus('fixture', computeStatus)
|
|
237
|
+
assert.equal(first.totalChanges, 0)
|
|
238
|
+
|
|
239
|
+
const cached = await manager.getStatus('fixture', computeStatus)
|
|
240
|
+
assert.equal(cached, first)
|
|
241
|
+
assert.equal(computeCount, 1)
|
|
242
|
+
|
|
243
|
+
manager.markDirty('fixture')
|
|
244
|
+
const staleWhileRefreshing = await manager.getStatus('fixture', computeStatus)
|
|
245
|
+
assert.equal(staleWhileRefreshing, first)
|
|
246
|
+
|
|
247
|
+
const entry = manager.cache.get('fixture')
|
|
248
|
+
if (entry && entry.inflight) {
|
|
249
|
+
await entry.inflight
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const refreshed = await manager.getStatus('fixture', computeStatus)
|
|
253
|
+
assert.equal(refreshed.totalChanges, 1)
|
|
254
|
+
assert.equal(computeCount, 2)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
test('appendWorkspaceSnapshot writes the same checkpoint shape for main and nested repos', async () => {
|
|
258
|
+
const root = makeTempRoot('pinokio-startup-checkpoint-save-')
|
|
259
|
+
try {
|
|
260
|
+
const apiRoot = path.join(root, 'api')
|
|
261
|
+
const workspaceName = 'fixture'
|
|
262
|
+
const workspace = path.join(apiRoot, workspaceName)
|
|
263
|
+
const nested = path.join(workspace, 'nested')
|
|
264
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
265
|
+
|
|
266
|
+
const topRemote = 'https://example.com/checkpoint-fixture.git'
|
|
267
|
+
const nestedRemote = 'https://example.com/checkpoint-fixture-nested.git'
|
|
268
|
+
const topHead = initRepo(workspace, { remote: topRemote, content: 'top\n', message: 'top baseline' })
|
|
269
|
+
const nestedHead = initRepo(nested, { remote: nestedRemote, file: 'nested.txt', content: 'nested\n', message: 'nested baseline' })
|
|
270
|
+
|
|
271
|
+
const kernel = createKernel(root)
|
|
272
|
+
await kernel.git.loadCheckpoints()
|
|
273
|
+
const repos = await kernel.git.repos(workspace)
|
|
274
|
+
const saved = await kernel.git.appendWorkspaceSnapshot(workspaceName, repos)
|
|
275
|
+
assert.ok(saved)
|
|
276
|
+
|
|
277
|
+
const payload = readCheckpointPayload(kernel.git, saved)
|
|
278
|
+
assert.deepEqual(payload, {
|
|
279
|
+
version: 1,
|
|
280
|
+
root: 'https://example.com/checkpoint-fixture',
|
|
281
|
+
repos: [
|
|
282
|
+
{
|
|
283
|
+
path: '.',
|
|
284
|
+
repo: 'https://example.com/checkpoint-fixture',
|
|
285
|
+
commit: topHead,
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
path: 'nested',
|
|
289
|
+
repo: 'https://example.com/checkpoint-fixture-nested',
|
|
290
|
+
commit: nestedHead,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
})
|
|
294
|
+
} finally {
|
|
295
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
test('restoreNewReposForActiveSnapshot pins nested repos created after the workspace before-set', async () => {
|
|
300
|
+
const root = makeLowercaseTmpRoot('pinokio-startup-restore')
|
|
301
|
+
try {
|
|
302
|
+
const home = path.join(root, 'home')
|
|
303
|
+
const apiRoot = path.join(home, 'api')
|
|
304
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
305
|
+
|
|
306
|
+
const main = pushWorkRepoToBare({
|
|
307
|
+
root,
|
|
308
|
+
bareName: 'main.git',
|
|
309
|
+
workName: 'main-work',
|
|
310
|
+
file: 'pinokio.json',
|
|
311
|
+
firstContent: '{"title":"Restore Fixture","menu":[]}\n',
|
|
312
|
+
firstMessage: 'main baseline',
|
|
313
|
+
})
|
|
314
|
+
const nested = pushWorkRepoToBare({
|
|
315
|
+
root,
|
|
316
|
+
bareName: 'nested.git',
|
|
317
|
+
workName: 'nested-work',
|
|
318
|
+
file: 'nested.txt',
|
|
319
|
+
firstContent: 'nested pinned\n',
|
|
320
|
+
firstMessage: 'nested pinned',
|
|
321
|
+
secondContent: 'nested latest\n',
|
|
322
|
+
secondMessage: 'nested latest',
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
const workspaceName = 'restore-fixture'
|
|
326
|
+
const workspace = path.join(apiRoot, workspaceName)
|
|
327
|
+
git(['clone', main.remote, workspace], apiRoot)
|
|
328
|
+
const beforeDirs = new Set([workspace])
|
|
329
|
+
git(['clone', nested.remote, path.join(workspace, 'nested')], workspace)
|
|
330
|
+
|
|
331
|
+
const nestedHeadBefore = git(['-C', path.join(workspace, 'nested'), 'rev-parse', 'HEAD'], root)
|
|
332
|
+
assert.equal(nestedHeadBefore, nested.secondHead)
|
|
333
|
+
|
|
334
|
+
const kernel = createKernel(home)
|
|
335
|
+
await kernel.git.loadCheckpoints()
|
|
336
|
+
const snapshotId = 'restore-fixture'
|
|
337
|
+
const remoteKey = kernel.git.normalizeRemote(main.remote)
|
|
338
|
+
const saved = await kernel.git.writeCheckpointPayload(remoteKey, main.remote, {
|
|
339
|
+
id: snapshotId,
|
|
340
|
+
visibility: 'public',
|
|
341
|
+
checkpoint: {
|
|
342
|
+
root: main.remote,
|
|
343
|
+
repos: [
|
|
344
|
+
{ path: '.', remote: main.remote, commit: main.firstHead },
|
|
345
|
+
{ path: 'nested', remote: nested.remote, commit: nested.firstHead },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
assert.ok(saved)
|
|
350
|
+
|
|
351
|
+
kernel.git.activeSnapshot[workspaceName] = { id: snapshotId, remoteKey }
|
|
352
|
+
await kernel.git.restoreNewReposForActiveSnapshot(workspaceName, workspace, beforeDirs)
|
|
353
|
+
|
|
354
|
+
const nestedHeadAfter = git(['-C', path.join(workspace, 'nested'), 'rev-parse', 'HEAD'], root)
|
|
355
|
+
assert.equal(nestedHeadAfter, nested.firstHead)
|
|
356
|
+
} finally {
|
|
357
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
test('shell launch computes checkpoint restore before-set from the selected workspace, not global git dirs', async () => {
|
|
362
|
+
const root = makeTempRoot('pinokio-startup-shell-before-dirs-')
|
|
363
|
+
const originalStart = Shell.prototype.start
|
|
364
|
+
try {
|
|
365
|
+
const apiRoot = path.join(root, 'api')
|
|
366
|
+
const workspaceName = 'fixture'
|
|
367
|
+
const workspace = path.join(apiRoot, workspaceName)
|
|
368
|
+
const existingNested = path.join(workspace, 'existing-nested')
|
|
369
|
+
const unrelatedRepo = path.join(apiRoot, 'other-app')
|
|
370
|
+
fs.mkdirSync(existingNested, { recursive: true })
|
|
371
|
+
fs.mkdirSync(unrelatedRepo, { recursive: true })
|
|
372
|
+
|
|
373
|
+
const reposCalls = []
|
|
374
|
+
let restoreArgs = null
|
|
375
|
+
const kernel = {
|
|
376
|
+
homedir: root,
|
|
377
|
+
platform: 'darwin',
|
|
378
|
+
path: (...parts) => path.join(root, ...parts),
|
|
379
|
+
bin: {
|
|
380
|
+
envs: (env = {}) => env,
|
|
381
|
+
},
|
|
382
|
+
api: {
|
|
383
|
+
running: {},
|
|
384
|
+
resolvePath: (cwd, targetPath) => path.resolve(cwd, targetPath),
|
|
385
|
+
},
|
|
386
|
+
git: {
|
|
387
|
+
dirs: new Set([unrelatedRepo, workspace]),
|
|
388
|
+
repos: async (scanRoot) => {
|
|
389
|
+
reposCalls.push(scanRoot)
|
|
390
|
+
return [
|
|
391
|
+
{ gitParentPath: workspace },
|
|
392
|
+
{ gitParentPath: existingNested },
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
restoreNewReposForActiveSnapshot: async (name, restoreRoot, beforeDirs) => {
|
|
396
|
+
restoreArgs = { name, restoreRoot, beforeDirs }
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
const shells = new Shells(kernel)
|
|
401
|
+
shells.ensureBracketedPasteSupport = async () => true
|
|
402
|
+
Shell.prototype.start = async () => ''
|
|
403
|
+
|
|
404
|
+
await shells.launch({
|
|
405
|
+
message: 'noop',
|
|
406
|
+
path: path.join(workspace, 'scripts'),
|
|
407
|
+
}, {}, () => {})
|
|
408
|
+
|
|
409
|
+
assert.deepEqual(reposCalls, [workspace])
|
|
410
|
+
assert.ok(restoreArgs)
|
|
411
|
+
assert.equal(restoreArgs.name, workspaceName)
|
|
412
|
+
assert.equal(restoreArgs.restoreRoot, workspace)
|
|
413
|
+
assert.deepEqual(
|
|
414
|
+
Array.from(restoreArgs.beforeDirs).sort(),
|
|
415
|
+
[workspace, existingNested].sort()
|
|
416
|
+
)
|
|
417
|
+
assert.equal(restoreArgs.beforeDirs.has(unrelatedRepo), false)
|
|
418
|
+
} finally {
|
|
419
|
+
Shell.prototype.start = originalStart
|
|
420
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
421
|
+
}
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
test('api.linkGit drops deleted top-level app remotes after an explicit refresh', async () => {
|
|
425
|
+
const root = makeTempRoot('pinokio-startup-linkgit-delete-')
|
|
426
|
+
try {
|
|
427
|
+
const apiRoot = path.join(root, 'api')
|
|
428
|
+
const workspace = path.join(apiRoot, 'delete-me')
|
|
429
|
+
fs.mkdirSync(apiRoot, { recursive: true })
|
|
430
|
+
const remote = 'https://example.com/delete-me.git'
|
|
431
|
+
initRepo(workspace, { remote, content: 'delete me\n' })
|
|
432
|
+
|
|
433
|
+
const api = new Api({
|
|
434
|
+
homedir: root,
|
|
435
|
+
path: (...parts) => path.join(root, ...parts),
|
|
436
|
+
})
|
|
437
|
+
api.userdir = apiRoot
|
|
438
|
+
|
|
439
|
+
await api.linkGit()
|
|
440
|
+
assert.deepEqual(api.gitPath, {
|
|
441
|
+
[remote]: workspace,
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
fs.rmSync(workspace, { recursive: true, force: true })
|
|
445
|
+
await api.linkGit()
|
|
446
|
+
assert.deepEqual(api.gitPath, {})
|
|
447
|
+
} finally {
|
|
448
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
449
|
+
}
|
|
450
|
+
})
|