fix(#4085): align dirty display probes

This commit is contained in:
Rod Boev committed 2026-08-01 11:16:57 -04:00
1 parent 5f8fd9d85a
commit 678a1ee926
2 files changed
+56 -21

No files matched your search

+14 -21
View File
@@ -431,21 +431,15 @@ def _dirty_suffix(path: Path, timeout=1) -> str:
out, ok = _run_git(['diff-index', '--quiet', 'HEAD', '--'], path, timeout=timeout)
if ok:
return ""
# diff-index --quiet exits 1 with no stdout/stderr to *signal* a dirty tree
# (not an error). _run_git() substitutes a synthetic "git exited with
# status N" diagnostic when both streams are empty, which makes the naive
# `if not out` guard always false on dirty trees — silently dropping the
# suffix and defeating dev-build cache busting (static/foo.js?v=… stays
# identical to the last-committed version). Treat the synthetic shape as
# the dirty signal; real errors (timeouts, missing git) carry a different
# diagnostic and correctly suppress the suffix.
if not out or out.startswith('git exited with status '):
diff, diff_ok = _run_git(['diff', '--binary', 'HEAD', '--'], path, timeout=timeout)
if diff_ok and diff:
digest = hashlib.sha1(diff.encode('utf-8', errors='replace')).hexdigest()[:8]
return f"-dirty-{digest}"
return "-dirty"
return ""
# Only diff-index status 1 means dirty. Keep version display consistent
# with the strict action-time probe; all other failures suppress the suffix.
if out != 'git exited with status 1':
return ""
diff, diff_ok = _run_git(['diff', '--binary', 'HEAD', '--'], path, timeout=timeout)
if diff_ok and diff:
digest = hashlib.sha1(diff.encode('utf-8', errors='replace')).hexdigest()[:8]
return f"-dirty-{digest}"
return "-dirty"
def _describe_git_version(path: Path, *, timeout=5, dirty_timeout=1) -> str | None:
@@ -1943,16 +1937,15 @@ def _discard_local_changes(path: Path, reset_ref: str) -> bool:
def apply_force_update(target: str, channel=None) -> dict:
"""Force-reset the target repo to the latest remote HEAD.
"""Discard local changes for the requested update target.
Unlike apply_update() which requires a clean working tree and refuses
merge conflicts, this discards all local modifications (checkout .) and
resets to origin/<branch> — equivalent to what the diverged/conflict
error messages ask the user to run manually.
resets to the selected update ref. A dirty stable WebUI checkout with no
promoted ref resets to its symbolic HEAD so the commit itself is unchanged.
Should only be called when apply_update() has already returned a
response with ``conflict: True`` or ``diverged: True`` and the user
has confirmed they want to discard local changes.
The endpoint is called after the user has confirmed they want to discard
local changes, including the stable no-ref dirty-checkout recovery path.
CHANNEL SAFETY (rewind guard): ``reset --hard`` is destructive. When the
selected channel resolves to a ref that is an ANCESTOR of HEAD (i.e. the
+42
View File
@@ -813,6 +813,48 @@ def test_run_git_handles_missing_stdout_after_decode_thread_failure(tmp_path):
assert out == ''
@pytest.mark.parametrize('probe_output', [
'git exited with status 2',
'git exited with status 128',
'git exited with status -9',
])
def test_describe_git_version_suppresses_unknown_dirty_probe_status(
tmp_path, monkeypatch, probe_output,
):
calls = []
def fake_git(args, cwd, timeout=10):
calls.append(args)
if args == ['describe', '--tags', '--always']:
return 'v0.52.5', True
if args == ['diff-index', '--quiet', 'HEAD', '--']:
return probe_output, False
raise AssertionError(f'unexpected git args: {args!r}')
monkeypatch.setattr(updates, '_run_git', fake_git)
assert updates._describe_git_version(tmp_path) == 'v0.52.5'
assert calls == [
['describe', '--tags', '--always'],
['diff-index', '--quiet', 'HEAD', '--'],
]
def test_describe_git_version_marks_exact_dirty_probe_status(tmp_path, monkeypatch):
def fake_git(args, cwd, timeout=10):
if args == ['describe', '--tags', '--always']:
return 'v0.52.5', True
if args == ['diff-index', '--quiet', 'HEAD', '--']:
return 'git exited with status 1', False
if args == ['diff', '--binary', 'HEAD', '--']:
return 'diff --git a/tracked.txt b/tracked.txt', True
raise AssertionError(f'unexpected git args: {args!r}')
monkeypatch.setattr(updates, '_run_git', fake_git)
assert updates._describe_git_version(tmp_path).startswith('v0.52.5-dirty-')
def test_split_remote_ref_splits_tracking_ref():
"""_split_remote_ref should correctly split origin/branch."""
assert updates._split_remote_ref('origin/master') == ('origin', 'master')