mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-02 20:13:53 +08:00
Restore fast Galaxy software updates
This commit is contained in:
@@ -180,8 +180,20 @@ def fast_update(repo, branch, *, params, parked, expected_commit, current_os=Non
|
||||
if not valid_receipt(git, target) and revision_digest(git, previous) != revision_digest(git, target):
|
||||
raise FastUpdateIncompatible('Native build inputs or artifacts changed without a matching build receipt; use a full validated update')
|
||||
tracked = set(git('ls-tree', '-rz', '--name-only', target).split('\0')) - {''}
|
||||
untracked = set(git('ls-files', '--others', '-z').split('\0')) - {''}
|
||||
directories = {str(parent) for name in tracked for parent in Path(name).parents if str(parent) != '.'}
|
||||
indexed = set(git('ls-files', '-z').split('\0')) - {''}
|
||||
candidates = tracked - indexed
|
||||
for name in tuple(candidates):
|
||||
for parent in Path(name).parents:
|
||||
if str(parent) != '.' and str(parent) not in indexed:
|
||||
local = repo / parent
|
||||
if local.is_symlink() or local.exists() and not local.is_dir():
|
||||
candidates.add(str(parent))
|
||||
paths = sorted(candidates)
|
||||
untracked = set()
|
||||
for offset in range(0, len(paths), 256):
|
||||
untracked.update(git('--literal-pathspecs', 'ls-files', '--others', '-z', '--', *paths[offset:offset + 256]).split('\0'))
|
||||
untracked.discard('')
|
||||
directories = {str(parent) for name in tracked for parent in Path(name).parents if str(parent) != '.'} if untracked else set()
|
||||
for name in untracked:
|
||||
if name in tracked or name in directories or any(str(parent) in tracked for parent in Path(name).parents):
|
||||
raise FastUpdateError('Update would overwrite untracked data')
|
||||
@@ -218,7 +230,7 @@ def fast_update(repo, branch, *, params, parked, expected_commit, current_os=Non
|
||||
raise FastUpdateError('Parked state changed before restart')
|
||||
notify('submodules', 'No submodules configured; dependencies are vendored in this release.')
|
||||
notify('rebooting', 'Update complete. Device is rebooting; please wait for reconnection.')
|
||||
(sleep or time.sleep)(6.0)
|
||||
(sleep or time.sleep)(0.25)
|
||||
if not parked():
|
||||
raise FastUpdateError('Parked state changed before restart')
|
||||
notify('complete', 'Update installed. Device is restarting; please wait for reconnection.')
|
||||
|
||||
@@ -435,7 +435,7 @@ class TestFastUpdate(unittest.TestCase):
|
||||
self.assertEqual(self.git(self.repo, 'symbolic-ref', '--short', 'HEAD'), 'Other')
|
||||
self.assertEqual(self.git(self.repo, 'rev-parse', 'HEAD'), other)
|
||||
self.assertEqual(self.git(self.repo, 'config', '--get', 'starpilot.previousVersion'), f'{self.previous} Dom')
|
||||
self.assertIn(('sleep', 6.0), stages)
|
||||
self.assertIn(('sleep', 0.25), stages)
|
||||
arguments['expected_commit'] = other
|
||||
self.assertEqual(owner.fast_update(self.repo, 'Other', rollback=True, **arguments), 'reboot-requested')
|
||||
self.assertEqual(self.git(self.repo, 'symbolic-ref', '--short', 'HEAD'), 'Dom')
|
||||
@@ -486,7 +486,7 @@ class TestFastUpdate(unittest.TestCase):
|
||||
|
||||
with self.assertRaises(owner.FastUpdateError):
|
||||
self.update(parked=lambda: parked[0], sleep=notice)
|
||||
self.assertEqual(waits, [6.0])
|
||||
self.assertEqual(waits, [0.25])
|
||||
self.assertEqual(self.git(self.repo, 'rev-parse', 'HEAD'), self.previous)
|
||||
self.assertFalse(self.params.calls)
|
||||
|
||||
@@ -500,6 +500,70 @@ class TestFastUpdate(unittest.TestCase):
|
||||
self.assertEqual((self.repo / 'new-model').read_bytes(), b'model')
|
||||
self.assertFalse(self.invalidations)
|
||||
|
||||
def test_ignored_model_cache_is_not_scanned_or_removed(self):
|
||||
self.git(self.repo, 'config', 'core.excludesFile', str(self.repo / '.git/local-ignore'))
|
||||
(self.repo / '.git/local-ignore').write_text('models/\n')
|
||||
cache = self.repo / 'models/nested/download'
|
||||
cache.parent.mkdir(parents=True)
|
||||
cache.write_bytes(b'keep downloaded model')
|
||||
commands = []
|
||||
|
||||
def traced(command, cwd):
|
||||
commands.append(command)
|
||||
return owner.run(command, cwd)
|
||||
|
||||
self.assertEqual(self.update(run=traced), 'reboot-requested')
|
||||
self.assertEqual(cache.read_bytes(), b'keep downloaded model')
|
||||
self.assertFalse(any('--others' in command for command in commands))
|
||||
|
||||
def test_ignored_update_collision_is_rejected(self):
|
||||
self.git(self.repo, 'config', 'core.excludesFile', str(self.repo / '.git/local-ignore'))
|
||||
(self.repo / '.git/local-ignore').write_text('[[]model]\n')
|
||||
local = self.repo / '[model]'
|
||||
local.write_bytes(b'private model')
|
||||
(self.remote / '[model]').write_bytes(b'new tracked model')
|
||||
self.git(self.remote, 'add', '[model]')
|
||||
self.receipt_commit()
|
||||
with self.assertRaisesRegex(owner.FastUpdateError, 'overwrite untracked data'):
|
||||
self.update()
|
||||
self.assertEqual(local.read_bytes(), b'private model')
|
||||
self.assertEqual(self.git(self.repo, 'rev-parse', 'HEAD'), self.previous)
|
||||
|
||||
def test_untracked_parent_file_and_directory_contents_are_protected(self):
|
||||
for parent_file in (True, False):
|
||||
with self.subTest(parent_file=parent_file):
|
||||
folder = self.repo / 'new-data'
|
||||
target = self.remote / 'new-data'
|
||||
if parent_file:
|
||||
folder.write_bytes(b'private data')
|
||||
target.mkdir()
|
||||
(target / 'source').write_text('new tracked source')
|
||||
else:
|
||||
folder.mkdir()
|
||||
(folder / 'download').write_bytes(b'private data')
|
||||
target.write_text('new tracked source')
|
||||
self.git(self.remote, 'add', 'new-data')
|
||||
self.receipt_commit()
|
||||
with self.assertRaisesRegex(owner.FastUpdateError, 'overwrite untracked data'):
|
||||
self.update()
|
||||
self.assertEqual((folder if parent_file else folder / 'download').read_bytes(), b'private data')
|
||||
self.assertEqual(self.git(self.repo, 'rev-parse', 'HEAD'), self.previous)
|
||||
self.git(self.remote, 'reset', '--hard', self.target)
|
||||
if parent_file:
|
||||
folder.unlink()
|
||||
else:
|
||||
(folder / 'download').unlink()
|
||||
folder.rmdir()
|
||||
|
||||
def test_tracked_directory_can_be_replaced_by_file(self):
|
||||
self.git(self.remote, 'rm', '-r', 'openpilot/starpilot/ui')
|
||||
(self.remote / 'openpilot/starpilot').mkdir(parents=True, exist_ok=True)
|
||||
(self.remote / 'openpilot/starpilot/ui').write_text('replacement')
|
||||
self.git(self.remote, 'add', '.')
|
||||
self.receipt_commit()
|
||||
self.assertEqual(self.update(), 'reboot-requested')
|
||||
self.assertEqual((self.repo / 'openpilot/starpilot/ui').read_text(), 'replacement')
|
||||
|
||||
def test_post_apply_validation_failure_rolls_back(self):
|
||||
original = owner.validate_revision
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "5cfd05f33d0a6367fd072ab3463b923ca56e8eb23596f0f83ca2ea38c3df9149", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "2c9c3e53617a5e2309d5b1f28621c560ab0d93906581eaa532e880e7bdc9b95a", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user