Merge ~nacc/git-ubuntu:lp1731554-importer-rework into git-ubuntu:master
- Git
- lp:~nacc/git-ubuntu
- lp1731554-importer-rework
- Merge into master
Status: | Superseded | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | ~nacc/git-ubuntu:lp1731554-importer-rework | ||||||||
Merge into: | git-ubuntu:master | ||||||||
Diff against target: |
1780 lines (+765/-608) 6 files modified
gitubuntu/dsc.py (+38/-0) gitubuntu/git_repository.py (+18/-5) gitubuntu/importer.py (+567/-493) gitubuntu/importerutils.py (+26/-0) gitubuntu/importppa.py (+2/-4) gitubuntu/source_information.py (+114/-106) |
||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
git-ubuntu developers | Pending | ||
Review via email: mp+333852@code.launchpad.net |
Commit message
Description of the change
Make jenkins happy.
- 0fd899e... by Nish Aravamudan
-
importer: more consolidation
Unmerged commits
- 0fd899e... by Nish Aravamudan
-
importer: more consolidation
- ff6d378... by Nish Aravamudan
-
importer: massive rework
I'm sorry, Robie :)
Basically, our current main importer loop looks like:
for applied in unapplied, applied:
for dist in debian, ubuntu:
for new publishes in $dist relative to $applied $dist branches
import $applied publish
this updates the branch pointer for the pocket + series
if dist == 'ubuntu': this also updates the devel pointersThat is a fair amount of branch manipulation that will be discarded (the
series might have multiple publishes). Esp. on the first/reimport import
(worst case).So instead:
for dist in debian, ubuntu:
for applied in unapplied, applied:
for new publishes in $dist relative to $applied $dist branches
if publish has already been imported: continue
import $applied publishupdate all affected branch pointers,
where affected is defined by running track of refs we *would* have
updated in the original algorithm, and we also store the 'last'
commit that it would have pointed to. This consolidates the branch
and devel updates into one place as well.
This also makes the importer algorithm match the spec on some level
-- update the commit graph by importing only new publishes (which
would create new import tags and new applied tags, while verifying
any repeated publish data matches exactly) and then forcibly moving
branch pointers to where they are "now" in Launchpad. The branch
pointers are not part of the commit graph, so they change in a
distinct step.In my testing, this drops the reimport time for ipsec-tools from ~65
minutes to ~42 minutes consistently. We could probably speed it up more
starting from this base, if need be -- e.g., I'm not sure it makes sense
to to do the import action itself in the loop. Perhaps it's better to
accumulate the set of unique publishes in a set and then iterate that
set in a second loop.Given that we are going to be doing a lot of reimporting soon, speeding
it up is a net win. This also tries to clean up code for readability,
and add comment as we go.Note this does not resolve the fundamental issue I am sure exists:
orphan tags. It simply removes support for them.LP: #1731554
- e138c26... by Nish Aravamudan
-
source_information: drop unused parent_
{applied, }_head_ name These are no longer used after the importer publishing parent drop.
- 2bbeba2... by Nish Aravamudan
-
importer: LP: #1731555
Preview Diff
1 | diff --git a/gitubuntu/dsc.py b/gitubuntu/dsc.py |
2 | index 1fc3129..91a78ea 100644 |
3 | --- a/gitubuntu/dsc.py |
4 | +++ b/gitubuntu/dsc.py |
5 | @@ -84,3 +84,41 @@ class GitUbuntuDsc(Dsc): |
6 | @property |
7 | def all_tarball_paths(self): |
8 | return [self.orig_tarball_path] + list(self.component_tarball_paths.values()) |
9 | + |
10 | + def _compare_dsc(self, other, field, key): |
11 | + if field not in self or field not in other: |
12 | + return False |
13 | + our_checksums = dict( |
14 | + (entry['name'], (int(entry['size']), entry[key])) |
15 | + for entry in self[field] |
16 | + ) |
17 | + their_checksums = dict( |
18 | + (entry['name'], (int(entry['size']), entry[key])) |
19 | + for entry in other[field] |
20 | + ) |
21 | + # check that every file in ours is in theirs |
22 | + for name, (size, checksum) in our_checksums.items(): |
23 | + if name not in their_checksums: |
24 | + return False |
25 | + if size != their_checksums[name][0] or checksum != their_checksums[name][1]: |
26 | + return False |
27 | + # check that every file in theirs is in ours |
28 | + for name, (size, checksum) in their_checksums.items(): |
29 | + if name not in our_checksums: |
30 | + return False |
31 | + if size != our_checksums[name][0] or checksum != our_checksums[name][1]: |
32 | + return False |
33 | + # all files in ours and theirs for this hash match |
34 | + return True |
35 | + |
36 | + def compare_dsc(self, other): |
37 | + """Check whether some set of hashes in these two dscs are |
38 | + identical |
39 | + """ |
40 | + return any( |
41 | + self._compare_dsc(other, field, key) for field, key in ( |
42 | + ('Checksums-Sha256', 'sha256'), |
43 | + ('Checksums-Sha1', 'sha1'), |
44 | + ('Files', 'md5sum'), |
45 | + ) |
46 | + ) |
47 | diff --git a/gitubuntu/git_repository.py b/gitubuntu/git_repository.py |
48 | index c598c10..45d5166 100644 |
49 | --- a/gitubuntu/git_repository.py |
50 | +++ b/gitubuntu/git_repository.py |
51 | @@ -537,6 +537,20 @@ class GitUbuntuRepository: |
52 | ) |
53 | |
54 | @contextmanager |
55 | + def dsc_branch(self, dist, namespace='pkg'): |
56 | + """Context manager wrapping dsc branch manipulation |
57 | + |
58 | + In this context, HEAD will point to the dsc branch for |
59 | + @namespace. |
60 | + """ |
61 | + dsc_branch = '%s/importer/%s/dsc' % (namespace, dist) |
62 | + with self.temporary_worktree(dsc_branch): |
63 | + try: |
64 | + yield |
65 | + except: |
66 | + raise |
67 | + |
68 | + @contextmanager |
69 | def pristine_tar_branches(self, dist, namespace='pkg'): |
70 | """Context manager wrapping pristine-tar branch manipulation |
71 | |
72 | @@ -1106,16 +1120,15 @@ class GitUbuntuRepository: |
73 | 'Cannot get changelog source package name' |
74 | ) |
75 | |
76 | - def get_heads_and_versions(self, head_prefix, namespace): |
77 | + def get_heads_and_versions(self, head_ref_namespace): |
78 | """Extract the last version in debian/changelog of all |
79 | - '<namespace>/<head_prefix>/debian/*' and |
80 | - '<namespace>/<head_prefix>/ubuntu/*' branches. |
81 | + '<namespace>/debian/*' and |
82 | + '<namespace>/ubuntu/*' branches. |
83 | """ |
84 | versions = dict() |
85 | errors = False |
86 | for head in self.local_branches: |
87 | - prefix = '%s/%s' % (namespace, head_prefix) |
88 | - if not head.branch_name.startswith(prefix): |
89 | + if not head.branch_name.startswith(head_ref_namespace): |
90 | continue |
91 | if 'ubuntu/devel' in head.branch_name: |
92 | continue |
93 | diff --git a/gitubuntu/importer.py b/gitubuntu/importer.py |
94 | index 2063f83..bea993d 100644 |
95 | --- a/gitubuntu/importer.py |
96 | +++ b/gitubuntu/importer.py |
97 | @@ -26,8 +26,6 @@ |
98 | |
99 | import argparse |
100 | import atexit |
101 | -import functools |
102 | -import getpass |
103 | import logging |
104 | import os |
105 | import re |
106 | @@ -45,14 +43,13 @@ from gitubuntu.dsc import ( |
107 | from gitubuntu.git_repository import ( |
108 | GitUbuntuRepository, |
109 | GitUbuntuRepositoryFetchError, |
110 | - orphan_tag, |
111 | applied_tag, |
112 | import_tag, |
113 | - upstream_tag, |
114 | PristineTarError, |
115 | is_dir_3_0_quilt, |
116 | ) |
117 | -from gitubuntu.run import decode_binary, run, runq |
118 | +import gitubuntu.importerutils |
119 | +from gitubuntu.run import run, runq |
120 | from gitubuntu.source_information import ( |
121 | GitUbuntuSourceInformation, |
122 | NoPublicationHistoryException, |
123 | @@ -130,6 +127,14 @@ def cleanup(no_clean, local_dir): |
124 | logging.info('Leaving %s as directed' % local_dir) |
125 | |
126 | |
127 | +def debug_log_head_versions(head_versions): |
128 | + for head_name in sorted(head_versions): |
129 | + logging.debug('Last imported %s version is %s', |
130 | + head_name, |
131 | + head_versions[head_name]['version'] |
132 | + ) |
133 | + |
134 | + |
135 | # XXX: need a namedtuple to hold common arguments |
136 | def main( |
137 | pkgname, |
138 | @@ -195,6 +200,9 @@ def main( |
139 | else: |
140 | namespace = owner |
141 | |
142 | + # now sets a global _PARENT_OVERRIDES |
143 | + parse_parentfile(parentfile, pkgname) |
144 | + |
145 | logging.info('Ubuntu Server Team importer v%s' % VERSION) |
146 | |
147 | repo = GitUbuntuRepository(directory, user, proto) |
148 | @@ -203,6 +211,13 @@ def main( |
149 | |
150 | atexit.register(cleanup, no_clean, repo.local_dir) |
151 | |
152 | + if dl_cache is None: |
153 | + workdir = os.path.join(repo.git_dir, CACHE_PATH) |
154 | + else: |
155 | + workdir = dl_cache |
156 | + |
157 | + os.makedirs(workdir, exist_ok=True) |
158 | + |
159 | repo.add_base_remotes(pkgname, repo_owner=owner) |
160 | if not no_fetch and not reimport: |
161 | try: |
162 | @@ -241,97 +256,21 @@ def main( |
163 | |
164 | repo.ensure_importer_branches_exist(namespace) |
165 | |
166 | - debian_sinfo = GitUbuntuSourceInformation( |
167 | - 'debian', |
168 | - pkgname, |
169 | - os.path.abspath(pullfile), |
170 | - retries, |
171 | - retry_backoffs, |
172 | - ) |
173 | - |
174 | - ubuntu_sinfo = GitUbuntuSourceInformation( |
175 | - 'ubuntu', |
176 | - pkgname, |
177 | - os.path.abspath(pullfile), |
178 | - retries, |
179 | - retry_backoffs, |
180 | - ) |
181 | - |
182 | - debian_head_versions = ( |
183 | - repo.get_heads_and_versions('debian', namespace) |
184 | - ) |
185 | - for head_name in sorted(debian_head_versions): |
186 | - _, _, pretty_head_name = head_name.partition('%s/' % namespace) |
187 | - logging.debug('Last imported %s version is %s', |
188 | - pretty_head_name, |
189 | - debian_head_versions[head_name]['version'] |
190 | - ) |
191 | - |
192 | - ubuntu_head_versions = ( |
193 | - repo.get_heads_and_versions('ubuntu', namespace) |
194 | - ) |
195 | - for head_name in sorted(ubuntu_head_versions): |
196 | - _, _, pretty_head_name = head_name.partition('%s/' % namespace) |
197 | - logging.debug('Last imported %s version is %s', |
198 | - pretty_head_name, |
199 | - ubuntu_head_versions[head_name]['version'] |
200 | - ) |
201 | - |
202 | - if not skip_applied: |
203 | - applied_debian_head_versions = ( |
204 | - repo.get_heads_and_versions( |
205 | - 'applied/debian', namespace |
206 | - ) |
207 | - ) |
208 | - for head_name in sorted(applied_debian_head_versions): |
209 | - _, _, pretty_head_name = head_name.partition( |
210 | - '%s/' % namespace |
211 | - ) |
212 | - logging.debug('Last applied %s version is %s', |
213 | - pretty_head_name, |
214 | - applied_debian_head_versions[head_name]['version'] |
215 | - ) |
216 | - |
217 | - applied_ubuntu_head_versions = ( |
218 | - repo.get_heads_and_versions( |
219 | - 'applied/ubuntu', namespace |
220 | - ) |
221 | - ) |
222 | - for head_name in sorted(applied_ubuntu_head_versions): |
223 | - _, _, pretty_head_name = head_name.partition( |
224 | - '%s/' % namespace |
225 | - ) |
226 | - logging.debug('Last applied %s version is %s', |
227 | - pretty_head_name, |
228 | - applied_ubuntu_head_versions[head_name]['version'] |
229 | - ) |
230 | - |
231 | oldcwd = os.getcwd() |
232 | os.chdir(repo.local_dir) |
233 | |
234 | - if dl_cache is None: |
235 | - workdir = os.path.join(repo.git_dir, CACHE_PATH) |
236 | - else: |
237 | - workdir = dl_cache |
238 | - |
239 | - os.makedirs(workdir, exist_ok=True) |
240 | - |
241 | - # now sets a global _PARENT_OVERRIDES |
242 | - parse_parentfile(parentfile, pkgname) |
243 | - |
244 | only_debian, history_found = import_publishes( |
245 | repo=repo, |
246 | pkgname=pkgname, |
247 | - namespace=namespace, |
248 | - patches_applied=False, |
249 | - debian_head_versions=debian_head_versions, |
250 | - ubuntu_head_versions=ubuntu_head_versions, |
251 | - debian_sinfo=debian_sinfo, |
252 | - ubuntu_sinfo=ubuntu_sinfo, |
253 | + base_ref_namespace=namespace, |
254 | active_series_only=active_series_only, |
255 | workdir=workdir, |
256 | skip_orig=skip_orig, |
257 | + skip_applied=skip_applied, |
258 | allow_applied_failures=allow_applied_failures, |
259 | + pullfile=pullfile, |
260 | + retries=retries, |
261 | + retry_backoffs=retry_backoffs, |
262 | ) |
263 | |
264 | if not history_found: |
265 | @@ -339,29 +278,6 @@ def main( |
266 | "Wrong source package name?", pkgname) |
267 | return 1 |
268 | |
269 | - if not skip_applied: |
270 | - import_publishes( |
271 | - repo=repo, |
272 | - pkgname=pkgname, |
273 | - namespace=namespace, |
274 | - patches_applied=True, |
275 | - debian_head_versions=applied_debian_head_versions, |
276 | - ubuntu_head_versions=applied_ubuntu_head_versions, |
277 | - debian_sinfo=debian_sinfo, |
278 | - ubuntu_sinfo=ubuntu_sinfo, |
279 | - active_series_only=active_series_only, |
280 | - workdir=workdir, |
281 | - skip_orig=skip_orig, |
282 | - allow_applied_failures=allow_applied_failures, |
283 | - ) |
284 | - |
285 | - update_devel_branches( |
286 | - repo=repo, |
287 | - namespace=namespace, |
288 | - pkgname=pkgname, |
289 | - ubuntu_sinfo=ubuntu_sinfo, |
290 | - ) |
291 | - |
292 | os.chdir(oldcwd) |
293 | |
294 | repo.garbage_collect() |
295 | @@ -476,49 +392,48 @@ def _commit_import( |
296 | debian/changelog. |
297 | |
298 | Arguments: |
299 | - spi - A SourcePackageInformation instance for this publish |
300 | - tree_hash - SHA1 from git-dsc-commit --tree-only of this publish |
301 | - changelog_parent_commit = SHA1 of changelog parent |
302 | - upload_parent_commit = SHA1 of upload parent |
303 | - unapplied_parent_commit = SHA1 of unapplied-patches import parent |
304 | + repo - a gitubuntu.git_repository.GitUbuntuRepositoory object |
305 | + spi - a |
306 | + gitubuntu.source_information.GitUbuntuSourcePackageInformation |
307 | + object for the publish in @tree_hash |
308 | + tree_hash - string SHA1 from git-dsc-commit --tree-only of this publish |
309 | + changelog_parent_commit - string SHA1 of changelog parent |
310 | + upload_parent_commit - string SHA1 of upload parent |
311 | + unapplied_parent_commit - string SHA1 of unapplied-patches import |
312 | + parent |
313 | """ |
314 | tag = None |
315 | |
316 | if unapplied_parent_commit: |
317 | import_type = 'patches-applied' |
318 | - target_head_name = spi.applied_head_name(namespace) |
319 | if repo.get_applied_tag(spi.version, namespace) is None: |
320 | # Not imported before |
321 | tag = applied_tag(spi.version, namespace) |
322 | else: |
323 | import_type = 'patches-unapplied' |
324 | - target_head_name = spi.head_name(namespace) |
325 | if repo.get_import_tag(spi.version, namespace) is None: |
326 | # Not imported before |
327 | tag = import_tag(spi.version, namespace) |
328 | |
329 | - # Do not show importer/ namespace to user |
330 | - _, _, pretty_head_name = target_head_name.partition('%s/' % namespace) |
331 | - |
332 | changelog_entry = get_changelog_for_commit( |
333 | repo, |
334 | tree_hash, |
335 | changelog_parent_commit |
336 | ) |
337 | - msg = (b'Import %s version %b to %b\n\nImported using git-ubuntu import.' % |
338 | - (import_type.encode(), spi.version.encode(), pretty_head_name.encode()) |
339 | - ) |
340 | + msg = b'Import %s version %b\n\nImported using git-ubuntu import.' % ( |
341 | + import_type.encode(), |
342 | + spi.version.encode(), |
343 | + ) |
344 | |
345 | parents = [] |
346 | |
347 | - if changelog_parent_commit is None and \ |
348 | - upload_parent_commit is None and \ |
349 | - unapplied_parent_commit is None and \ |
350 | - target_head_name in repo.local_branch_names: |
351 | - # No parents and not creating a new branch |
352 | - tag = orphan_tag(spi.version, namespace) |
353 | - else: |
354 | - msg += b'\n' |
355 | + #if changelog_parent_commit is None and \ |
356 | + # upload_parent_commit is None and \ |
357 | + # unapplied_parent_commit is None: |
358 | + # # XXX: No parents and not creating a new branch |
359 | + # tag = orphan_tag(spi.version, namespace) |
360 | + #else: |
361 | + msg += b'\n' |
362 | |
363 | if changelog_parent_commit is not None: |
364 | parents.append(changelog_parent_commit) |
365 | @@ -539,19 +454,26 @@ def _commit_import( |
366 | environment_spi=spi |
367 | ) |
368 | |
369 | - repo.update_head_to_commit(target_head_name, commit_hash) |
370 | - |
371 | - logging.debug('Committed %s import of %s as %s in %s', |
372 | - import_type, spi.version, commit_hash, pretty_head_name |
373 | - ) |
374 | + logging.debug( |
375 | + "Committed %s import of %s as %s", |
376 | + import_type, |
377 | + spi.version, |
378 | + commit_hash, |
379 | + ) |
380 | |
381 | if tag is not None: |
382 | # should be annotated to use create_tag API |
383 | logging.debug('Creating tag %s pointing to %s', tag, commit_hash) |
384 | - repo.git_run(['tag', '-a', '-m', 'git-ubuntu import v%s' % VERSION, |
385 | - tag, commit_hash |
386 | - ] |
387 | - ) |
388 | + repo.git_run( |
389 | + [ |
390 | + 'tag', |
391 | + '-a', |
392 | + '-m', |
393 | + 'git-ubuntu import v%s' % VERSION, |
394 | + tag, |
395 | + commit_hash, |
396 | + ], |
397 | + ) |
398 | |
399 | def commit_unapplied_patches_import( |
400 | repo, |
401 | @@ -592,9 +514,63 @@ def commit_applied_patches_import( |
402 | unapplied_parent_commit, |
403 | ) |
404 | |
405 | +def dsc_already_imported( |
406 | + repo, |
407 | + spi, |
408 | + ref_namespace, |
409 | + distname, |
410 | +): |
411 | + # always check if the same dist has a matching dsc |
412 | + with repo.dsc_branch( |
413 | + namespace=ref_namespace, |
414 | + dist=distname, |
415 | + ): |
416 | + cached_dsc_path = os.path.join( |
417 | + os.getcwd(), |
418 | + spi.version, |
419 | + os.path.basename(spi.dsc_pathname), |
420 | + ) |
421 | + |
422 | + if os.path.exists(cached_dsc_path): |
423 | + # If we have imported this version before, compare the |
424 | + # DSC in the DSC branch to this one |
425 | + imported_dsc = GitUbuntuDsc(cached_dsc_path) |
426 | + return GitUbuntuDsc(spi.dsc_pathname).compare_dsc(imported_dsc) |
427 | + |
428 | + # if a debian dsc exists and matches, but an ubuntu dsc does not |
429 | + # exist, import the ubuntu dsc, but signal that a full import is |
430 | + # not necessary |
431 | + if distname == 'ubuntu': |
432 | + with repo.dsc_branch( |
433 | + namespace=ref_namespace, |
434 | + dist='debian', |
435 | + ): |
436 | + imported_dsc_path = os.path.join( |
437 | + os.getcwd(), |
438 | + spi.version, |
439 | + os.path.basename(spi.dsc_pathname), |
440 | + ) |
441 | + |
442 | + if os.path.exists(imported_dsc_path): |
443 | + # If we have imported this version before, compare the |
444 | + # DSC in the DSC branch to this one |
445 | + imported_dsc = GitUbuntuDsc(imported_dsc_path) |
446 | + if GitUbuntuDsc(spi.dsc_pathname).compare_dsc(imported_dsc): |
447 | + import_dsc( |
448 | + repo, |
449 | + GitUbuntuDsc(spi.dsc_pathname), |
450 | + ref_namespace, |
451 | + str(spi.version), |
452 | + spi.distribution.name.lower(), |
453 | + ) |
454 | + return True |
455 | + return False |
456 | + |
457 | def import_dsc(repo, dsc, namespace, version, dist): |
458 | """Imports the dsc file to importer/{debian,ubuntu}/dsc |
459 | |
460 | + XXX use a context manager? |
461 | + |
462 | Arguments: |
463 | spi - A GitUbuntuSourcePackageInformation instance |
464 | """ |
465 | @@ -937,7 +913,7 @@ def _devel_branch_updates( |
466 | |
467 | devel_hash = None |
468 | devel_name = None |
469 | - devel_branch_name = ref_prefix + 'ubuntu/devel' |
470 | + devel_branch_name = ref_prefix + '/devel' |
471 | |
472 | for series_name in series_name_list: |
473 | series_devel_hash = None |
474 | @@ -946,7 +922,7 @@ def _devel_branch_updates( |
475 | # Find highest version publish in these pockets, favoring this |
476 | # order of pockets |
477 | for suff in ['-proposed', '-updates', '-security', '']: |
478 | - name = "%subuntu/%s%s" % (ref_prefix, series_name, suff) |
479 | + name = "%s/%s%s" % (ref_prefix, series_name, suff) |
480 | if name in head_versions: |
481 | head_commit, head_version = head_versions[name] |
482 | if version_compare(head_version, series_devel_version) > 0: |
483 | @@ -954,7 +930,7 @@ def _devel_branch_updates( |
484 | series_devel_version = head_version |
485 | series_devel_hash = head_commit |
486 | if series_devel_hash: |
487 | - series_devel_branch_name = '%subuntu/%s-devel' % ( |
488 | + series_devel_branch_name = '%s/%s-devel' % ( |
489 | ref_prefix, |
490 | series_name, |
491 | ) |
492 | @@ -969,87 +945,213 @@ def _devel_branch_updates( |
493 | devel_name = series_devel_name |
494 | devel_hash = series_devel_hash |
495 | |
496 | - yield ref_prefix + 'ubuntu/devel', devel_hash |
497 | + yield devel_branch_name, devel_hash |
498 | |
499 | |
500 | -def update_devel_branches( |
501 | +def update_branches( |
502 | + distname, |
503 | + dist_sinfo, |
504 | repo, |
505 | - namespace, |
506 | + head_ref_namespace, |
507 | pkgname, |
508 | - ubuntu_sinfo, |
509 | + updated_head_refs, |
510 | ): |
511 | - """update_devel_branches - move the 'meta' -devel branches to the |
512 | - latest publication in each series |
513 | + """update_branches - move the series+pocket branches based upon Launchpad imports |
514 | |
515 | - For all known series in @ubuntu_sinfo, update the |
516 | - ubuntu/series-devel branch to the latest version in the series' |
517 | - pocket branches. |
518 | - |
519 | - Update the ubuntu/devel branch to the latest ubuntu/series-devel |
520 | - branch. |
521 | - |
522 | - Do this for both the unapplied and applied branches. |
523 | + If distname is 'ubuntu', ubuntu/series-devel branches and update the |
524 | + ubuntu/devel branch to the latest ubuntu/series-devel branch. |
525 | |
526 | Arguments: |
527 | + distname - string distribution name (one of 'debian', 'ubuntu') |
528 | + dist_sinfo - gitubuntu.source_information.GitUbuntuSourceInformation object |
529 | repo - gitubuntu.git_repository.GitUbuntuRepository object |
530 | + head_ref_namespace - string namespace under which the relevant |
531 | + branch refs can be found |
532 | pkgname - string source package name |
533 | - namespace - string namespace under which the relevant branch refs |
534 | - can be found |
535 | - ubuntu_sinfo - GitUbuntuSourceInformation object for the ubuntu |
536 | - archive |
537 | """ |
538 | - for applied_prefix in ['', 'applied/']: |
539 | - head_versions = { |
540 | - ref: (str(target['head'].peel().id), target['version']) |
541 | - for ref, target in repo.get_heads_and_versions( |
542 | - '%subuntu' % applied_prefix, |
543 | - namespace=namespace, |
544 | - ).items() |
545 | - } |
546 | - for ref, commit in _devel_branch_updates( |
547 | - ref_prefix=('%s/%s' % (namespace, applied_prefix)), |
548 | - head_versions=head_versions, |
549 | - series_name_list=ubuntu_sinfo.all_series_name_list, |
550 | - ): |
551 | - if commit is None: |
552 | - logging.warn( |
553 | - "Source package '%s' does not appear to have been published " |
554 | - "in Ubuntu. No %s created.", |
555 | - pkgname, |
556 | - ref, |
557 | + for ref, commit_hash in updated_head_refs.items(): |
558 | + repo.update_head_to_commit( |
559 | + ref, |
560 | + commit_hash, |
561 | + ) |
562 | + |
563 | + if distname == 'debian': |
564 | + return |
565 | + |
566 | + head_versions = { |
567 | + ref: (str(target['head'].peel().id), target['version']) |
568 | + for ref, target in repo.get_heads_and_versions( |
569 | + head_ref_namespace, |
570 | + ).items() |
571 | + } |
572 | + for ref, commit in _devel_branch_updates( |
573 | + ref_prefix=head_ref_namespace, |
574 | + head_versions=head_versions, |
575 | + series_name_list=dist_sinfo.all_series_name_list, |
576 | + ): |
577 | + if commit is None: |
578 | + logging.warn( |
579 | + "Source package '%s' does not appear to have been published " |
580 | + "in Ubuntu. No %s created.", |
581 | + pkgname, |
582 | + ref, |
583 | + ) |
584 | + else: |
585 | + logging.debug( |
586 | + "Setting %s to '%s'", |
587 | + ref, |
588 | + commit, |
589 | + ) |
590 | + repo.update_head_to_commit(ref, commit) |
591 | + |
592 | + |
593 | +def get_changelog_parent_commit( |
594 | + repo, |
595 | + spi, |
596 | + ref_namespace, |
597 | + import_tree_versions, |
598 | + patches_applied, |
599 | +): |
600 | + if spi.version in _PARENT_OVERRIDES: |
601 | + logging.debug( |
602 | + '%s is specified in the parent override file.', |
603 | + spi.version |
604 | + ) |
605 | + |
606 | + try: |
607 | + ( |
608 | + unapplied_changelog_parent_commit, |
609 | + applied_changelog_parent_commit, |
610 | + ) = override_parents(repo, spi, ref_namespace) |
611 | + if patches_applied: |
612 | + return applied_changelog_parent_commit |
613 | + else: |
614 | + return unapplied_changelog_parent_commit |
615 | + except ParentOverrideError as e: |
616 | + logging.error("%s" % e) |
617 | + return None |
618 | + else: |
619 | + # Walk changelog backwards until we find an imported version |
620 | + for version in import_tree_versions: |
621 | + if patches_applied: |
622 | + changelog_parent_tag = repo.get_applied_tag( |
623 | + version, |
624 | + ref_namespace, |
625 | ) |
626 | else: |
627 | - logging.debug( |
628 | - "Setting %s to '%s'", |
629 | - ref, |
630 | - commit, |
631 | + changelog_parent_tag = repo.get_import_tag( |
632 | + version, |
633 | + ref_namespace, |
634 | ) |
635 | - repo.update_head_to_commit(ref, commit) |
636 | + if changelog_parent_tag: |
637 | + # sanity check that version from d/changelog of the |
638 | + # tagged parent matches ours |
639 | + parent_changelog_version, _ = \ |
640 | + repo.get_changelog_versions_from_treeish( |
641 | + str(changelog_parent_tag.peel(pygit2.Tree).id), |
642 | + ) |
643 | + # if the parent was imported via a parent override |
644 | + # itself, assume it is valid without checking its |
645 | + # tree's debian/changelog, as it wasn't used |
646 | + if (version not in _PARENT_OVERRIDES and |
647 | + parent_changelog_version != version |
648 | + ): |
649 | + logging.error( |
650 | + "Found a tag corresponding to parent version " |
651 | + "%s, but d/changelog version (%s) differs. Will " |
652 | + "orphan commit.", |
653 | + version, |
654 | + parent_changelog_version, |
655 | + ) |
656 | + return None |
657 | + else: |
658 | + logging.debug("Changelog parent (tag) is %s", |
659 | + repo.tag_to_pretty_name(changelog_parent_tag) |
660 | + ) |
661 | + return str(changelog_parent_tag.peel().id) |
662 | + return None |
663 | + |
664 | + |
665 | +def get_unapplied_changelog_parent_commit( |
666 | + repo, |
667 | + spi, |
668 | + ref_namespace, |
669 | + import_tree_versions, |
670 | +): |
671 | + return get_changelog_parent_commit( |
672 | + repo, |
673 | + spi, |
674 | + ref_namespace, |
675 | + import_tree_versions, |
676 | + patches_applied=False, |
677 | + ) |
678 | + |
679 | + |
680 | +def get_applied_changelog_parent_commit( |
681 | + repo, |
682 | + spi, |
683 | + ref_namespace, |
684 | + import_tree_versions, |
685 | +): |
686 | + return get_changelog_parent_commit( |
687 | + repo, |
688 | + spi, |
689 | + ref_namespace, |
690 | + import_tree_versions, |
691 | + patches_applied=True, |
692 | + ) |
693 | + |
694 | + |
695 | +def get_upload_parent_commit( |
696 | + repo, |
697 | + spi, |
698 | + ref_namespace, |
699 | + unapplied_import_tree_hash, |
700 | +): |
701 | + upload_tag = repo.get_upload_tag(spi.version, ref_namespace) |
702 | + if upload_tag: |
703 | + if str(upload_tag.peel().tree.id) != unapplied_import_tree_hash: |
704 | + logging.warn( |
705 | + "Found upload tag %s, but the corresponding tree " |
706 | + "does not match the published version. Will import %s as " |
707 | + "normal, ignoring the upload tag.", |
708 | + repo.tag_to_pretty_name(upload_tag), |
709 | + spi.version, |
710 | + ) |
711 | + return None |
712 | + else: |
713 | + upload_parent_commit = str(upload_tag.peel().id) |
714 | + return upload_parent_commit |
715 | + |
716 | + return None |
717 | |
718 | |
719 | # imports a package based upon source package information |
720 | -def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
721 | +def import_unapplied_spi(repo, spi, ref_namespace, skip_orig): |
722 | """Imports a source package from Launchpad into the git |
723 | repository |
724 | |
725 | + Only called if the version has not been imported before |
726 | + |
727 | Arguments: |
728 | - spi - a SourcePackageInformation instance |
729 | + repo - a gitubutu.git_repository.GitUbuntuRepository object |
730 | + spi - a |
731 | + gitubuntu.source_information.GitUbuntuSourcePackageInformation object |
732 | + ref_namespace - a string Git-ref namespace this import will live in |
733 | + skip_orig - a boolean indicating the orig tarball should not |
734 | + be imported |
735 | """ |
736 | - pretty_head_name = spi.pretty_head_name |
737 | |
738 | - logging.info('Importing patches-unapplied %s to %s', |
739 | - spi.version, pretty_head_name |
740 | - ) |
741 | - unapplied_tip_head = repo.get_head_by_name(spi.head_name(namespace)) |
742 | + logging.info("Importing patches-unapplied %s", spi.version) |
743 | |
744 | - spi.pull() |
745 | + assert not repo.get_import_tag(spi.version, ref_namespace) |
746 | |
747 | repo.clean_repository_state() |
748 | |
749 | import_dsc( |
750 | repo, |
751 | GitUbuntuDsc(spi.dsc_pathname), |
752 | - namespace, |
753 | + ref_namespace, |
754 | str(spi.version), |
755 | spi.distribution.name.lower(), |
756 | ) |
757 | @@ -1058,7 +1160,7 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
758 | import_orig( |
759 | repo, |
760 | GitUbuntuDsc(spi.dsc_pathname), |
761 | - namespace, |
762 | + ref_namespace, |
763 | str(spi.version), |
764 | spi.distribution.name.lower(), |
765 | ) |
766 | @@ -1094,132 +1196,46 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
767 | 'source pkg version: {} != changelog version: {}'.format( |
768 | spi.version, changelog_version)) |
769 | |
770 | - unapplied_tip_version = None |
771 | - # check if the version to import has already been imported to |
772 | - # this head |
773 | - if unapplied_tip_head is not None: |
774 | - if repo.treeishs_identical( |
775 | - unapplied_import_tree_hash, str(unapplied_tip_head.peel().id) |
776 | - ): |
777 | - logging.warn('%s is identical to %s', |
778 | - pretty_head_name, spi.version |
779 | - ) |
780 | - return |
781 | - unapplied_tip_version, _ = repo.get_changelog_versions_from_treeish( |
782 | - str(unapplied_tip_head.peel().id), |
783 | - ) |
784 | - |
785 | - logging.debug('Tip version is %s', unapplied_tip_version) |
786 | - |
787 | - unapplied_changelog_parent_commit = None |
788 | - upload_parent_commit = None |
789 | - unapplied_parent_commit = None |
790 | - |
791 | - if spi.version in _PARENT_OVERRIDES: |
792 | - logging.debug( |
793 | - '%s is specified in the parent override file.', |
794 | - spi.version |
795 | - ) |
796 | - |
797 | - try: |
798 | - ( |
799 | - unapplied_changelog_parent_commit, |
800 | - _ |
801 | - ) = override_parents(repo, spi, namespace) |
802 | - except ParentOverrideError as e: |
803 | - logging.error("%s" % e) |
804 | - return |
805 | - else: |
806 | - if version_compare(str(spi.version), unapplied_tip_version) <= 0: |
807 | - logging.warn( |
808 | - "Version to import (%s) is not after %s tip (%s)", |
809 | - spi.version, |
810 | - pretty_head_name, |
811 | - unapplied_tip_version, |
812 | - ) |
813 | - |
814 | - # Walk changelog backwards until we find an imported version |
815 | - for version in import_tree_versions: |
816 | - unapplied_changelog_parent_tag = repo.get_import_tag(version, namespace) |
817 | - if unapplied_changelog_parent_tag is not None: |
818 | - # sanity check that version from d/changelog of the |
819 | - # tagged parent matches ours |
820 | - parent_changelog_version, _ = \ |
821 | - repo.get_changelog_versions_from_treeish( |
822 | - str(unapplied_changelog_parent_tag.peel(pygit2.Tree).id), |
823 | - ) |
824 | - # if the parent was imported via a parent override |
825 | - # itself, assume it is valid without checking its |
826 | - # tree's debian/changelog, as it wasn't used |
827 | - if (version not in _PARENT_OVERRIDES and |
828 | - parent_changelog_version != version |
829 | - ): |
830 | - logging.error( |
831 | - "Found a tag corresponding to parent version " |
832 | - "%s, but d/changelog version (%s) differs. Will " |
833 | - "orphan commit.", |
834 | - version, |
835 | - parent_changelog_version, |
836 | - ) |
837 | - else: |
838 | - unapplied_changelog_parent_commit = str(unapplied_changelog_parent_tag.peel().id) |
839 | - logging.debug("Changelog parent (tag) is %s", |
840 | - repo.tag_to_pretty_name(unapplied_changelog_parent_tag) |
841 | - ) |
842 | - break |
843 | + unapplied_changelog_parent_commit = get_unapplied_changelog_parent_commit( |
844 | + repo, |
845 | + spi, |
846 | + ref_namespace, |
847 | + import_tree_versions, |
848 | + ) |
849 | |
850 | + # XXX: if we are here, the spi has not been imported -- but that |
851 | + # does not mean the the contents of the imported spi are identical |
852 | + # (rework orphan tags) |
853 | # check if the version to import has already been uploaded to |
854 | # this head -- we leverage the above code to perform a 'dry-run' |
855 | # of the import tree, to obtain the changelog parent |
856 | - upload_tag = repo.get_upload_tag(spi.version, namespace) |
857 | - import_tag = repo.get_import_tag(spi.version, namespace) |
858 | - if import_tag: |
859 | - if str(import_tag.peel().tree.id) != unapplied_import_tree_hash: |
860 | - logging.error( |
861 | - "Found import tag %s, but the corresponding tree " |
862 | - "does not match the published version. Will orphan commit.", |
863 | - repo.tag_to_pretty_name(import_tag), |
864 | + upload_parent_commit = get_upload_parent_commit( |
865 | + repo, |
866 | + spi, |
867 | + ref_namespace, |
868 | + unapplied_import_tree_hash, |
869 | + ) |
870 | + if upload_parent_commit and unapplied_changelog_parent_commit: |
871 | + try: |
872 | + repo.git_run( |
873 | + [ |
874 | + 'merge-base', |
875 | + '--is-ancestor', |
876 | + unapplied_changelog_parent_commit, |
877 | + upload_parent_commit, |
878 | + ], |
879 | + verbose_on_failure=False, |
880 | ) |
881 | unapplied_changelog_parent_commit = None |
882 | - else: |
883 | - repo.update_head_to_commit( |
884 | - spi.head_name(namespace), |
885 | - str(import_tag.peel().id), |
886 | - ) |
887 | - return |
888 | - elif upload_tag: |
889 | - if str(upload_tag.peel().tree.id) != unapplied_import_tree_hash: |
890 | - logging.warn( |
891 | - "Found upload tag %s, but the corresponding tree " |
892 | - "does not match the published version. Will import %s as " |
893 | - "normal, ignoring the upload tag.", |
894 | - repo.tag_to_pretty_name(upload_tag), |
895 | - spi.version, |
896 | - ) |
897 | - else: |
898 | - upload_parent_commit = str(upload_tag.peel().id) |
899 | - |
900 | - if unapplied_changelog_parent_commit is not None: |
901 | - try: |
902 | - repo.git_run( |
903 | - [ |
904 | - 'merge-base', |
905 | - '--is-ancestor', |
906 | - unapplied_changelog_parent_commit, |
907 | - upload_parent_commit, |
908 | - ], |
909 | - verbose_on_failure=False, |
910 | - ) |
911 | - unapplied_changelog_parent_commit = None |
912 | - except CalledProcessError as e: |
913 | - if e.returncode != 1: |
914 | - raise |
915 | + except CalledProcessError as e: |
916 | + if e.returncode != 1: |
917 | + raise |
918 | |
919 | commit_unapplied_patches_import( |
920 | repo, |
921 | spi, |
922 | unapplied_import_tree_hash, |
923 | - namespace, |
924 | + ref_namespace, |
925 | unapplied_changelog_parent_commit, |
926 | upload_parent_commit, |
927 | ) |
928 | @@ -1227,242 +1243,300 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
929 | def import_applied_spi( |
930 | repo, |
931 | spi, |
932 | - namespace, |
933 | - ubuntu_sinfo, |
934 | - allow_applied_failures |
935 | + ref_namespace, |
936 | ): |
937 | """Imports a source package from Launchpad into the git |
938 | repository |
939 | |
940 | Arguments: |
941 | - spi - a SourcePackageInformation instance |
942 | + repo - a gitubutu.git_repository.GitUbuntuRepository object |
943 | + spi - a |
944 | + gitubuntu.source_information.GitUbuntuSourcePackageInformation object |
945 | + ref_namespace - a string Git-ref namespace this import will live in |
946 | """ |
947 | - pretty_head_name = spi.pretty_head_name |
948 | - |
949 | - logging.info('Importing patches-applied %s to %s' % (spi.version, pretty_head_name)) |
950 | - applied_tip_head = repo.get_head_by_name(spi.applied_head_name(namespace)) |
951 | - |
952 | - spi.pull() |
953 | + logging.info('Importing patches-applied %s', spi.version) |
954 | |
955 | repo.clean_repository_state() |
956 | |
957 | - unapplied_parent_tag = repo.get_import_tag(spi.version, namespace) |
958 | + unapplied_parent_tag = repo.get_import_tag(spi.version, ref_namespace) |
959 | unapplied_parent_commit = str(unapplied_parent_tag.peel().id) |
960 | unapplied_import_tree_hash = str(unapplied_parent_tag.peel(pygit2.Tree).id) |
961 | logging.debug('Found patches-unapplied version %s as commit %s', |
962 | str(spi.version), unapplied_parent_commit) |
963 | |
964 | import_tree_versions = repo.get_all_changelog_versions_from_treeish( |
965 | - unapplied_import_tree_hash |
966 | - ) |
967 | - changelog_version = import_tree_versions[0] |
968 | - |
969 | - applied_tip_version = None |
970 | - # check if the version to import has already been imported to |
971 | - # this head |
972 | - if applied_tip_head is not None: |
973 | - if repo.treeishs_identical( |
974 | - unapplied_import_tree_hash, str(applied_tip_head.peel().id) |
975 | - ): |
976 | - logging.warn('%s is identical to %s', |
977 | - pretty_head_name, spi.version |
978 | - ) |
979 | - return |
980 | - applied_tip_version, _ = repo.get_changelog_versions_from_treeish( |
981 | - str(applied_tip_head.peel().id), |
982 | - ) |
983 | - |
984 | - logging.debug('Tip version is %s', applied_tip_version) |
985 | - |
986 | - applied_changelog_parent_commit = None |
987 | - |
988 | - if spi.version in _PARENT_OVERRIDES: |
989 | - logging.debug('%s is specified in the parent override file.' % |
990 | - spi.version |
991 | - ) |
992 | - |
993 | - ( |
994 | - _, |
995 | - applied_changelog_parent_commit, |
996 | - ) = override_parents( |
997 | - repo, |
998 | - spi, |
999 | - namespace, |
1000 | - ) |
1001 | - else: |
1002 | - if version_compare(str(spi.version), applied_tip_version) <= 0: |
1003 | - logging.warn('Version to import (%s) is not after %s tip (%s)', |
1004 | - spi.version, pretty_head_name, applied_tip_version |
1005 | - ) |
1006 | - |
1007 | - # Walk changelog backwards until we find an imported version |
1008 | - for version in import_tree_versions: |
1009 | - applied_changelog_parent_tag = repo.get_applied_tag(version, namespace) |
1010 | - if applied_changelog_parent_tag is not None: |
1011 | - # sanity check that version from d/changelog of the |
1012 | - # tagged parent matches ours |
1013 | - parent_changelog_version, _ = \ |
1014 | - repo.get_changelog_versions_from_treeish( |
1015 | - str(applied_changelog_parent_tag.peel(pygit2.Tree).id), |
1016 | - ) |
1017 | - # if the parent was imported via a parent override |
1018 | - # itself, assume it is valid without checking its |
1019 | - # tree's debian/changelog, as it wasn't used |
1020 | - if (version not in _PARENT_OVERRIDES and |
1021 | - parent_changelog_version != version |
1022 | - ): |
1023 | - logging.error('Found a tag corresponding to ' |
1024 | - 'parent version %s, but d/changelog ' |
1025 | - 'version (%s) differs. Will ' |
1026 | - 'orphan commit.' % ( |
1027 | - version, |
1028 | - parent_changelog_version |
1029 | - ) |
1030 | - ) |
1031 | - else: |
1032 | - applied_changelog_parent_commit = str(applied_changelog_parent_tag.peel().id) |
1033 | - logging.debug('Changelog parent (tag) is %s', |
1034 | - repo.tag_to_pretty_name(applied_changelog_parent_tag) |
1035 | - ) |
1036 | - break |
1037 | + unapplied_import_tree_hash |
1038 | + ) |
1039 | |
1040 | - applied_tag = repo.get_applied_tag(version, namespace) |
1041 | - if applied_tag: |
1042 | - # XXX: What should be checked here? The result of pushing all |
1043 | - # the patches? How to do it most non-destructively? (Might be |
1044 | - # able to use our new helper to get treeish after running a |
1045 | - # command, in this case, `quilt push -a`) |
1046 | - # if str(applied_tag.peel().tree.id) != applied_import_tree_hash: |
1047 | - # logging.error( |
1048 | - # "Found patches-applied import tag %s, but the " |
1049 | - # "corresponding tree does not match the published " |
1050 | - # "version. Will orphan commit.", |
1051 | - # repo.tag_to_pretty_name(applied_tag), |
1052 | - # ) |
1053 | - # applied_changelog_parent_commit = None |
1054 | - #else: |
1055 | - repo.update_head_to_commit( |
1056 | - spi.applied_head_name(namespace), |
1057 | - str(applied_tag.peel().id), |
1058 | - ) |
1059 | - return |
1060 | + applied_changelog_parent_commit = get_applied_changelog_parent_commit( |
1061 | + repo, |
1062 | + spi, |
1063 | + ref_namespace, |
1064 | + import_tree_versions, |
1065 | + ) |
1066 | |
1067 | # Assume no patches to apply |
1068 | applied_import_tree_hash = unapplied_import_tree_hash |
1069 | # get tree id from above commit |
1070 | - try: |
1071 | - for ( |
1072 | - applied_import_tree_hash, |
1073 | - patch_name, |
1074 | - patch_desc |
1075 | - ) in import_patches_applied_tree(repo, spi.dsc_pathname): |
1076 | - # special case for .pc removal |
1077 | - if patch_name is None: |
1078 | - msg = b'%b.' % (patch_desc.encode()) |
1079 | - else: |
1080 | - if patch_desc is None: |
1081 | - patch_desc = ( |
1082 | - "%s\n\nNo DEP3 Subject or Description header found" % |
1083 | - patch_name |
1084 | - ) |
1085 | - msg = b'%b\n\nGbp-Pq: %b.' % ( |
1086 | - patch_desc.encode(), |
1087 | - patch_name.encode() |
1088 | + for ( |
1089 | + applied_import_tree_hash, |
1090 | + patch_name, |
1091 | + patch_desc |
1092 | + ) in import_patches_applied_tree(repo, spi.dsc_pathname): |
1093 | + # special case for .pc removal |
1094 | + if not patch_name is None: |
1095 | + msg = b'%b.' % (patch_desc.encode()) |
1096 | + else: |
1097 | + if patch_desc is None: |
1098 | + patch_desc = ( |
1099 | + "%s\n\nNo DEP3 Subject or Description header found" % |
1100 | + patch_name |
1101 | ) |
1102 | - unapplied_parent_commit = repo.commit_tree_hash( |
1103 | - applied_import_tree_hash, |
1104 | - [unapplied_parent_commit], |
1105 | - msg, |
1106 | - spi |
1107 | + msg = b'%b\n\nGbp-Pq: %b.' % ( |
1108 | + patch_desc.encode(), |
1109 | + patch_name.encode() |
1110 | ) |
1111 | + unapplied_parent_commit = repo.commit_tree_hash( |
1112 | + applied_import_tree_hash, |
1113 | + [unapplied_parent_commit], |
1114 | + msg, |
1115 | + spi |
1116 | + ) |
1117 | |
1118 | - logging.debug( |
1119 | - "Committed patch-application of %s as %s", |
1120 | - patch_name, unapplied_parent_commit |
1121 | - ) |
1122 | - except CalledProcessError as e: |
1123 | - if allow_applied_failures: |
1124 | - return |
1125 | - raise |
1126 | + logging.debug( |
1127 | + "Committed patch-application of %s as %s", |
1128 | + patch_name, unapplied_parent_commit |
1129 | + ) |
1130 | |
1131 | commit_applied_patches_import( |
1132 | repo, |
1133 | spi, |
1134 | applied_import_tree_hash, |
1135 | - namespace, |
1136 | + ref_namespace, |
1137 | applied_changelog_parent_commit, |
1138 | unapplied_parent_commit, |
1139 | ) |
1140 | |
1141 | -def import_publishes( |
1142 | + |
1143 | +def import_new_publishes( |
1144 | repo, |
1145 | pkgname, |
1146 | - namespace, |
1147 | + distname, |
1148 | + new_spi, |
1149 | + ref_namespace, |
1150 | + head_ref_namespace, |
1151 | + skip_orig, |
1152 | + allow_applied_failures, |
1153 | patches_applied, |
1154 | - debian_head_versions, |
1155 | - ubuntu_head_versions, |
1156 | - debian_sinfo, |
1157 | - ubuntu_sinfo, |
1158 | +): |
1159 | + history_found = False |
1160 | + spi = None |
1161 | + |
1162 | + updated_head_refs = dict() |
1163 | + try: |
1164 | + for spi in new_spi: |
1165 | + history_found = True |
1166 | + spi.pull() |
1167 | + # DSC can only be already imported if the import tag |
1168 | + # exists |
1169 | + |
1170 | + if patches_applied: |
1171 | + existing_tag = repo.get_applied_tag(spi.version, ref_namespace) |
1172 | + head_ref_name = gitubuntu.importerutils.applied_head_ref_name( |
1173 | + spi, |
1174 | + ref_namespace, |
1175 | + ) |
1176 | + else: |
1177 | + existing_tag = repo.get_import_tag(spi.version, ref_namespace) |
1178 | + head_ref_name = gitubuntu.importerutils.head_ref_name( |
1179 | + spi, |
1180 | + ref_namespace, |
1181 | + ) |
1182 | + |
1183 | + if existing_tag: |
1184 | + # if an identical DSC has already been imported, |
1185 | + # then we only need to update our branch tracker |
1186 | + if dsc_already_imported( |
1187 | + repo, |
1188 | + spi, |
1189 | + ref_namespace, |
1190 | + distname, |
1191 | + ): |
1192 | + updated_head_refs[head_ref_name] = str( |
1193 | + existing_tag.peel(pygit2.Commit).id |
1194 | + ) |
1195 | + continue |
1196 | + |
1197 | + try: |
1198 | + if patches_applied: |
1199 | + import_applied_spi( |
1200 | + repo=repo, |
1201 | + spi=spi, |
1202 | + ref_namespace=ref_namespace, |
1203 | + ) |
1204 | + else: |
1205 | + import_unapplied_spi( |
1206 | + repo=repo, |
1207 | + spi=spi, |
1208 | + ref_namespace=ref_namespace, |
1209 | + skip_orig=skip_orig, |
1210 | + ) |
1211 | + except CalledProcessError: |
1212 | + if patches_applied and allow_applied_failures: |
1213 | + continue |
1214 | + raise |
1215 | + |
1216 | + if patches_applied: |
1217 | + existing_tag = repo.get_applied_tag(spi.version, ref_namespace) |
1218 | + else: |
1219 | + existing_tag = repo.get_import_tag(spi.version, ref_namespace) |
1220 | + |
1221 | + updated_head_refs[head_ref_name] = str( |
1222 | + existing_tag.peel(pygit2.Commit).id |
1223 | + ) |
1224 | + except Exception as e: |
1225 | + if patches_applied: |
1226 | + if not spi: |
1227 | + msg = "Unable to import patches-applied to %s" % distname |
1228 | + else: |
1229 | + msg = "Unable to import patches-applied %s to %s" % ( |
1230 | + str(spi.version), |
1231 | + distname, |
1232 | + ) |
1233 | + logging.error(msg) |
1234 | + else: |
1235 | + if not spi: |
1236 | + msg = "Unable to import patches-unapplied to %s" % distname |
1237 | + else: |
1238 | + msg = "Unable to import patches-unapplied %s to %s" % ( |
1239 | + str(spi.version), |
1240 | + distname, |
1241 | + ) |
1242 | + raise GitUbuntuImportError(msg) from e |
1243 | + else: |
1244 | + history_found = True |
1245 | + |
1246 | + return history_found, updated_head_refs |
1247 | + |
1248 | + |
1249 | +def import_publishes( |
1250 | + repo, |
1251 | + pkgname, |
1252 | + base_ref_namespace, |
1253 | active_series_only, |
1254 | workdir, |
1255 | skip_orig, |
1256 | + skip_applied, |
1257 | allow_applied_failures, |
1258 | + pullfile, |
1259 | + retries, |
1260 | + retry_backoffs, |
1261 | ): |
1262 | - history_found = False |
1263 | only_debian = False |
1264 | - srcpkg_information = None |
1265 | - if patches_applied: |
1266 | - _namespace = namespace |
1267 | - namespace = '%s/applied' % namespace |
1268 | - import_type = 'patches-applied' |
1269 | - import_func = functools.partial( |
1270 | - import_applied_spi, |
1271 | - allow_applied_failures=allow_applied_failures, |
1272 | + history_found = False |
1273 | + |
1274 | + debian_sinfo = GitUbuntuSourceInformation( |
1275 | + 'debian', |
1276 | + pkgname, |
1277 | + os.path.abspath(pullfile), |
1278 | + retries, |
1279 | + retry_backoffs, |
1280 | + ) |
1281 | + |
1282 | + ubuntu_sinfo = GitUbuntuSourceInformation( |
1283 | + 'ubuntu', |
1284 | + pkgname, |
1285 | + os.path.abspath(pullfile), |
1286 | + retries, |
1287 | + retry_backoffs, |
1288 | + ) |
1289 | + |
1290 | + for distname, dist_sinfo in ( |
1291 | + ('debian', debian_sinfo), |
1292 | + ('ubuntu', ubuntu_sinfo), |
1293 | + ): |
1294 | + unapplied_head_ref_namespace = '%s/%s' % (base_ref_namespace, distname) |
1295 | + applied_head_ref_namespace='%s/applied/%s' % (base_ref_namespace, distname) |
1296 | + |
1297 | + unapplied_versions = repo.get_heads_and_versions( |
1298 | + head_ref_namespace=unapplied_head_ref_namespace, |
1299 | ) |
1300 | - else: |
1301 | - _namespace = namespace |
1302 | - import_type = 'patches-unapplied' |
1303 | - import_func = functools.partial( |
1304 | - import_unapplied_spi, |
1305 | - skip_orig=skip_orig, |
1306 | + applied_versions = repo.get_heads_and_versions( |
1307 | + head_ref_namespace=applied_head_ref_namespace |
1308 | ) |
1309 | - for distname, versions, dist_sinfo in ( |
1310 | - ("debian", debian_head_versions, debian_sinfo), |
1311 | - ("ubuntu", ubuntu_head_versions, ubuntu_sinfo)): |
1312 | + debug_log_head_versions(unapplied_versions) |
1313 | + debug_log_head_versions(applied_versions) |
1314 | if active_series_only and distname == "debian": |
1315 | continue |
1316 | + |
1317 | + try: |
1318 | + history_found, updated_head_refs = import_new_publishes( |
1319 | + repo, |
1320 | + pkgname, |
1321 | + distname, |
1322 | + dist_sinfo.launchpad_versions_published_after( |
1323 | + unapplied_versions, |
1324 | + unapplied_head_ref_namespace, |
1325 | + workdir, |
1326 | + active_series_only, |
1327 | + ), |
1328 | + base_ref_namespace, |
1329 | + unapplied_head_ref_namespace, |
1330 | + skip_orig, |
1331 | + allow_applied_failures=None, |
1332 | + patches_applied=False, |
1333 | + ) |
1334 | + update_branches( |
1335 | + distname, |
1336 | + dist_sinfo, |
1337 | + repo, |
1338 | + unapplied_head_ref_namespace, |
1339 | + pkgname, |
1340 | + updated_head_refs, |
1341 | + ) |
1342 | + except NoPublicationHistoryException: |
1343 | + logging.warning("No publication history found for %s in %s.", |
1344 | + pkgname, distname |
1345 | + ) |
1346 | + if distname == 'ubuntu': |
1347 | + only_debian = True |
1348 | + continue |
1349 | + |
1350 | + if skip_applied: |
1351 | + continue |
1352 | + |
1353 | try: |
1354 | - for srcpkg_information in dist_sinfo.launchpad_versions_published_after( |
1355 | - versions, |
1356 | - namespace, |
1357 | - workdir=workdir, |
1358 | - active_series_only=active_series_only |
1359 | - ): |
1360 | - history_found = True |
1361 | - import_func( |
1362 | - repo=repo, |
1363 | - spi=srcpkg_information, |
1364 | - namespace=_namespace, |
1365 | - ubuntu_sinfo=ubuntu_sinfo, |
1366 | - ) |
1367 | + _, updated_head_refs = import_new_publishes( |
1368 | + repo, |
1369 | + pkgname, |
1370 | + distname, |
1371 | + dist_sinfo.launchpad_versions_published_after( |
1372 | + applied_versions, |
1373 | + applied_head_ref_namespace, |
1374 | + workdir, |
1375 | + active_series_only, |
1376 | + ), |
1377 | + base_ref_namespace, |
1378 | + applied_head_ref_namespace, |
1379 | + skip_orig=None, |
1380 | + allow_applied_failures=allow_applied_failures, |
1381 | + patches_applied=True, |
1382 | + ) |
1383 | + update_branches( |
1384 | + distname, |
1385 | + dist_sinfo, |
1386 | + repo, |
1387 | + applied_head_ref_namespace, |
1388 | + pkgname, |
1389 | + updated_head_refs, |
1390 | + ) |
1391 | except NoPublicationHistoryException: |
1392 | logging.warning("No publication history found for %s in %s.", |
1393 | pkgname, distname |
1394 | ) |
1395 | if distname == 'ubuntu': |
1396 | only_debian = True |
1397 | - except Exception as e: |
1398 | - if srcpkg_information is None: |
1399 | - msg = 'Unable to import %s to %s' % (import_type, distname) |
1400 | - else: |
1401 | - msg = 'Unable to import %s %s to %s' % (import_type, |
1402 | - str(srcpkg_information.version), distname) |
1403 | - if not patches_applied: |
1404 | - raise GitUbuntuImportError(msg) from e |
1405 | - else: |
1406 | - logging.error(msg) |
1407 | - else: |
1408 | - history_found = True |
1409 | + continue |
1410 | |
1411 | return (only_debian, history_found) |
1412 | |
1413 | diff --git a/gitubuntu/importerutils.py b/gitubuntu/importerutils.py |
1414 | new file mode 100644 |
1415 | index 0000000..6035c45 |
1416 | --- /dev/null |
1417 | +++ b/gitubuntu/importerutils.py |
1418 | @@ -0,0 +1,26 @@ |
1419 | +def head_ref_name(spi, ref_namespace): |
1420 | + """head_ref_name - get the patches-unapplied Git ref name a spi would import to |
1421 | + |
1422 | + spi - a gitubuntu.source_information.GitUbuntuSourcePackageInformation object |
1423 | + ref_namespace - string namespace the resulting Git ref should be in |
1424 | + """ |
1425 | + if spi.pocket.lower() == 'release': |
1426 | + pocket_suffix = '' |
1427 | + else: |
1428 | + pocket_suffix = '-' + spi.pocket.lower() |
1429 | + |
1430 | + return '%s/%s/%s%s' % ( |
1431 | + ref_namespace, |
1432 | + spi.distribution.name.lower(), |
1433 | + spi.series.name.lower(), |
1434 | + pocket_suffix, |
1435 | + ) |
1436 | + |
1437 | +def applied_head_ref_name(spi, ref_namespace): |
1438 | + """applied_head_ref_name - get the patches-applied Git ref name a spi would import to |
1439 | + |
1440 | + Arguments: |
1441 | + spi - a gitubuntu.source_information.GitUbuntuSourcePackageInformation object |
1442 | + ref_namespace - string namespace the resulting Git ref should be in |
1443 | + """ |
1444 | + return head_ref_name(spi, '%s/applied' % ref_namespace) |
1445 | diff --git a/gitubuntu/importppa.py b/gitubuntu/importppa.py |
1446 | index 1f3283c..3bfde5e 100644 |
1447 | --- a/gitubuntu/importppa.py |
1448 | +++ b/gitubuntu/importppa.py |
1449 | @@ -83,9 +83,8 @@ def main( |
1450 | namespace, |
1451 | ) |
1452 | for head_name in sorted(ubuntu_head_versions): |
1453 | - _, _, pretty_head_name = head_name.partition('%s/', namespace) |
1454 | logging.debug('Last imported %s version is %s', |
1455 | - pretty_head_name, |
1456 | + head_name, |
1457 | ubuntu_head_versions[head_name]['version'] |
1458 | ) |
1459 | |
1460 | @@ -94,9 +93,8 @@ def main( |
1461 | namespace, |
1462 | ) |
1463 | for head_name in sorted(applied_ubuntu_head_versions): |
1464 | - _, _, pretty_head_name = head_name.partition('%s/', namespace) |
1465 | logging.debug('Last applied %s version is %s', |
1466 | - pretty_head_name, |
1467 | + head_name, |
1468 | applied_ubuntu_head_versions[head_name]['version'] |
1469 | ) |
1470 | |
1471 | diff --git a/gitubuntu/source_information.py b/gitubuntu/source_information.py |
1472 | index b97bc8a..527e2f0 100644 |
1473 | --- a/gitubuntu/source_information.py |
1474 | +++ b/gitubuntu/source_information.py |
1475 | @@ -21,6 +21,8 @@ except ImportError: |
1476 | logging.error('Is %s installed?', pkg) |
1477 | sys.exit(1) |
1478 | |
1479 | +import gitubuntu.importerutils |
1480 | + |
1481 | _ddi = DebianDistroInfo() |
1482 | _udi = UbuntuDistroInfo() |
1483 | |
1484 | @@ -131,8 +133,18 @@ class GitUbuntuPPASourcePackage(UbuntuSourcePackage): |
1485 | |
1486 | |
1487 | class GitUbuntuSourcePackageInformation: |
1488 | - def __init__(self, spphr, dist_name, retries=0, retry_backoffs=[], |
1489 | - workdir=None, dsc=None, files=list()): |
1490 | + def __init__( |
1491 | + self, |
1492 | + spphr, |
1493 | + dist_name, |
1494 | + retries=0, |
1495 | + retry_backoffs=[], |
1496 | + workdir=None, |
1497 | + dsc=None, |
1498 | + files=None, |
1499 | + ): |
1500 | + if not files: |
1501 | + files = list() |
1502 | self._spphr = spphr |
1503 | self._dist_name = dist_name |
1504 | self._pkgname = self.spphr.source_package_name |
1505 | @@ -151,13 +163,15 @@ class GitUbuntuSourcePackageInformation: |
1506 | |
1507 | # do this here, in case files is passed |
1508 | if workdir and not os.path.isdir(workdir): |
1509 | - os.makedirs(workdir, exist_ok=True) |
1510 | - |
1511 | - self._archive_srcpkg = func(package=self._pkgname, |
1512 | - version=self._version, |
1513 | - workdir=workdir, |
1514 | - quiet=True, |
1515 | - dscfile=dsc) |
1516 | + os.makedirs(workdir, exist_ok=True) |
1517 | + |
1518 | + self._archive_srcpkg = func( |
1519 | + package=self._pkgname, |
1520 | + version=self._version, |
1521 | + workdir=workdir, |
1522 | + quiet=True, |
1523 | + dscfile=dsc, |
1524 | + ) |
1525 | for f in files: |
1526 | self._archive_srcpkg._download_file(f, f.split('/')[-1]) |
1527 | |
1528 | @@ -202,62 +216,6 @@ class GitUbuntuSourcePackageInformation: |
1529 | return self._spphr.pocket |
1530 | |
1531 | @property |
1532 | - def pretty_head_name(self): |
1533 | - if self._spphr.pocket.lower() == 'release': |
1534 | - head_name = '%s/%s' % ( |
1535 | - self.distribution.name.lower(), |
1536 | - self.series.name.lower(), |
1537 | - ) |
1538 | - else: |
1539 | - head_name = '%s/%s-%s' % ( |
1540 | - self.distribution.name.lower(), |
1541 | - self.series.name.lower(), |
1542 | - self.pocket.lower() |
1543 | - ) |
1544 | - return head_name |
1545 | - |
1546 | - def head_name(self, prefix): |
1547 | - if self._spphr.pocket.lower() == 'release': |
1548 | - head_name = '%s/%s/%s' % ( |
1549 | - prefix, |
1550 | - self.distribution.name.lower(), |
1551 | - self.series.name.lower(), |
1552 | - ) |
1553 | - else: |
1554 | - head_name = '%s/%s/%s-%s' % ( |
1555 | - prefix, |
1556 | - self.distribution.name.lower(), |
1557 | - self.series.name.lower(), |
1558 | - self.pocket.lower() |
1559 | - ) |
1560 | - return head_name |
1561 | - |
1562 | - def applied_head_name(self, prefix): |
1563 | - return self.head_name('%s/applied' % prefix) |
1564 | - |
1565 | - def parent_head_name(self, prefix): |
1566 | - if self.parent_series == None: |
1567 | - return None |
1568 | - if self.pocket.lower() == 'release': |
1569 | - # release pockets descend from prior series |
1570 | - head_name = '%s/%s/%s' % ( |
1571 | - prefix, |
1572 | - self.distribution.name.lower(), |
1573 | - self.parent_series.name.lower() |
1574 | - ) |
1575 | - else: |
1576 | - # non-release pockets descend from release |
1577 | - head_name = '%s/%s/%s' % ( |
1578 | - prefix, |
1579 | - self.distribution.name.lower(), |
1580 | - self.series.name.lower() |
1581 | - ) |
1582 | - return head_name |
1583 | - |
1584 | - def parent_applied_head_name(self, prefix): |
1585 | - return self.parent_head_name('%s/applied' % prefix) |
1586 | - |
1587 | - @property |
1588 | def dsc(self): |
1589 | return self._archive_srcpkg.dsc |
1590 | |
1591 | @@ -295,11 +253,14 @@ class GitUbuntuSourceInformation(object): |
1592 | _stable_series_list = None |
1593 | _current_series = None |
1594 | |
1595 | - def __init__(self, dist_name, pkgname=None, |
1596 | - pull_overrides_filename='/dev/null', |
1597 | - retries=0, |
1598 | - retry_backoffs=[] |
1599 | - ): |
1600 | + def __init__( |
1601 | + self, |
1602 | + dist_name, |
1603 | + pkgname=None, |
1604 | + pull_overrides_filename='/dev/null', |
1605 | + retries=0, |
1606 | + retry_backoffs=[] |
1607 | + ): |
1608 | self.launchpad = launchpad_login() |
1609 | self.dist_name = dist_name |
1610 | if self.dist_name.startswith('ppa:'): |
1611 | @@ -324,18 +285,6 @@ class GitUbuntuSourceInformation(object): |
1612 | self.retries = retries |
1613 | self.retry_backoffs = retry_backoffs |
1614 | |
1615 | - @staticmethod |
1616 | - def _head_version_is_equal(head_versions, namespace, spi): |
1617 | - try: |
1618 | - if (head_versions[spi.head_name(namespace)]['version'] == spi.version and |
1619 | - (spi.date_published is None or |
1620 | - int(spi.date_published.timestamp()) == head_versions[spi.head_name(namespace)]['head'].peel().commit_time) |
1621 | - ): |
1622 | - return True |
1623 | - return False |
1624 | - except KeyError: |
1625 | - return False |
1626 | - |
1627 | @property |
1628 | def current_series(self): |
1629 | if self.dist_name.startswith('ppa:'): |
1630 | @@ -402,7 +351,7 @@ class GitUbuntuSourceInformation(object): |
1631 | def all_series_name_list(self): |
1632 | return [r.name for r in self.all_series] |
1633 | |
1634 | - def get_corrected_spi(self, srcpkg, workdir=None): |
1635 | + def get_corrected_spi(self, srcpkg, workdir): |
1636 | try: |
1637 | pull_override = self.pull_overrides[srcpkg.source_package_version] |
1638 | dsc = pull_override['dsc'] |
1639 | @@ -410,11 +359,17 @@ class GitUbuntuSourceInformation(object): |
1640 | except KeyError: |
1641 | dsc = None |
1642 | files = list() |
1643 | - return GitUbuntuSourcePackageInformation(srcpkg, self.dist_name, |
1644 | - self.retries, self.retry_backoffs, workdir=workdir, |
1645 | - dsc=dsc, files=files) |
1646 | - |
1647 | - def launchpad_version_is_published(self, version, workdir=None): |
1648 | + return GitUbuntuSourcePackageInformation( |
1649 | + srcpkg, |
1650 | + self.dist_name, |
1651 | + self.retries, |
1652 | + self.retry_backoffs, |
1653 | + workdir=workdir, |
1654 | + dsc=dsc, |
1655 | + files=files, |
1656 | + ) |
1657 | + |
1658 | + def launchpad_version_is_published(self, version): |
1659 | spph = self.archive.getPublishedSources( |
1660 | exact_match=True, |
1661 | source_name=self.pkgname, |
1662 | @@ -423,13 +378,16 @@ class GitUbuntuSourceInformation(object): |
1663 | ) |
1664 | return len(spph) != 0 |
1665 | |
1666 | - def launchpad_versions_published(self, workdir=None, |
1667 | - sorted_by_version=False, series=None |
1668 | + def launchpad_versions_published( |
1669 | + self, |
1670 | + workdir, |
1671 | + sorted_by_version=False, |
1672 | + series=None, |
1673 | ): |
1674 | args = { |
1675 | - 'exact_match':True, |
1676 | - 'source_name':self.pkgname, |
1677 | - } |
1678 | + 'exact_match':True, |
1679 | + 'source_name':self.pkgname, |
1680 | + } |
1681 | if not sorted_by_version: |
1682 | args['order_by_date'] = True |
1683 | if series: |
1684 | @@ -437,18 +395,49 @@ class GitUbuntuSourceInformation(object): |
1685 | |
1686 | spph = self.archive.getPublishedSources(**args) |
1687 | if len(spph) == 0: |
1688 | - raise NoPublicationHistoryException("Is %s published in %s?" % |
1689 | - (self.pkgname, self.dist_name)) |
1690 | + raise NoPublicationHistoryException( |
1691 | + "Is %s published in %s?" % ( |
1692 | + self.pkgname, |
1693 | + self.dist_name, |
1694 | + ) |
1695 | + ) |
1696 | |
1697 | for srcpkg in spph: |
1698 | yield self.get_corrected_spi(srcpkg, workdir) |
1699 | |
1700 | - def launchpad_versions_published_after(self, head_versions, namespace, workdir=None, active_series_only=False): |
1701 | + def launchpad_versions_published_after( |
1702 | + self, |
1703 | + head_versions, |
1704 | + head_ref_namespace, |
1705 | + workdir, |
1706 | + active_series_only=False, |
1707 | + ): |
1708 | + """Provide Launchpad publishing information after a certain publish |
1709 | + |
1710 | + Arguments: |
1711 | + head_versions - a dictionary of dictionaries, keyed by Git |
1712 | + branch names, where each element is a dictionary with elements |
1713 | + 'version' and 'head'. |
1714 | + head_ref_namespace - a string Git ref namespace corresponding to |
1715 | + the branches in @head_versions |
1716 | + workdir - a string filesystem path to store the downloaded |
1717 | + source package in |
1718 | + active_series_only - a boolean indicating only publishing |
1719 | + records from active series should be considered |
1720 | + |
1721 | + @head_versions[<branch>]['head'] is a pygit2.Branch object. |
1722 | + @head_versions[<branch>]['version'] is the string version of the |
1723 | + debian/changelog in the corresponnding Git branch. |
1724 | + |
1725 | + Returns: |
1726 | + an iterable over GitUbuntuSourcePackageInformation objects, one |
1727 | + per Launchpad publishing record after @head_version |
1728 | + """ |
1729 | args = { |
1730 | - 'exact_match':True, |
1731 | - 'source_name':self.pkgname, |
1732 | - 'order_by_date':True, |
1733 | - } |
1734 | + 'exact_match':True, |
1735 | + 'source_name':self.pkgname, |
1736 | + 'order_by_date':True, |
1737 | + } |
1738 | |
1739 | # we have the date of the commit too, so we can double-check |
1740 | # that it matches |
1741 | @@ -469,14 +458,33 @@ class GitUbuntuSourceInformation(object): |
1742 | # Sanity check that the passed in srcpkg name has a publication |
1743 | # history |
1744 | if len(spph) == 0: |
1745 | - raise NoPublicationHistoryException("Is %s published in %s?" % |
1746 | - (self.pkgname, self.dist_name)) |
1747 | - if len(head_versions) > 0: |
1748 | + raise NoPublicationHistoryException( |
1749 | + "Is %s published in %s?" % ( |
1750 | + self.pkgname, |
1751 | + self.dist_name, |
1752 | + ) |
1753 | + ) |
1754 | + if head_versions: |
1755 | _spph = list() |
1756 | for spphr in spph: |
1757 | - spi = GitUbuntuSourcePackageInformation(spphr, self.dist_name, |
1758 | - workdir=workdir) |
1759 | - if self._head_version_is_equal(head_versions, namespace, spi): |
1760 | + spi = GitUbuntuSourcePackageInformation( |
1761 | + spphr, |
1762 | + self.dist_name, |
1763 | + workdir=workdir, |
1764 | + ) |
1765 | + head_version = head_versions[ |
1766 | + gitubuntu.importerutils.head_ref_name( |
1767 | + spi, |
1768 | + head_ref_namespace, |
1769 | + ) |
1770 | + ] |
1771 | + if ( |
1772 | + head_version['version'] == spi.version and |
1773 | + spi.date_published and |
1774 | + int(spi.date_published.timestamp()) == head_version[ |
1775 | + 'head' |
1776 | + ].peel().commit_time |
1777 | + ): |
1778 | break |
1779 | _spph.append(spphr) |
1780 | spph = _spph |