Merge ~nacc/git-ubuntu:gu-review into git-ubuntu:master
- Git
- lp:~nacc/git-ubuntu
- gu-review
- Merge into master
Status: | Superseded | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Proposed branch: | ~nacc/git-ubuntu:gu-review | ||||||||||||
Merge into: | git-ubuntu:master | ||||||||||||
Prerequisite: | ~nacc/git-ubuntu:refactor-main-v2 | ||||||||||||
Diff against target: |
873 lines (+230/-348) 9 files modified
gitubuntu/__main__.py (+2/-0) gitubuntu/clone.py (+0/-20) gitubuntu/git_repository.py (+62/-36) gitubuntu/importer.py (+0/-87) gitubuntu/merge.py (+0/-149) gitubuntu/remote.py (+0/-18) gitubuntu/review.py (+140/-0) gitubuntu/submit.py (+0/-23) gitubuntu/versioning.py (+26/-15) |
||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andreas Hasenack | Needs Fixing | ||
Server Team CI bot | continuous-integration | Approve | |
Robie Basak | Pending | ||
Review via email: mp+330463@code.launchpad.net |
This proposal supersedes a proposal from 2017-09-08.
This proposal has been superseded by a proposal from 2017-09-12.
Commit message
Description of the change
This is probably not perfect, but it's basically (right now) just a wrapper around other commands, after the refactoring to call into them directly without the shell.
Bikesheddable.
Esp.: do we want review to create local repos? Or only with a flag? This was suggested in a recent bug where behaviorally only clone is allowed to create repositories.
Even less mergeable now, but shows how the interplay between modules can work after further refactoring.
Robie Basak (racb) : Posted in a previous version of this proposal | # |
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:ad8c802eddc
https:/
Executed test runs:
SUCCESS: Checkout
FAILED: Style Check
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:ad8c802eddc
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Style Check
SUCCESS: Unit Tests
FAILED: Integration Tests
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:d672a163d7e
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Style Check
SUCCESS: Unit Tests
SUCCESS: Integration Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:1b6612100ce
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Style Check
SUCCESS: Unit Tests
SUCCESS: Integration Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Andreas Hasenack (ahasenack) wrote : | # |
A few comments inline, just one needs-fixing really. The other one about making it work with an existing local repository is future work I think, for another branch.
There is no outstanding merge MP, so I tried running this review command against an open MP that is not a debian merge (cpaelzer's libvirt one) and also against an already-merged MP of mine (samba take 4), and got crashes in both cases. I understand this is somewhat work-in-progress, being the first new command to take advantage of the latest refactoring that is about to land. But I wonder: what do you think about having a way to hide these wip commands from --help?
- 68ddcab... by Nish Aravamudan
-
git_repository: use top-level-default for fetch_proto if not specified
This should not result in any functional change, but makes eventual
git-ubuntu review easier to implement. - b32d24b... by Nish Aravamudan
-
git_repository:
:fetch* : set GIT_TERMINAL_ PROMPT= 0 in env When using http/https, LP will prompt for auth if a repository
is not found, because LP cannot distinguish between a private
repository (permission issue) and a non-existent repository until
the user is authenticated. This causes git itself to prompt
for authentication on the command-line, which we do not want. Since
we are only using http/https for fetch, disable the Git terminal
prompt via an environment variable. This does make clone and
review noisier, but that can be resolved in a future commit.LP: #1716745
- 3f2aa06... by Nish Aravamudan
-
versioning: add support for non-series information in the version string
libvirt uploads (SRUs) have versions like 1.2.2-0ubuntu13
.1.23 which break
our parsing, which assumes anything between ubuntu_maj and ubuntu_min
is either a series or invalid. Extend our parsing to allow for an "other"
field, which is set when the substring between maj and min cannot be
parsed as a version.LP: #1716737
Unmerged commits
- 78e9c71... by Nish Aravamudan
-
git ubuntu review: add new subcommand
Given a MP URL, this attempts to provide a local directory to use for
reviewing the MP (and allows one to auto-approve with lint results).LP: #1702960
- 3f2aa06... by Nish Aravamudan
-
versioning: add support for non-series information in the version string
libvirt uploads (SRUs) have versions like 1.2.2-0ubuntu13
.1.23 which break
our parsing, which assumes anything between ubuntu_maj and ubuntu_min
is either a series or invalid. Extend our parsing to allow for an "other"
field, which is set when the substring between maj and min cannot be
parsed as a version.LP: #1716737
- b32d24b... by Nish Aravamudan
-
git_repository:
:fetch* : set GIT_TERMINAL_ PROMPT= 0 in env When using http/https, LP will prompt for auth if a repository
is not found, because LP cannot distinguish between a private
repository (permission issue) and a non-existent repository until
the user is authenticated. This causes git itself to prompt
for authentication on the command-line, which we do not want. Since
we are only using http/https for fetch, disable the Git terminal
prompt via an environment variable. This does make clone and
review noisier, but that can be resolved in a future commit.LP: #1716745
- 68ddcab... by Nish Aravamudan
-
git_repository: use top-level-default for fetch_proto if not specified
This should not result in any functional change, but makes eventual
git-ubuntu review easier to implement.
Preview Diff
1 | diff --git a/gitubuntu/__main__.py b/gitubuntu/__main__.py |
2 | index edeee43..27fbe14 100644 |
3 | --- a/gitubuntu/__main__.py |
4 | +++ b/gitubuntu/__main__.py |
5 | @@ -66,6 +66,7 @@ def main(): |
6 | 'remote': 'gitubuntu.remote', |
7 | 'submit': 'gitubuntu.submit', |
8 | 'lint': 'gitubuntu.lint', |
9 | + 'review': 'gitubuntu.review', |
10 | } |
11 | |
12 | known_network_subcommands = { |
13 | @@ -78,6 +79,7 @@ def main(): |
14 | 'queue', |
15 | 'remote', |
16 | 'submit', |
17 | + 'review', |
18 | } |
19 | |
20 | parser = argparse.ArgumentParser( |
21 | diff --git a/gitubuntu/clone.py b/gitubuntu/clone.py |
22 | index 832945e..c4d8dbd 100644 |
23 | --- a/gitubuntu/clone.py |
24 | +++ b/gitubuntu/clone.py |
25 | @@ -31,26 +31,18 @@ def copy_hooks(src, dst): |
26 | dst, |
27 | ) |
28 | |
29 | -<<<<<<< gitubuntu/clone.py |
30 | def main( |
31 | package, |
32 | directory=None, |
33 | lp_user=None, |
34 | proto=top_level_defaults.proto, |
35 | ): |
36 | -======= |
37 | -def main(package, directory=None, lp_user=None, proto=None): |
38 | ->>>>>>> gitubuntu/clone.py |
39 | """Entry point to clone subcommand |
40 | |
41 | @package: Name of source package |
42 | @directory: directory to clone the repository into |
43 | @lp_user: user to authenticate to Launchpad as |
44 | -<<<<<<< gitubuntu/clone.py |
45 | @proto: string protocol to use (one of 'http', 'https', 'git') |
46 | -======= |
47 | - @proto: protocol to use (one of 'http', 'https', 'git') |
48 | ->>>>>>> gitubuntu/clone.py |
49 | |
50 | If directory is None, a relative directory with the same name as |
51 | package will be used. |
52 | @@ -58,12 +50,6 @@ def main(package, directory=None, lp_user=None, proto=None): |
53 | If lp_user is None, value of `git config gitubuntu.lpuser` will be |
54 | used. |
55 | |
56 | -<<<<<<< gitubuntu/clone.py |
57 | -======= |
58 | - If proto is None, the default value from gitubuntu.__main__ will be |
59 | - used (top_level_defaults.proto). |
60 | - |
61 | ->>>>>>> gitubuntu/clone.py |
62 | Returns the resulting GitUbuntuRepository object |
63 | """ |
64 | directory = ( |
65 | @@ -75,12 +61,6 @@ def main(package, directory=None, lp_user=None, proto=None): |
66 | logging.error('directory %s exists' % directory) |
67 | sys.exit(1) |
68 | |
69 | -<<<<<<< gitubuntu/clone.py |
70 | -======= |
71 | - if proto is None: |
72 | - proto = top_level_defaults.proto |
73 | - |
74 | ->>>>>>> gitubuntu/clone.py |
75 | local_repo = GitUbuntuRepository( |
76 | local_dir=directory, |
77 | lp_user=lp_user, |
78 | diff --git a/gitubuntu/git_repository.py b/gitubuntu/git_repository.py |
79 | index 9fdf7bb..e97a0ea 100644 |
80 | --- a/gitubuntu/git_repository.py |
81 | +++ b/gitubuntu/git_repository.py |
82 | @@ -16,6 +16,7 @@ from subprocess import CalledProcessError |
83 | import sys |
84 | import tempfile |
85 | import time |
86 | +from gitubuntu.__main__ import top_level_defaults |
87 | from gitubuntu.dsc import component_tarball_matches |
88 | from gitubuntu.run import run, runq, decode_binary |
89 | try: |
90 | @@ -426,9 +427,14 @@ class GitUbuntuRepository: |
91 | |
92 | To access the underlying pygit2.Repository object, use the raw_repo |
93 | property. |
94 | + |
95 | """ |
96 | |
97 | def __init__(self, local_dir, lp_user=None, fetch_proto=None): |
98 | + """ |
99 | + If fetch_proto is None, the default value from |
100 | + gitubuntu.__main__ will be used (top_level_defaults.proto). |
101 | + """ |
102 | if local_dir is None: |
103 | self._local_dir = tempfile.mkdtemp() |
104 | else: |
105 | @@ -465,6 +471,9 @@ class GitUbuntuRepository: |
106 | logging.error("Unable to determine Launchpad user") |
107 | sys.exit(1) |
108 | |
109 | + if fetch_proto is None: |
110 | + fetch_proto = top_level_defaults.proto |
111 | + |
112 | self._fetch_proto = fetch_proto |
113 | |
114 | def create_orphan_branch(self, branch_name, msg): |
115 | @@ -764,51 +773,68 @@ class GitUbuntuRepository: |
116 | # self.git_run(['config', 'url.%s.insteadof' % self.lp_user, 'lpme']) |
117 | |
118 | def fetch_remote(self, remote_name, verbose=False): |
119 | + # Does not seem to be working with https |
120 | + # https://github.com/libgit2/pygit2/issues/573 |
121 | + # https://github.com/libgit2/libgit2/issues/3786 |
122 | + # self.raw_repo.remotes[remote_name].fetch() |
123 | + kwargs = {} |
124 | + kwargs['verbose_on_failure'] = True |
125 | + if verbose: |
126 | + # If we are redirecting stdout/stderr to the console, we |
127 | + # do not need to have run() also emit it |
128 | + kwargs['verbose_on_failure'] = False |
129 | + kwargs['stdout'] = None |
130 | + kwargs['stderr'] = None |
131 | try: |
132 | - # Does not seem to be working with https |
133 | - # https://github.com/libgit2/pygit2/issues/573 |
134 | - # https://github.com/libgit2/libgit2/issues/3786 |
135 | - # self.raw_repo.remotes[remote_name].fetch() |
136 | - logging.debug('Fetching remote %s' % remote_name) |
137 | - kwargs = {} |
138 | - kwargs['verbose_on_failure'] = True |
139 | - if verbose: |
140 | - # If we are redirecting stdout/stderr to the console, we |
141 | - # do not need to have run() also emit it |
142 | - kwargs['verbose_on_failure'] = False |
143 | - kwargs['stdout'] = None |
144 | - kwargs['stderr'] = None |
145 | - self.git_run(['fetch', remote_name], **kwargs) |
146 | + logging.debug("Fetching remote %s", remote_name) |
147 | + cp = self.git_run( |
148 | + args=['fetch', remote_name], |
149 | + env={'GIT_TERMINAL_PROMPT': '0',}, |
150 | + **kwargs |
151 | + ) |
152 | except CalledProcessError: |
153 | raise GitUbuntuRepositoryFetchError( |
154 | - 'Unable to fetch remote %s' % remote_name |
155 | + "Unable to fetch remote %s" % remote_name |
156 | ) |
157 | |
158 | def fetch_base_remotes(self, verbose=False): |
159 | self.fetch_remote(remote_name='pkg', verbose=verbose) |
160 | |
161 | def fetch_remote_refspecs(self, remote_name, refspecs, verbose=False): |
162 | - try: |
163 | - # Does not seem to be working with https |
164 | - # https://github.com/libgit2/pygit2/issues/573 |
165 | - # https://github.com/libgit2/libgit2/issues/3786 |
166 | - # self.raw_repo.remotes[remote_name].fetch() |
167 | - for refspec in refspecs: |
168 | - logging.debug('Fetching refspec %s from remote %s' % |
169 | - (refspec, remote_name)) |
170 | - kwargs = {} |
171 | - kwargs['verbose_on_failure'] = True |
172 | - if verbose: |
173 | - # If we are redirecting stdout/stderr to the console, we |
174 | - # do not need to have run() also emit it |
175 | - kwargs['verbose_on_failure'] = False |
176 | - kwargs['stdout'] = None |
177 | - kwargs['stderr'] = None |
178 | - self.git_run(['fetch', remote_name, refspec], **kwargs) |
179 | - except CalledProcessError: |
180 | - raise GitUbuntuRepositoryFetchError( |
181 | - 'Unable to fetch %s from remote %s' % (refspecs, remote_name) |
182 | - ) |
183 | + # Does not seem to be working with https |
184 | + # https://github.com/libgit2/pygit2/issues/573 |
185 | + # https://github.com/libgit2/libgit2/issues/3786 |
186 | + # self.raw_repo.remotes[remote_name].fetch() |
187 | + for refspec in refspecs: |
188 | + kwargs = {} |
189 | + kwargs['verbose_on_failure'] = True |
190 | + if verbose: |
191 | + # If we are redirecting stdout/stderr to the console, we |
192 | + # do not need to have run() also emit it |
193 | + kwargs['verbose_on_failure'] = False |
194 | + kwargs['stdout'] = None |
195 | + kwargs['stderr'] = None |
196 | + kwargs['env'] = { |
197 | + 'GIT_TERMINAL_PROMPT': '0', |
198 | + } |
199 | + try: |
200 | + logging.debug( |
201 | + "Fetching refspec %s from remote %s", |
202 | + refspec, |
203 | + remote_name, |
204 | + ) |
205 | + self.git_run( |
206 | + args=['fetch', remote_name, refspec], |
207 | + env={'GIT_TERMINAL_PROMPT': '0',}, |
208 | + **kwargs, |
209 | + ) |
210 | + except CalledProcessError: |
211 | + raise GitUbuntuRepositoryFetchError( |
212 | + "Unable to fetch %s from remote %s" % ( |
213 | + refspecs, |
214 | + remote_name, |
215 | + ) |
216 | + ) |
217 | |
218 | def fetch_lpuser_remote(self, verbose=False): |
219 | if not self._fetch_proto: |
220 | diff --git a/gitubuntu/importer.py b/gitubuntu/importer.py |
221 | index 201f31e..6311047 100644 |
222 | --- a/gitubuntu/importer.py |
223 | +++ b/gitubuntu/importer.py |
224 | @@ -115,7 +115,6 @@ def dsc_to_tree_hash(repo, dsc_path): |
225 | |
226 | def cleanup(no_clean, local_dir): |
227 | '''Remove a local directory, conditionally |
228 | -<<<<<<< gitubuntu/importer.py |
229 | |
230 | no_clean: if True, do not remove @local_dir and print a message instead |
231 | local_dir: directory to remove |
232 | @@ -188,31 +187,6 @@ def main( |
233 | |
234 | logging.info('Ubuntu Server Team importer v%s' % VERSION) |
235 | |
236 | -======= |
237 | - |
238 | - no_clean: if True, do not remove @local_dir and print a message instead |
239 | - local_dir: directory to remove |
240 | - ''' |
241 | - if not no_clean: |
242 | - shutil.rmtree(local_dir) |
243 | - else: |
244 | - logging.info('Leaving %s as directed' % local_dir) |
245 | - |
246 | - |
247 | -# XXX: need a namedtuple to hold common arguments |
248 | -def main(pkgname, owner, no_clean, directory, user, proto, no_fetch, |
249 | - no_push, dl_cache, fixup_devel, active_series_only, skip_orig, |
250 | - pullfile, parentfile, retries, retry_backoffs, skip_applied, |
251 | - reimport, |
252 | -): |
253 | - if owner == 'usd-import-team': |
254 | - namespace = 'importer' |
255 | - else: |
256 | - namespace = owner |
257 | - |
258 | - logging.info('Ubuntu Server Team importer v%s' % VERSION) |
259 | - |
260 | ->>>>>>> gitubuntu/importer.py |
261 | repo = GitUbuntuRepository(directory, user, proto) |
262 | if not directory: |
263 | logging.info('Using git repository at %s', repo.local_dir) |
264 | @@ -1210,45 +1184,6 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
265 | ) |
266 | except AttributeError: |
267 | pass |
268 | -<<<<<<< gitubuntu/importer.py |
269 | - |
270 | - if version_compare(str(spi.version), unapplied_tip_version) <= 0: |
271 | - logging.warn('Version to import (%s) is not after %s tip (%s)', |
272 | - spi.version, pretty_head_name, unapplied_tip_version |
273 | - ) |
274 | - |
275 | - # Walk changelog backwards until we find an imported version |
276 | - for version in import_tree_versions: |
277 | - unapplied_changelog_parent_tag = repo.get_import_tag(version, namespace) |
278 | - if unapplied_changelog_parent_tag is not None: |
279 | - # sanity check that version from d/changelog of the |
280 | - # tagged parent matches ours |
281 | - parent_changelog_version, _ = \ |
282 | - repo.get_changelog_versions_from_treeish( |
283 | - str(unapplied_changelog_parent_tag.peel(pygit2.Tree).id), |
284 | - ) |
285 | - # if the parent was imported via a parent override |
286 | - # itself, assume it is valid without checking its |
287 | - # tree's debian/changelog, as it wasn't used |
288 | - if (version not in _PARENT_OVERRIDES and |
289 | - parent_changelog_version != version |
290 | - ): |
291 | - logging.error('Found a tag corresponding to ' |
292 | - 'parent version %s, but d/changelog ' |
293 | - 'version (%s) differs. Will ' |
294 | - 'orphan commit.' % ( |
295 | - version, |
296 | - parent_changelog_version |
297 | - ) |
298 | - ) |
299 | - else: |
300 | - unapplied_changelog_parent_commit = str(unapplied_changelog_parent_tag.peel().id) |
301 | - logging.debug('Changelog parent (tag) is %s', |
302 | - repo.tag_to_pretty_name(unapplied_changelog_parent_tag) |
303 | - ) |
304 | - break |
305 | - |
306 | -======= |
307 | |
308 | if version_compare(str(spi.version), unapplied_tip_version) <= 0: |
309 | logging.warn('Version to import (%s) is not after %s tip (%s)', |
310 | @@ -1286,7 +1221,6 @@ def import_unapplied_spi(repo, spi, namespace, skip_orig, ubuntu_sinfo): |
311 | ) |
312 | break |
313 | |
314 | ->>>>>>> gitubuntu/importer.py |
315 | # If the two parents are tree-identical, then favor publication |
316 | # history. This deals with copy-forwards between series and with |
317 | # syncs. |
318 | @@ -1668,7 +1602,6 @@ def cli_main(args): |
319 | dl_cache = None |
320 | |
321 | main( |
322 | -<<<<<<< gitubuntu/importer.py |
323 | pkgname=args.package, |
324 | owner=args.lp_owner, |
325 | no_clean=no_clean, |
326 | @@ -1687,24 +1620,4 @@ def cli_main(args): |
327 | parentfile=args.parentfile, |
328 | retries=args.retries, |
329 | retry_backoffs=args.retry_backoffs, |
330 | -======= |
331 | - args.package, |
332 | - args.lp_owner, |
333 | - no_clean, |
334 | - directory, |
335 | - user, |
336 | - args.proto, |
337 | - args.no_fetch, |
338 | - no_push, |
339 | - dl_cache, |
340 | - args.fixup_devel, |
341 | - args.active_series_only, |
342 | - args.skip_orig, |
343 | - args.pullfile, |
344 | - args.parentfile, |
345 | - args.retries, |
346 | - args.retry_backoffs, |
347 | - args.skip_applied, |
348 | - args.reimport, |
349 | ->>>>>>> gitubuntu/importer.py |
350 | ) |
351 | diff --git a/gitubuntu/merge.py b/gitubuntu/merge.py |
352 | index b6098b7..cc16cc0 100644 |
353 | --- a/gitubuntu/merge.py |
354 | +++ b/gitubuntu/merge.py |
355 | @@ -98,7 +98,6 @@ def parse_args(subparsers=None, base_subparsers=None): |
356 | |
357 | def cli_main(args): |
358 | main( |
359 | -<<<<<<< gitubuntu/merge.py |
360 | directory=args.directory, |
361 | commitish=args.commitish, |
362 | onto=args.onto, |
363 | @@ -107,17 +106,6 @@ def cli_main(args): |
364 | bug=args.bug, |
365 | release=args.release, |
366 | subcommand=args.subsubcommand, |
367 | -======= |
368 | - args.directory, |
369 | - args.commitish, |
370 | - args.onto, |
371 | - args.force, |
372 | - args.tag_only, |
373 | - args.pullfile, |
374 | - args.bug, |
375 | - args.release, |
376 | - args.subsubcommand, |
377 | ->>>>>>> gitubuntu/merge.py |
378 | ) |
379 | |
380 | |
381 | @@ -327,7 +315,6 @@ def do_finish(repo, tag_prefix, commitish, merge_base_id, onto, release, bug, fo |
382 | ancestor_check = True |
383 | except subprocess.CalledProcessError as e: |
384 | pass |
385 | -<<<<<<< gitubuntu/merge.py |
386 | |
387 | if not ancestor_check: |
388 | if len(ancestors_checked) > 1: |
389 | @@ -474,142 +461,6 @@ def main( |
390 | merge_base_id, |
391 | ) |
392 | |
393 | -======= |
394 | - |
395 | - if not ancestor_check: |
396 | - if len(ancestors_checked) > 1: |
397 | - msg = ' and '.join(ancestors_checked) + ' are not ancestors' |
398 | - else: |
399 | - msg = '%s is not an ancestor' % ancestors_checked.pop() |
400 | - if force: |
401 | - logging.error("%s of the HEAD commit, but --force passed.", msg) |
402 | - else: |
403 | - logging.error( |
404 | - "%s of the HEAD commit. Did you run `git-ubuntu merge " |
405 | - "start` first? (Pass -f to force the merge).", |
406 | - msg |
407 | - ) |
408 | - sys.exit(1) |
409 | - |
410 | - # 1) git merge-changelogs old/ubuntu old/debian new/debian |
411 | - do_merge_changelogs(repo, commitish, merge_base_id, onto) |
412 | - # 2) git reconstruct-changelog <onto> |
413 | - do_reconstruct_changelog(repo, onto, release, bug) |
414 | - # TODO elide from each entry any lines outside of changelog-markers |
415 | - # (add flag to specify it?) |
416 | - # 3) update-maintainer |
417 | - do_update_maintainer(repo) |
418 | - |
419 | - |
420 | -def main( |
421 | - directory, |
422 | - commitish, |
423 | - onto, |
424 | - force, |
425 | - tag_only, |
426 | - pullfile, |
427 | - bug, |
428 | - release, |
429 | - subcommand, |
430 | -): |
431 | - repo = GitUbuntuRepository(directory) |
432 | - tag_prefix = '' |
433 | - if bug: |
434 | - tag_prefix = 'lp%s/' % bug |
435 | - |
436 | - try: |
437 | - commitish_obj = repo.get_commitish(commitish) |
438 | - except KeyError: |
439 | - logging.error( |
440 | - "%s is not a defined object in this git repository.", |
441 | - commitish |
442 | - ) |
443 | - sys.exit(1) |
444 | - |
445 | - commitish_version, _ = repo.get_changelog_versions_from_treeish( |
446 | - str(commitish_obj.id), |
447 | - ) |
448 | - |
449 | - try: |
450 | - onto_obj = repo.get_commitish(onto) |
451 | - onto_version, _ = repo.get_changelog_versions_from_treeish(onto) |
452 | - if version_compare(commitish_version, onto_version) >= 0: |
453 | - if force: |
454 | - logging.info( |
455 | - "%s version (%s) is after %s version (%s), " |
456 | - "but --force passed.", |
457 | - commitish, |
458 | - commitish_version, |
459 | - onto, |
460 | - onto_version, |
461 | - ) |
462 | - else: |
463 | - logging.error( |
464 | - "%s version (%s) is after %s version (%s). " |
465 | - "Are you sure you want to merge? " |
466 | - "(Pass -f to force the merge).", |
467 | - commitish, |
468 | - commitish_version, |
469 | - onto, |
470 | - onto_version, |
471 | - ) |
472 | - sys.exit(1) |
473 | - except KeyError: |
474 | - logging.info("%s is not a defined object in this git repository.", onto) |
475 | - logging.info( |
476 | - "Creating a local branch named %s tracking pkg/%s.", |
477 | - onto, |
478 | - onto, |
479 | - ) |
480 | - try: |
481 | - branch = repo.create_tracking_branch( |
482 | - onto, |
483 | - 'pkg/%s' % onto, |
484 | - force=force, |
485 | - ) |
486 | - except: |
487 | - logging.error( |
488 | - "Failed to create local branch (%s). " |
489 | - "Does it already exist (pass -f)?", |
490 | - onto, |
491 | - ) |
492 | - sys.exit(1) |
493 | - onto_obj = repo.get_commitish(onto) |
494 | - |
495 | - cp = run(['git', 'status', '--porcelain']) |
496 | - if len(cp.stdout) > 0: |
497 | - logging.error('Working tree must be clean to continue:') |
498 | - logging.error(decode_binary(cp.stdout)) |
499 | - sys.exit(1) |
500 | - |
501 | - merge_base_id = repo.raw_repo.merge_base( |
502 | - onto_obj.id, |
503 | - commitish_obj.id, |
504 | - ) |
505 | - if merge_base_id is None: |
506 | - logging.error( |
507 | - "Unable to find a common ancestor for %s and %s.", |
508 | - onto, |
509 | - commitish, |
510 | - ) |
511 | - sys.exit(1) |
512 | - |
513 | - if merge_base_id == onto_obj.id: |
514 | - logging.error( |
515 | - "The common ancestor of %s and %s is " |
516 | - "identical to %s. No merge is necessary.", |
517 | - onto, |
518 | - commitish, |
519 | - onto, |
520 | - ) |
521 | - sys.exit(1) |
522 | - |
523 | - merge_base_id = str(merge_base_id) |
524 | - merge_base_version, _ = repo.get_changelog_versions_from_treeish( |
525 | - merge_base_id, |
526 | - ) |
527 | - |
528 | ->>>>>>> gitubuntu/merge.py |
529 | if subcommand == 'start': |
530 | do_start( |
531 | repo, |
532 | diff --git a/gitubuntu/remote.py b/gitubuntu/remote.py |
533 | index 41981ee..db42168 100644 |
534 | --- a/gitubuntu/remote.py |
535 | +++ b/gitubuntu/remote.py |
536 | @@ -120,7 +120,6 @@ def main( |
537 | directory=None, |
538 | remote_name=None, |
539 | no_fetch=False, |
540 | -<<<<<<< gitubuntu/remote.py |
541 | proto=top_level_defaults.proto, |
542 | ): |
543 | """Entry point to remote subcommand |
544 | @@ -130,17 +129,6 @@ def main( |
545 | @url: the string URL of the remote to add |
546 | @lp_user: string user to authenticate to Launchpad as |
547 | @proto: string protocol to use (one of 'http', 'https', 'git') |
548 | -======= |
549 | - proto=None, |
550 | -): |
551 | - """Entry point to remote subcommand |
552 | - @subcommand: what action to take. Currently only 'add' supported. |
553 | - @user: which Launchpad user's repository to add |
554 | - @package: name of source package |
555 | - @url: the URL of the remote to add |
556 | - @lp_user: user to authenticate to Launchpad as |
557 | - @proto: protocol to use (one of 'http', 'https', 'git') |
558 | ->>>>>>> gitubuntu/remote.py |
559 | |
560 | If package is None, it will be derived from HEAD's debian/changelog. |
561 | |
562 | @@ -152,12 +140,6 @@ def main( |
563 | If directory is None, the current directory is used. |
564 | |
565 | If remote_name is None, the remote will be named @user. |
566 | -<<<<<<< gitubuntu/remote.py |
567 | -======= |
568 | - |
569 | - If proto is None, the default value from gitubuntu.__main__ will be |
570 | - used (top_level_defaults.proto). |
571 | ->>>>>>> gitubuntu/remote.py |
572 | """ |
573 | if directory is None: |
574 | directory = os.path.abspath(os.getcwd()) |
575 | diff --git a/gitubuntu/review.py b/gitubuntu/review.py |
576 | new file mode 100644 |
577 | index 0000000..0acdbd3 |
578 | --- /dev/null |
579 | +++ b/gitubuntu/review.py |
580 | @@ -0,0 +1,140 @@ |
581 | +import argparse |
582 | +from contextlib import redirect_stdout |
583 | +import io |
584 | +import logging |
585 | +import os |
586 | +import shutil |
587 | +import sys |
588 | +import tempfile |
589 | +import urllib.parse |
590 | +import gitubuntu.clone |
591 | +import gitubuntu.lint |
592 | +from gitubuntu.__main__ import top_level_defaults |
593 | +from gitubuntu.git_repository import ( |
594 | + GitUbuntuRepository, |
595 | + GitUbuntuRepositoryFetchError, |
596 | +) |
597 | +from gitubuntu.source_information import launchpad_login_auth |
598 | + |
599 | + |
600 | +def parse_args(subparsers=None, base_subparsers=None): |
601 | + kwargs = dict( |
602 | + description="Given a Launchpad MP URL, perform a review (EXPERIMENTAL)", |
603 | + formatter_class=argparse.RawTextHelpFormatter, |
604 | + epilog="An exit code of 0 indicates all checks passed", |
605 | + ) |
606 | + if base_subparsers: |
607 | + kwargs["parents"] = base_subparsers |
608 | + if subparsers: |
609 | + parser = subparsers.add_parser("review", **kwargs) |
610 | + parser.set_defaults(func=cli_main) |
611 | + else: |
612 | + parser = argparse.ArgumentParser(**kwargs) |
613 | + |
614 | + parser.add_argument( |
615 | + "url", |
616 | + type=str, |
617 | + help="Full URL of Launchpad Merge Proposal to review", |
618 | + ) |
619 | + parser.add_argument( |
620 | + '--clone', |
621 | + action='store_true', |
622 | + help="Create a new local repository by cloning, rather than " |
623 | + "using the repository at the current directory.", |
624 | + ) |
625 | + parser.add_argument( |
626 | + '--add-comment', |
627 | + action='store_true', |
628 | + help="Add a comment to the MP with the lint results. " |
629 | + "This may also change the review state", |
630 | + ) |
631 | + |
632 | + if not subparsers: |
633 | + return parser.parse_args() |
634 | + return "review - %s" % kwargs["description"] |
635 | + |
636 | + |
637 | +def cli_main(args): |
638 | + main( |
639 | + args.clone, |
640 | + args.url, |
641 | + args.add_comment, |
642 | + ) |
643 | + |
644 | +def main(clone, url, add_comment): |
645 | + """Entry point to review |
646 | + |
647 | + Arguments: |
648 | + @clone: boolean to indicate a new repository should be cloned. |
649 | + @url: string URL of MP to review |
650 | + @add_comment: boolean to indicate the MP should be updated with the |
651 | + review results. |
652 | + """ |
653 | + url = urllib.parse.urlparse(url).path |
654 | + lp = launchpad_login_auth() |
655 | + mp = lp.load(url) |
656 | + target_url = mp.target_git_repository.git_https_url |
657 | + target_branch = mp.target_git_path[len('refs/heads/'):] |
658 | + idx = target_url.index('~') |
659 | + target_user = target_url[idx+1:target_url.index('/', idx)] |
660 | + if target_user != 'usd-import-team': |
661 | + logging.error( |
662 | + "Performing arbitrary reviews is not yet supported (user %s " |
663 | + "specified).", |
664 | + target_user |
665 | + ) |
666 | + sys.exit(1) |
667 | + |
668 | + source_url = mp.source_git_repository.git_https_url |
669 | + source_branch = mp.source_git_path[len('refs/heads/'):] |
670 | + idx = source_url.index('~') |
671 | + source_user = source_url[idx+1:source_url.index('/', idx)] |
672 | + srcpkg = target_url.split('/')[-1] |
673 | + if clone: |
674 | + repo = gitubuntu.clone.main(package=srcpkg) |
675 | + else: |
676 | + repo = GitUbuntuRepository('.') |
677 | + try: |
678 | + repo.fetch_base_remotes() |
679 | + except GitUbuntuRepositoryFetchError: |
680 | + sys.exit(1) |
681 | + os.chdir(repo.local_dir) |
682 | + repo.add_remote( |
683 | + pkgname=srcpkg, |
684 | + repo_owner=source_user, |
685 | + remote_name=source_user, |
686 | + ) |
687 | + try: |
688 | + repo.fetch_remote(remote_name=source_user) |
689 | + except GitUbuntuRepositoryFetchError: |
690 | + sys.exit(1) |
691 | + |
692 | + logging.info( |
693 | + "Linting merge of %s/%s into pkg/%s" % ( |
694 | + source_user, |
695 | + source_branch, |
696 | + target_branch |
697 | + ) |
698 | + ) |
699 | + try: |
700 | + f = io.StringIO() |
701 | + with redirect_stdout(f): |
702 | + gitubuntu.lint.do_lint( |
703 | + repo=repo, |
704 | + commitish='%s/%s' % (source_user, source_branch), |
705 | + target_branch='pkg/%s' % target_branch, |
706 | + verbose=True, |
707 | + ) |
708 | + except SystemExit as e: |
709 | + if e.code == 0: |
710 | + vote='Approve' |
711 | + else: |
712 | + vote='Needs Fixing' |
713 | + lint_out = f.getvalue() |
714 | + if add_comment: |
715 | + mp.createComment(content=lint_out, vote=vote, |
716 | + subject='Automated lint result' |
717 | + ) |
718 | + else: |
719 | + logging.info('git ubuntu lint result:') |
720 | + print(lint_out, end='') |
721 | diff --git a/gitubuntu/submit.py b/gitubuntu/submit.py |
722 | index 0bf1319..fb080f3 100644 |
723 | --- a/gitubuntu/submit.py |
724 | +++ b/gitubuntu/submit.py |
725 | @@ -75,7 +75,6 @@ def cli_main(args): |
726 | user = None |
727 | |
728 | main( |
729 | -<<<<<<< gitubuntu/submit.py |
730 | directory=args.directory, |
731 | force=args.force, |
732 | target_user=args.target_user, |
733 | @@ -85,23 +84,11 @@ def cli_main(args): |
734 | user=user, |
735 | branch=args.branch, |
736 | proto=args.proto, |
737 | -======= |
738 | - args.directory, |
739 | - args.force, |
740 | - user, |
741 | - args.branch, |
742 | - args.target_user, |
743 | - args.target_branch, |
744 | - args.no_push, |
745 | - args.proto, |
746 | - args.reviewer, |
747 | ->>>>>>> gitubuntu/submit.py |
748 | ) |
749 | |
750 | def main( |
751 | directory, |
752 | force, |
753 | -<<<<<<< gitubuntu/submit.py |
754 | target_user, |
755 | target_branch, |
756 | no_push, |
757 | @@ -130,16 +117,6 @@ def main( |
758 | |
759 | If branch is None, HEAD is used. |
760 | """ |
761 | -======= |
762 | - user, |
763 | - branch, |
764 | - target_user, |
765 | - target_branch, |
766 | - no_push, |
767 | - proto, |
768 | - reviewers |
769 | -): |
770 | ->>>>>>> gitubuntu/submit.py |
771 | repo = GitUbuntuRepository(directory, user, proto) |
772 | |
773 | if branch: |
774 | diff --git a/gitubuntu/versioning.py b/gitubuntu/versioning.py |
775 | index c3187f5..3a5dc53 100644 |
776 | --- a/gitubuntu/versioning.py |
777 | +++ b/gitubuntu/versioning.py |
778 | @@ -27,7 +27,7 @@ __all__ = [ |
779 | # see _decompose_version_string for explanation of members |
780 | _VersionComps = namedtuple( |
781 | "_VersionComps", |
782 | - ["prefix_parts", "ubuntu_maj", "ubuntu_series", "ubuntu_min"] |
783 | + ["prefix_parts", "ubuntu_maj", "ubuntu_series", "ubuntu_other", "ubuntu_min"] |
784 | ) |
785 | |
786 | def _decompose_version_string(version_string): |
787 | @@ -52,10 +52,19 @@ def _decompose_version_string(version_string): |
788 | "1.0.2ubuntu3.17.04.4", 17.04 is the Ubuntu series string. In both |
789 | "1.0-2ubuntu3.4" and "1.0-2", the Ubuntu series is not present. |
790 | |
791 | + ubuntu_other is any non-series information as a string between the |
792 | + major and minor version numbers. For example, in the version strings |
793 | + "1.0-2ubuntu3.4" and "1.0-2ubuntu3.17.04.4", the other Ubuntu |
794 | + version string is not present. In both "1.0-2" and "1.0-2ubuntu3", |
795 | + the other Ubuntu version number is not present. In |
796 | + "1.2.2-0ubuntu13.1.23", "1" is the Ubuntu other version string. |
797 | + |
798 | ubuntu_min is the "minor" Ubuntu version number as an integer, or None if |
799 | not present. For example, in the version strings "1.0-2ubuntu3.4" and |
800 | "1.0-2ubuntu3.17.04.4", 4 is the Ubuntu minor version number. In both |
801 | - "1.0-2" and "1.0-2ubuntu3", the minor Ubuntu version number is not present. |
802 | + "1.0-2" and "1.0-2ubuntu3", the minor Ubuntu version number is not |
803 | + present. In "1.2.2-0ubuntu13.1.23", 23 is the Ubuntu minor version |
804 | + number. |
805 | |
806 | For version strings not following these standard forms, the result is |
807 | undefined except where specific test cases exist for them. |
808 | @@ -73,7 +82,7 @@ def _decompose_version_string(version_string): |
809 | ) |
810 | elif 'build' in parts: |
811 | return _VersionComps._make( |
812 | - [parts[:parts.index('build')], None, None, None] |
813 | + [parts[:parts.index('build')], None, None, None, None] |
814 | ) |
815 | elif 'ubuntu' in parts: |
816 | ubuntu_idx = parts.index('ubuntu') |
817 | @@ -85,6 +94,7 @@ def _decompose_version_string(version_string): |
818 | # nothing after "Xubuntu", treated as 1 |
819 | ubuntu_maj = 1 |
820 | ubuntu_series = None |
821 | + ubuntu_other = None |
822 | if len(suffix_parts) < 2: |
823 | ubuntu_min = None |
824 | else: |
825 | @@ -95,15 +105,13 @@ def _decompose_version_string(version_string): |
826 | if len(suffix_parts[1:-1]) > 2: |
827 | ubuntu_series = ''.join(suffix_parts[2:-2]) |
828 | if not re.match(r'\d\d\.\d\d', ubuntu_series): |
829 | - raise ValueError("Not sure how to decompose %s: " |
830 | - "series %s not in expected format" % |
831 | - (version_string, ubuntu_series) |
832 | - ) |
833 | + ubuntu_series = None |
834 | + ubuntu_other = ''.join(suffix_parts[2:-2]) |
835 | return _VersionComps._make( |
836 | - [static_parts, ubuntu_maj, ubuntu_series, ubuntu_min] |
837 | + [static_parts, ubuntu_maj, ubuntu_series, ubuntu_other, ubuntu_min] |
838 | ) |
839 | else: |
840 | - return _VersionComps._make([parts, None, None, None]) |
841 | + return _VersionComps._make([parts, None, None, None, None]) |
842 | |
843 | |
844 | def _bump_version_object(old_version_object, bump_function): |
845 | @@ -169,6 +177,8 @@ def _bump_sru_version_suffix_string(befores, series_string, afters, |
846 | bumped_parts = ['ubuntu', str(new_maj), '.'] |
847 | if old_suffix_comps.ubuntu_series: |
848 | bumped_parts.extend([old_suffix_comps.ubuntu_series, '.']) |
849 | + elif old_suffix_comps.ubuntu_other: |
850 | + bumped_parts.extend([old_suffix_comps.ubuntu_other, '.']) |
851 | elif str(version) in [str(v) for v in befores + afters]: |
852 | bumped_parts.extend([series_string, '.']) |
853 | elif any( |
854 | @@ -251,12 +261,13 @@ def version_compare(a, b): |
855 | |
856 | |
857 | @pytest.mark.parametrize('test_input, expected', [ |
858 | - ('2', _VersionComps._make([['2'], None, None, None])), |
859 | - ('2ubuntu1', _VersionComps._make([['2'], 1, None, None])), |
860 | - ('2ubuntu1.3', _VersionComps._make([['2'], 1, None, 3])), |
861 | - ('2ubuntu0.16.04.3', _VersionComps._make([['2'], 0, '16.04', 3])), |
862 | - ('2build1', _VersionComps._make([['2'], None, None, None])), |
863 | - ('2build1.3', _VersionComps._make([['2'], None, None, None])), |
864 | + ('2', _VersionComps._make([['2'], None, None, None, None])), |
865 | + ('2ubuntu1', _VersionComps._make([['2'], 1, None, None, None])), |
866 | + ('2ubuntu1.3', _VersionComps._make([['2'], 1, None, None, 3])), |
867 | + ('2ubuntu0.16.04.3', _VersionComps._make([['2'], 0, '16.04', None, 3])), |
868 | + ('2build1', _VersionComps._make([['2'], None, None, None, None])), |
869 | + ('2build1.3', _VersionComps._make([['2'], None, None, None, None])), |
870 | + ('0ubuntu13.1.22', _VersionComps._make([['0'], 13, None, '1', 22])), |
871 | ]) |
872 | def test_decompose_version_string(test_input, expected): |
873 | assert _decompose_version_string(test_input) == expected |
FAILED: Continuous integration, rev:e0831bb2864 a48129d9143f1b0 e32831634101e8 /jenkins. ubuntu. com/server/ job/git- ubuntu- ci/26/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Tests
FAILED: Build
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/git- ubuntu- ci/26/rebuild
https:/