diff options
author | Philip Lorenz <philip.lorenz@bmw.de> | 2024-02-22 14:14:47 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-02-23 14:34:05 +0000 |
commit | 0e3bcc51037aa8e1f7992756f7476a879625b114 (patch) | |
tree | 348709c9d92fb8972ebf7383054c522e59511864 /bitbake/lib/bb/fetch2/git.py | |
parent | fff242b5d21f9d856557ed9367fa43fa8b435be5 (diff) | |
download | poky-0e3bcc51037aa8e1f7992756f7476a879625b114.tar.gz |
bitbake: fetch2: Ensure that git LFS objects are available
The current implementation only performs a git lfs fetch alongside of a
regular git fetch. This causes issues when the downloaded revision is
already part of the fetched repository (e.g. because of moving back in
history or the updated revision already being part of the repository at
the time of the initial clone).
Fix this by explicitly checking whether the required LFS objects are
available in the downloade directory before confirming that a downloaded
repository is up-to-date.
This issue previously went unnoticed as git lfs would silently fetch the
missing objects during the `unpack` task. With network isolation turned
on, this no longer works, and unpacking fails.
(Bitbake rev: cfae1556bf671acec119a6c8bbc4b667a856b9ae)
Signed-off-by: Philip Lorenz <philip.lorenz@bmw.de>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/fetch2/git.py')
-rw-r--r-- | bitbake/lib/bb/fetch2/git.py | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/bitbake/lib/bb/fetch2/git.py b/bitbake/lib/bb/fetch2/git.py index df33fb6aeb..b43ee0da39 100644 --- a/bitbake/lib/bb/fetch2/git.py +++ b/bitbake/lib/bb/fetch2/git.py | |||
@@ -325,7 +325,10 @@ class Git(FetchMethod): | |||
325 | return ud.clonedir | 325 | return ud.clonedir |
326 | 326 | ||
327 | def need_update(self, ud, d): | 327 | def need_update(self, ud, d): |
328 | return self.clonedir_need_update(ud, d) or self.shallow_tarball_need_update(ud) or self.tarball_need_update(ud) | 328 | return self.clonedir_need_update(ud, d) \ |
329 | or self.shallow_tarball_need_update(ud) \ | ||
330 | or self.tarball_need_update(ud) \ | ||
331 | or self.lfs_need_update(ud, d) | ||
329 | 332 | ||
330 | def clonedir_need_update(self, ud, d): | 333 | def clonedir_need_update(self, ud, d): |
331 | if not os.path.exists(ud.clonedir): | 334 | if not os.path.exists(ud.clonedir): |
@@ -337,6 +340,15 @@ class Git(FetchMethod): | |||
337 | return True | 340 | return True |
338 | return False | 341 | return False |
339 | 342 | ||
343 | def lfs_need_update(self, ud, d): | ||
344 | if self.clonedir_need_update(ud, d): | ||
345 | return True | ||
346 | |||
347 | for name in ud.names: | ||
348 | if not self._lfs_objects_downloaded(ud, d, name, ud.clonedir): | ||
349 | return True | ||
350 | return False | ||
351 | |||
340 | def clonedir_need_shallow_revs(self, ud, d): | 352 | def clonedir_need_shallow_revs(self, ud, d): |
341 | for rev in ud.shallow_revs: | 353 | for rev in ud.shallow_revs: |
342 | try: | 354 | try: |
@@ -467,7 +479,7 @@ class Git(FetchMethod): | |||
467 | if missing_rev: | 479 | if missing_rev: |
468 | raise bb.fetch2.FetchError("Unable to find revision %s even from upstream" % missing_rev) | 480 | raise bb.fetch2.FetchError("Unable to find revision %s even from upstream" % missing_rev) |
469 | 481 | ||
470 | if self._contains_lfs(ud, d, ud.clonedir) and self._need_lfs(ud): | 482 | if self.lfs_need_update(ud, d): |
471 | # Unpack temporary working copy, use it to run 'git checkout' to force pre-fetching | 483 | # Unpack temporary working copy, use it to run 'git checkout' to force pre-fetching |
472 | # of all LFS blobs needed at the srcrev. | 484 | # of all LFS blobs needed at the srcrev. |
473 | # | 485 | # |
@@ -710,6 +722,35 @@ class Git(FetchMethod): | |||
710 | raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output)) | 722 | raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output)) |
711 | return output.split()[0] != "0" | 723 | return output.split()[0] != "0" |
712 | 724 | ||
725 | def _lfs_objects_downloaded(self, ud, d, name, wd): | ||
726 | """ | ||
727 | Verifies whether the LFS objects for requested revisions have already been downloaded | ||
728 | """ | ||
729 | # Bail out early if this repository doesn't use LFS | ||
730 | if not self._need_lfs(ud) or not self._contains_lfs(ud, d, wd): | ||
731 | return True | ||
732 | |||
733 | # The Git LFS specification specifies ([1]) the LFS folder layout so it should be safe to check for file | ||
734 | # existence. | ||
735 | # [1] https://github.com/git-lfs/git-lfs/blob/main/docs/spec.md#intercepting-git | ||
736 | cmd = "%s lfs ls-files -l %s" \ | ||
737 | % (ud.basecmd, ud.revisions[name]) | ||
738 | output = runfetchcmd(cmd, d, quiet=True, workdir=wd).rstrip() | ||
739 | # Do not do any further matching if no objects are managed by LFS | ||
740 | if not output: | ||
741 | return True | ||
742 | |||
743 | # Match all lines beginning with the hexadecimal OID | ||
744 | oid_regex = re.compile("^(([a-fA-F0-9]{2})([a-fA-F0-9]{2})[A-Fa-f0-9]+)") | ||
745 | for line in output.split("\n"): | ||
746 | oid = re.search(oid_regex, line) | ||
747 | if not oid: | ||
748 | bb.warn("git lfs ls-files output '%s' did not match expected format." % line) | ||
749 | if not os.path.exists(os.path.join(wd, "lfs", "objects", oid.group(2), oid.group(3), oid.group(1))): | ||
750 | return False | ||
751 | |||
752 | return True | ||
753 | |||
713 | def _need_lfs(self, ud): | 754 | def _need_lfs(self, ud): |
714 | return ud.parm.get("lfs", "1") == "1" | 755 | return ud.parm.get("lfs", "1") == "1" |
715 | 756 | ||