diff options
-rwxr-xr-x | repo | 50 | ||||
-rw-r--r-- | subcmds/init.py | 11 | ||||
-rw-r--r-- | tests/test_wrapper.py | 156 |
3 files changed, 195 insertions, 22 deletions
@@ -463,6 +463,34 @@ class CloneFailure(Exception): | |||
463 | """ | 463 | """ |
464 | 464 | ||
465 | 465 | ||
466 | def check_repo_verify(repo_verify, quiet=False): | ||
467 | """Check the --repo-verify state.""" | ||
468 | if not repo_verify: | ||
469 | print('repo: warning: verification of repo code has been disabled;\n' | ||
470 | 'repo will not be able to verify the integrity of itself.\n', | ||
471 | file=sys.stderr) | ||
472 | return False | ||
473 | |||
474 | if NeedSetupGnuPG(): | ||
475 | return SetupGnuPG(quiet) | ||
476 | |||
477 | return True | ||
478 | |||
479 | |||
480 | def check_repo_rev(dst, rev, repo_verify=True, quiet=False): | ||
481 | """Check that |rev| is valid.""" | ||
482 | do_verify = check_repo_verify(repo_verify, quiet=quiet) | ||
483 | remote_ref, local_rev = resolve_repo_rev(dst, rev) | ||
484 | if not quiet and not remote_ref.startswith('refs/heads/'): | ||
485 | print('warning: repo is not tracking a remote branch, so it will not ' | ||
486 | 'receive updates', file=sys.stderr) | ||
487 | if do_verify: | ||
488 | rev = verify_rev(dst, remote_ref, local_rev, quiet) | ||
489 | else: | ||
490 | rev = local_rev | ||
491 | return (remote_ref, rev) | ||
492 | |||
493 | |||
466 | def _Init(args, gitc_init=False): | 494 | def _Init(args, gitc_init=False): |
467 | """Installs repo by cloning it over the network. | 495 | """Installs repo by cloning it over the network. |
468 | """ | 496 | """ |
@@ -510,30 +538,12 @@ def _Init(args, gitc_init=False): | |||
510 | 538 | ||
511 | _CheckGitVersion() | 539 | _CheckGitVersion() |
512 | try: | 540 | try: |
513 | if not opt.repo_verify: | ||
514 | do_verify = False | ||
515 | print('repo: warning: verification of repo code has been disabled;\n' | ||
516 | 'repo will not be able to verify the integrity of itself.\n', | ||
517 | file=sys.stderr) | ||
518 | else: | ||
519 | if NeedSetupGnuPG(): | ||
520 | do_verify = SetupGnuPG(opt.quiet) | ||
521 | else: | ||
522 | do_verify = True | ||
523 | |||
524 | if not opt.quiet: | 541 | if not opt.quiet: |
525 | print('Downloading Repo source from', url) | 542 | print('Downloading Repo source from', url) |
526 | dst = os.path.abspath(os.path.join(repodir, S_repo)) | 543 | dst = os.path.abspath(os.path.join(repodir, S_repo)) |
527 | _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose) | 544 | _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose) |
528 | 545 | ||
529 | remote_ref, local_rev = resolve_repo_rev(dst, rev) | 546 | remote_ref, rev = check_repo_rev(dst, rev, opt.repo_verify, quiet=opt.quiet) |
530 | if not opt.quiet and not remote_ref.startswith('refs/heads/'): | ||
531 | print('warning: repo is not tracking a remote branch, so it will not ' | ||
532 | 'receive updates', file=sys.stderr) | ||
533 | if do_verify: | ||
534 | rev = _Verify(dst, remote_ref, local_rev, opt.quiet) | ||
535 | else: | ||
536 | rev = local_rev | ||
537 | _Checkout(dst, remote_ref, rev, opt.quiet) | 547 | _Checkout(dst, remote_ref, rev, opt.quiet) |
538 | 548 | ||
539 | if not os.path.isfile(os.path.join(dst, 'repo')): | 549 | if not os.path.isfile(os.path.join(dst, 'repo')): |
@@ -907,7 +917,7 @@ def resolve_repo_rev(cwd, committish): | |||
907 | raise CloneFailure() | 917 | raise CloneFailure() |
908 | 918 | ||
909 | 919 | ||
910 | def _Verify(cwd, remote_ref, rev, quiet): | 920 | def verify_rev(cwd, remote_ref, rev, quiet): |
911 | """Verify the commit has been signed by a tag.""" | 921 | """Verify the commit has been signed by a tag.""" |
912 | ret = run_git('describe', rev, cwd=cwd) | 922 | ret = run_git('describe', rev, cwd=cwd) |
913 | cur = ret.stdout.strip() | 923 | cur = ret.stdout.strip() |
diff --git a/subcmds/init.py b/subcmds/init.py index 431165d4..ce8b0187 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -38,6 +38,7 @@ from project import SyncBuffer | |||
38 | from git_config import GitConfig | 38 | from git_config import GitConfig |
39 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD | 39 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD |
40 | import platform_utils | 40 | import platform_utils |
41 | from wrapper import Wrapper | ||
41 | 42 | ||
42 | 43 | ||
43 | class Init(InteractiveCommand, MirrorSafeCommand): | 44 | class Init(InteractiveCommand, MirrorSafeCommand): |
@@ -499,6 +500,16 @@ to update the working directory files. | |||
499 | remote.url = opt.repo_url | 500 | remote.url = opt.repo_url |
500 | remote.Save() | 501 | remote.Save() |
501 | 502 | ||
503 | # Handle new --repo-rev requests. | ||
504 | if opt.repo_rev: | ||
505 | wrapper = Wrapper() | ||
506 | remote_ref, rev = wrapper.check_repo_rev( | ||
507 | rp.gitdir, opt.repo_rev, repo_verify=opt.repo_verify, quiet=opt.quiet) | ||
508 | branch = rp.GetBranch('default') | ||
509 | branch.merge = remote_ref | ||
510 | rp.work_git.update_ref('refs/heads/default', rev) | ||
511 | branch.Save() | ||
512 | |||
502 | if opt.worktree: | 513 | if opt.worktree: |
503 | # Older versions of git supported worktree, but had dangerous gc bugs. | 514 | # Older versions of git supported worktree, but had dangerous gc bugs. |
504 | git_require((2, 15, 0), fail=True, msg='git gc worktree corruption') | 515 | git_require((2, 15, 0), fail=True, msg='git gc worktree corruption') |
diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 73c62cc1..136f7f11 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py | |||
@@ -18,12 +18,14 @@ | |||
18 | 18 | ||
19 | from __future__ import print_function | 19 | from __future__ import print_function |
20 | 20 | ||
21 | import contextlib | ||
21 | import os | 22 | import os |
22 | import re | 23 | import re |
23 | import shutil | 24 | import shutil |
24 | import tempfile | 25 | import tempfile |
25 | import unittest | 26 | import unittest |
26 | 27 | ||
28 | import platform_utils | ||
27 | from pyversion import is_python3 | 29 | from pyversion import is_python3 |
28 | import wrapper | 30 | import wrapper |
29 | 31 | ||
@@ -36,6 +38,18 @@ else: | |||
36 | from StringIO import StringIO | 38 | from StringIO import StringIO |
37 | 39 | ||
38 | 40 | ||
41 | @contextlib.contextmanager | ||
42 | def TemporaryDirectory(): | ||
43 | """Create a new empty git checkout for testing.""" | ||
44 | # TODO(vapier): Convert this to tempfile.TemporaryDirectory once we drop | ||
45 | # Python 2 support entirely. | ||
46 | try: | ||
47 | tempdir = tempfile.mkdtemp(prefix='repo-tests') | ||
48 | yield tempdir | ||
49 | finally: | ||
50 | platform_utils.rmtree(tempdir) | ||
51 | |||
52 | |||
39 | def fixture(*paths): | 53 | def fixture(*paths): |
40 | """Return a path relative to tests/fixtures. | 54 | """Return a path relative to tests/fixtures. |
41 | """ | 55 | """ |
@@ -243,8 +257,93 @@ class CheckGitVersion(RepoWrapperTestCase): | |||
243 | self.wrapper._CheckGitVersion() | 257 | self.wrapper._CheckGitVersion() |
244 | 258 | ||
245 | 259 | ||
246 | class ResolveRepoRev(RepoWrapperTestCase): | 260 | class NeedSetupGnuPG(RepoWrapperTestCase): |
247 | """Check resolve_repo_rev behavior.""" | 261 | """Check NeedSetupGnuPG behavior.""" |
262 | |||
263 | def test_missing_dir(self): | ||
264 | """The ~/.repoconfig tree doesn't exist yet.""" | ||
265 | with TemporaryDirectory() as tempdir: | ||
266 | self.wrapper.home_dot_repo = os.path.join(tempdir, 'foo') | ||
267 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
268 | |||
269 | def test_missing_keyring(self): | ||
270 | """The keyring-version file doesn't exist yet.""" | ||
271 | with TemporaryDirectory() as tempdir: | ||
272 | self.wrapper.home_dot_repo = tempdir | ||
273 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
274 | |||
275 | def test_empty_keyring(self): | ||
276 | """The keyring-version file exists, but is empty.""" | ||
277 | with TemporaryDirectory() as tempdir: | ||
278 | self.wrapper.home_dot_repo = tempdir | ||
279 | with open(os.path.join(tempdir, 'keyring-version'), 'w'): | ||
280 | pass | ||
281 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
282 | |||
283 | def test_old_keyring(self): | ||
284 | """The keyring-version file exists, but it's old.""" | ||
285 | with TemporaryDirectory() as tempdir: | ||
286 | self.wrapper.home_dot_repo = tempdir | ||
287 | with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp: | ||
288 | fp.write('1.0\n') | ||
289 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
290 | |||
291 | def test_new_keyring(self): | ||
292 | """The keyring-version file exists, and is up-to-date.""" | ||
293 | with TemporaryDirectory() as tempdir: | ||
294 | self.wrapper.home_dot_repo = tempdir | ||
295 | with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp: | ||
296 | fp.write('1000.0\n') | ||
297 | self.assertFalse(self.wrapper.NeedSetupGnuPG()) | ||
298 | |||
299 | |||
300 | class SetupGnuPG(RepoWrapperTestCase): | ||
301 | """Check SetupGnuPG behavior.""" | ||
302 | |||
303 | def test_full(self): | ||
304 | """Make sure it works completely.""" | ||
305 | with TemporaryDirectory() as tempdir: | ||
306 | self.wrapper.home_dot_repo = tempdir | ||
307 | self.assertTrue(self.wrapper.SetupGnuPG(True)) | ||
308 | with open(os.path.join(tempdir, 'keyring-version'), 'r') as fp: | ||
309 | data = fp.read() | ||
310 | self.assertEqual('.'.join(str(x) for x in self.wrapper.KEYRING_VERSION), | ||
311 | data.strip()) | ||
312 | |||
313 | |||
314 | class VerifyRev(RepoWrapperTestCase): | ||
315 | """Check verify_rev behavior.""" | ||
316 | |||
317 | def test_verify_passes(self): | ||
318 | """Check when we have a valid signed tag.""" | ||
319 | desc_result = self.wrapper.RunResult(0, 'v1.0\n', '') | ||
320 | gpg_result = self.wrapper.RunResult(0, '', '') | ||
321 | with mock.patch.object(self.wrapper, 'run_git', | ||
322 | side_effect=(desc_result, gpg_result)): | ||
323 | ret = self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | ||
324 | self.assertEqual('v1.0^0', ret) | ||
325 | |||
326 | def test_unsigned_commit(self): | ||
327 | """Check we fall back to signed tag when we have an unsigned commit.""" | ||
328 | desc_result = self.wrapper.RunResult(0, 'v1.0-10-g1234\n', '') | ||
329 | gpg_result = self.wrapper.RunResult(0, '', '') | ||
330 | with mock.patch.object(self.wrapper, 'run_git', | ||
331 | side_effect=(desc_result, gpg_result)): | ||
332 | ret = self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | ||
333 | self.assertEqual('v1.0^0', ret) | ||
334 | |||
335 | def test_verify_fails(self): | ||
336 | """Check we fall back to signed tag when we have an unsigned commit.""" | ||
337 | desc_result = self.wrapper.RunResult(0, 'v1.0-10-g1234\n', '') | ||
338 | gpg_result = Exception | ||
339 | with mock.patch.object(self.wrapper, 'run_git', | ||
340 | side_effect=(desc_result, gpg_result)): | ||
341 | with self.assertRaises(Exception): | ||
342 | self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | ||
343 | |||
344 | |||
345 | class GitCheckoutTestCase(RepoWrapperTestCase): | ||
346 | """Tests that use a real/small git checkout.""" | ||
248 | 347 | ||
249 | GIT_DIR = None | 348 | GIT_DIR = None |
250 | REV_LIST = None | 349 | REV_LIST = None |
@@ -274,6 +373,10 @@ class ResolveRepoRev(RepoWrapperTestCase): | |||
274 | 373 | ||
275 | shutil.rmtree(cls.GIT_DIR) | 374 | shutil.rmtree(cls.GIT_DIR) |
276 | 375 | ||
376 | |||
377 | class ResolveRepoRev(GitCheckoutTestCase): | ||
378 | """Check resolve_repo_rev behavior.""" | ||
379 | |||
277 | def test_explicit_branch(self): | 380 | def test_explicit_branch(self): |
278 | """Check refs/heads/branch argument.""" | 381 | """Check refs/heads/branch argument.""" |
279 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/stable') | 382 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/stable') |
@@ -328,5 +431,54 @@ class ResolveRepoRev(RepoWrapperTestCase): | |||
328 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'boooooooya') | 431 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'boooooooya') |
329 | 432 | ||
330 | 433 | ||
434 | class CheckRepoVerify(RepoWrapperTestCase): | ||
435 | """Check check_repo_verify behavior.""" | ||
436 | |||
437 | def test_no_verify(self): | ||
438 | """Always fail with --no-repo-verify.""" | ||
439 | self.assertFalse(self.wrapper.check_repo_verify(False)) | ||
440 | |||
441 | def test_gpg_initialized(self): | ||
442 | """Should pass if gpg is setup already.""" | ||
443 | with mock.patch.object(self.wrapper, 'NeedSetupGnuPG', return_value=False): | ||
444 | self.assertTrue(self.wrapper.check_repo_verify(True)) | ||
445 | |||
446 | def test_need_gpg_setup(self): | ||
447 | """Should pass/fail based on gpg setup.""" | ||
448 | with mock.patch.object(self.wrapper, 'NeedSetupGnuPG', return_value=True): | ||
449 | with mock.patch.object(self.wrapper, 'SetupGnuPG') as m: | ||
450 | m.return_value = True | ||
451 | self.assertTrue(self.wrapper.check_repo_verify(True)) | ||
452 | |||
453 | m.return_value = False | ||
454 | self.assertFalse(self.wrapper.check_repo_verify(True)) | ||
455 | |||
456 | |||
457 | class CheckRepoRev(GitCheckoutTestCase): | ||
458 | """Check check_repo_rev behavior.""" | ||
459 | |||
460 | def test_verify_works(self): | ||
461 | """Should pass when verification passes.""" | ||
462 | with mock.patch.object(self.wrapper, 'check_repo_verify', return_value=True): | ||
463 | with mock.patch.object(self.wrapper, 'verify_rev', return_value='12345'): | ||
464 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, 'stable') | ||
465 | self.assertEqual('refs/heads/stable', rrev) | ||
466 | self.assertEqual('12345', lrev) | ||
467 | |||
468 | def test_verify_fails(self): | ||
469 | """Should fail when verification fails.""" | ||
470 | with mock.patch.object(self.wrapper, 'check_repo_verify', return_value=True): | ||
471 | with mock.patch.object(self.wrapper, 'verify_rev', side_effect=Exception): | ||
472 | with self.assertRaises(Exception): | ||
473 | self.wrapper.check_repo_rev(self.GIT_DIR, 'stable') | ||
474 | |||
475 | def test_verify_ignore(self): | ||
476 | """Should pass when verification is disabled.""" | ||
477 | with mock.patch.object(self.wrapper, 'verify_rev', side_effect=Exception): | ||
478 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, 'stable', repo_verify=False) | ||
479 | self.assertEqual('refs/heads/stable', rrev) | ||
480 | self.assertEqual(self.REV_LIST[1], lrev) | ||
481 | |||
482 | |||
331 | if __name__ == '__main__': | 483 | if __name__ == '__main__': |
332 | unittest.main() | 484 | unittest.main() |