summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2020-02-29 02:53:41 -0500
committerMike Frysinger <vapier@google.com>2020-03-25 04:55:50 +0000
commit3599cc397581086b7fddcd2e07308bfdac7751b1 (patch)
tree5ae663f1830fa7d29ec66c86e2088244f7fa2e5a
parentcfc8111f5e04ece139892bb9af4bd63aac2b75f3 (diff)
downloadgit-repo-3599cc397581086b7fddcd2e07308bfdac7751b1.tar.gz
init: respect --repo-rev changes
We respect this option when running the first `repo init`, but then silently ignore it once the initial sync is done. Make sure users are able to change things on the fly. We refactor the wrapper API to allow reuse between the two init's. Bug: https://crbug.com/gerrit/11045 Change-Id: Icb89a8cddca32f39a760a6283152457810b2392d Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/260032 Tested-by: Mike Frysinger <vapier@google.com> Reviewed-by: Jonathan Nieder <jrn@google.com>
-rwxr-xr-xrepo50
-rw-r--r--subcmds/init.py11
-rw-r--r--tests/test_wrapper.py156
3 files changed, 195 insertions, 22 deletions
diff --git a/repo b/repo
index c78fcacd..66a2a07d 100755
--- a/repo
+++ b/repo
@@ -463,6 +463,34 @@ class CloneFailure(Exception):
463 """ 463 """
464 464
465 465
466def 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
480def 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
466def _Init(args, gitc_init=False): 494def _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
910def _Verify(cwd, remote_ref, rev, quiet): 920def 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
38from git_config import GitConfig 38from git_config import GitConfig
39from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD 39from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
40import platform_utils 40import platform_utils
41from wrapper import Wrapper
41 42
42 43
43class Init(InteractiveCommand, MirrorSafeCommand): 44class 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
19from __future__ import print_function 19from __future__ import print_function
20 20
21import contextlib
21import os 22import os
22import re 23import re
23import shutil 24import shutil
24import tempfile 25import tempfile
25import unittest 26import unittest
26 27
28import platform_utils
27from pyversion import is_python3 29from pyversion import is_python3
28import wrapper 30import 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
42def 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
39def fixture(*paths): 53def 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
246class ResolveRepoRev(RepoWrapperTestCase): 260class 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
300class 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
314class 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
345class 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
377class 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
434class 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
457class 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
331if __name__ == '__main__': 483if __name__ == '__main__':
332 unittest.main() 484 unittest.main()