Skip to content

Commit 0fdfdf9

Browse files
Improve auto-cache robustness
- Detect corrupt caches and automatically recreate them. - Enables fully offline work with auto-cache.: Skip remote fetches when the revision is already cached. - Support temporary offline remotes: Skip remote fetches when the remote is unreachable.
1 parent 76890ef commit 0fdfdf9

File tree

1 file changed

+56
-7
lines changed

1 file changed

+56
-7
lines changed

src/west/app/project.py

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,9 +1177,9 @@ def do_add_parser(self, parser_adder):
11771177
'--auto-cache',
11781178
help='''automatically setup local cache repositories
11791179
in a flat folder hierarchy, but with an additional
1180-
subfolder (hashed name) for different remote URLs.
1181-
Each local cache repository is automatically cloned
1182-
on first usage and synced on subsequent clones.
1180+
subfolder (hashed name) for different remote URLs. Each
1181+
local cache repository is automatically cloned on first
1182+
usage and synced on subsequent fetches (on demand).
11831183
This cache has the lowest priority (Prio 2).''',
11841184
)
11851185

@@ -1203,7 +1203,8 @@ def do_add_parser(self, parser_adder):
12031203
workspace setup.
12041204
Only in case of auto-cache the 'west update' process updates the local
12051205
caches first, which then serve as the source for pulling changes into
1206-
the workspace.
1206+
the workspace. Thereby, the auto-cache only fetches updates from remote
1207+
if the specified revision is not already present in the local cache.
12071208
12081209
Example: Assume your manifest describes this workspace structure:
12091210
(workspace)
@@ -1233,7 +1234,8 @@ def do_add_parser(self, parser_adder):
12331234
folder hierarchy is setup automatically. Each repository is stored under a
12341235
directory named after the basename of its remote URL. To prevent conflicts
12351236
between repos with same name, a hash of the remote URL is used as subfolder.
1236-
Note: Each local cache repo is automatically synced on subsequent updates.
1237+
Note: Each local cache repo is automatically synced on subsequent updates
1238+
on demand (if the used revision is not cached yet).
12371239
(auto cache directory)
12381240
├── bar.git
12391241
│ ├── <hash>
@@ -1757,13 +1759,60 @@ def handle_auto_cache(self, project):
17571759
# Then clone the repository into the local cache.
17581760
cache_dir_parent = Path(cache_dir).parent
17591761
cache_dir_parent.mkdir(parents=True, exist_ok=True)
1762+
self.dbg(f'{project.name}: Setup auto-cache for {project.url} in {cache_dir}')
17601763
project.git(
17611764
['clone', '--mirror', '--', project.url, os.fspath(cache_dir)], cwd=cache_dir_parent
17621765
)
17631766
self.create_auto_cache_info(project, cache_dir)
17641767
else:
1765-
# The local cache already exists. Sync it with remote.
1766-
project.git(['remote', 'update', '--prune'], cwd=cache_dir)
1768+
# The local cache already exists
1769+
1770+
# helper function to check if git commands run successful
1771+
def check_git(cmd) -> bool:
1772+
'''
1773+
run git command in the auto-cache directory without suppressed output
1774+
return True if the command was successful (returncode 0), otherwise False
1775+
'''
1776+
p = project.git(
1777+
cmd, cwd=cache_dir, capture_stdout=True, capture_stderr=True, check=False
1778+
)
1779+
return not p.returncode
1780+
1781+
# Check if the auto-cache is corrupt
1782+
corrupt = not check_git(['cat-file', '-e', 'HEAD'])
1783+
1784+
# Check if the revision is already contained in the auto-cache.
1785+
contains_rev = check_git(['cat-file', '-e', f'{project.revision}^{{commit}}'])
1786+
1787+
# Check if the remote is reachable
1788+
reachable = check_git(['ls-remote'])
1789+
1790+
if not corrupt:
1791+
# early exit if auto-cache is intact, but remote is not reachable (even if
1792+
# fetch stratefy 'always' is specified), since we know that remote update will fail.
1793+
if not reachable:
1794+
self.dbg(
1795+
f'{project.name}: remote ({project.url}) not reachable. '
1796+
'Skip auto-cache update with remote.'
1797+
)
1798+
return
1799+
1800+
# early exit if auto-cache is intact and revision is already contained
1801+
if contains_rev and self.fs != 'always':
1802+
self.dbg(
1803+
f'{project.name}: auto-cache remote update is skipped '
1804+
f'as it already contains revision {project.revision}'
1805+
)
1806+
return
1807+
1808+
# The auto-cache needs to be updated.
1809+
self.dbg(f'{project.name}: update auto-cache ({cache_dir}) with remote')
1810+
project.git(['remote', 'update', '--prune'], cwd=cache_dir, check=False)
1811+
else:
1812+
# Remove the auto-cache and freshly setup again.
1813+
self.small_banner(f'{project.name}: remove corrupt auto-cache {cache_dir}')
1814+
shutil.rmtree(cache_dir)
1815+
self.handle_auto_cache(project)
17671816

17681817
def init_project(self, project):
17691818
# update() helper. Initialize an uncloned project repository.

0 commit comments

Comments
 (0)