Merge ~nacc/git-ubuntu:lp1731554-importer-rework into git-ubuntu:master

Proposed by Nish Aravamudan
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)
Reviewer Review Type Date Requested Status
git-ubuntu developers Pending
Review via email: mp+333852@code.launchpad.net

Description of the change

Make jenkins happy.

To post a comment you must log in.
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 pointers

That 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 publish

update 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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/gitubuntu/dsc.py b/gitubuntu/dsc.py
index 1fc3129..91a78ea 100644
--- a/gitubuntu/dsc.py
+++ b/gitubuntu/dsc.py
@@ -84,3 +84,41 @@ class GitUbuntuDsc(Dsc):
84 @property84 @property
85 def all_tarball_paths(self):85 def all_tarball_paths(self):
86 return [self.orig_tarball_path] + list(self.component_tarball_paths.values())86 return [self.orig_tarball_path] + list(self.component_tarball_paths.values())
87
88 def _compare_dsc(self, other, field, key):
89 if field not in self or field not in other:
90 return False
91 our_checksums = dict(
92 (entry['name'], (int(entry['size']), entry[key]))
93 for entry in self[field]
94 )
95 their_checksums = dict(
96 (entry['name'], (int(entry['size']), entry[key]))
97 for entry in other[field]
98 )
99 # check that every file in ours is in theirs
100 for name, (size, checksum) in our_checksums.items():
101 if name not in their_checksums:
102 return False
103 if size != their_checksums[name][0] or checksum != their_checksums[name][1]:
104 return False
105 # check that every file in theirs is in ours
106 for name, (size, checksum) in their_checksums.items():
107 if name not in our_checksums:
108 return False
109 if size != our_checksums[name][0] or checksum != our_checksums[name][1]:
110 return False
111 # all files in ours and theirs for this hash match
112 return True
113
114 def compare_dsc(self, other):
115 """Check whether some set of hashes in these two dscs are
116 identical
117 """
118 return any(
119 self._compare_dsc(other, field, key) for field, key in (
120 ('Checksums-Sha256', 'sha256'),
121 ('Checksums-Sha1', 'sha1'),
122 ('Files', 'md5sum'),
123 )
124 )
diff --git a/gitubuntu/git_repository.py b/gitubuntu/git_repository.py
index c598c10..45d5166 100644
--- a/gitubuntu/git_repository.py
+++ b/gitubuntu/git_repository.py
@@ -537,6 +537,20 @@ class GitUbuntuRepository:
537 )537 )
538538
539 @contextmanager539 @contextmanager
540 def dsc_branch(self, dist, namespace='pkg'):
541 """Context manager wrapping dsc branch manipulation
542
543 In this context, HEAD will point to the dsc branch for
544 @namespace.
545 """
546 dsc_branch = '%s/importer/%s/dsc' % (namespace, dist)
547 with self.temporary_worktree(dsc_branch):
548 try:
549 yield
550 except:
551 raise
552
553 @contextmanager
540 def pristine_tar_branches(self, dist, namespace='pkg'):554 def pristine_tar_branches(self, dist, namespace='pkg'):
541 """Context manager wrapping pristine-tar branch manipulation555 """Context manager wrapping pristine-tar branch manipulation
542556
@@ -1106,16 +1120,15 @@ class GitUbuntuRepository:
1106 'Cannot get changelog source package name'1120 'Cannot get changelog source package name'
1107 )1121 )
11081122
1109 def get_heads_and_versions(self, head_prefix, namespace):1123 def get_heads_and_versions(self, head_ref_namespace):
1110 """Extract the last version in debian/changelog of all1124 """Extract the last version in debian/changelog of all
1111 '<namespace>/<head_prefix>/debian/*' and1125 '<namespace>/debian/*' and
1112 '<namespace>/<head_prefix>/ubuntu/*' branches.1126 '<namespace>/ubuntu/*' branches.
1113 """1127 """
1114 versions = dict()1128 versions = dict()
1115 errors = False1129 errors = False
1116 for head in self.local_branches:1130 for head in self.local_branches:
1117 prefix = '%s/%s' % (namespace, head_prefix)1131 if not head.branch_name.startswith(head_ref_namespace):
1118 if not head.branch_name.startswith(prefix):
1119 continue1132 continue
1120 if 'ubuntu/devel' in head.branch_name:1133 if 'ubuntu/devel' in head.branch_name:
1121 continue1134 continue
diff --git a/gitubuntu/importer.py b/gitubuntu/importer.py
index 2063f83..bea993d 100644
--- a/gitubuntu/importer.py
+++ b/gitubuntu/importer.py
@@ -26,8 +26,6 @@
2626
27import argparse27import argparse
28import atexit28import atexit
29import functools
30import getpass
31import logging29import logging
32import os30import os
33import re31import re
@@ -45,14 +43,13 @@ from gitubuntu.dsc import (
45from gitubuntu.git_repository import (43from gitubuntu.git_repository import (
46 GitUbuntuRepository,44 GitUbuntuRepository,
47 GitUbuntuRepositoryFetchError,45 GitUbuntuRepositoryFetchError,
48 orphan_tag,
49 applied_tag,46 applied_tag,
50 import_tag,47 import_tag,
51 upstream_tag,
52 PristineTarError,48 PristineTarError,
53 is_dir_3_0_quilt,49 is_dir_3_0_quilt,
54)50)
55from gitubuntu.run import decode_binary, run, runq51import gitubuntu.importerutils
52from gitubuntu.run import run, runq
56from gitubuntu.source_information import (53from gitubuntu.source_information import (
57 GitUbuntuSourceInformation,54 GitUbuntuSourceInformation,
58 NoPublicationHistoryException,55 NoPublicationHistoryException,
@@ -130,6 +127,14 @@ def cleanup(no_clean, local_dir):
130 logging.info('Leaving %s as directed' % local_dir)127 logging.info('Leaving %s as directed' % local_dir)
131128
132129
130def debug_log_head_versions(head_versions):
131 for head_name in sorted(head_versions):
132 logging.debug('Last imported %s version is %s',
133 head_name,
134 head_versions[head_name]['version']
135 )
136
137
133# XXX: need a namedtuple to hold common arguments138# XXX: need a namedtuple to hold common arguments
134def main(139def main(
135 pkgname,140 pkgname,
@@ -195,6 +200,9 @@ def main(
195 else:200 else:
196 namespace = owner201 namespace = owner
197202
203 # now sets a global _PARENT_OVERRIDES
204 parse_parentfile(parentfile, pkgname)
205
198 logging.info('Ubuntu Server Team importer v%s' % VERSION)206 logging.info('Ubuntu Server Team importer v%s' % VERSION)
199207
200 repo = GitUbuntuRepository(directory, user, proto)208 repo = GitUbuntuRepository(directory, user, proto)
@@ -203,6 +211,13 @@ def main(
203211
204 atexit.register(cleanup, no_clean, repo.local_dir)212 atexit.register(cleanup, no_clean, repo.local_dir)
205213
214 if dl_cache is None:
215 workdir = os.path.join(repo.git_dir, CACHE_PATH)
216 else:
217 workdir = dl_cache
218
219 os.makedirs(workdir, exist_ok=True)
220
206 repo.add_base_remotes(pkgname, repo_owner=owner)221 repo.add_base_remotes(pkgname, repo_owner=owner)
207 if not no_fetch and not reimport:222 if not no_fetch and not reimport:
208 try:223 try:
@@ -241,97 +256,21 @@ def main(
241256
242 repo.ensure_importer_branches_exist(namespace)257 repo.ensure_importer_branches_exist(namespace)
243258
244 debian_sinfo = GitUbuntuSourceInformation(
245 'debian',
246 pkgname,
247 os.path.abspath(pullfile),
248 retries,
249 retry_backoffs,
250 )
251
252 ubuntu_sinfo = GitUbuntuSourceInformation(
253 'ubuntu',
254 pkgname,
255 os.path.abspath(pullfile),
256 retries,
257 retry_backoffs,
258 )
259
260 debian_head_versions = (
261 repo.get_heads_and_versions('debian', namespace)
262 )
263 for head_name in sorted(debian_head_versions):
264 _, _, pretty_head_name = head_name.partition('%s/' % namespace)
265 logging.debug('Last imported %s version is %s',
266 pretty_head_name,
267 debian_head_versions[head_name]['version']
268 )
269
270 ubuntu_head_versions = (
271 repo.get_heads_and_versions('ubuntu', namespace)
272 )
273 for head_name in sorted(ubuntu_head_versions):
274 _, _, pretty_head_name = head_name.partition('%s/' % namespace)
275 logging.debug('Last imported %s version is %s',
276 pretty_head_name,
277 ubuntu_head_versions[head_name]['version']
278 )
279
280 if not skip_applied:
281 applied_debian_head_versions = (
282 repo.get_heads_and_versions(
283 'applied/debian', namespace
284 )
285 )
286 for head_name in sorted(applied_debian_head_versions):
287 _, _, pretty_head_name = head_name.partition(
288 '%s/' % namespace
289 )
290 logging.debug('Last applied %s version is %s',
291 pretty_head_name,
292 applied_debian_head_versions[head_name]['version']
293 )
294
295 applied_ubuntu_head_versions = (
296 repo.get_heads_and_versions(
297 'applied/ubuntu', namespace
298 )
299 )
300 for head_name in sorted(applied_ubuntu_head_versions):
301 _, _, pretty_head_name = head_name.partition(
302 '%s/' % namespace
303 )
304 logging.debug('Last applied %s version is %s',
305 pretty_head_name,
306 applied_ubuntu_head_versions[head_name]['version']
307 )
308
309 oldcwd = os.getcwd()259 oldcwd = os.getcwd()
310 os.chdir(repo.local_dir)260 os.chdir(repo.local_dir)
311261
312 if dl_cache is None:
313 workdir = os.path.join(repo.git_dir, CACHE_PATH)
314 else:
315 workdir = dl_cache
316
317 os.makedirs(workdir, exist_ok=True)
318
319 # now sets a global _PARENT_OVERRIDES
320 parse_parentfile(parentfile, pkgname)
321
322 only_debian, history_found = import_publishes(262 only_debian, history_found = import_publishes(
323 repo=repo,263 repo=repo,
324 pkgname=pkgname,264 pkgname=pkgname,
325 namespace=namespace,265 base_ref_namespace=namespace,
326 patches_applied=False,
327 debian_head_versions=debian_head_versions,
328 ubuntu_head_versions=ubuntu_head_versions,
329 debian_sinfo=debian_sinfo,
330 ubuntu_sinfo=ubuntu_sinfo,
331 active_series_only=active_series_only,266 active_series_only=active_series_only,
332 workdir=workdir,267 workdir=workdir,
333 skip_orig=skip_orig,268 skip_orig=skip_orig,
269 skip_applied=skip_applied,
334 allow_applied_failures=allow_applied_failures,270 allow_applied_failures=allow_applied_failures,
271 pullfile=pullfile,
272 retries=retries,
273 retry_backoffs=retry_backoffs,
335 )274 )
336275
337 if not history_found:276 if not history_found:
@@ -339,29 +278,6 @@ def main(
339 "Wrong source package name?", pkgname)278 "Wrong source package name?", pkgname)
340 return 1279 return 1
341280
342 if not skip_applied:
343 import_publishes(
344 repo=repo,
345 pkgname=pkgname,
346 namespace=namespace,
347 patches_applied=True,
348 debian_head_versions=applied_debian_head_versions,
349 ubuntu_head_versions=applied_ubuntu_head_versions,
350 debian_sinfo=debian_sinfo,
351 ubuntu_sinfo=ubuntu_sinfo,
352 active_series_only=active_series_only,
353 workdir=workdir,
354 skip_orig=skip_orig,
355 allow_applied_failures=allow_applied_failures,
356 )
357
358 update_devel_branches(
359 repo=repo,
360 namespace=namespace,
361 pkgname=pkgname,
362 ubuntu_sinfo=ubuntu_sinfo,
363 )
364
365 os.chdir(oldcwd)281 os.chdir(oldcwd)
366282
367 repo.garbage_collect()283 repo.garbage_collect()
@@ -476,49 +392,48 @@ def _commit_import(
476 debian/changelog.392 debian/changelog.
477393
478 Arguments:394 Arguments:
479 spi - A SourcePackageInformation instance for this publish395 repo - a gitubuntu.git_repository.GitUbuntuRepositoory object
480 tree_hash - SHA1 from git-dsc-commit --tree-only of this publish396 spi - a
481 changelog_parent_commit = SHA1 of changelog parent397 gitubuntu.source_information.GitUbuntuSourcePackageInformation
482 upload_parent_commit = SHA1 of upload parent398 object for the publish in @tree_hash
483 unapplied_parent_commit = SHA1 of unapplied-patches import parent399 tree_hash - string SHA1 from git-dsc-commit --tree-only of this publish
400 changelog_parent_commit - string SHA1 of changelog parent
401 upload_parent_commit - string SHA1 of upload parent
402 unapplied_parent_commit - string SHA1 of unapplied-patches import
403 parent
484 """404 """
485 tag = None405 tag = None
486406
487 if unapplied_parent_commit:407 if unapplied_parent_commit:
488 import_type = 'patches-applied'408 import_type = 'patches-applied'
489 target_head_name = spi.applied_head_name(namespace)
490 if repo.get_applied_tag(spi.version, namespace) is None:409 if repo.get_applied_tag(spi.version, namespace) is None:
491 # Not imported before410 # Not imported before
492 tag = applied_tag(spi.version, namespace)411 tag = applied_tag(spi.version, namespace)
493 else:412 else:
494 import_type = 'patches-unapplied'413 import_type = 'patches-unapplied'
495 target_head_name = spi.head_name(namespace)
496 if repo.get_import_tag(spi.version, namespace) is None:414 if repo.get_import_tag(spi.version, namespace) is None:
497 # Not imported before415 # Not imported before
498 tag = import_tag(spi.version, namespace)416 tag = import_tag(spi.version, namespace)
499417
500 # Do not show importer/ namespace to user
501 _, _, pretty_head_name = target_head_name.partition('%s/' % namespace)
502
503 changelog_entry = get_changelog_for_commit(418 changelog_entry = get_changelog_for_commit(
504 repo,419 repo,
505 tree_hash,420 tree_hash,
506 changelog_parent_commit421 changelog_parent_commit
507 )422 )
508 msg = (b'Import %s version %b to %b\n\nImported using git-ubuntu import.' %423 msg = b'Import %s version %b\n\nImported using git-ubuntu import.' % (
509 (import_type.encode(), spi.version.encode(), pretty_head_name.encode())424 import_type.encode(),
510 )425 spi.version.encode(),
426 )
511427
512 parents = []428 parents = []
513429
514 if changelog_parent_commit is None and \430 #if changelog_parent_commit is None and \
515 upload_parent_commit is None and \431 # upload_parent_commit is None and \
516 unapplied_parent_commit is None and \432 # unapplied_parent_commit is None:
517 target_head_name in repo.local_branch_names:433 # # XXX: No parents and not creating a new branch
518 # No parents and not creating a new branch434 # tag = orphan_tag(spi.version, namespace)
519 tag = orphan_tag(spi.version, namespace)435 #else:
520 else:436 msg += b'\n'
521 msg += b'\n'
522437
523 if changelog_parent_commit is not None:438 if changelog_parent_commit is not None:
524 parents.append(changelog_parent_commit)439 parents.append(changelog_parent_commit)
@@ -539,19 +454,26 @@ def _commit_import(
539 environment_spi=spi454 environment_spi=spi
540 )455 )
541456
542 repo.update_head_to_commit(target_head_name, commit_hash)457 logging.debug(
543458 "Committed %s import of %s as %s",
544 logging.debug('Committed %s import of %s as %s in %s',459 import_type,
545 import_type, spi.version, commit_hash, pretty_head_name460 spi.version,
546 )461 commit_hash,
462 )
547463
548 if tag is not None:464 if tag is not None:
549 # should be annotated to use create_tag API465 # should be annotated to use create_tag API
550 logging.debug('Creating tag %s pointing to %s', tag, commit_hash)466 logging.debug('Creating tag %s pointing to %s', tag, commit_hash)
551 repo.git_run(['tag', '-a', '-m', 'git-ubuntu import v%s' % VERSION,467 repo.git_run(
552 tag, commit_hash468 [
553 ]469 'tag',
554 )470 '-a',
471 '-m',
472 'git-ubuntu import v%s' % VERSION,
473 tag,
474 commit_hash,
475 ],
476 )
555477
556def commit_unapplied_patches_import(478def commit_unapplied_patches_import(
557 repo,479 repo,
@@ -592,9 +514,63 @@ def commit_applied_patches_import(
592 unapplied_parent_commit,514 unapplied_parent_commit,
593 )515 )
594516
517def dsc_already_imported(
518 repo,
519 spi,
520 ref_namespace,
521 distname,
522):
523 # always check if the same dist has a matching dsc
524 with repo.dsc_branch(
525 namespace=ref_namespace,
526 dist=distname,
527 ):
528 cached_dsc_path = os.path.join(
529 os.getcwd(),
530 spi.version,
531 os.path.basename(spi.dsc_pathname),
532 )
533
534 if os.path.exists(cached_dsc_path):
535 # If we have imported this version before, compare the
536 # DSC in the DSC branch to this one
537 imported_dsc = GitUbuntuDsc(cached_dsc_path)
538 return GitUbuntuDsc(spi.dsc_pathname).compare_dsc(imported_dsc)
539
540 # if a debian dsc exists and matches, but an ubuntu dsc does not
541 # exist, import the ubuntu dsc, but signal that a full import is
542 # not necessary
543 if distname == 'ubuntu':
544 with repo.dsc_branch(
545 namespace=ref_namespace,
546 dist='debian',
547 ):
548 imported_dsc_path = os.path.join(
549 os.getcwd(),
550 spi.version,
551 os.path.basename(spi.dsc_pathname),
552 )
553
554 if os.path.exists(imported_dsc_path):
555 # If we have imported this version before, compare the
556 # DSC in the DSC branch to this one
557 imported_dsc = GitUbuntuDsc(imported_dsc_path)
558 if GitUbuntuDsc(spi.dsc_pathname).compare_dsc(imported_dsc):
559 import_dsc(
560 repo,
561 GitUbuntuDsc(spi.dsc_pathname),
562 ref_namespace,
563 str(spi.version),
564 spi.distribution.name.lower(),
565 )
566 return True
567 return False
568
595def import_dsc(repo, dsc, namespace, version, dist):569def import_dsc(repo, dsc, namespace, version, dist):
596 """Imports the dsc file to importer/{debian,ubuntu}/dsc570 """Imports the dsc file to importer/{debian,ubuntu}/dsc
597571
572 XXX use a context manager?
573
598 Arguments:574 Arguments:
599 spi - A GitUbuntuSourcePackageInformation instance575 spi - A GitUbuntuSourcePackageInformation instance
600 """576 """
@@ -937,7 +913,7 @@ def _devel_branch_updates(
937913
938 devel_hash = None914 devel_hash = None
939 devel_name = None915 devel_name = None
940 devel_branch_name = ref_prefix + 'ubuntu/devel'916 devel_branch_name = ref_prefix + '/devel'
941917
942 for series_name in series_name_list:918 for series_name in series_name_list:
943 series_devel_hash = None919 series_devel_hash = None
@@ -946,7 +922,7 @@ def _devel_branch_updates(
946 # Find highest version publish in these pockets, favoring this922 # Find highest version publish in these pockets, favoring this
947 # order of pockets923 # order of pockets
948 for suff in ['-proposed', '-updates', '-security', '']:924 for suff in ['-proposed', '-updates', '-security', '']:
949 name = "%subuntu/%s%s" % (ref_prefix, series_name, suff)925 name = "%s/%s%s" % (ref_prefix, series_name, suff)
950 if name in head_versions:926 if name in head_versions:
951 head_commit, head_version = head_versions[name]927 head_commit, head_version = head_versions[name]
952 if version_compare(head_version, series_devel_version) > 0:928 if version_compare(head_version, series_devel_version) > 0:
@@ -954,7 +930,7 @@ def _devel_branch_updates(
954 series_devel_version = head_version930 series_devel_version = head_version
955 series_devel_hash = head_commit931 series_devel_hash = head_commit
956 if series_devel_hash:932 if series_devel_hash:
957 series_devel_branch_name = '%subuntu/%s-devel' % (933 series_devel_branch_name = '%s/%s-devel' % (
958 ref_prefix,934 ref_prefix,
959 series_name,935 series_name,
960 )936 )
@@ -969,87 +945,213 @@ def _devel_branch_updates(
969 devel_name = series_devel_name945 devel_name = series_devel_name
970 devel_hash = series_devel_hash946 devel_hash = series_devel_hash
971947
972 yield ref_prefix + 'ubuntu/devel', devel_hash948 yield devel_branch_name, devel_hash
973949
974950
975def update_devel_branches(951def update_branches(
952 distname,
953 dist_sinfo,
976 repo,954 repo,
977 namespace,955 head_ref_namespace,
978 pkgname,956 pkgname,
979 ubuntu_sinfo,957 updated_head_refs,
980):958):
981 """update_devel_branches - move the 'meta' -devel branches to the959 """update_branches - move the series+pocket branches based upon Launchpad imports
982 latest publication in each series
983960
984 For all known series in @ubuntu_sinfo, update the961 If distname is 'ubuntu', ubuntu/series-devel branches and update the
985 ubuntu/series-devel branch to the latest version in the series'962 ubuntu/devel branch to the latest ubuntu/series-devel branch.
986 pocket branches.
987
988 Update the ubuntu/devel branch to the latest ubuntu/series-devel
989 branch.
990
991 Do this for both the unapplied and applied branches.
992963
993 Arguments:964 Arguments:
965 distname - string distribution name (one of 'debian', 'ubuntu')
966 dist_sinfo - gitubuntu.source_information.GitUbuntuSourceInformation object
994 repo - gitubuntu.git_repository.GitUbuntuRepository object967 repo - gitubuntu.git_repository.GitUbuntuRepository object
968 head_ref_namespace - string namespace under which the relevant
969 branch refs can be found
995 pkgname - string source package name970 pkgname - string source package name
996 namespace - string namespace under which the relevant branch refs
997 can be found
998 ubuntu_sinfo - GitUbuntuSourceInformation object for the ubuntu
999 archive
1000 """971 """
1001 for applied_prefix in ['', 'applied/']:972 for ref, commit_hash in updated_head_refs.items():
1002 head_versions = {973 repo.update_head_to_commit(
1003 ref: (str(target['head'].peel().id), target['version'])974 ref,
1004 for ref, target in repo.get_heads_and_versions(975 commit_hash,
1005 '%subuntu' % applied_prefix,976 )
1006 namespace=namespace,977
1007 ).items()978 if distname == 'debian':
1008 }979 return
1009 for ref, commit in _devel_branch_updates(980
1010 ref_prefix=('%s/%s' % (namespace, applied_prefix)),981 head_versions = {
1011 head_versions=head_versions,982 ref: (str(target['head'].peel().id), target['version'])
1012 series_name_list=ubuntu_sinfo.all_series_name_list,983 for ref, target in repo.get_heads_and_versions(
1013 ):984 head_ref_namespace,
1014 if commit is None:985 ).items()
1015 logging.warn(986 }
1016 "Source package '%s' does not appear to have been published "987 for ref, commit in _devel_branch_updates(
1017 "in Ubuntu. No %s created.",988 ref_prefix=head_ref_namespace,
1018 pkgname,989 head_versions=head_versions,
1019 ref,990 series_name_list=dist_sinfo.all_series_name_list,
991 ):
992 if commit is None:
993 logging.warn(
994 "Source package '%s' does not appear to have been published "
995 "in Ubuntu. No %s created.",
996 pkgname,
997 ref,
998 )
999 else:
1000 logging.debug(
1001 "Setting %s to '%s'",
1002 ref,
1003 commit,
1004 )
1005 repo.update_head_to_commit(ref, commit)
1006
1007
1008def get_changelog_parent_commit(
1009 repo,
1010 spi,
1011 ref_namespace,
1012 import_tree_versions,
1013 patches_applied,
1014):
1015 if spi.version in _PARENT_OVERRIDES:
1016 logging.debug(
1017 '%s is specified in the parent override file.',
1018 spi.version
1019 )
1020
1021 try:
1022 (
1023 unapplied_changelog_parent_commit,
1024 applied_changelog_parent_commit,
1025 ) = override_parents(repo, spi, ref_namespace)
1026 if patches_applied:
1027 return applied_changelog_parent_commit
1028 else:
1029 return unapplied_changelog_parent_commit
1030 except ParentOverrideError as e:
1031 logging.error("%s" % e)
1032 return None
1033 else:
1034 # Walk changelog backwards until we find an imported version
1035 for version in import_tree_versions:
1036 if patches_applied:
1037 changelog_parent_tag = repo.get_applied_tag(
1038 version,
1039 ref_namespace,
1020 )1040 )
1021 else:1041 else:
1022 logging.debug(1042 changelog_parent_tag = repo.get_import_tag(
1023 "Setting %s to '%s'",1043 version,
1024 ref,1044 ref_namespace,
1025 commit,
1026 )1045 )
1027 repo.update_head_to_commit(ref, commit)1046 if changelog_parent_tag:
1047 # sanity check that version from d/changelog of the
1048 # tagged parent matches ours
1049 parent_changelog_version, _ = \
1050 repo.get_changelog_versions_from_treeish(
1051 str(changelog_parent_tag.peel(pygit2.Tree).id),
1052 )
1053 # if the parent was imported via a parent override
1054 # itself, assume it is valid without checking its
1055 # tree's debian/changelog, as it wasn't used
1056 if (version not in _PARENT_OVERRIDES and
1057 parent_changelog_version != version
1058 ):
1059 logging.error(
1060 "Found a tag corresponding to parent version "
1061 "%s, but d/changelog version (%s) differs. Will "
1062 "orphan commit.",
1063 version,
1064 parent_changelog_version,
1065 )
1066 return None
1067 else:
1068 logging.debug("Changelog parent (tag) is %s",
1069 repo.tag_to_pretty_name(changelog_parent_tag)
1070 )
1071 return str(changelog_parent_tag.peel().id)
1072 return None
1073
1074
1075def get_unapplied_changelog_parent_commit(
1076 repo,
1077 spi,
1078 ref_namespace,
1079 import_tree_versions,
1080):
1081 return get_changelog_parent_commit(
1082 repo,
1083 spi,
1084 ref_namespace,
1085 import_tree_versions,
1086 patches_applied=False,
1087 )
1088
1089
1090def get_applied_changelog_parent_commit(
1091 repo,
1092 spi,
1093 ref_namespace,
1094 import_tree_versions,
1095):
1096 return get_changelog_parent_commit(
1097 repo,
1098 spi,
1099 ref_namespace,
1100 import_tree_versions,
1101 patches_applied=True,
1102 )
1103
1104
1105def get_upload_parent_commit(
1106 repo,
1107 spi,
1108 ref_namespace,
1109 unapplied_import_tree_hash,
1110):
1111 upload_tag = repo.get_upload_tag(spi.version, ref_namespace)
1112 if upload_tag:
1113 if str(upload_tag.peel().tree.id) != unapplied_import_tree_hash:
1114 logging.warn(
1115 "Found upload tag %s, but the corresponding tree "
1116 "does not match the published version. Will import %s as "
1117 "normal, ignoring the upload tag.",
1118 repo.tag_to_pretty_name(upload_tag),
1119 spi.version,
1120 )
1121 return None
1122 else:
1123 upload_parent_commit = str(upload_tag.peel().id)
1124 return upload_parent_commit
1125
1126 return None
10281127
10291128
1030# imports a package based upon source package information1129# imports a package based upon source package information
1031def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo):1130def import_unapplied_spi(repo, spi, ref_namespace, skip_orig):
1032 """Imports a source package from Launchpad into the git1131 """Imports a source package from Launchpad into the git
1033 repository1132 repository
10341133
1134 Only called if the version has not been imported before
1135
1035 Arguments:1136 Arguments:
1036 spi - a SourcePackageInformation instance1137 repo - a gitubutu.git_repository.GitUbuntuRepository object
1138 spi - a
1139 gitubuntu.source_information.GitUbuntuSourcePackageInformation object
1140 ref_namespace - a string Git-ref namespace this import will live in
1141 skip_orig - a boolean indicating the orig tarball should not
1142 be imported
1037 """1143 """
1038 pretty_head_name = spi.pretty_head_name
10391144
1040 logging.info('Importing patches-unapplied %s to %s',1145 logging.info("Importing patches-unapplied %s", spi.version)
1041 spi.version, pretty_head_name
1042 )
1043 unapplied_tip_head = repo.get_head_by_name(spi.head_name(namespace))
10441146
1045 spi.pull()1147 assert not repo.get_import_tag(spi.version, ref_namespace)
10461148
1047 repo.clean_repository_state()1149 repo.clean_repository_state()
10481150
1049 import_dsc(1151 import_dsc(
1050 repo,1152 repo,
1051 GitUbuntuDsc(spi.dsc_pathname),1153 GitUbuntuDsc(spi.dsc_pathname),
1052 namespace,1154 ref_namespace,
1053 str(spi.version),1155 str(spi.version),
1054 spi.distribution.name.lower(),1156 spi.distribution.name.lower(),
1055 )1157 )
@@ -1058,7 +1160,7 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo):
1058 import_orig(1160 import_orig(
1059 repo,1161 repo,
1060 GitUbuntuDsc(spi.dsc_pathname),1162 GitUbuntuDsc(spi.dsc_pathname),
1061 namespace,1163 ref_namespace,
1062 str(spi.version),1164 str(spi.version),
1063 spi.distribution.name.lower(),1165 spi.distribution.name.lower(),
1064 )1166 )
@@ -1094,132 +1196,46 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo):
1094 'source pkg version: {} != changelog version: {}'.format(1196 'source pkg version: {} != changelog version: {}'.format(
1095 spi.version, changelog_version))1197 spi.version, changelog_version))
10961198
1097 unapplied_tip_version = None1199 unapplied_changelog_parent_commit = get_unapplied_changelog_parent_commit(
1098 # check if the version to import has already been imported to1200 repo,
1099 # this head1201 spi,
1100 if unapplied_tip_head is not None:1202 ref_namespace,
1101 if repo.treeishs_identical(1203 import_tree_versions,
1102 unapplied_import_tree_hash, str(unapplied_tip_head.peel().id)1204 )
1103 ):
1104 logging.warn('%s is identical to %s',
1105 pretty_head_name, spi.version
1106 )
1107 return
1108 unapplied_tip_version, _ = repo.get_changelog_versions_from_treeish(
1109 str(unapplied_tip_head.peel().id),
1110 )
1111
1112 logging.debug('Tip version is %s', unapplied_tip_version)
1113
1114 unapplied_changelog_parent_commit = None
1115 upload_parent_commit = None
1116 unapplied_parent_commit = None
1117
1118 if spi.version in _PARENT_OVERRIDES:
1119 logging.debug(
1120 '%s is specified in the parent override file.',
1121 spi.version
1122 )
1123
1124 try:
1125 (
1126 unapplied_changelog_parent_commit,
1127 _
1128 ) = override_parents(repo, spi, namespace)
1129 except ParentOverrideError as e:
1130 logging.error("%s" % e)
1131 return
1132 else:
1133 if version_compare(str(spi.version), unapplied_tip_version) <= 0:
1134 logging.warn(
1135 "Version to import (%s) is not after %s tip (%s)",
1136 spi.version,
1137 pretty_head_name,
1138 unapplied_tip_version,
1139 )
1140
1141 # Walk changelog backwards until we find an imported version
1142 for version in import_tree_versions:
1143 unapplied_changelog_parent_tag = repo.get_import_tag(version, namespace)
1144 if unapplied_changelog_parent_tag is not None:
1145 # sanity check that version from d/changelog of the
1146 # tagged parent matches ours
1147 parent_changelog_version, _ = \
1148 repo.get_changelog_versions_from_treeish(
1149 str(unapplied_changelog_parent_tag.peel(pygit2.Tree).id),
1150 )
1151 # if the parent was imported via a parent override
1152 # itself, assume it is valid without checking its
1153 # tree's debian/changelog, as it wasn't used
1154 if (version not in _PARENT_OVERRIDES and
1155 parent_changelog_version != version
1156 ):
1157 logging.error(
1158 "Found a tag corresponding to parent version "
1159 "%s, but d/changelog version (%s) differs. Will "
1160 "orphan commit.",
1161 version,
1162 parent_changelog_version,
1163 )
1164 else:
1165 unapplied_changelog_parent_commit = str(unapplied_changelog_parent_tag.peel().id)
1166 logging.debug("Changelog parent (tag) is %s",
1167 repo.tag_to_pretty_name(unapplied_changelog_parent_tag)
1168 )
1169 break
11701205
1206 # XXX: if we are here, the spi has not been imported -- but that
1207 # does not mean the the contents of the imported spi are identical
1208 # (rework orphan tags)
1171 # check if the version to import has already been uploaded to1209 # check if the version to import has already been uploaded to
1172 # this head -- we leverage the above code to perform a 'dry-run'1210 # this head -- we leverage the above code to perform a 'dry-run'
1173 # of the import tree, to obtain the changelog parent1211 # of the import tree, to obtain the changelog parent
1174 upload_tag = repo.get_upload_tag(spi.version, namespace)1212 upload_parent_commit = get_upload_parent_commit(
1175 import_tag = repo.get_import_tag(spi.version, namespace)1213 repo,
1176 if import_tag:1214 spi,
1177 if str(import_tag.peel().tree.id) != unapplied_import_tree_hash:1215 ref_namespace,
1178 logging.error(1216 unapplied_import_tree_hash,
1179 "Found import tag %s, but the corresponding tree "1217 )
1180 "does not match the published version. Will orphan commit.",1218 if upload_parent_commit and unapplied_changelog_parent_commit:
1181 repo.tag_to_pretty_name(import_tag),1219 try:
1220 repo.git_run(
1221 [
1222 'merge-base',
1223 '--is-ancestor',
1224 unapplied_changelog_parent_commit,
1225 upload_parent_commit,
1226 ],
1227 verbose_on_failure=False,
1182 )1228 )
1183 unapplied_changelog_parent_commit = None1229 unapplied_changelog_parent_commit = None
1184 else:1230 except CalledProcessError as e:
1185 repo.update_head_to_commit(1231 if e.returncode != 1:
1186 spi.head_name(namespace),1232 raise
1187 str(import_tag.peel().id),
1188 )
1189 return
1190 elif upload_tag:
1191 if str(upload_tag.peel().tree.id) != unapplied_import_tree_hash:
1192 logging.warn(
1193 "Found upload tag %s, but the corresponding tree "
1194 "does not match the published version. Will import %s as "
1195 "normal, ignoring the upload tag.",
1196 repo.tag_to_pretty_name(upload_tag),
1197 spi.version,
1198 )
1199 else:
1200 upload_parent_commit = str(upload_tag.peel().id)
1201
1202 if unapplied_changelog_parent_commit is not None:
1203 try:
1204 repo.git_run(
1205 [
1206 'merge-base',
1207 '--is-ancestor',
1208 unapplied_changelog_parent_commit,
1209 upload_parent_commit,
1210 ],
1211 verbose_on_failure=False,
1212 )
1213 unapplied_changelog_parent_commit = None
1214 except CalledProcessError as e:
1215 if e.returncode != 1:
1216 raise
12171233
1218 commit_unapplied_patches_import(1234 commit_unapplied_patches_import(
1219 repo,1235 repo,
1220 spi,1236 spi,
1221 unapplied_import_tree_hash,1237 unapplied_import_tree_hash,
1222 namespace,1238 ref_namespace,
1223 unapplied_changelog_parent_commit,1239 unapplied_changelog_parent_commit,
1224 upload_parent_commit,1240 upload_parent_commit,
1225 )1241 )
@@ -1227,242 +1243,300 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo):
1227def import_applied_spi(1243def import_applied_spi(
1228 repo,1244 repo,
1229 spi,1245 spi,
1230 namespace,1246 ref_namespace,
1231 ubuntu_sinfo,
1232 allow_applied_failures
1233):1247):
1234 """Imports a source package from Launchpad into the git1248 """Imports a source package from Launchpad into the git
1235 repository1249 repository
12361250
1237 Arguments:1251 Arguments:
1238 spi - a SourcePackageInformation instance1252 repo - a gitubutu.git_repository.GitUbuntuRepository object
1253 spi - a
1254 gitubuntu.source_information.GitUbuntuSourcePackageInformation object
1255 ref_namespace - a string Git-ref namespace this import will live in
1239 """1256 """
1240 pretty_head_name = spi.pretty_head_name1257 logging.info('Importing patches-applied %s', spi.version)
1241
1242 logging.info('Importing patches-applied %s to %s' % (spi.version, pretty_head_name))
1243 applied_tip_head = repo.get_head_by_name(spi.applied_head_name(namespace))
1244
1245 spi.pull()
12461258
1247 repo.clean_repository_state()1259 repo.clean_repository_state()
12481260
1249 unapplied_parent_tag = repo.get_import_tag(spi.version, namespace)1261 unapplied_parent_tag = repo.get_import_tag(spi.version, ref_namespace)
1250 unapplied_parent_commit = str(unapplied_parent_tag.peel().id)1262 unapplied_parent_commit = str(unapplied_parent_tag.peel().id)
1251 unapplied_import_tree_hash = str(unapplied_parent_tag.peel(pygit2.Tree).id)1263 unapplied_import_tree_hash = str(unapplied_parent_tag.peel(pygit2.Tree).id)
1252 logging.debug('Found patches-unapplied version %s as commit %s',1264 logging.debug('Found patches-unapplied version %s as commit %s',
1253 str(spi.version), unapplied_parent_commit)1265 str(spi.version), unapplied_parent_commit)
12541266
1255 import_tree_versions = repo.get_all_changelog_versions_from_treeish(1267 import_tree_versions = repo.get_all_changelog_versions_from_treeish(
1256 unapplied_import_tree_hash1268 unapplied_import_tree_hash
1257 )1269 )
1258 changelog_version = import_tree_versions[0]
1259
1260 applied_tip_version = None
1261 # check if the version to import has already been imported to
1262 # this head
1263 if applied_tip_head is not None:
1264 if repo.treeishs_identical(
1265 unapplied_import_tree_hash, str(applied_tip_head.peel().id)
1266 ):
1267 logging.warn('%s is identical to %s',
1268 pretty_head_name, spi.version
1269 )
1270 return
1271 applied_tip_version, _ = repo.get_changelog_versions_from_treeish(
1272 str(applied_tip_head.peel().id),
1273 )
1274
1275 logging.debug('Tip version is %s', applied_tip_version)
1276
1277 applied_changelog_parent_commit = None
1278
1279 if spi.version in _PARENT_OVERRIDES:
1280 logging.debug('%s is specified in the parent override file.' %
1281 spi.version
1282 )
1283
1284 (
1285 _,
1286 applied_changelog_parent_commit,
1287 ) = override_parents(
1288 repo,
1289 spi,
1290 namespace,
1291 )
1292 else:
1293 if version_compare(str(spi.version), applied_tip_version) <= 0:
1294 logging.warn('Version to import (%s) is not after %s tip (%s)',
1295 spi.version, pretty_head_name, applied_tip_version
1296 )
1297
1298 # Walk changelog backwards until we find an imported version
1299 for version in import_tree_versions:
1300 applied_changelog_parent_tag = repo.get_applied_tag(version, namespace)
1301 if applied_changelog_parent_tag is not None:
1302 # sanity check that version from d/changelog of the
1303 # tagged parent matches ours
1304 parent_changelog_version, _ = \
1305 repo.get_changelog_versions_from_treeish(
1306 str(applied_changelog_parent_tag.peel(pygit2.Tree).id),
1307 )
1308 # if the parent was imported via a parent override
1309 # itself, assume it is valid without checking its
1310 # tree's debian/changelog, as it wasn't used
1311 if (version not in _PARENT_OVERRIDES and
1312 parent_changelog_version != version
1313 ):
1314 logging.error('Found a tag corresponding to '
1315 'parent version %s, but d/changelog '
1316 'version (%s) differs. Will '
1317 'orphan commit.' % (
1318 version,
1319 parent_changelog_version
1320 )
1321 )
1322 else:
1323 applied_changelog_parent_commit = str(applied_changelog_parent_tag.peel().id)
1324 logging.debug('Changelog parent (tag) is %s',
1325 repo.tag_to_pretty_name(applied_changelog_parent_tag)
1326 )
1327 break
13281270
1329 applied_tag = repo.get_applied_tag(version, namespace)1271 applied_changelog_parent_commit = get_applied_changelog_parent_commit(
1330 if applied_tag:1272 repo,
1331 # XXX: What should be checked here? The result of pushing all1273 spi,
1332 # the patches? How to do it most non-destructively? (Might be1274 ref_namespace,
1333 # able to use our new helper to get treeish after running a1275 import_tree_versions,
1334 # command, in this case, `quilt push -a`)1276 )
1335 # if str(applied_tag.peel().tree.id) != applied_import_tree_hash:
1336 # logging.error(
1337 # "Found patches-applied import tag %s, but the "
1338 # "corresponding tree does not match the published "
1339 # "version. Will orphan commit.",
1340 # repo.tag_to_pretty_name(applied_tag),
1341 # )
1342 # applied_changelog_parent_commit = None
1343 #else:
1344 repo.update_head_to_commit(
1345 spi.applied_head_name(namespace),
1346 str(applied_tag.peel().id),
1347 )
1348 return
13491277
1350 # Assume no patches to apply1278 # Assume no patches to apply
1351 applied_import_tree_hash = unapplied_import_tree_hash1279 applied_import_tree_hash = unapplied_import_tree_hash
1352 # get tree id from above commit1280 # get tree id from above commit
1353 try:1281 for (
1354 for (1282 applied_import_tree_hash,
1355 applied_import_tree_hash,1283 patch_name,
1356 patch_name,1284 patch_desc
1357 patch_desc1285 ) in import_patches_applied_tree(repo, spi.dsc_pathname):
1358 ) in import_patches_applied_tree(repo, spi.dsc_pathname):1286 # special case for .pc removal
1359 # special case for .pc removal1287 if not patch_name is None:
1360 if patch_name is None:1288 msg = b'%b.' % (patch_desc.encode())
1361 msg = b'%b.' % (patch_desc.encode())1289 else:
1362 else:1290 if patch_desc is None:
1363 if patch_desc is None:1291 patch_desc = (
1364 patch_desc = (1292 "%s\n\nNo DEP3 Subject or Description header found" %
1365 "%s\n\nNo DEP3 Subject or Description header found" %1293 patch_name
1366 patch_name
1367 )
1368 msg = b'%b\n\nGbp-Pq: %b.' % (
1369 patch_desc.encode(),
1370 patch_name.encode()
1371 )1294 )
1372 unapplied_parent_commit = repo.commit_tree_hash(1295 msg = b'%b\n\nGbp-Pq: %b.' % (
1373 applied_import_tree_hash,1296 patch_desc.encode(),
1374 [unapplied_parent_commit],1297 patch_name.encode()
1375 msg,
1376 spi
1377 )1298 )
1299 unapplied_parent_commit = repo.commit_tree_hash(
1300 applied_import_tree_hash,
1301 [unapplied_parent_commit],
1302 msg,
1303 spi
1304 )
13781305
1379 logging.debug(1306 logging.debug(
1380 "Committed patch-application of %s as %s",1307 "Committed patch-application of %s as %s",
1381 patch_name, unapplied_parent_commit1308 patch_name, unapplied_parent_commit
1382 )1309 )
1383 except CalledProcessError as e:
1384 if allow_applied_failures:
1385 return
1386 raise
13871310
1388 commit_applied_patches_import(1311 commit_applied_patches_import(
1389 repo,1312 repo,
1390 spi,1313 spi,
1391 applied_import_tree_hash,1314 applied_import_tree_hash,
1392 namespace,1315 ref_namespace,
1393 applied_changelog_parent_commit,1316 applied_changelog_parent_commit,
1394 unapplied_parent_commit,1317 unapplied_parent_commit,
1395 )1318 )
13961319
1397def import_publishes(1320
1321def import_new_publishes(
1398 repo,1322 repo,
1399 pkgname,1323 pkgname,
1400 namespace,1324 distname,
1325 new_spi,
1326 ref_namespace,
1327 head_ref_namespace,
1328 skip_orig,
1329 allow_applied_failures,
1401 patches_applied,1330 patches_applied,
1402 debian_head_versions,1331):
1403 ubuntu_head_versions,1332 history_found = False
1404 debian_sinfo,1333 spi = None
1405 ubuntu_sinfo,1334
1335 updated_head_refs = dict()
1336 try:
1337 for spi in new_spi:
1338 history_found = True
1339 spi.pull()
1340 # DSC can only be already imported if the import tag
1341 # exists
1342
1343 if patches_applied:
1344 existing_tag = repo.get_applied_tag(spi.version, ref_namespace)
1345 head_ref_name = gitubuntu.importerutils.applied_head_ref_name(
1346 spi,
1347 ref_namespace,
1348 )
1349 else:
1350 existing_tag = repo.get_import_tag(spi.version, ref_namespace)
1351 head_ref_name = gitubuntu.importerutils.head_ref_name(
1352 spi,
1353 ref_namespace,
1354 )
1355
1356 if existing_tag:
1357 # if an identical DSC has already been imported,
1358 # then we only need to update our branch tracker
1359 if dsc_already_imported(
1360 repo,
1361 spi,
1362 ref_namespace,
1363 distname,
1364 ):
1365 updated_head_refs[head_ref_name] = str(
1366 existing_tag.peel(pygit2.Commit).id
1367 )
1368 continue
1369
1370 try:
1371 if patches_applied:
1372 import_applied_spi(
1373 repo=repo,
1374 spi=spi,
1375 ref_namespace=ref_namespace,
1376 )
1377 else:
1378 import_unapplied_spi(
1379 repo=repo,
1380 spi=spi,
1381 ref_namespace=ref_namespace,
1382 skip_orig=skip_orig,
1383 )
1384 except CalledProcessError:
1385 if patches_applied and allow_applied_failures:
1386 continue
1387 raise
1388
1389 if patches_applied:
1390 existing_tag = repo.get_applied_tag(spi.version, ref_namespace)
1391 else:
1392 existing_tag = repo.get_import_tag(spi.version, ref_namespace)
1393
1394 updated_head_refs[head_ref_name] = str(
1395 existing_tag.peel(pygit2.Commit).id
1396 )
1397 except Exception as e:
1398 if patches_applied:
1399 if not spi:
1400 msg = "Unable to import patches-applied to %s" % distname
1401 else:
1402 msg = "Unable to import patches-applied %s to %s" % (
1403 str(spi.version),
1404 distname,
1405 )
1406 logging.error(msg)
1407 else:
1408 if not spi:
1409 msg = "Unable to import patches-unapplied to %s" % distname
1410 else:
1411 msg = "Unable to import patches-unapplied %s to %s" % (
1412 str(spi.version),
1413 distname,
1414 )
1415 raise GitUbuntuImportError(msg) from e
1416 else:
1417 history_found = True
1418
1419 return history_found, updated_head_refs
1420
1421
1422def import_publishes(
1423 repo,
1424 pkgname,
1425 base_ref_namespace,
1406 active_series_only,1426 active_series_only,
1407 workdir,1427 workdir,
1408 skip_orig,1428 skip_orig,
1429 skip_applied,
1409 allow_applied_failures,1430 allow_applied_failures,
1431 pullfile,
1432 retries,
1433 retry_backoffs,
1410):1434):
1411 history_found = False
1412 only_debian = False1435 only_debian = False
1413 srcpkg_information = None1436 history_found = False
1414 if patches_applied:1437
1415 _namespace = namespace1438 debian_sinfo = GitUbuntuSourceInformation(
1416 namespace = '%s/applied' % namespace1439 'debian',
1417 import_type = 'patches-applied'1440 pkgname,
1418 import_func = functools.partial(1441 os.path.abspath(pullfile),
1419 import_applied_spi,1442 retries,
1420 allow_applied_failures=allow_applied_failures,1443 retry_backoffs,
1444 )
1445
1446 ubuntu_sinfo = GitUbuntuSourceInformation(
1447 'ubuntu',
1448 pkgname,
1449 os.path.abspath(pullfile),
1450 retries,
1451 retry_backoffs,
1452 )
1453
1454 for distname, dist_sinfo in (
1455 ('debian', debian_sinfo),
1456 ('ubuntu', ubuntu_sinfo),
1457 ):
1458 unapplied_head_ref_namespace = '%s/%s' % (base_ref_namespace, distname)
1459 applied_head_ref_namespace='%s/applied/%s' % (base_ref_namespace, distname)
1460
1461 unapplied_versions = repo.get_heads_and_versions(
1462 head_ref_namespace=unapplied_head_ref_namespace,
1421 )1463 )
1422 else:1464 applied_versions = repo.get_heads_and_versions(
1423 _namespace = namespace1465 head_ref_namespace=applied_head_ref_namespace
1424 import_type = 'patches-unapplied'
1425 import_func = functools.partial(
1426 import_unapplied_spi,
1427 skip_orig=skip_orig,
1428 )1466 )
1429 for distname, versions, dist_sinfo in (1467 debug_log_head_versions(unapplied_versions)
1430 ("debian", debian_head_versions, debian_sinfo),1468 debug_log_head_versions(applied_versions)
1431 ("ubuntu", ubuntu_head_versions, ubuntu_sinfo)):
1432 if active_series_only and distname == "debian":1469 if active_series_only and distname == "debian":
1433 continue1470 continue
1471
1472 try:
1473 history_found, updated_head_refs = import_new_publishes(
1474 repo,
1475 pkgname,
1476 distname,
1477 dist_sinfo.launchpad_versions_published_after(
1478 unapplied_versions,
1479 unapplied_head_ref_namespace,
1480 workdir,
1481 active_series_only,
1482 ),
1483 base_ref_namespace,
1484 unapplied_head_ref_namespace,
1485 skip_orig,
1486 allow_applied_failures=None,
1487 patches_applied=False,
1488 )
1489 update_branches(
1490 distname,
1491 dist_sinfo,
1492 repo,
1493 unapplied_head_ref_namespace,
1494 pkgname,
1495 updated_head_refs,
1496 )
1497 except NoPublicationHistoryException:
1498 logging.warning("No publication history found for %s in %s.",
1499 pkgname, distname
1500 )
1501 if distname == 'ubuntu':
1502 only_debian = True
1503 continue
1504
1505 if skip_applied:
1506 continue
1507
1434 try:1508 try:
1435 for srcpkg_information in dist_sinfo.launchpad_versions_published_after(1509 _, updated_head_refs = import_new_publishes(
1436 versions,1510 repo,
1437 namespace,1511 pkgname,
1438 workdir=workdir,1512 distname,
1439 active_series_only=active_series_only1513 dist_sinfo.launchpad_versions_published_after(
1440 ):1514 applied_versions,
1441 history_found = True1515 applied_head_ref_namespace,
1442 import_func(1516 workdir,
1443 repo=repo,1517 active_series_only,
1444 spi=srcpkg_information,1518 ),
1445 namespace=_namespace,1519 base_ref_namespace,
1446 ubuntu_sinfo=ubuntu_sinfo,1520 applied_head_ref_namespace,
1447 )1521 skip_orig=None,
1522 allow_applied_failures=allow_applied_failures,
1523 patches_applied=True,
1524 )
1525 update_branches(
1526 distname,
1527 dist_sinfo,
1528 repo,
1529 applied_head_ref_namespace,
1530 pkgname,
1531 updated_head_refs,
1532 )
1448 except NoPublicationHistoryException:1533 except NoPublicationHistoryException:
1449 logging.warning("No publication history found for %s in %s.",1534 logging.warning("No publication history found for %s in %s.",
1450 pkgname, distname1535 pkgname, distname
1451 )1536 )
1452 if distname == 'ubuntu':1537 if distname == 'ubuntu':
1453 only_debian = True1538 only_debian = True
1454 except Exception as e:1539 continue
1455 if srcpkg_information is None:
1456 msg = 'Unable to import %s to %s' % (import_type, distname)
1457 else:
1458 msg = 'Unable to import %s %s to %s' % (import_type,
1459 str(srcpkg_information.version), distname)
1460 if not patches_applied:
1461 raise GitUbuntuImportError(msg) from e
1462 else:
1463 logging.error(msg)
1464 else:
1465 history_found = True
14661540
1467 return (only_debian, history_found)1541 return (only_debian, history_found)
14681542
diff --git a/gitubuntu/importerutils.py b/gitubuntu/importerutils.py
1469new file mode 1006441543new file mode 100644
index 0000000..6035c45
--- /dev/null
+++ b/gitubuntu/importerutils.py
@@ -0,0 +1,26 @@
1def head_ref_name(spi, ref_namespace):
2 """head_ref_name - get the patches-unapplied Git ref name a spi would import to
3
4 spi - a gitubuntu.source_information.GitUbuntuSourcePackageInformation object
5 ref_namespace - string namespace the resulting Git ref should be in
6 """
7 if spi.pocket.lower() == 'release':
8 pocket_suffix = ''
9 else:
10 pocket_suffix = '-' + spi.pocket.lower()
11
12 return '%s/%s/%s%s' % (
13 ref_namespace,
14 spi.distribution.name.lower(),
15 spi.series.name.lower(),
16 pocket_suffix,
17 )
18
19def applied_head_ref_name(spi, ref_namespace):
20 """applied_head_ref_name - get the patches-applied Git ref name a spi would import to
21
22 Arguments:
23 spi - a gitubuntu.source_information.GitUbuntuSourcePackageInformation object
24 ref_namespace - string namespace the resulting Git ref should be in
25 """
26 return head_ref_name(spi, '%s/applied' % ref_namespace)
diff --git a/gitubuntu/importppa.py b/gitubuntu/importppa.py
index 1f3283c..3bfde5e 100644
--- a/gitubuntu/importppa.py
+++ b/gitubuntu/importppa.py
@@ -83,9 +83,8 @@ def main(
83 namespace,83 namespace,
84 )84 )
85 for head_name in sorted(ubuntu_head_versions):85 for head_name in sorted(ubuntu_head_versions):
86 _, _, pretty_head_name = head_name.partition('%s/', namespace)
87 logging.debug('Last imported %s version is %s',86 logging.debug('Last imported %s version is %s',
88 pretty_head_name,87 head_name,
89 ubuntu_head_versions[head_name]['version']88 ubuntu_head_versions[head_name]['version']
90 )89 )
9190
@@ -94,9 +93,8 @@ def main(
94 namespace,93 namespace,
95 )94 )
96 for head_name in sorted(applied_ubuntu_head_versions):95 for head_name in sorted(applied_ubuntu_head_versions):
97 _, _, pretty_head_name = head_name.partition('%s/', namespace)
98 logging.debug('Last applied %s version is %s',96 logging.debug('Last applied %s version is %s',
99 pretty_head_name,97 head_name,
100 applied_ubuntu_head_versions[head_name]['version']98 applied_ubuntu_head_versions[head_name]['version']
101 )99 )
102100
diff --git a/gitubuntu/source_information.py b/gitubuntu/source_information.py
index b97bc8a..527e2f0 100644
--- a/gitubuntu/source_information.py
+++ b/gitubuntu/source_information.py
@@ -21,6 +21,8 @@ except ImportError:
21 logging.error('Is %s installed?', pkg)21 logging.error('Is %s installed?', pkg)
22 sys.exit(1)22 sys.exit(1)
2323
24import gitubuntu.importerutils
25
24_ddi = DebianDistroInfo()26_ddi = DebianDistroInfo()
25_udi = UbuntuDistroInfo()27_udi = UbuntuDistroInfo()
2628
@@ -131,8 +133,18 @@ class GitUbuntuPPASourcePackage(UbuntuSourcePackage):
131133
132134
133class GitUbuntuSourcePackageInformation:135class GitUbuntuSourcePackageInformation:
134 def __init__(self, spphr, dist_name, retries=0, retry_backoffs=[],136 def __init__(
135 workdir=None, dsc=None, files=list()):137 self,
138 spphr,
139 dist_name,
140 retries=0,
141 retry_backoffs=[],
142 workdir=None,
143 dsc=None,
144 files=None,
145 ):
146 if not files:
147 files = list()
136 self._spphr = spphr148 self._spphr = spphr
137 self._dist_name = dist_name149 self._dist_name = dist_name
138 self._pkgname = self.spphr.source_package_name150 self._pkgname = self.spphr.source_package_name
@@ -151,13 +163,15 @@ class GitUbuntuSourcePackageInformation:
151163
152 # do this here, in case files is passed164 # do this here, in case files is passed
153 if workdir and not os.path.isdir(workdir):165 if workdir and not os.path.isdir(workdir):
154 os.makedirs(workdir, exist_ok=True)166 os.makedirs(workdir, exist_ok=True)
155167
156 self._archive_srcpkg = func(package=self._pkgname,168 self._archive_srcpkg = func(
157 version=self._version,169 package=self._pkgname,
158 workdir=workdir,170 version=self._version,
159 quiet=True,171 workdir=workdir,
160 dscfile=dsc)172 quiet=True,
173 dscfile=dsc,
174 )
161 for f in files:175 for f in files:
162 self._archive_srcpkg._download_file(f, f.split('/')[-1])176 self._archive_srcpkg._download_file(f, f.split('/')[-1])
163177
@@ -202,62 +216,6 @@ class GitUbuntuSourcePackageInformation:
202 return self._spphr.pocket216 return self._spphr.pocket
203217
204 @property218 @property
205 def pretty_head_name(self):
206 if self._spphr.pocket.lower() == 'release':
207 head_name = '%s/%s' % (
208 self.distribution.name.lower(),
209 self.series.name.lower(),
210 )
211 else:
212 head_name = '%s/%s-%s' % (
213 self.distribution.name.lower(),
214 self.series.name.lower(),
215 self.pocket.lower()
216 )
217 return head_name
218
219 def head_name(self, prefix):
220 if self._spphr.pocket.lower() == 'release':
221 head_name = '%s/%s/%s' % (
222 prefix,
223 self.distribution.name.lower(),
224 self.series.name.lower(),
225 )
226 else:
227 head_name = '%s/%s/%s-%s' % (
228 prefix,
229 self.distribution.name.lower(),
230 self.series.name.lower(),
231 self.pocket.lower()
232 )
233 return head_name
234
235 def applied_head_name(self, prefix):
236 return self.head_name('%s/applied' % prefix)
237
238 def parent_head_name(self, prefix):
239 if self.parent_series == None:
240 return None
241 if self.pocket.lower() == 'release':
242 # release pockets descend from prior series
243 head_name = '%s/%s/%s' % (
244 prefix,
245 self.distribution.name.lower(),
246 self.parent_series.name.lower()
247 )
248 else:
249 # non-release pockets descend from release
250 head_name = '%s/%s/%s' % (
251 prefix,
252 self.distribution.name.lower(),
253 self.series.name.lower()
254 )
255 return head_name
256
257 def parent_applied_head_name(self, prefix):
258 return self.parent_head_name('%s/applied' % prefix)
259
260 @property
261 def dsc(self):219 def dsc(self):
262 return self._archive_srcpkg.dsc220 return self._archive_srcpkg.dsc
263221
@@ -295,11 +253,14 @@ class GitUbuntuSourceInformation(object):
295 _stable_series_list = None253 _stable_series_list = None
296 _current_series = None254 _current_series = None
297255
298 def __init__(self, dist_name, pkgname=None,256 def __init__(
299 pull_overrides_filename='/dev/null',257 self,
300 retries=0,258 dist_name,
301 retry_backoffs=[]259 pkgname=None,
302 ):260 pull_overrides_filename='/dev/null',
261 retries=0,
262 retry_backoffs=[]
263 ):
303 self.launchpad = launchpad_login()264 self.launchpad = launchpad_login()
304 self.dist_name = dist_name265 self.dist_name = dist_name
305 if self.dist_name.startswith('ppa:'):266 if self.dist_name.startswith('ppa:'):
@@ -324,18 +285,6 @@ class GitUbuntuSourceInformation(object):
324 self.retries = retries285 self.retries = retries
325 self.retry_backoffs = retry_backoffs286 self.retry_backoffs = retry_backoffs
326287
327 @staticmethod
328 def _head_version_is_equal(head_versions, namespace, spi):
329 try:
330 if (head_versions[spi.head_name(namespace)]['version'] == spi.version and
331 (spi.date_published is None or
332 int(spi.date_published.timestamp()) == head_versions[spi.head_name(namespace)]['head'].peel().commit_time)
333 ):
334 return True
335 return False
336 except KeyError:
337 return False
338
339 @property288 @property
340 def current_series(self):289 def current_series(self):
341 if self.dist_name.startswith('ppa:'):290 if self.dist_name.startswith('ppa:'):
@@ -402,7 +351,7 @@ class GitUbuntuSourceInformation(object):
402 def all_series_name_list(self):351 def all_series_name_list(self):
403 return [r.name for r in self.all_series]352 return [r.name for r in self.all_series]
404353
405 def get_corrected_spi(self, srcpkg, workdir=None):354 def get_corrected_spi(self, srcpkg, workdir):
406 try:355 try:
407 pull_override = self.pull_overrides[srcpkg.source_package_version]356 pull_override = self.pull_overrides[srcpkg.source_package_version]
408 dsc = pull_override['dsc']357 dsc = pull_override['dsc']
@@ -410,11 +359,17 @@ class GitUbuntuSourceInformation(object):
410 except KeyError:359 except KeyError:
411 dsc = None360 dsc = None
412 files = list()361 files = list()
413 return GitUbuntuSourcePackageInformation(srcpkg, self.dist_name,362 return GitUbuntuSourcePackageInformation(
414 self.retries, self.retry_backoffs, workdir=workdir,363 srcpkg,
415 dsc=dsc, files=files)364 self.dist_name,
416365 self.retries,
417 def launchpad_version_is_published(self, version, workdir=None):366 self.retry_backoffs,
367 workdir=workdir,
368 dsc=dsc,
369 files=files,
370 )
371
372 def launchpad_version_is_published(self, version):
418 spph = self.archive.getPublishedSources(373 spph = self.archive.getPublishedSources(
419 exact_match=True,374 exact_match=True,
420 source_name=self.pkgname,375 source_name=self.pkgname,
@@ -423,13 +378,16 @@ class GitUbuntuSourceInformation(object):
423 )378 )
424 return len(spph) != 0379 return len(spph) != 0
425380
426 def launchpad_versions_published(self, workdir=None,381 def launchpad_versions_published(
427 sorted_by_version=False, series=None382 self,
383 workdir,
384 sorted_by_version=False,
385 series=None,
428 ):386 ):
429 args = {387 args = {
430 'exact_match':True,388 'exact_match':True,
431 'source_name':self.pkgname,389 'source_name':self.pkgname,
432 }390 }
433 if not sorted_by_version:391 if not sorted_by_version:
434 args['order_by_date'] = True392 args['order_by_date'] = True
435 if series:393 if series:
@@ -437,18 +395,49 @@ class GitUbuntuSourceInformation(object):
437395
438 spph = self.archive.getPublishedSources(**args)396 spph = self.archive.getPublishedSources(**args)
439 if len(spph) == 0:397 if len(spph) == 0:
440 raise NoPublicationHistoryException("Is %s published in %s?" %398 raise NoPublicationHistoryException(
441 (self.pkgname, self.dist_name))399 "Is %s published in %s?" % (
400 self.pkgname,
401 self.dist_name,
402 )
403 )
442404
443 for srcpkg in spph:405 for srcpkg in spph:
444 yield self.get_corrected_spi(srcpkg, workdir)406 yield self.get_corrected_spi(srcpkg, workdir)
445407
446 def launchpad_versions_published_after(self, head_versions, namespace, workdir=None, active_series_only=False):408 def launchpad_versions_published_after(
409 self,
410 head_versions,
411 head_ref_namespace,
412 workdir,
413 active_series_only=False,
414 ):
415 """Provide Launchpad publishing information after a certain publish
416
417 Arguments:
418 head_versions - a dictionary of dictionaries, keyed by Git
419 branch names, where each element is a dictionary with elements
420 'version' and 'head'.
421 head_ref_namespace - a string Git ref namespace corresponding to
422 the branches in @head_versions
423 workdir - a string filesystem path to store the downloaded
424 source package in
425 active_series_only - a boolean indicating only publishing
426 records from active series should be considered
427
428 @head_versions[<branch>]['head'] is a pygit2.Branch object.
429 @head_versions[<branch>]['version'] is the string version of the
430 debian/changelog in the corresponnding Git branch.
431
432 Returns:
433 an iterable over GitUbuntuSourcePackageInformation objects, one
434 per Launchpad publishing record after @head_version
435 """
447 args = {436 args = {
448 'exact_match':True,437 'exact_match':True,
449 'source_name':self.pkgname,438 'source_name':self.pkgname,
450 'order_by_date':True,439 'order_by_date':True,
451 }440 }
452441
453 # we have the date of the commit too, so we can double-check442 # we have the date of the commit too, so we can double-check
454 # that it matches443 # that it matches
@@ -469,14 +458,33 @@ class GitUbuntuSourceInformation(object):
469 # Sanity check that the passed in srcpkg name has a publication458 # Sanity check that the passed in srcpkg name has a publication
470 # history459 # history
471 if len(spph) == 0:460 if len(spph) == 0:
472 raise NoPublicationHistoryException("Is %s published in %s?" %461 raise NoPublicationHistoryException(
473 (self.pkgname, self.dist_name))462 "Is %s published in %s?" % (
474 if len(head_versions) > 0:463 self.pkgname,
464 self.dist_name,
465 )
466 )
467 if head_versions:
475 _spph = list()468 _spph = list()
476 for spphr in spph:469 for spphr in spph:
477 spi = GitUbuntuSourcePackageInformation(spphr, self.dist_name,470 spi = GitUbuntuSourcePackageInformation(
478 workdir=workdir)471 spphr,
479 if self._head_version_is_equal(head_versions, namespace, spi):472 self.dist_name,
473 workdir=workdir,
474 )
475 head_version = head_versions[
476 gitubuntu.importerutils.head_ref_name(
477 spi,
478 head_ref_namespace,
479 )
480 ]
481 if (
482 head_version['version'] == spi.version and
483 spi.date_published and
484 int(spi.date_published.timestamp()) == head_version[
485 'head'
486 ].peel().commit_time
487 ):
480 break488 break
481 _spph.append(spphr)489 _spph.append(spphr)
482 spph = _spph490 spph = _spph

Subscribers

People subscribed via source and target branches