Skip to content

Commit 1201c72

Browse files
committed
GitRepo::fetch(): Fall back to using libgit2 for fetching
In the builtin:fetch-tree sandbox, we don't have the `git` executable available, so let's use libgit2 instead. This generally won't work very well for SSH, but that currently doesn't work anyway because the sandbox doesn't have access to SSH keys.
1 parent 486c48a commit 1201c72

File tree

2 files changed

+88
-15
lines changed

2 files changed

+88
-15
lines changed

src/libfetchers/git-utils.cc

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "nix/util/sync.hh"
1111
#include "nix/util/thread-pool.hh"
1212
#include "nix/util/pool.hh"
13+
#include "nix/util/executable-path.hh"
1314

1415
#include <git2/attr.h>
1516
#include <git2/blob.h>
@@ -549,21 +550,44 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
549550
// that)
550551
// then use code that was removed in this commit (see blame)
551552

552-
auto dir = this->path;
553-
Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--quiet", "--force"};
554-
if (shallow)
555-
append(gitArgs, {"--depth", "1"});
556-
append(gitArgs, {std::string("--"), url, refspec});
557-
558-
runProgram(
559-
RunOptions{
560-
.program = "git",
561-
.lookupPath = true,
562-
// FIXME: git stderr messes up our progress indicator, so
563-
// we're using --quiet for now. Should process its stderr.
564-
.args = gitArgs,
565-
.input = {},
566-
.isInteractive = true});
553+
if (ExecutablePath::load().findName("git")) {
554+
auto dir = this->path;
555+
Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--quiet", "--force"};
556+
if (shallow)
557+
append(gitArgs, {"--depth", "1"});
558+
append(gitArgs, {std::string("--"), url, refspec});
559+
560+
runProgram(
561+
RunOptions{
562+
.program = "git",
563+
.lookupPath = true,
564+
// FIXME: git stderr messes up our progress indicator, so
565+
// we're using --quiet for now. Should process its stderr.
566+
.args = gitArgs,
567+
.input = {},
568+
.isInteractive = true});
569+
} else {
570+
// Fall back to using libgit2 for fetching. This does not
571+
// support SSH very well.
572+
Remote remote;
573+
574+
if (git_remote_create_anonymous(Setter(remote), *this, url.c_str()))
575+
throw Error("cannot create Git remote '%s': %s", url, git_error_last()->message);
576+
577+
char * refspecs[] = {(char *) refspec.c_str()};
578+
git_strarray refspecs2{.strings = refspecs, .count = 1};
579+
580+
git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
581+
// FIXME: for some reason, shallow fetching over ssh barfs
582+
// with "could not read from remote repository".
583+
opts.depth = shallow && parseURL(url).scheme != "ssh" ? 1 : GIT_FETCH_DEPTH_FULL;
584+
opts.callbacks.payload = &act;
585+
opts.callbacks.sideband_progress = sidebandProgressCallback;
586+
opts.callbacks.transfer_progress = transferProgressCallback;
587+
588+
if (git_remote_fetch(remote.get(), &refspecs2, &opts, nullptr))
589+
throw Error("fetching '%s' from '%s': %s", refspec, url, git_error_last()->message);
590+
}
567591
}
568592

569593
void verifyCommit(const Hash & rev, const std::vector<fetchers::PublicKey> & publicKeys) override
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{ config, ... }:
2+
{
3+
description = "build-time fetching";
4+
script = ''
5+
import json
6+
7+
# add a file to the repo
8+
client.succeed(f"""
9+
echo ${config.name # to make the git tree and store path unique
10+
} > {repo.path}/test-case \
11+
&& echo chiang-mai > {repo.path}/thailand \
12+
&& {repo.git} add test-case thailand \
13+
&& {repo.git} commit -m 'commit1' \
14+
&& {repo.git} push origin main
15+
""")
16+
17+
# get the NAR hash
18+
nar_hash = json.loads(client.succeed(f"""
19+
nix flake prefetch --flake-registry "" git+{repo.remote} --json
20+
"""))['hash']
21+
22+
# construct the derivation
23+
expr = f"""
24+
derivation {{
25+
name = "source";
26+
builder = "builtin:fetch-tree";
27+
system = "builtin";
28+
__structuredAttrs = true;
29+
input = {{
30+
type = "git";
31+
url = "{repo.remote}";
32+
ref = "main";
33+
}};
34+
outputHashMode = "recursive";
35+
outputHash = "{nar_hash}";
36+
}}
37+
"""
38+
39+
# do the build-time fetch
40+
out_path = client.succeed(f"""
41+
nix build --print-out-paths --store /run/store --flake-registry "" --extra-experimental-features build-time-fetch-tree --expr '{expr}'
42+
""").strip()
43+
44+
# check if the committed file is there
45+
client.succeed(f"""
46+
test -f /run/store/{out_path}/thailand
47+
""")
48+
'';
49+
}

0 commit comments

Comments
 (0)