From e794f6a2ef7a634a4cf162ce347b923e736190ae Mon Sep 17 00:00:00 2001 From: leyoonafr Date: Fri, 24 Jul 2026 18:19:03 +0800 Subject: [PATCH] fix(loop): publish trusted control plane on macOS --- .../scripts/install-trusted-control-plane.mjs | 11 +- .../install-trusted-control-plane.test.mjs | 114 ++++++++++++++++++ 2 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 loops/issue-dev-loop/tests/install-trusted-control-plane.test.mjs diff --git a/loops/issue-dev-loop/scripts/install-trusted-control-plane.mjs b/loops/issue-dev-loop/scripts/install-trusted-control-plane.mjs index a27221a1..e158b48a 100644 --- a/loops/issue-dev-loop/scripts/install-trusted-control-plane.mjs +++ b/loops/issue-dev-loop/scripts/install-trusted-control-plane.mjs @@ -96,6 +96,7 @@ async function main() { } const temporaryBundleRoot = `${targetBundleRoot}.tmp-${process.pid}-${randomBytes(4).toString('hex')}` const targetLoopRoot = path.join(temporaryBundleRoot, 'issue-dev-loop') + let cleanupBundleRoot = temporaryBundleRoot try { await mkdir(targetLoopRoot, { recursive: true }) await cp(path.join(sourceLoopRoot, 'scripts'), path.join(targetLoopRoot, 'scripts'), { @@ -198,9 +199,11 @@ async function main() { } await chmod(path.join(targetLoopRoot, 'trusted-control-plane.json'), 0o444) for (const directory of (await directories(temporaryBundleRoot)).reverse()) { - await chmod(directory, 0o555) + if (directory !== temporaryBundleRoot) await chmod(directory, 0o555) } await rename(temporaryBundleRoot, targetBundleRoot) + cleanupBundleRoot = targetBundleRoot + await chmod(targetBundleRoot, 0o555) process.stdout.write( `${JSON.stringify({ bundleRoot: targetBundleRoot, @@ -210,13 +213,13 @@ async function main() { ) } catch (error) { try { - for (const directory of await directories(temporaryBundleRoot)) { + for (const directory of await directories(cleanupBundleRoot)) { await chmod(directory, 0o755) } } catch { - // Best-effort permission restoration for an interrupted pre-rename install. + // Best-effort permission restoration for an interrupted install. } - await rm(temporaryBundleRoot, { recursive: true, force: true }) + await rm(cleanupBundleRoot, { recursive: true, force: true }) throw error } } diff --git a/loops/issue-dev-loop/tests/install-trusted-control-plane.test.mjs b/loops/issue-dev-loop/tests/install-trusted-control-plane.test.mjs new file mode 100644 index 00000000..7945a065 --- /dev/null +++ b/loops/issue-dev-loop/tests/install-trusted-control-plane.test.mjs @@ -0,0 +1,114 @@ +import assert from 'node:assert/strict' +import { execFile } from 'node:child_process' +import { + chmod, + cp, + lstat, + mkdir, + mkdtemp, + readFile, + readdir, + realpath, + rm, + stat, + writeFile, +} from 'node:fs/promises' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' +import { promisify } from 'node:util' +import { fileURLToPath } from 'node:url' + +const execFileAsync = promisify(execFile) +const testDirectory = path.dirname(fileURLToPath(import.meta.url)) +const installerSource = path.resolve( + testDirectory, + '..', + 'scripts', + 'install-trusted-control-plane.mjs', +) + +async function makeWritable(target) { + let targetStat + try { + targetStat = await lstat(target) + } catch (error) { + if (error?.code === 'ENOENT') return + throw error + } + if (!targetStat.isDirectory()) { + await chmod(target, 0o644) + return + } + await chmod(target, 0o755) + for (const entry of await readdir(target)) { + await makeWritable(path.join(target, entry)) + } +} + +async function git(repositoryRoot, args) { + return execFileAsync('git', args, { cwd: repositoryRoot }) +} + +test('installer atomically publishes a read-only trusted control plane', async (context) => { + const fixtureRoot = await mkdtemp(path.join(os.tmpdir(), 'echo-ui-control-plane-install-')) + context.after(async () => { + await makeWritable(fixtureRoot) + await rm(fixtureRoot, { recursive: true, force: true }) + }) + + const repositoryRoot = path.join(fixtureRoot, 'repository') + const loopRoot = path.join(repositoryRoot, 'loops', 'issue-dev-loop') + const installer = path.join(loopRoot, 'scripts', 'install-trusted-control-plane.mjs') + const target = path.join(fixtureRoot, 'trusted-control-plane') + const canonicalTarget = path.join(await realpath(fixtureRoot), 'trusted-control-plane') + await Promise.all([ + mkdir(path.dirname(installer), { recursive: true }), + mkdir(path.join(loopRoot, 'triggers'), { recursive: true }), + mkdir(path.join(repositoryRoot, 'loops', '_shared', 'owner-channel'), { recursive: true }), + ]) + await Promise.all([ + cp(installerSource, installer), + writeFile(path.join(loopRoot, 'triggers', 'detect-work.mjs'), 'export {}\n', 'utf8'), + writeFile( + path.join(repositoryRoot, 'loops', '_shared', 'owner-channel', 'channel.json'), + '{}\n', + 'utf8', + ), + ]) + + await git(repositoryRoot, ['init', '--initial-branch=dev']) + await git(repositoryRoot, ['add', '.']) + await git(repositoryRoot, [ + '-c', + 'user.name=Echo UI Test', + '-c', + 'user.email=echo-ui-test@example.invalid', + 'commit', + '-m', + 'fixture', + ]) + const { stdout: sourceCommitOutput } = await git(repositoryRoot, ['rev-parse', 'HEAD']) + const sourceCommit = sourceCommitOutput.trim() + await git(repositoryRoot, ['update-ref', 'refs/remotes/origin/dev', sourceCommit]) + + const { stdout } = await execFileAsync(process.execPath, [installer, '--target', target], { + cwd: repositoryRoot, + }) + + const result = JSON.parse(stdout) + assert.equal(result.bundleRoot, canonicalTarget) + assert.equal(result.controlPlaneRoot, path.join(canonicalTarget, 'issue-dev-loop')) + assert.equal(result.sourceCommit, sourceCommit) + assert.equal((await stat(target)).mode & 0o777, 0o555) + assert.equal((await stat(path.join(target, 'issue-dev-loop'))).mode & 0o777, 0o555) + + const manifest = JSON.parse( + await readFile( + path.join(target, 'issue-dev-loop', 'trusted-control-plane.json'), + 'utf8', + ), + ) + assert.equal(manifest.bundleRoot, canonicalTarget) + assert.equal(manifest.sourceCommit, sourceCommit) +})