-
Notifications
You must be signed in to change notification settings - Fork 0
Fix Mac workspace sync for guest symlinks #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -939,7 +939,7 @@ export function migrate(): void { | |
| const platformBackend = process.platform === "darwin" ? "apple" : "oci"; | ||
| const configuredBackend = String(process.env.HELM_CHANNEL_COMPUTER_BACKEND || platformBackend); | ||
| const backend = ["apple", "oci", "native", "mock"].includes(configuredBackend) ? configuredBackend : platformBackend; | ||
| const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.31"); | ||
| const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.32"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 5 \
'DEFAULT_CHANNEL_IMAGE|HELM_CHANNEL_MACHINE_IMAGE|INSERT OR IGNORE INTO channel_computers|UPDATE channel_computers.*image|reprovision|repair' \
src testRepository: gitcommit90/1Helm Length of output: 38056 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- db.ts outline and initialization region ---'
ast-grep outline src/server/db.ts --match 'function $_($$$) { $$$ }' --view summary | head -200
sed -n '700,780p' src/server/db.ts
sed -n '900,980p' src/server/db.ts
printf '%s\n' '--- channel-computers.ts outline ---'
ast-grep outline src/server/channel-computers.ts --match 'function $_($$$) { $$$ }' --view summary | head -260
printf '%s\n' '--- candidate lifecycle and migration code ---'
rg -n -C 8 \
'channelComputer\(|ensureChannelComputer|reconcileChannelComputers|provision_status|computer\.image|image: computer|image\)|CREATE TABLE.*channel_computers|ALTER TABLE.*channel_computers|PRAGMA user_version|schema|migration' \
src/server/channel-computers.ts src/server/db.ts src/server/*.tsRepository: gitcommit90/1Helm Length of output: 354 🏁 Script executed: #!/usr/bin/env bash
set -u
printf '%s\n' '--- db.ts initialization region ---'
sed -n '700,780p' src/server/db.ts
sed -n '900,980p' src/server/db.ts
printf '%s\n' '--- channel-computers.ts declarations ---'
ast-grep outline src/server/channel-computers.ts --view signatures | head -260
printf '%s\n' '--- lifecycle and migration references ---'
rg -n -C 8 \
'channelComputer\(|ensureChannelComputer|reconcileChannelComputers|provision_status|computer\.image|image: computer|CREATE TABLE.*channel_computers|ALTER TABLE.*channel_computers|PRAGMA user_version|migration|schema' \
src/server/channel-computers.ts src/server/db.ts src/server/*.tsRepository: gitcommit90/1Helm Length of output: 50374 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- record creation and provisioning ---'
sed -n '120,230p' src/server/channel-computers.ts
sed -n '583,805p' src/server/channel-computers.ts
printf '%s\n' '--- reconciliation and update paths ---'
sed -n '1310,1515p' src/server/channel-computers.ts
printf '%s\n' '--- all channel_computers image writes and image consumers ---'
rg -n -C 3 \
'channel_computers.*image|SET[^"\n]*image|image[^"\n]*channel_computers|ensureAppleProvisioned|ensureOciProvisioned|provisionChannelComputer|reconcileOne' \
src/server/db.ts src/server/channel-computers.tsRepository: gitcommit90/1Helm Length of output: 43491 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE channel_computers (
channel_id INTEGER PRIMARY KEY,
image TEXT NOT NULL
);
INSERT INTO channel_computers VALUES (7, 'local/1helm-channel-machine:0.0.31');
""")
db.execute(
"""INSERT OR IGNORE INTO channel_computers(channel_id, image)
VALUES (?, ?)""",
(7, "local/1helm-channel-machine:0.0.32"),
)
row = db.execute(
"SELECT channel_id, image FROM channel_computers WHERE channel_id=7"
).fetchone()
print({"after_insert_or_ignore": row})
db.execute(
"UPDATE channel_computers SET image=? WHERE channel_id=?",
("local/1helm-channel-machine:0.0.32", 7),
)
row = db.execute(
"SELECT channel_id, image FROM channel_computers WHERE channel_id=7"
).fetchone()
print({"after_explicit_update": row})
PY
printf '%s\n' '--- image versions and machine replacement controls ---'
rg -n -C 3 \
'0\.0\.3[01-9]|image-contract|machine (create|delete|rm|stop|run)|container (create|rm|stop|start)|DELETE FROM channel_computers|UPDATE channel_computers SET[^;]*image' \
src container scripts testRepository: gitcommit90/1Helm Length of output: 17475 Migrate existing channel computers to image
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| for (const channel of q(`SELECT c.id FROM channels c JOIN agent_channels ac ON ac.channel_id=c.id | ||
| WHERE c.kind='channel' AND c.status<>'deleted'`)) { | ||
| const channelId = Number(channel.id); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,6 +102,13 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive | |
| assert.equal(readFileSync(join(dataDir, "channels", String(alpha.channelId), "workspace", "name with spaces.txt"), "utf8"), "semi; quote ' and space", "guest command quoting survives Apple's login-shell handoff"); | ||
| assert.equal(existsSync(join(dataDir, "channels", String(alpha.channelId), "files", "guest.txt")), false, "guest deletions do not resurrect stale Files entries"); | ||
|
|
||
| result = await computers.runChannelCommand(alpha.channelId, "mkdir -p .venv/bin; ln -s /usr/bin/python3 .venv/bin/python3; ln -s python3 .venv/bin/python; printf venv-ok > .venv/marker"); | ||
| assert.equal(result.exit_code, 0, "a normal Python-style venv does not make the command fail during mirror sync"); | ||
| assert.equal(readFileSync(join(dataDir, "channels", String(alpha.channelId), "workspace", ".venv", "marker"), "utf8"), "venv-ok"); | ||
| assert.equal(existsSync(join(dataDir, "channels", String(alpha.channelId), "workspace", ".venv", "bin", "python3")), false, "guest symlinks stay in the VM instead of crossing the host mirror boundary"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
node <<'NODE'
const { existsSync, lstatSync, mkdtempSync, rmSync, symlinkSync } = require("node:fs");
const { join } = require("node:path");
const { tmpdir } = require("node:os");
const dir = mkdtempSync(join(tmpdir(), "symlink-test-"));
const link = join(dir, "python3");
try {
symlinkSync("/definitely-missing-python3", link);
if (existsSync(link)) throw new Error("existsSync unexpectedly returned true");
if (!lstatSync(link).isSymbolicLink()) throw new Error("lstatSync did not detect the symlink");
} finally {
rmSync(dir, { recursive: true, force: true });
}
NODERepository: gitcommit90/1Helm Length of output: 155 🏁 Script executed: sed -n '1,125p' test/channel-computers.mjsRepository: gitcommit90/1Helm Length of output: 10019 🏁 Script executed: rg -n -C 5 'symlink|mirror|lstat|existsSync|workspace' src test/fake-container.mjsRepository: gitcommit90/1Helm Length of output: 50374 🏁 Script executed: fd -i 'channel-computers|fake-container' src testRepository: gitcommit90/1Helm Length of output: 324 🏁 Script executed: rg -n -C 8 'sync|mirror|symlink|workspace|files' src/server/channel-computers.ts test/fake-container.mjsRepository: gitcommit90/1Helm Length of output: 50374 🏁 Script executed: sed -n '808,980p' src/server/channel-computers.tsRepository: gitcommit90/1Helm Length of output: 11571 🏁 Script executed: rg -n -A 90 'function syncGuestToHostUnlocked|validateMirrorTree' src/server/channel-computers.tsRepository: gitcommit90/1Helm Length of output: 18663 Inspect the directory entry instead of following the symlink.
🤖 Prompt for AI Agents |
||
| result = await computers.runChannelCommand(alpha.channelId, "test -L .venv/bin/python3 && readlink .venv/bin/python3"); | ||
| assert.match(result.output, /\/usr\/bin\/python3/, "the venv symlink remains durable and usable inside the guest"); | ||
|
|
||
| computers.upsertObligation(alpha.channelId, "command", "held", "resident", "active work"); | ||
| await computers.stopChannelComputer(alpha.channelId, "idle"); | ||
| assert.equal(db.q1("SELECT observed_state FROM channel_computers WHERE channel_id=?", alpha.channelId).observed_state, "running", "resident work prevents idle sleep"); | ||
|
|
@@ -167,7 +174,7 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive | |
| test("runtime digest and packaged image recipe stay pinned", async () => { | ||
| assert.equal(computers.APPLE_RUNTIME_SHA256, "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714"); | ||
| assert.match(computers.APPLE_RUNTIME_URL, /\/1\.1\.0\/container-1\.1\.0-installer-signed\.pkg$/); | ||
| assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.31"); | ||
| assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.32"); | ||
| const packaging = await readFile(join(root, "scripts", "package-mac-dmg.cjs"), "utf8"); | ||
| assert.match(packaging, /container\(\?:\$\|\\\/\)/, "release packaging includes container/ image assets"); | ||
| const image = await readFile(join(root, "container", "Containerfile"), "utf8"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: gitcommit90/1Helm
Length of output: 192
🏁 Script executed:
Repository: gitcommit90/1Helm
Length of output: 50373
🏁 Script executed:
Repository: gitcommit90/1Helm
Length of output: 231
🏁 Script executed:
Repository: gitcommit90/1Helm
Length of output: 15962
Make symlink exclusion atomic with the archive.
find -Ppasses paths to a separatetarprocess while active guest commands and terminals can modify/workspace. If a guest process replaces an emitted directory with a symlink,tar --no-recursion -Tcan archive data outside/workspace.validateMirrorTreecannot detect the source inode thattarread.Use a read-only snapshot or quiesce guest writes during export. Add a race regression test.
🤖 Prompt for AI Agents