summaryrefslogtreecommitdiff
path: root/pkgs/build-support/fetchgit/default.nix
blob: b2f5f15a309daf1c452a4ae5ed24be147a482ad3 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
{
  lib,
  stdenvNoCC,
  git,
  git-lfs,
  cacert,
}:

let
  urlToName =
    {
      url,
      rev,
      append,
    }:
    let
      shortRev = lib.sources.shortRev rev;
      appendShort = lib.optionalString ((builtins.match "[a-f0-9]*" rev) != null) "-${shortRev}";
    in
    "${lib.sources.urlToName url}${if append == "" then appendShort else append}";
in

lib.makeOverridable (
  lib.fetchers.withNormalizedHash { } (
    # NOTE Please document parameter additions or changes in
    #   ../../../doc/build-helpers/fetchers.chapter.md
    {
      url,
      tag ? null,
      rev ? null,
      name ? urlToName {
        inherit url;
        rev = lib.revOrTag rev tag;
        # when rootDir is specified, avoid invalidating the result when rev changes
        append = if rootDir != "" then "-${lib.strings.sanitizeDerivationName rootDir}" else "";
      },
      leaveDotGit ? deepClone || fetchTags,
      outputHash ? lib.fakeHash,
      outputHashAlgo ? null,
      fetchSubmodules ? true,
      deepClone ? false,
      branchName ? null,
      sparseCheckout ? lib.optional (rootDir != "") rootDir,
      nonConeMode ? rootDir != "",
      nativeBuildInputs ? [ ],
      # Shell code executed before the file has been fetched.  This, in
      # particular, can do things like set NIX_PREFETCH_GIT_CHECKOUT_HOOK to
      # run operations between the checkout completing and deleting the .git
      # directory.
      preFetch ? "",
      # Shell code executed after the file has been fetched
      # successfully. This can do things like check or transform the file.
      postFetch ? "",
      preferLocalBuild ? true,
      fetchLFS ? false,
      # Shell code to build a netrc file for BASIC auth
      netrcPhase ? null,
      # Impure env vars (https://nixos.org/nix/manual/#sec-advanced-attributes)
      # needed for netrcPhase
      netrcImpureEnvVars ? [ ],
      meta ? { },
      allowedRequisites ? null,
      # fetch all tags after tree (useful for git describe)
      fetchTags ? false,
      # make this subdirectory the root of the result
      rootDir ? "",
    }:

    /*
      NOTE:
      fetchgit has one problem: git fetch only works for refs.
      This is because fetching arbitrary (maybe dangling) commits creates garbage collection risks
      and checking whether a commit belongs to a ref is expensive. This may
      change in the future when some caching is added to git (?)
      Usually refs are either tags (refs/tags/*) or branches (refs/heads/*)
      Cloning branches will make the hash check fail when there is an update.
      But not all patches we want can be accessed by tags.

      The workaround is getting the last n commits so that it's likely that they
      still contain the hash we want.

      for now : increase depth iteratively (TODO)

      real fix: ask git folks to add a
      git fetch $HASH contained in $BRANCH
      facility because checking that $HASH is contained in $BRANCH is less
      expensive than fetching --depth $N.
      Even if git folks implemented this feature soon it may take years until
      server admins start using the new version?
    */

    assert nonConeMode -> (sparseCheckout != [ ]);
    assert fetchTags -> leaveDotGit;
    assert rootDir != "" -> !leaveDotGit;

    let
      revWithTag =
        let
          warningMsg = "fetchgit requires one of either `rev` or `tag` to be provided (not both).";
          otherIsNull = other: lib.assertMsg (other == null) warningMsg;
        in
        if tag != null then
          assert (otherIsNull rev);
          "refs/tags/${tag}"
        else if rev != null then
          assert (otherIsNull tag);
          rev
        else
          # FIXME fetching HEAD if no rev or tag is provided is problematic at best
          "HEAD";
    in

    if builtins.isString sparseCheckout then
      # Changed to throw on 2023-06-04
      throw
        "Please provide directories/patterns for sparse checkout as a list of strings. Passing a (multi-line) string is not supported any more."
    else
      stdenvNoCC.mkDerivation {
        inherit name;

        builder = ./builder.sh;
        fetcher = ./nix-prefetch-git;

        nativeBuildInputs = [
          git
          cacert
        ]
        ++ lib.optionals fetchLFS [ git-lfs ]
        ++ nativeBuildInputs;

        inherit outputHash outputHashAlgo;
        outputHashMode = "recursive";

        # git-sparse-checkout(1) says:
        # > When the --stdin option is provided, the directories or patterns are read
        # > from standard in as a newline-delimited list instead of from the arguments.
        sparseCheckout = builtins.concatStringsSep "\n" sparseCheckout;

        inherit
          url
          leaveDotGit
          fetchLFS
          fetchSubmodules
          deepClone
          branchName
          nonConeMode
          preFetch
          postFetch
          fetchTags
          rootDir
          ;
        rev = revWithTag;

        postHook =
          if netrcPhase == null then
            null
          else
            ''
              ${netrcPhase}
              # required that git uses the netrc file
              mv {,.}netrc
              export NETRC=$PWD/.netrc
              export HOME=$PWD
            '';

        impureEnvVars =
          lib.fetchers.proxyImpureEnvVars
          ++ netrcImpureEnvVars
          ++ [
            "GIT_PROXY_COMMAND"
            "NIX_GIT_SSL_CAINFO"
            "SOCKS_SERVER"

            # This is a parameter intended to be set by setup hooks or preFetch
            # scripts that want per-URL control over HTTP proxies used by Git
            # (if per-URL control isn't needed, `http_proxy` etc. will
            # suffice). It must be a whitespace-separated (with backslash as an
            # escape character) list of pairs like this:
            #
            #   http://domain1/path1 proxy1 https://domain2/path2 proxy2
            #
            # where the URLs are as documented in the `git-config` manual page
            # under `http.<url>.*`, and the proxies are as documented on the
            # same page under `http.proxy`.
            "FETCHGIT_HTTP_PROXIES"
          ];

        inherit preferLocalBuild meta allowedRequisites;

        passthru = {
          gitRepoUrl = url;
          inherit tag;
        };
      }
  )
)