Update from the live installed Git revision

This commit is contained in:
firestar5683
2026-10-30 13:18:48 -05:00
parent bf0243f41b
commit ebe7f28aae
2 changed files with 62 additions and 17 deletions
@@ -271,7 +271,8 @@ class SoftwareOperations:
status["updater"]["lastSuccessAt"] != self._baseline[0]:
if request["action"] in SOURCE_ACTIONS:
if status["updater"]["lastFetchAt"] != self._baseline[1]:
restarting = self._flag("DoReboot") is True or status["installed"]["commit"] != self._fast_commit
installed = self.git_identity(self.installed)
restarting = self._flag("DoReboot") is True or installed is not None and installed[1] != self._fast_commit
if request['action'] == 'versions':
request.update(state='complete', error=None)
else:
@@ -453,12 +454,11 @@ class SoftwareOperations:
(branch != view["selectedTarget"] or not view["canDownload"])):
raise SoftwareOperationError("Selected updater target changed", 409)
if action in SOURCE_ACTIONS:
installed = self.status.snapshot()["installed"]
identity = self.git_identity(self.installed)
if (not view["canFastUpdate"] or identity is None or identity != (installed["branch"], installed["commit"]) or
(action in ("update", "fast", "versions", "version") and branch != installed["branch"] and branch not in view["availableBranches"]) or
(action == "rollback" and (branch != installed["branch"] or not view["canRollback"]))):
raise SoftwareOperationError("Installed branch changed; refresh before updating", 409)
if (identity is None or not self._valid_branch(identity[0]) or
(action in ("update", "fast", "versions", "version") and branch != identity[0] and branch not in view["availableBranches"]) or
(action == "rollback" and (branch != identity[0] or not view["canRollback"]))):
raise SoftwareOperationError("Installed checkout or update target is unavailable", 409)
if action == 'version':
target = payload['selectedCommit']
recent = view['history']['recent']
@@ -472,8 +472,7 @@ class SoftwareOperations:
current = self.status.snapshot()
if (current["updater"]["state"] != "idle" or self._flag("DisableUpdates") is not False or
self._flag("DoReboot") is not False or not self.process.available() or
(action not in SOURCE_ACTIONS and branch is not None and branch not in self._branches()) or
(action == "rollback" and current["installed"]["branch"] != branch)):
(action not in SOURCE_ACTIONS and branch is not None and branch not in self._branches())):
raise SoftwareOperationError("Updater state changed", 409)
if action == "select":
self.params.put("UpdaterTargetBranch", branch, block=True)
@@ -494,12 +493,12 @@ class SoftwareOperations:
if not authorized() or not self._parked():
raise SoftwareOperationError("Session or parked state changed", 403)
if action in SOURCE_ACTIONS:
identity = self.git_identity(self.installed)
if identity is None or identity != (status["installed"]["branch"], status["installed"]["commit"]):
raise SoftwareOperationError("Installed branch changed; refresh before updating", 409)
latest_identity = self.git_identity(self.installed)
if latest_identity is None or latest_identity != identity:
raise SoftwareOperationError("Installed checkout changed while preparing the update; retry when it finishes", 409)
if action == 'version' and identity[1] != payload['expectedCommit']:
raise SoftwareOperationError('Installed revision changed before version request; refresh and try again', 409)
self._fast_commit = status["installed"]["commit"]
self._fast_commit = identity[1]
if action in SOURCE_ACTIONS:
if action == 'version':
accepted_owner = self.process.send(action, branch=branch, commit=identity[1], selected_commit=payload['selectedCommit'])
@@ -481,14 +481,60 @@ class SoftwareOperationsTest(unittest.TestCase):
self.act("fast", "main")
self.assertEqual(self.process.sent, ["fast"])
def test_fast_rejects_stale_installed_identity(self):
def test_fast_refreshes_stale_cached_identity_without_rewriting_running_metadata(self):
self.git[self.installed] = ("main", "b" * 40)
self.denied("fast", 409, "main")
self.git[self.installed] = ("release", "a" * 40)
self.denied("fast", 409, "main")
self.assertEqual(self.process.sent, [])
self.act("fast", "main")
self.assertEqual(self.process.fast_requests, [{"branch": "main", "commit": "b" * 40}])
self.assertEqual(self.params.get("GitCommit"), "a" * 40)
self.assertEqual(self.owner._fast_commit, "b" * 40)
self.params.put("LastUpdateTime", datetime(2026, 10, 30, 12), block=True)
self.params.put("UpdaterLastFetchTime", datetime(2026, 10, 30, 12), block=True)
self.assertEqual(self.owner.snapshot()["request"]["outcome"], "up_to_date")
self.assertFalse(self.params.get_bool("DoReboot"))
def test_changed_cached_branch_does_not_block_requested_live_branch(self):
self.git[self.installed] = ("release", "b" * 40)
self.params.remove("UpdaterAvailableBranches")
self.act("update", "release")
self.assertEqual(self.process.fast_requests, [{"branch": "release", "commit": "b" * 40}])
self.assertEqual(self.params.get("GitBranch"), "main")
def test_real_git_commit_after_manager_start_can_update(self):
import subprocess
from openpilot.starpilot.galaxy.software_operations import _git_identity
def git(*args):
return subprocess.check_output(["git", "-c", "user.name=Updater Test", "-c", "user.email=test@example.invalid",
"-c", "core.hooksPath=/dev/null", "-C", str(self.installed), *args], text=True).strip()
git("init", "-b", "main")
git("commit", "--allow-empty", "-m", "First")
previous = git("rev-parse", "HEAD")
self.params.put("GitCommit", previous, block=True)
git("commit", "--allow-empty", "-m", "Hotfix")
current = git("rev-parse", "HEAD")
self.owner.git_identity = _git_identity
self.act("fast", "main")
self.assertEqual(self.process.fast_requests, [{"branch": "main", "commit": current}])
self.assertEqual(self.params.get("GitCommit"), previous)
def test_actual_checkout_race_still_prevents_dispatch(self):
calls = 0
def authorized():
nonlocal calls
calls += 1
if calls == 3:
self.git[self.installed] = ("main", "b" * 40)
return True
with self.assertRaises(SoftwareOperationError):
self.owner.action("fast", {"action": "fast", "branch": "main"}, authorized=authorized)
self.assertEqual(self.process.sent, [])
def test_missing_live_checkout_and_unknown_target_still_reject(self):
self.git[self.installed] = None
self.denied("fast", 409, "main")
self.git[self.installed] = ("main", "b" * 40)
self.denied("fast", 409, "unknown-branch")
self.assertEqual(self.process.sent, [])
def test_fast_keeps_existing_admission_and_observes_failure(self):
for key in ("DisableUpdates", "DoReboot"):
self.params.put_bool(key, True, block=True)