1{
2 lib,
3 fetchgit,
4 fetchzip,
5}:
6
7lib.makeOverridable (
8 {
9 owner,
10 repo,
11 tag ? null,
12 rev ? null,
13 name ? "source",
14 fetchSubmodules ? false,
15 leaveDotGit ? null,
16 deepClone ? false,
17 private ? false,
18 forceFetchGit ? false,
19 fetchLFS ? false,
20 sparseCheckout ? [ ],
21 githubBase ? "github.com",
22 varPrefix ? null,
23 meta ? { },
24 ... # For hash agility
25 }@args:
26
27 assert (
28 lib.assertMsg (lib.xor (tag == null) (
29 rev == null
30 )) "fetchFromGitHub requires one of either `rev` or `tag` to be provided (not both)."
31 );
32
33 let
34
35 position = (
36 if args.meta.description or null != null then
37 builtins.unsafeGetAttrPos "description" args.meta
38 else if tag != null then
39 builtins.unsafeGetAttrPos "tag" args
40 else
41 builtins.unsafeGetAttrPos "rev" args
42 );
43 baseUrl = "https://${githubBase}/${owner}/${repo}";
44 newMeta =
45 meta
46 // {
47 homepage = meta.homepage or baseUrl;
48 }
49 // lib.optionalAttrs (position != null) {
50 # to indicate where derivation originates, similar to make-derivation.nix's mkDerivation
51 position = "${position.file}:${toString position.line}";
52 };
53 passthruAttrs = removeAttrs args [
54 "owner"
55 "repo"
56 "tag"
57 "rev"
58 "fetchSubmodules"
59 "forceFetchGit"
60 "private"
61 "githubBase"
62 "varPrefix"
63 ];
64 varBase = "NIX${lib.optionalString (varPrefix != null) "_${varPrefix}"}_GITHUB_PRIVATE_";
65 useFetchGit =
66 fetchSubmodules
67 || (leaveDotGit == true)
68 || deepClone
69 || forceFetchGit
70 || fetchLFS
71 || (sparseCheckout != [ ]);
72 # We prefer fetchzip in cases we don't need submodules as the hash
73 # is more stable in that case.
74 fetcher =
75 if useFetchGit then
76 fetchgit
77 # fetchzip may not be overridable when using external tools, for example nix-prefetch
78 else if fetchzip ? override then
79 fetchzip.override { withUnzip = false; }
80 else
81 fetchzip;
82 privateAttrs = lib.optionalAttrs private {
83 netrcPhase =
84 # When using private repos:
85 # - Fetching with git works using https://github.com but not with the GitHub API endpoint
86 # - Fetching a tarball from a private repo requires to use the GitHub API endpoint
87 let
88 machineName = if githubBase == "github.com" && !useFetchGit then "api.github.com" else githubBase;
89 in
90 ''
91 if [ -z "''$${varBase}USERNAME" -o -z "''$${varBase}PASSWORD" ]; then
92 echo "Error: Private fetchFromGitHub requires the nix building process (nix-daemon in multi user mode) to have the ${varBase}USERNAME and ${varBase}PASSWORD env vars set." >&2
93 exit 1
94 fi
95 cat > netrc <<EOF
96 machine ${machineName}
97 login ''$${varBase}USERNAME
98 password ''$${varBase}PASSWORD
99 EOF
100 '';
101 netrcImpureEnvVars = [
102 "${varBase}USERNAME"
103 "${varBase}PASSWORD"
104 ];
105 };
106
107 gitRepoUrl = "${baseUrl}.git";
108
109 revWithTag = if tag != null then "refs/tags/${tag}" else rev;
110
111 fetcherArgs =
112 (
113 if useFetchGit then
114 {
115 inherit
116 tag
117 rev
118 deepClone
119 fetchSubmodules
120 sparseCheckout
121 fetchLFS
122 ;
123 url = gitRepoUrl;
124 }
125 // lib.optionalAttrs (leaveDotGit != null) { inherit leaveDotGit; }
126 else
127 {
128 # Use the API endpoint for private repos, as the archive URI doesn't
129 # support access with GitHub's fine-grained access tokens.
130 #
131 # Use the archive URI for non-private repos, as the API endpoint has
132 # relatively restrictive rate limits for unauthenticated users.
133 url =
134 if private then
135 let
136 endpoint = "/repos/${owner}/${repo}/tarball/${revWithTag}";
137 in
138 if githubBase == "github.com" then
139 "https://api.github.com${endpoint}"
140 else
141 "https://${githubBase}/api/v3${endpoint}"
142 else
143 "${baseUrl}/archive/${revWithTag}.tar.gz";
144 extension = "tar.gz";
145
146 passthru = {
147 inherit gitRepoUrl;
148 };
149 }
150 )
151 // privateAttrs
152 // passthruAttrs
153 // {
154 inherit name;
155 };
156 in
157
158 fetcher fetcherArgs
159 // {
160 meta = newMeta;
161 inherit owner repo tag;
162 rev = revWithTag;
163 }
164)