blob: 0deeb5cee1f2efca98b0a586b5b5841e36f1d7ac [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'Fetch' git implementation
3
4git fetcher support the SRC_URI with format of:
5SRC_URI = "git://some.host/somepath;OptionA=xxx;OptionB=xxx;..."
6
7Supported SRC_URI options are:
8
9- branch
10 The git branch to retrieve from. The default is "master"
11
12 This option also supports multiple branch fetching, with branches
13 separated by commas. In multiple branches case, the name option
14 must have the same number of names to match the branches, which is
15 used to specify the SRC_REV for the branch
16 e.g:
17 SRC_URI="git://some.host/somepath;branch=branchX,branchY;name=nameX,nameY"
18 SRCREV_nameX = "xxxxxxxxxxxxxxxxxxxx"
19 SRCREV_nameY = "YYYYYYYYYYYYYYYYYYYY"
20
21- tag
22 The git tag to retrieve. The default is "master"
23
24- protocol
25 The method to use to access the repository. Common options are "git",
26 "http", "https", "file", "ssh" and "rsync". The default is "git".
27
28- rebaseable
29 rebaseable indicates that the upstream git repo may rebase in the future,
30 and current revision may disappear from upstream repo. This option will
31 remind fetcher to preserve local cache carefully for future use.
32 The default value is "0", set rebaseable=1 for rebaseable git repo.
33
34- nocheckout
35 Don't checkout source code when unpacking. set this option for the recipe
36 who has its own routine to checkout code.
37 The default is "0", set nocheckout=1 if needed.
38
39- bareclone
40 Create a bare clone of the source code and don't checkout the source code
41 when unpacking. Set this option for the recipe who has its own routine to
42 checkout code and tracking branch requirements.
43 The default is "0", set bareclone=1 if needed.
44
45- nobranch
46 Don't check the SHA validation for branch. set this option for the recipe
Andrew Geissler517393d2023-01-13 08:55:19 -060047 referring to commit which is valid in any namespace (branch, tag, ...)
48 instead of branch.
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049 The default is "0", set nobranch=1 if needed.
50
Patrick Williamsac13d5f2023-11-24 18:59:46 -060051- subpath
52 Limit the checkout to a specific subpath of the tree.
53 By default, checkout the whole tree, set subpath=<path> if needed
54
55- destsuffix
56 The name of the path in which to place the checkout.
57 By default, the path is git/, set destsuffix=<suffix> if needed
58
Patrick Williamsc0f7c042017-02-23 20:41:17 -060059- usehead
Brad Bishop6e60e8b2018-02-01 10:27:11 -050060 For local git:// urls to use the current branch HEAD as the revision for use with
Patrick Williamsc0f7c042017-02-23 20:41:17 -060061 AUTOREV. Implies nobranch.
62
Patrick Williamsac13d5f2023-11-24 18:59:46 -060063- lfs
64 Enable the checkout to use LFS for large files. This will download all LFS files
65 in the download step, as the unpack step does not have network access.
66 The default is "1", set lfs=0 to skip.
67
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068"""
69
Brad Bishopc342db32019-05-15 21:57:59 -040070# Copyright (C) 2005 Richard Purdie
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071#
Brad Bishopc342db32019-05-15 21:57:59 -040072# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050074
Brad Bishopd7bf8c12018-02-25 22:55:05 -050075import collections
Patrick Williamsd7e96312015-09-22 08:09:05 -050076import errno
Brad Bishopd7bf8c12018-02-25 22:55:05 -050077import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078import os
79import re
Andrew Geissler4c19ea12020-10-27 13:52:24 -050080import shlex
Andrew Geissler5082cc72023-09-11 08:41:39 -040081import shutil
Brad Bishopd7bf8c12018-02-25 22:55:05 -050082import subprocess
83import tempfile
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084import bb
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085import bb.progress
Andrew Geissler5199d832021-09-24 16:47:35 -050086from contextlib import contextmanager
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087from bb.fetch2 import FetchMethod
88from bb.fetch2 import runfetchcmd
89from bb.fetch2 import logger
90
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091
Patrick Williams03907ee2022-05-01 06:28:52 -050092sha1_re = re.compile(r'^[0-9a-f]{40}$')
93slash_re = re.compile(r"/+")
94
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095class GitProgressHandler(bb.progress.LineFilterProgressHandler):
96 """Extract progress information from git output"""
97 def __init__(self, d):
98 self._buffer = ''
99 self._count = 0
100 super(GitProgressHandler, self).__init__(d)
101 # Send an initial progress event so the bar gets shown
102 self._fire_progress(-1)
103
104 def write(self, string):
105 self._buffer += string
106 stages = ['Counting objects', 'Compressing objects', 'Receiving objects', 'Resolving deltas']
107 stage_weights = [0.2, 0.05, 0.5, 0.25]
108 stagenum = 0
109 for i, stage in reversed(list(enumerate(stages))):
110 if stage in self._buffer:
111 stagenum = i
112 self._buffer = ''
113 break
114 self._status = stages[stagenum]
115 percs = re.findall(r'(\d+)%', string)
116 if percs:
117 progress = int(round((int(percs[-1]) * stage_weights[stagenum]) + (sum(stage_weights[:stagenum]) * 100)))
118 rates = re.findall(r'([\d.]+ [a-zA-Z]*/s+)', string)
119 if rates:
120 rate = rates[-1]
121 else:
122 rate = None
123 self.update(progress, rate)
124 else:
125 if stagenum == 0:
126 percs = re.findall(r': (\d+)', string)
127 if percs:
128 count = int(percs[-1])
129 if count > self._count:
130 self._count = count
131 self._fire_progress(-count)
132 super(GitProgressHandler, self).write(string)
133
134
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135class Git(FetchMethod):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400136 bitbake_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.join(os.path.abspath(__file__))), '..', '..', '..'))
137 make_shallow_path = os.path.join(bitbake_dir, 'bin', 'git-make-shallow')
138
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 """Class to fetch a module or modules from git repositories"""
140 def init(self, d):
141 pass
142
143 def supports(self, ud, d):
144 """
145 Check to see if a given url can be fetched with git.
146 """
147 return ud.type in ['git']
148
149 def supports_checksum(self, urldata):
150 return False
151
152 def urldata_init(self, ud, d):
153 """
154 init git specific variable within url data
155 so that the git method like latest_revision() can work
156 """
157 if 'protocol' in ud.parm:
158 ud.proto = ud.parm['protocol']
159 elif not ud.host:
160 ud.proto = 'file'
161 else:
162 ud.proto = "git"
Andrew Geissler595f6302022-01-24 19:11:47 +0000163 if ud.host == "github.com" and ud.proto == "git":
164 # github stopped supporting git protocol
165 # https://github.blog/2021-09-01-improving-git-protocol-security-github/#no-more-unauthenticated-git
166 ud.proto = "https"
167 bb.warn("URL: %s uses git protocol which is no longer supported by github. Please change to ;protocol=https in the url." % ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500168
169 if not ud.proto in ('git', 'file', 'ssh', 'http', 'https', 'rsync'):
170 raise bb.fetch2.ParameterError("Invalid protocol type", ud.url)
171
172 ud.nocheckout = ud.parm.get("nocheckout","0") == "1"
173
174 ud.rebaseable = ud.parm.get("rebaseable","0") == "1"
175
176 ud.nobranch = ud.parm.get("nobranch","0") == "1"
177
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 # usehead implies nobranch
179 ud.usehead = ud.parm.get("usehead","0") == "1"
180 if ud.usehead:
181 if ud.proto != "file":
182 raise bb.fetch2.ParameterError("The usehead option is only for use with local ('protocol=file') git repositories", ud.url)
183 ud.nobranch = 1
184
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185 # bareclone implies nocheckout
186 ud.bareclone = ud.parm.get("bareclone","0") == "1"
187 if ud.bareclone:
188 ud.nocheckout = 1
189
190 ud.unresolvedrev = {}
Andrew Geissler595f6302022-01-24 19:11:47 +0000191 branches = ud.parm.get("branch", "").split(',')
192 if branches == [""] and not ud.nobranch:
193 bb.warn("URL: %s does not set any branch parameter. The future default branch used by tools and repositories is uncertain and we will therefore soon require this is set in all git urls." % ud.url)
194 branches = ["master"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 if len(branches) != len(ud.names):
196 raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500197
Andrew Geisslerc926e172021-05-07 16:11:35 -0500198 ud.noshared = d.getVar("BB_GIT_NOSHARED") == "1"
199
200 ud.cloneflags = "-n"
201 if not ud.noshared:
202 ud.cloneflags += " -s"
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500203 if ud.bareclone:
204 ud.cloneflags += " --mirror"
205
206 ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1"
207 ud.shallow_extra_refs = (d.getVar("BB_GIT_SHALLOW_EXTRA_REFS") or "").split()
208
209 depth_default = d.getVar("BB_GIT_SHALLOW_DEPTH")
210 if depth_default is not None:
211 try:
212 depth_default = int(depth_default or 0)
213 except ValueError:
214 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
215 else:
216 if depth_default < 0:
217 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
218 else:
219 depth_default = 1
220 ud.shallow_depths = collections.defaultdict(lambda: depth_default)
221
Brad Bishop19323692019-04-05 15:28:33 -0400222 revs_default = d.getVar("BB_GIT_SHALLOW_REVS")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500223 ud.shallow_revs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500224 ud.branches = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225 for pos, name in enumerate(ud.names):
226 branch = branches[pos]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227 ud.branches[name] = branch
228 ud.unresolvedrev[name] = branch
229
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500230 shallow_depth = d.getVar("BB_GIT_SHALLOW_DEPTH_%s" % name)
231 if shallow_depth is not None:
232 try:
233 shallow_depth = int(shallow_depth or 0)
234 except ValueError:
235 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
236 else:
237 if shallow_depth < 0:
238 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
239 ud.shallow_depths[name] = shallow_depth
240
241 revs = d.getVar("BB_GIT_SHALLOW_REVS_%s" % name)
242 if revs is not None:
243 ud.shallow_revs.extend(revs.split())
244 elif revs_default is not None:
245 ud.shallow_revs.extend(revs_default.split())
246
247 if (ud.shallow and
248 not ud.shallow_revs and
249 all(ud.shallow_depths[n] == 0 for n in ud.names)):
250 # Shallow disabled for this URL
251 ud.shallow = False
252
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600253 if ud.usehead:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600254 # When usehead is set let's associate 'HEAD' with the unresolved
255 # rev of this repository. This will get resolved into a revision
256 # later. If an actual revision happens to have also been provided
257 # then this setting will be overridden.
258 for name in ud.names:
259 ud.unresolvedrev[name] = 'HEAD'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600260
Patrick Williams2390b1b2022-11-03 13:47:49 -0500261 ud.basecmd = d.getVar("FETCHCMD_git") or "git -c gc.autoDetach=false -c core.pager=cat"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500263 write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0"
264 ud.write_tarballs = write_tarballs != "0" or ud.rebaseable
265 ud.write_shallow_tarballs = (d.getVar("BB_GENERATE_SHALLOW_TARBALLS") or write_tarballs) != "0"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500267 ud.setup_revisions(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268
269 for name in ud.names:
Patrick Williams03907ee2022-05-01 06:28:52 -0500270 # Ensure any revision that doesn't look like a SHA-1 is translated into one
271 if not sha1_re.match(ud.revisions[name] or ''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 if ud.revisions[name]:
273 ud.unresolvedrev[name] = ud.revisions[name]
274 ud.revisions[name] = self.latest_revision(ud, d, name)
275
Andrew Geisslerc3d88e42020-10-02 09:45:00 -0500276 gitsrcname = '%s%s' % (ud.host.replace(':', '.'), ud.path.replace('/', '.').replace('*', '.').replace(' ','_'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 if gitsrcname.startswith('.'):
278 gitsrcname = gitsrcname[1:]
279
Patrick Williams03907ee2022-05-01 06:28:52 -0500280 # For a rebaseable git repo, it is necessary to keep a mirror tar ball
281 # per revision, so that even if the revision disappears from the
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 # upstream repo in the future, the mirror will remain intact and still
Patrick Williams03907ee2022-05-01 06:28:52 -0500283 # contain the revision
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 if ud.rebaseable:
285 for name in ud.names:
286 gitsrcname = gitsrcname + '_' + ud.revisions[name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500288 dl_dir = d.getVar("DL_DIR")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800289 gitdir = d.getVar("GITDIR") or (dl_dir + "/git2")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500290 ud.clonedir = os.path.join(gitdir, gitsrcname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500291 ud.localfile = ud.clonedir
292
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500293 mirrortarball = 'git2_%s.tar.gz' % gitsrcname
294 ud.fullmirror = os.path.join(dl_dir, mirrortarball)
295 ud.mirrortarballs = [mirrortarball]
296 if ud.shallow:
297 tarballname = gitsrcname
298 if ud.bareclone:
299 tarballname = "%s_bare" % tarballname
300
301 if ud.shallow_revs:
302 tarballname = "%s_%s" % (tarballname, "_".join(sorted(ud.shallow_revs)))
303
304 for name, revision in sorted(ud.revisions.items()):
305 tarballname = "%s_%s" % (tarballname, ud.revisions[name][:7])
306 depth = ud.shallow_depths[name]
307 if depth:
308 tarballname = "%s-%s" % (tarballname, depth)
309
310 shallow_refs = []
311 if not ud.nobranch:
312 shallow_refs.extend(ud.branches.values())
313 if ud.shallow_extra_refs:
314 shallow_refs.extend(r.replace('refs/heads/', '').replace('*', 'ALL') for r in ud.shallow_extra_refs)
315 if shallow_refs:
316 tarballname = "%s_%s" % (tarballname, "_".join(sorted(shallow_refs)).replace('/', '.'))
317
318 fetcher = self.__class__.__name__.lower()
319 ud.shallowtarball = '%sshallow_%s.tar.gz' % (fetcher, tarballname)
320 ud.fullshallow = os.path.join(dl_dir, ud.shallowtarball)
321 ud.mirrortarballs.insert(0, ud.shallowtarball)
322
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323 def localpath(self, ud, d):
324 return ud.clonedir
325
326 def need_update(self, ud, d):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800327 return self.clonedir_need_update(ud, d) or self.shallow_tarball_need_update(ud) or self.tarball_need_update(ud)
328
329 def clonedir_need_update(self, ud, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330 if not os.path.exists(ud.clonedir):
331 return True
Brad Bishop64c979e2019-11-04 13:55:29 -0500332 if ud.shallow and ud.write_shallow_tarballs and self.clonedir_need_shallow_revs(ud, d):
333 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600335 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337 return False
338
Brad Bishop64c979e2019-11-04 13:55:29 -0500339 def clonedir_need_shallow_revs(self, ud, d):
340 for rev in ud.shallow_revs:
341 try:
342 runfetchcmd('%s rev-parse -q --verify %s' % (ud.basecmd, rev), d, quiet=True, workdir=ud.clonedir)
343 except bb.fetch2.FetchError:
344 return rev
345 return None
346
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800347 def shallow_tarball_need_update(self, ud):
348 return ud.shallow and ud.write_shallow_tarballs and not os.path.exists(ud.fullshallow)
349
350 def tarball_need_update(self, ud):
351 return ud.write_tarballs and not os.path.exists(ud.fullmirror)
352
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353 def try_premirror(self, ud, d):
354 # If we don't do this, updating an existing checkout with only premirrors
355 # is not possible
Brad Bishop19323692019-04-05 15:28:33 -0400356 if bb.utils.to_boolean(d.getVar("BB_FETCH_PREMIRRORONLY")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500357 return True
358 if os.path.exists(ud.clonedir):
359 return False
360 return True
361
362 def download(self, ud, d):
363 """Fetch url"""
364
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500365 # A current clone is preferred to either tarball, a shallow tarball is
366 # preferred to an out of date clone, and a missing clone will use
367 # either tarball.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800368 if ud.shallow and os.path.exists(ud.fullshallow) and self.need_update(ud, d):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500369 ud.localpath = ud.fullshallow
370 return
Andrew Geissler78b72792022-06-14 06:47:25 -0500371 elif os.path.exists(ud.fullmirror) and self.need_update(ud, d):
372 if not os.path.exists(ud.clonedir):
373 bb.utils.mkdirhier(ud.clonedir)
374 runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir)
375 else:
376 tmpdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
377 runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=tmpdir)
378 fetch_cmd = "LANG=C %s fetch -f --progress %s " % (ud.basecmd, shlex.quote(tmpdir))
379 runfetchcmd(fetch_cmd, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 repourl = self._get_repo_url(ud)
381
Andrew Geissler5082cc72023-09-11 08:41:39 -0400382 needs_clone = False
383 if os.path.exists(ud.clonedir):
384 # The directory may exist, but not be the top level of a bare git
385 # repository in which case it needs to be deleted and re-cloned.
386 try:
387 # Since clones can be bare, use --absolute-git-dir instead of --show-toplevel
388 output = runfetchcmd("LANG=C %s rev-parse --absolute-git-dir" % ud.basecmd, d, workdir=ud.clonedir)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500389 toplevel = output.rstrip()
Andrew Geissler5082cc72023-09-11 08:41:39 -0400390
Andrew Geissler220dafd2023-10-04 10:18:08 -0500391 if not bb.utils.path_is_descendant(toplevel, ud.clonedir):
392 logger.warning("Top level directory '%s' is not a descendant of '%s'. Re-cloning", toplevel, ud.clonedir)
Andrew Geissler5082cc72023-09-11 08:41:39 -0400393 needs_clone = True
394 except bb.fetch2.FetchError as e:
395 logger.warning("Unable to get top level for %s (not a git directory?): %s", ud.clonedir, e)
396 needs_clone = True
Andrew Geissler220dafd2023-10-04 10:18:08 -0500397 except FileNotFoundError as e:
398 logger.warning("%s", e)
399 needs_clone = True
Andrew Geissler5082cc72023-09-11 08:41:39 -0400400
401 if needs_clone:
402 shutil.rmtree(ud.clonedir)
403 else:
404 needs_clone = True
405
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406 # If the repo still doesn't exist, fallback to cloning it
Andrew Geissler5082cc72023-09-11 08:41:39 -0400407 if needs_clone:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500408 # We do this since git will use a "-l" option automatically for local urls where possible,
409 # but it doesn't work when git/objects is a symlink, only works when it is a directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500410 if repourl.startswith("file://"):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500411 repourl_path = repourl[7:]
412 objects = os.path.join(repourl_path, 'objects')
413 if os.path.isdir(objects) and not os.path.islink(objects):
414 repourl = repourl_path
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500415 clone_cmd = "LANG=C %s clone --bare --mirror %s %s --progress" % (ud.basecmd, shlex.quote(repourl), ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416 if ud.proto.lower() != 'file':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417 bb.fetch2.check_network_access(d, clone_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600418 progresshandler = GitProgressHandler(d)
419 runfetchcmd(clone_cmd, d, log=progresshandler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500421 # Update the checkout if needed
Brad Bishop64c979e2019-11-04 13:55:29 -0500422 if self.clonedir_need_update(ud, d):
Brad Bishop6ef32652018-10-09 18:59:25 +0100423 output = runfetchcmd("%s remote" % ud.basecmd, d, quiet=True, workdir=ud.clonedir)
424 if "origin" in output:
425 runfetchcmd("%s remote rm origin" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500426
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500427 runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=ud.clonedir)
Andrew Geissler517393d2023-01-13 08:55:19 -0600428
429 if ud.nobranch:
430 fetch_cmd = "LANG=C %s fetch -f --progress %s refs/*:refs/*" % (ud.basecmd, shlex.quote(repourl))
431 else:
432 fetch_cmd = "LANG=C %s fetch -f --progress %s refs/heads/*:refs/heads/* refs/tags/*:refs/tags/*" % (ud.basecmd, shlex.quote(repourl))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 if ud.proto.lower() != 'file':
434 bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435 progresshandler = GitProgressHandler(d)
436 runfetchcmd(fetch_cmd, d, log=progresshandler, workdir=ud.clonedir)
437 runfetchcmd("%s prune-packed" % ud.basecmd, d, workdir=ud.clonedir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400438 runfetchcmd("%s pack-refs --all" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600439 runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500440 try:
441 os.unlink(ud.fullmirror)
442 except OSError as exc:
443 if exc.errno != errno.ENOENT:
444 raise
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800445
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600447 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448 raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
449
Brad Bishop64c979e2019-11-04 13:55:29 -0500450 if ud.shallow and ud.write_shallow_tarballs:
451 missing_rev = self.clonedir_need_shallow_revs(ud, d)
452 if missing_rev:
453 raise bb.fetch2.FetchError("Unable to find revision %s even from upstream" % missing_rev)
454
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600455 if self._contains_lfs(ud, d, ud.clonedir) and self._need_lfs(ud):
456 # Unpack temporary working copy, use it to run 'git checkout' to force pre-fetching
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000457 # of all LFS blobs needed at the srcrev.
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600458 #
459 # It would be nice to just do this inline here by running 'git-lfs fetch'
460 # on the bare clonedir, but that operation requires a working copy on some
461 # releases of Git LFS.
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600462 with tempfile.TemporaryDirectory(dir=d.getVar('DL_DIR')) as tmpdir:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600463 # Do the checkout. This implicitly involves a Git LFS fetch.
Andrew Geisslerc926e172021-05-07 16:11:35 -0500464 Git.unpack(self, ud, tmpdir, d)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600465
466 # Scoop up a copy of any stuff that Git LFS downloaded. Merge them into
467 # the bare clonedir.
468 #
469 # As this procedure is invoked repeatedly on incremental fetches as
470 # a recipe's SRCREV is bumped throughout its lifetime, this will
471 # result in a gradual accumulation of LFS blobs in <ud.clonedir>/lfs
472 # corresponding to all the blobs reachable from the different revs
473 # fetched across time.
474 #
475 # Only do this if the unpack resulted in a .git/lfs directory being
476 # created; this only happens if at least one blob needed to be
477 # downloaded.
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600478 if os.path.exists(os.path.join(ud.destdir, ".git", "lfs")):
479 runfetchcmd("tar -cf - lfs | tar -xf - -C %s" % ud.clonedir, d, workdir="%s/.git" % ud.destdir)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600480
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 def build_mirror_data(self, ud, d):
Andrew Geissler5199d832021-09-24 16:47:35 -0500482
483 # Create as a temp file and move atomically into position to avoid races
484 @contextmanager
485 def create_atomic(filename):
486 fd, tfile = tempfile.mkstemp(dir=os.path.dirname(filename))
487 try:
488 yield tfile
489 umask = os.umask(0o666)
490 os.umask(umask)
491 os.chmod(tfile, (0o666 & ~umask))
492 os.rename(tfile, filename)
493 finally:
494 os.close(fd)
495
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500496 if ud.shallow and ud.write_shallow_tarballs:
497 if not os.path.exists(ud.fullshallow):
498 if os.path.islink(ud.fullshallow):
499 os.unlink(ud.fullshallow)
500 tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
501 shallowclone = os.path.join(tempdir, 'git')
502 try:
503 self.clone_shallow_local(ud, shallowclone, d)
504
505 logger.info("Creating tarball of git repository")
Andrew Geissler5199d832021-09-24 16:47:35 -0500506 with create_atomic(ud.fullshallow) as tfile:
507 runfetchcmd("tar -czf %s ." % tfile, d, workdir=shallowclone)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500508 runfetchcmd("touch %s.done" % ud.fullshallow, d)
509 finally:
510 bb.utils.remove(tempdir, recurse=True)
511 elif ud.write_tarballs and not os.path.exists(ud.fullmirror):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 if os.path.islink(ud.fullmirror):
513 os.unlink(ud.fullmirror)
514
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500515 logger.info("Creating tarball of git repository")
Andrew Geissler5199d832021-09-24 16:47:35 -0500516 with create_atomic(ud.fullmirror) as tfile:
Andrew Geissler9aee5002022-03-30 16:27:02 +0000517 mtime = runfetchcmd("git log --all -1 --format=%cD", d,
518 quiet=True, workdir=ud.clonedir)
Patrick Williams03907ee2022-05-01 06:28:52 -0500519 runfetchcmd("tar -czf %s --owner oe:0 --group oe:0 --mtime \"%s\" ."
Andrew Geissler9aee5002022-03-30 16:27:02 +0000520 % (tfile, mtime), d, workdir=ud.clonedir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 runfetchcmd("touch %s.done" % ud.fullmirror, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500523 def clone_shallow_local(self, ud, dest, d):
524 """Clone the repo and make it shallow.
525
526 The upstream url of the new clone isn't set at this time, as it'll be
527 set correctly when unpacked."""
528 runfetchcmd("%s clone %s %s %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, dest), d)
529
530 to_parse, shallow_branches = [], []
531 for name in ud.names:
532 revision = ud.revisions[name]
533 depth = ud.shallow_depths[name]
534 if depth:
535 to_parse.append('%s~%d^{}' % (revision, depth - 1))
536
537 # For nobranch, we need a ref, otherwise the commits will be
538 # removed, and for non-nobranch, we truncate the branch to our
539 # srcrev, to avoid keeping unnecessary history beyond that.
540 branch = ud.branches[name]
541 if ud.nobranch:
542 ref = "refs/shallow/%s" % name
543 elif ud.bareclone:
544 ref = "refs/heads/%s" % branch
545 else:
546 ref = "refs/remotes/origin/%s" % branch
547
548 shallow_branches.append(ref)
549 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest)
550
551 # Map srcrev+depths to revisions
552 parsed_depths = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join(to_parse)), d, workdir=dest)
553
554 # Resolve specified revisions
555 parsed_revs = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join('"%s^{}"' % r for r in ud.shallow_revs)), d, workdir=dest)
556 shallow_revisions = parsed_depths.splitlines() + parsed_revs.splitlines()
557
558 # Apply extra ref wildcards
559 all_refs = runfetchcmd('%s for-each-ref "--format=%%(refname)"' % ud.basecmd,
560 d, workdir=dest).splitlines()
561 for r in ud.shallow_extra_refs:
562 if not ud.bareclone:
563 r = r.replace('refs/heads/', 'refs/remotes/origin/')
564
565 if '*' in r:
566 matches = filter(lambda a: fnmatch.fnmatchcase(a, r), all_refs)
567 shallow_branches.extend(matches)
568 else:
569 shallow_branches.append(r)
570
571 # Make the repository shallow
Brad Bishop316dfdd2018-06-25 12:45:53 -0400572 shallow_cmd = [self.make_shallow_path, '-s']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500573 for b in shallow_branches:
574 shallow_cmd.append('-r')
575 shallow_cmd.append(b)
576 shallow_cmd.extend(shallow_revisions)
577 runfetchcmd(subprocess.list2cmdline(shallow_cmd), d, workdir=dest)
578
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579 def unpack(self, ud, destdir, d):
580 """ unpack the downloaded src to destdir"""
581
Andrew Geissler595f6302022-01-24 19:11:47 +0000582 subdir = ud.parm.get("subdir")
583 subpath = ud.parm.get("subpath")
584 readpathspec = ""
585 def_destsuffix = "git/"
586
587 if subpath:
588 readpathspec = ":%s" % subpath
589 def_destsuffix = "%s/" % os.path.basename(subpath.rstrip('/'))
590
591 if subdir:
592 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
593 if os.path.isabs(subdir):
594 if not os.path.realpath(subdir).startswith(os.path.realpath(destdir)):
595 raise bb.fetch2.UnpackError("subdir argument isn't a subdirectory of unpack root %s" % destdir, ud.url)
596 destdir = subdir
597 else:
598 destdir = os.path.join(destdir, subdir)
599 def_destsuffix = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500600
601 destsuffix = ud.parm.get("destsuffix", def_destsuffix)
602 destdir = ud.destdir = os.path.join(destdir, destsuffix)
603 if os.path.exists(destdir):
604 bb.utils.prunedir(destdir)
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600605 if not ud.bareclone:
606 ud.unpack_tracer.unpack("git", destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600608 need_lfs = self._need_lfs(ud)
Brad Bishopa34c0302019-09-23 22:34:48 -0400609
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500610 if not need_lfs:
611 ud.basecmd = "GIT_LFS_SKIP_SMUDGE=1 " + ud.basecmd
612
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800613 source_found = False
614 source_error = []
615
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500616 clonedir_is_up_to_date = not self.clonedir_need_update(ud, d)
617 if clonedir_is_up_to_date:
618 runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, destdir), d)
619 source_found = True
620 else:
621 source_error.append("clone directory not available or not up to date: " + ud.clonedir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800622
623 if not source_found:
624 if ud.shallow:
625 if os.path.exists(ud.fullshallow):
626 bb.utils.mkdirhier(destdir)
627 runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=destdir)
628 source_found = True
629 else:
630 source_error.append("shallow clone not available: " + ud.fullshallow)
631 else:
632 source_error.append("shallow clone not enabled")
633
634 if not source_found:
635 raise bb.fetch2.UnpackError("No up to date source found: " + "; ".join(source_error), ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637 repourl = self._get_repo_url(ud)
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500638 runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=destdir)
Brad Bishopc342db32019-05-15 21:57:59 -0400639
640 if self._contains_lfs(ud, d, destdir):
Brad Bishop00e122a2019-10-05 11:10:57 -0400641 if need_lfs and not self._find_git_lfs(d):
642 raise bb.fetch2.FetchError("Repository %s has LFS content, install git-lfs on host to download (or set lfs=0 to ignore it)" % (repourl))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500643 elif not need_lfs:
Brad Bishopa34c0302019-09-23 22:34:48 -0400644 bb.note("Repository %s has LFS content but it is not being fetched" % (repourl))
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600645 else:
646 runfetchcmd("%s lfs install" % ud.basecmd, d, workdir=destdir)
Brad Bishopc342db32019-05-15 21:57:59 -0400647
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648 if not ud.nocheckout:
Andrew Geissler595f6302022-01-24 19:11:47 +0000649 if subpath:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d,
651 workdir=destdir)
652 runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653 elif not ud.nobranch:
654 branchname = ud.branches[ud.names[0]]
655 runfetchcmd("%s checkout -B %s %s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 ud.revisions[ud.names[0]]), d, workdir=destdir)
Andre Rosa49271d42017-09-07 11:15:55 +0200657 runfetchcmd("%s branch %s --set-upstream-to origin/%s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 branchname), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 runfetchcmd("%s checkout %s" % (ud.basecmd, ud.revisions[ud.names[0]]), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661
662 return True
663
664 def clean(self, ud, d):
665 """ clean the git directory """
666
Brad Bishop19323692019-04-05 15:28:33 -0400667 to_remove = [ud.localpath, ud.fullmirror, ud.fullmirror + ".done"]
668 # The localpath is a symlink to clonedir when it is cloned from a
669 # mirror, so remove both of them.
670 if os.path.islink(ud.localpath):
671 clonedir = os.path.realpath(ud.localpath)
672 to_remove.append(clonedir)
673
674 for r in to_remove:
675 if os.path.exists(r):
676 bb.note('Removing %s' % r)
677 bb.utils.remove(r, True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500678
679 def supports_srcrev(self):
680 return True
681
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600682 def _contains_ref(self, ud, d, name, wd):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683 cmd = ""
684 if ud.nobranch:
685 cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
686 ud.basecmd, ud.revisions[name])
687 else:
688 cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
689 ud.basecmd, ud.revisions[name], ud.branches[name])
690 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600691 output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692 except bb.fetch2.FetchError:
693 return False
694 if len(output.split()) > 1:
695 raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output))
696 return output.split()[0] != "0"
697
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600698 def _need_lfs(self, ud):
699 return ud.parm.get("lfs", "1") == "1"
700
Brad Bishopc342db32019-05-15 21:57:59 -0400701 def _contains_lfs(self, ud, d, wd):
702 """
703 Check if the repository has 'lfs' (large file) content
704 """
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500705
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600706 if ud.nobranch:
707 # If no branch is specified, use the current git commit
708 refname = self._build_revision(ud, d, ud.names[0])
709 elif wd == ud.clonedir:
710 # The bare clonedir doesn't use the remote names; it has the branch immediately.
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600711 refname = ud.branches[ud.names[0]]
712 else:
713 refname = "origin/%s" % ud.branches[ud.names[0]]
714
715 cmd = "%s grep lfs %s:.gitattributes | wc -l" % (
716 ud.basecmd, refname)
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500717
Brad Bishopc342db32019-05-15 21:57:59 -0400718 try:
719 output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
720 if int(output) > 0:
721 return True
722 except (bb.fetch2.FetchError,ValueError):
723 pass
724 return False
725
Brad Bishop00e122a2019-10-05 11:10:57 -0400726 def _find_git_lfs(self, d):
727 """
728 Return True if git-lfs can be found, False otherwise.
729 """
730 import shutil
731 return shutil.which("git-lfs", path=d.getVar('PATH')) is not None
732
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733 def _get_repo_url(self, ud):
734 """
735 Return the repository URL
736 """
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600737 # Note that we do not support passwords directly in the git urls. There are several
738 # reasons. SRC_URI can be written out to things like buildhistory and people don't
739 # want to leak passwords like that. Its also all too easy to share metadata without
740 # removing the password. ssh keys, ~/.netrc and ~/.ssh/config files can be used as
741 # alternatives so we will not take patches adding password support here.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500742 if ud.user:
743 username = ud.user + '@'
744 else:
745 username = ""
746 return "%s://%s%s%s" % (ud.proto, username, ud.host, ud.path)
747
748 def _revision_key(self, ud, d, name):
749 """
750 Return a unique key for the url
751 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500752 # Collapse adjacent slashes
Andrew Geissler82c905d2020-04-13 13:39:40 -0500753 return "git:" + ud.host + slash_re.sub(".", ud.path) + ud.unresolvedrev[name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754
755 def _lsremote(self, ud, d, search):
756 """
757 Run git ls-remote with the specified search string
758 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500759 # Prevent recursion e.g. in OE if SRCPV is in PV, PV is in WORKDIR,
760 # and WORKDIR is in PATH (as a result of RSS), our call to
761 # runfetchcmd() exports PATH so this function will get called again (!)
762 # In this scenario the return call of the function isn't actually
763 # important - WORKDIR isn't needed in PATH to call git ls-remote
764 # anyway.
765 if d.getVar('_BB_GIT_IN_LSREMOTE', False):
766 return ''
767 d.setVar('_BB_GIT_IN_LSREMOTE', '1')
768 try:
769 repourl = self._get_repo_url(ud)
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500770 cmd = "%s ls-remote %s %s" % \
771 (ud.basecmd, shlex.quote(repourl), search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500772 if ud.proto.lower() != 'file':
773 bb.fetch2.check_network_access(d, cmd, repourl)
774 output = runfetchcmd(cmd, d, True)
775 if not output:
776 raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
777 finally:
778 d.delVar('_BB_GIT_IN_LSREMOTE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779 return output
780
781 def _latest_revision(self, ud, d, name):
782 """
783 Compute the HEAD revision for the url
784 """
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500785 if not d.getVar("__BBSRCREV_SEEN"):
Andrew Geissler615f2f12022-07-15 14:00:58 -0500786 raise bb.fetch2.FetchError("Recipe uses a floating tag/branch '%s' for repo '%s' without a fixed SRCREV yet doesn't call bb.fetch2.get_srcrev() (use SRCPV in PV for OE)." % (ud.unresolvedrev[name], ud.host+ud.path))
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000787
788 # Ensure we mark as not cached
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500789 bb.fetch2.mark_recipe_nocache(d)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000790
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791 output = self._lsremote(ud, d, "")
792 # Tags of the form ^{} may not work, need to fallback to other form
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 if ud.unresolvedrev[name][:5] == "refs/" or ud.usehead:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500794 head = ud.unresolvedrev[name]
795 tag = ud.unresolvedrev[name]
796 else:
797 head = "refs/heads/%s" % ud.unresolvedrev[name]
798 tag = "refs/tags/%s" % ud.unresolvedrev[name]
799 for s in [head, tag + "^{}", tag]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 for l in output.strip().split('\n'):
801 sha1, ref = l.split()
802 if s == ref:
803 return sha1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804 raise bb.fetch2.FetchError("Unable to resolve '%s' in upstream git repository in git ls-remote output for %s" % \
805 (ud.unresolvedrev[name], ud.host+ud.path))
806
807 def latest_versionstring(self, ud, d):
808 """
809 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
810 by searching through the tags output of ls-remote, comparing
811 versions and returning the highest match.
812 """
813 pupver = ('', '')
814
Brad Bishop19323692019-04-05 15:28:33 -0400815 tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX') or r"(?P<pver>([0-9][\.|_]?)+)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816 try:
817 output = self._lsremote(ud, d, "refs/tags/*")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400818 except (bb.fetch2.FetchError, bb.fetch2.NetworkAccess) as e:
819 bb.note("Could not list remote: %s" % str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500820 return pupver
821
822 verstring = ""
823 revision = ""
824 for line in output.split("\n"):
825 if not line:
826 break
827
828 tag_head = line.split("/")[-1]
829 # Ignore non-released branches
Brad Bishop19323692019-04-05 15:28:33 -0400830 m = re.search(r"(alpha|beta|rc|final)+", tag_head)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831 if m:
832 continue
833
834 # search for version in the line
835 tag = tagregex.search(tag_head)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500836 if tag is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500837 continue
838
839 tag = tag.group('pver')
840 tag = tag.replace("_", ".")
841
842 if verstring and bb.utils.vercmp(("0", tag, ""), ("0", verstring, "")) < 0:
843 continue
844
845 verstring = tag
846 revision = line.split()[0]
847 pupver = (verstring, revision)
848
849 return pupver
850
851 def _build_revision(self, ud, d, name):
852 return ud.revisions[name]
853
854 def gitpkgv_revision(self, ud, d, name):
855 """
856 Return a sortable revision number by counting commits in the history
857 Based on gitpkgv.bblass in meta-openembedded
858 """
859 rev = self._build_revision(ud, d, name)
860 localpath = ud.localpath
861 rev_file = os.path.join(localpath, "oe-gitpkgv_" + rev)
862 if not os.path.exists(localpath):
863 commits = None
864 else:
865 if not os.path.exists(rev_file) or not os.path.getsize(rev_file):
866 from pipes import quote
867 commits = bb.fetch2.runfetchcmd(
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500868 "git rev-list %s -- | wc -l" % quote(rev),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869 d, quiet=True).strip().lstrip('0')
870 if commits:
871 open(rev_file, "w").write("%d\n" % int(commits))
872 else:
873 commits = open(rev_file, "r").readline(128).strip()
874 if commits:
875 return False, "%s+%s" % (commits, rev[:7])
876 else:
877 return True, str(rev)
878
879 def checkstatus(self, fetch, ud, d):
880 try:
881 self._lsremote(ud, d, "")
882 return True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500883 except bb.fetch2.FetchError:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884 return False