test(install-e2e): accept the current checker's countless update offer and the macOS installer marker
The current source checker reports updateAvailable with behind null when GitHub compare cannot count staged commits; the app-update predicate demanded an integer behind and refused every HEAD->NEXT leg. Require behind > 0 only for the historical shape without updateAvailable. v2026.6.19's DMG bootstrap runs the same install.sh that writes .install_method, so its macOS leg saw a dirty tree. Share the Linux driver's guarded exclude through source-driver.sh and apply it before every app-driven macOS update.
This commit is contained in:
@@ -74,7 +74,8 @@ test('checks the live Desktop bridge against the staged target before opening Ab
|
||||
const f = fixture({ statusOverride: { supported: true, branch: 'main', currentSha: 'b'.repeat(40), targetSha: sha, behind: 1, dirty: false } })
|
||||
await updateUi.assertStagedBranch(f.page, sha, f.log)
|
||||
expect(f.log).toHaveBeenCalledWith(expect.stringContaining('"targetSha"'))
|
||||
const current = fixture({ statusOverride: { supported: true, branch: 'main', currentSha: 'b'.repeat(40), targetSha: sha, behind: 1, updateAvailable: true } })
|
||||
// The current checker's hosted shape: it cannot count staged commits via GitHub compare.
|
||||
const current = fixture({ statusOverride: { supported: true, branch: 'main', currentSha: 'b'.repeat(40), targetSha: sha, behind: null, commits: [], updateAvailable: true } })
|
||||
await updateUi.assertStagedBranch(current.page, sha, current.log)
|
||||
const racing = fixture()
|
||||
let checks = 0
|
||||
@@ -90,6 +91,7 @@ test('checks the live Desktop bridge against the staged target before opening Ab
|
||||
{ supported: true, branch: 'main', targetSha: sha, behind: 0 },
|
||||
{ supported: true, branch: 'main', currentSha: sha, targetSha: sha, behind: 1 },
|
||||
{ supported: true, branch: 'main', targetSha: sha, behind: 1, updateAvailable: false },
|
||||
{ supported: true, branch: 'main', currentSha: 'b'.repeat(40), targetSha: sha, behind: null },
|
||||
]) {
|
||||
const refused = fixture({ statusOverride })
|
||||
await expect(updateUi.assertStagedBranch(refused.page, sha, refused.log)).rejects.toThrow(/refusing to click/)
|
||||
|
||||
@@ -20,6 +20,21 @@ source_hermes() {
|
||||
printf '%s\n' "$command"
|
||||
}
|
||||
|
||||
# v2026.6.19's install.sh (also run by its DMG bootstrap) writes .install_method
|
||||
# into the checkout without ignoring it, and that release's Desktop checker
|
||||
# reports git status --porcelain verbatim. Teach only this disposable clone that
|
||||
# the installer's own marker is not a source edit; keep the marker for
|
||||
# install-method detection and refuse any other dirty state.
|
||||
accept_installer_marker() {
|
||||
local root="$1" status
|
||||
status="$(git -C "$root" status --porcelain --untracked-files=all)" || return 1
|
||||
if [ "$status" = '?? .install_method' ] && [ "$(cat "$root/.install_method")" = git ]; then
|
||||
printf '\n/.install_method\n' >> "$(git -C "$root" rev-parse --absolute-git-dir)/info/exclude"
|
||||
status="$(git -C "$root" status --porcelain --untracked-files=all)" || return 1
|
||||
fi
|
||||
[ -z "$status" ] || { printf 'installed source has changes other than the installer marker:\n%s\n' "$status" >&2; return 1; }
|
||||
}
|
||||
|
||||
# Hand out a command to DRIVE the next ordinary startup, even when the
|
||||
# published launcher is not there yet.
|
||||
#
|
||||
|
||||
@@ -90,12 +90,16 @@ async function assertStagedBranch(page, expectedSha, log) {
|
||||
await page.waitForTimeout(1_000)
|
||||
}
|
||||
// Historical Desktop status has no updateAvailable field: its About/overlay
|
||||
// offers the button when behind > 0. Never accept an explicit false from a
|
||||
// newer checker, a dirty source tree, or merely a matching remote tip.
|
||||
// offers the button when behind > 0. A newer checker states updateAvailable
|
||||
// and leaves behind null when it cannot count (GitHub compare does not know a
|
||||
// staged commit). Never accept an explicit false, a dirty source tree, or
|
||||
// merely a matching remote tip.
|
||||
const offered = status.updateAvailable === undefined
|
||||
? Number.isInteger(status.behind) && status.behind > 0
|
||||
: status.updateAvailable === true
|
||||
if (status.supported !== true || status.error || status.dirty === true ||
|
||||
status.branch !== 'main' || status.targetSha !== expectedSha ||
|
||||
!Number.isInteger(status.behind) || status.behind <= 0 ||
|
||||
status.currentSha === expectedSha || status.updateAvailable === false) {
|
||||
status.currentSha === expectedSha || !offered) {
|
||||
throw new Error('Desktop source check did not offer staged Git main; refusing to click an unrelated update')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,18 +452,7 @@ case "$UPDATE_METHOD" in
|
||||
# fixup) runs for real in the installed code.
|
||||
EXPECT_DESKTOP=present
|
||||
HERMES="$(source_hermes "$INSTALL_DIR")" || fail "no installed desktop command"
|
||||
# v2026.6.19's installer writes this marker into its checkout without
|
||||
# ignoring it. Its Electron checker reports git status --porcelain verbatim;
|
||||
# teach only this disposable clone that the installer's own state is not
|
||||
# a source edit. Keep the marker for install-method detection and refuse
|
||||
# any other dirty state rather than masking it for the GUI update.
|
||||
marker_status="$(git -C "$INSTALL_DIR" status --porcelain --untracked-files=all)"
|
||||
if [ "$marker_status" = '?? .install_method' ] &&
|
||||
[ "$(cat "$INSTALL_DIR/.install_method")" = git ]; then
|
||||
printf '\n/.install_method\n' >> "$(git -C "$INSTALL_DIR" rev-parse --absolute-git-dir)/info/exclude"
|
||||
ok "ignored only the installer's generated .install_method in local Git exclude"
|
||||
fi
|
||||
[ -z "$(git -C "$INSTALL_DIR" status --porcelain --untracked-files=all)" ] \
|
||||
accept_installer_marker "$INSTALL_DIR" \
|
||||
|| fail "installed source has changes other than the generated install marker"
|
||||
ASSETS="$REPO_ROOT/tests/install/e2e-assets"
|
||||
SPEC="$WORK_ROOT/launch-spec.json"
|
||||
|
||||
@@ -271,6 +271,8 @@ run_playwright_update() {
|
||||
# $1: spec file to launch from.
|
||||
local spec="$1"
|
||||
local rc=0
|
||||
accept_installer_marker "$INSTALL_DIR" \
|
||||
|| fail "installed source has changes other than the generated install marker"
|
||||
(cd "$WORK_ROOT" && "$HERMES_E2E_NODE" "$ASSETS/launch-from-spec.mjs" \
|
||||
--spec "$spec" \
|
||||
--old-sha "$OLD_SHA" --chat-out "$LOG_DIR/update-window" --mock-url "$HERMES_E2E_MOCK_URL" \
|
||||
|
||||
@@ -47,6 +47,31 @@ if source_hermes "$INSTALL_DIR"; then exit 92; fi
|
||||
assert result.stdout.splitlines() == ["published:literal argument", "legacy"]
|
||||
|
||||
|
||||
@pytest.mark.platforms("posix")
|
||||
def test_installer_marker_is_the_only_dirty_state_a_driver_accepts(tmp_path):
|
||||
root = tmp_path / "installed source"
|
||||
root.mkdir()
|
||||
git = ["git", "-C", str(root), "-c", "user.name=t", "-c", "user.email=t@t"]
|
||||
subprocess.run([*git, "init", "-q"], check=True)
|
||||
(root / "tracked").write_text("v1\n", encoding="utf-8")
|
||||
subprocess.run([*git, "add", "tracked"], check=True)
|
||||
subprocess.run([*git, "commit", "-qm", "base"], check=True)
|
||||
(root / ".install_method").write_text("git\n", encoding="utf-8")
|
||||
env = dict(os.environ, INSTALL_DIR=str(root), ASSETS=str(ASSETS), HOME=str(tmp_path))
|
||||
|
||||
def accept():
|
||||
return subprocess.run(["bash", "-euc", 'source "$ASSETS/source-driver.sh"; accept_installer_marker "$INSTALL_DIR"'],
|
||||
env=env, capture_output=True, text=True, timeout=30)
|
||||
|
||||
assert accept().returncode == 0
|
||||
assert (root / ".install_method").read_text(encoding="utf-8") == "git\n"
|
||||
assert subprocess.run([*git, "status", "--porcelain", "--untracked-files=all"],
|
||||
capture_output=True, text=True, check=True).stdout == ""
|
||||
(root / "tracked").write_text("edited\n", encoding="utf-8")
|
||||
refused = accept()
|
||||
assert refused.returncode != 0 and "M tracked" in refused.stderr
|
||||
|
||||
|
||||
@pytest.mark.platforms("windows", "posix")
|
||||
def test_powershell_selects_exact_exe_or_cmd_and_legacy_fallback(tmp_path):
|
||||
# These are file-selection rules, not Windows execution emulation. Run
|
||||
|
||||
Reference in New Issue
Block a user