]>
git.scottworley.com Git - git-cache/blob - git_cache.py
1 # It would be nice if we could share the nix git cache, but as of the
2 # time of writing it is transitioning from gitv2 (deprecated) to gitv3
3 # (not ready yet), and trying to straddle them both is too far into nix
4 # implementation details for my comfort. So we re-implement here half of
5 # nix's builtins.fetchGit. :(
13 from typing
import Tuple
17 Path
= str # eg: "/home/user/.cache/git-cache/v1"
18 Repo
= str # eg: "https://github.com/NixOS/nixpkgs.git"
19 Ref
= str # eg: "master" or "v1.0.0"
20 Rev
= str # eg: "53a27350551844e1ed1a9257690294767389ef0d"
23 def git_cachedir(repo
: Repo
) -> Path
:
24 # Use xdg module when it's less painful to have as a dependency
25 XDG_CACHE_HOME
= Path(
26 os
.environ
.get('XDG_CACHE_HOME', os
.path
.expanduser('~/.cache')))
28 return Path(os
.path
.join(
31 hashlib
.sha256(repo
.encode()).hexdigest()))
34 def verify_ancestry(repo
: Repo
, ref
: Ref
, rev
: Rev
) -> None:
35 cachedir
= git_cachedir(repo
)
36 logging
.debug('Verifying rev %s is an ancestor of ref "%s"', rev
, ref
)
37 subprocess
.run(['git', '-C', cachedir
, 'merge-base', '--is-ancestor',
38 rev
, ref
], check
=True)
41 @backoff.on_exception(
43 subprocess
.CalledProcessError
,
44 max_time
=lambda: int(os
.environ
.get('BACKOFF_MAX_TIME', '30')))
45 def _git_fetch(cachedir
: Path
, repo
: Repo
, ref
: Ref
) -> None:
46 # We don't use --force here because we want to abort and freak out if forced
47 # updates are happening.
48 subprocess
.run(['git', '-C', cachedir
, 'fetch', repo
,
49 '%s:%s' % (ref
, ref
)], check
=True)
52 def fetch(repo
: Repo
, ref
: Ref
) -> Tuple
[Path
, Rev
]:
53 cachedir
= git_cachedir(repo
)
54 if not os
.path
.exists(cachedir
):
55 logging
.debug("Initializing git repo")
56 subprocess
.run(['git', 'init', '--bare', cachedir
], check
=True)
58 logging
.debug('Fetching ref "%s" from %s', ref
, repo
)
59 _git_fetch(cachedir
, repo
, ref
)
61 with open(os
.path
.join(cachedir
, 'refs', 'heads', ref
)) as rev_file
:
62 rev
= Rev(rev_file
.read(999).strip())
63 verify_ancestry(repo
, ref
, rev
)
68 def ensure_rev_available(repo
: Repo
, ref
: Ref
, rev
: Rev
) -> Path
:
69 cachedir
= git_cachedir(repo
)
70 if os
.path
.exists(cachedir
):
71 logging
.debug('Checking if we already have rev %s', rev
)
72 process
= subprocess
.run(
73 ['git', '-C', cachedir
, 'cat-file', '-e', rev
], check
=False)
74 if process
.returncode
== 0:
75 logging
.debug('We already have rev %s', rev
)
76 verify_ancestry(repo
, ref
, rev
)
78 if process
.returncode
!= 1:
80 'Could not test for presence of rev %s. Is cache dir "%s" messed up?' %
84 'We do not have rev %s. We will fetch ref "%s" and hope it appears.',
87 logging
.debug('Verifying that fetch retrieved rev %s', rev
)
88 subprocess
.run(['git', '-C', cachedir
, 'cat-file', '-e', rev
], check
=True)
94 if len(sys
.argv
) == 3:
95 print('{1} {0}'.format(*fetch(Repo(sys
.argv
[1]), Ref(sys
.argv
[2]))))
96 elif len(sys
.argv
) == 4:
97 print(ensure_rev_available(
98 Repo(sys
.argv
[1]), Ref(sys
.argv
[2]), Rev(sys
.argv
[3])))
100 usage
= '''usage: git-cache repo ref [rev]
101 example: git-cache https://github.com/NixOS/nixpkgs.git master'''
102 print(usage
, file=sys
.stderr
)