From e17a0d44c379b2ae40b91b8ba43f9a9c5bede90f Mon Sep 17 00:00:00 2001 From: Wes Date: Tue, 1 Sep 2026 11:55:42 -0600 Subject: [PATCH] fix(dev): keep the canonical profile when launching from desktop/ (#7143) ## Summary Carl, an automated agent, implementing on Wes's request. Fix ordinary Desktop dev checkouts being misclassified as linked worktrees when launched through `just production`, `staging`, or other recipes that source `instance-env.sh` from `desktop/`. From the repository root, Git reports both directories as `.git`. From `desktop/`, it can report an absolute `--git-dir` and `../.git` for `--git-common-dir`. The textual comparison treats them as different and chooses a branch-suffixed app profile (for example `.dev.main`) instead of the canonical `.dev` profile populated by the agent-adoption script. - Request `--path-format=absolute` for both paths. Actual linked worktrees still have distinct Git/common directories and retain their existing isolated app identities. - Add eight isolated fixture cases executing the real environment script with real Git: ordinary root/subdirectory on main and feature branches, linked root/subdirectory, detached linked subdirectory, and symlinked ordinary subdirectory. Paths contain spaces. Only native icon generation is stubbed; no user profile, keychain, app, or relay is accessed. - Run the regression in the existing always-run CI contract job. No agent migration, global-settings copy, keychain changes, profile merging, branch-label redesign, or packaged-release behavior changes. Existing Git `--path-format` support is required (Git 2.31+). ### Related issue Searched open PRs for `instance-env` and worktree titles, and open issues for dev-profile/adoption reports. No matching path-normalization fix found. - #7142 touches `instance-env.sh` for descriptive labels. This fix is independent; it uses a distinct test filename to avoid colliding with that PR's new `test-instance-env.sh`. - #6915 proposes a separate release-profile launch recipe; this fix preserves the current dev-profile contract instead. ### Testing At exact head `0a09a2f3716b0378546b66be2302b750ed4330b9`: - `scripts/test-desktop-instance-detection.sh`: **8/8 passed**. - Before the production change, the same regression failed on the ordinary checkout's `desktop/` case: actual `xyz.block.buzz.app.dev.main`, expected `xyz.block.buzz.app.dev`. - `bash -n scripts/instance-env.sh scripts/test-desktop-instance-detection.sh`: passed. - `git diff --check`: passed before commit; pre-commit and commit-msg hooks completed. - Applicable pre-push gates passed: branch-skew, push-head-scope, and file-size policy (6 tests plus all three entrypoints). Rust/Desktop/Mobile package lanes were correctly inapplicable to this shell/CI-only diff. No native app launch or first-boot key import was performed. The validated boundary is the generated launch environment/profile identity, not successful agent startup. Hosted CI has not been checked or represented as green. Signed-off-by: Wes Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> --- .github/workflows/ci.yml | 2 + scripts/instance-env.sh | 10 +-- scripts/test-desktop-instance-detection.sh | 78 ++++++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) create mode 100755 scripts/test-desktop-instance-detection.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0f3b0e4b2..3fe1294089 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,8 @@ jobs: run: | scripts/test-mobile-release-contract.sh scripts/test-mobile-release-candidate-publisher.sh + - name: Desktop instance environment contract + run: scripts/test-desktop-instance-detection.sh - name: Mobile worktree identity contract run: scripts/test-mobile-worktree-overrides.sh - name: Codex security review contract diff --git a/scripts/instance-env.sh b/scripts/instance-env.sh index bc185d98f1..5dbd0777da 100755 --- a/scripts/instance-env.sh +++ b/scripts/instance-env.sh @@ -32,11 +32,13 @@ unset VITE_DEV_BRANCH # identity and icon so multiple local desktop instances can run side by side. # # Worktree detection: compare --git-dir to --git-common-dir. In the main -# working tree these are identical; in any worktree (whether under .worktrees/, -# .claude/worktrees/, or elsewhere on disk) they differ. +# working tree these identify the same directory; in any linked worktree +# (whether under .worktrees/, .claude/worktrees/, or elsewhere) they differ. +# Normalize both paths: callers run from desktop/, where Git can otherwise +# return an absolute --git-dir but a relative --git-common-dir for the same .git. if git rev-parse --is-inside-work-tree &>/dev/null; then - GIT_DIR=$(git rev-parse --git-dir) - GIT_COMMON_DIR=$(git rev-parse --git-common-dir 2>/dev/null) + GIT_DIR=$(git rev-parse --path-format=absolute --git-dir) + GIT_COMMON_DIR=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) if [[ -n "$GIT_COMMON_DIR" && "$GIT_DIR" != "$GIT_COMMON_DIR" ]]; then BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) export BUZZ_WORKTREE_LABEL="${BRANCH_NAME##*/}" diff --git a/scripts/test-desktop-instance-detection.sh b/scripts/test-desktop-instance-detection.sh new file mode 100755 index 0000000000..8b4ec28043 --- /dev/null +++ b/scripts/test-desktop-instance-detection.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Exercise the real desktop launcher environment in ordinary and linked Git +# worktrees, including desktop/ (the cwd used by just production/staging/dev). +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +script="$repo_root/scripts/instance-env.sh" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +mkdir -p "$tmp/home" "$tmp/bin" + +# No inherited Git hooks/signing/repository variables, app state, or credentials. +# Real Git handles the fixtures; only native icon generation is stubbed. +clean_env() { + env -i HOME="$tmp/home" PATH="$tmp/bin:$PATH" "$@" +} +cat > "$tmp/bin/swift" <<'STUB' +#!/usr/bin/env bash +exit 0 +STUB +chmod +x "$tmp/bin/swift" + +main="$tmp/main checkout" +linked="$tmp/linked checkout" +mkdir -p "$main/desktop" +clean_env git -C "$main" init -q -b main +clean_env git -C "$main" -c user.name=Test -c user.email=test@example.com \ + -c core.hooksPath=/dev/null -c commit.gpgsign=false \ + commit -q --allow-empty -m fixture +clean_env git -C "$main" -c core.hooksPath=/dev/null \ + worktree add -q -b feature/other "$linked" +mkdir -p "$linked/desktop" + +check_instance() { + local cwd="$1" identifier="$2" label="$3" + clean_env bash -euo pipefail -c ' + cd "$1" + export BUZZ_RELAY_URL=wss://relay.example.test + source "$2" >/dev/null + python3 - "$3" "$4" <<'"'"'PY'"'"' +import json +import os +import sys + +config = json.loads(os.environ["BUZZ_TAURI_CONFIG"]) +assert config["identifier"] == sys.argv[1], config +assert os.environ.get("VITE_DEV_BRANCH", "") == sys.argv[2] +assert os.environ["BUZZ_RELAY_URL"] == "wss://relay.example.test" +assert "BUZZ_PRIVATE_KEY" not in os.environ +assert config["build"]["devUrl"] == "http://localhost:" + os.environ["BUZZ_VITE_PORT"] +PY + ' bash "$cwd" "$script" "$identifier" "$label" + printf 'ok: %s -> %s\n' "$cwd" "$identifier" +} + +# The regression: --git-dir is absolute from desktop/, while --git-common-dir +# can be ../.git. They still name the same directory in an ordinary checkout. +check_instance "$main" xyz.block.buzz.app.dev '' +check_instance "$main/desktop" xyz.block.buzz.app.dev '' + +# A branch name alone must never turn an ordinary checkout into a linked one. +clean_env git -C "$main" -c core.hooksPath=/dev/null switch -q -c feature/local +check_instance "$main" xyz.block.buzz.app.dev '' +check_instance "$main/desktop" xyz.block.buzz.app.dev '' + +# Linked worktrees must retain their existing isolated identity from either cwd. +check_instance "$linked" xyz.block.buzz.app.dev.feature-other other +check_instance "$linked/desktop" xyz.block.buzz.app.dev.feature-other other + +# Preserve the current detached-worktree identity rather than conflating it +# with the ordinary checkout. Detached naming itself is outside this fix. +clean_env git -C "$linked" -c core.hooksPath=/dev/null checkout -q --detach +check_instance "$linked/desktop" xyz.block.buzz.app.dev.head HEAD + +ln -s "$main" "$tmp/main-link" +check_instance "$tmp/main-link/desktop" xyz.block.buzz.app.dev '' + +printf 'Desktop instance environment: 8 cases passed\n'