summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/fetch2/git.py
diff options
context:
space:
mode:
authorPhilip Lorenz <philip.lorenz@bmw.de>2024-02-22 14:14:47 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-02-23 14:34:05 +0000
commit0e3bcc51037aa8e1f7992756f7476a879625b114 (patch)
tree348709c9d92fb8972ebf7383054c522e59511864 /bitbake/lib/bb/fetch2/git.py
parentfff242b5d21f9d856557ed9367fa43fa8b435be5 (diff)
downloadpoky-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.py45
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