Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix git auth #9806

Merged
merged 4 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions src/libfetchers/git-utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type)

struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{
/** Location of the repository on disk. */
CanonPath path;
Repository repo;

Expand Down Expand Up @@ -382,27 +383,20 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{
Activity act(*logger, lvlTalkative, actFetchTree, fmt("fetching Git repository '%s'", url));

Remote remote;
// TODO: implement git-credential helper support (preferably via libgit2, which as of 2024-01 does not support that)
// then use code that was removed in this commit (see blame)

if (git_remote_create_anonymous(Setter(remote), *this, url.c_str()))
throw Error("cannot create Git remote '%s': %s", url, git_error_last()->message);
auto dir = this->path;

char * refspecs[] = {(char *) refspec.c_str()};
git_strarray refspecs2 {
.strings = refspecs,
.count = 1
};

git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
// FIXME: for some reason, shallow fetching over ssh barfs
// with "could not read from remote repository".
opts.depth = shallow && parseURL(url).scheme != "ssh" ? 1 : GIT_FETCH_DEPTH_FULL;
opts.callbacks.payload = &act;
opts.callbacks.sideband_progress = sidebandProgressCallback;
opts.callbacks.transfer_progress = transferProgressCallback;

if (git_remote_fetch(remote.get(), &refspecs2, &opts, nullptr))
throw Error("fetching '%s' from '%s': %s", refspec, url, git_error_last()->message);
runProgram(RunOptions {
.program = "git",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.searchPath = true,
// FIXME: git stderr messes up our progress indicator, so
// we're using --quiet for now. Should process its stderr.
.args = { "-C", path.abs(), "fetch", "--quiet", "--force", "--", url, refspec },
.input = {},
.isInteractive = true
});
}

void verifyCommit(
Expand Down
40 changes: 40 additions & 0 deletions tests/nixos/fetch-git/test-cases/http-auth/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{ config, ... }:
{
description = "can fetch a private git repo via http";
repo.private = true;
script = ''
# add a file to the repo
client.succeed(f"""
echo ${config.name /* to make the git tree and store path unique */} > {repo.path}/test-case \
&& echo lutyabrook > {repo.path}/new-york-state \
&& {repo.git} add test-case new-york-state \
&& {repo.git} commit -m 'commit1'
""")

# memoize the revision
rev1 = client.succeed(f"""
{repo.git} rev-parse HEAD
""").strip()

# push to the server
client.succeed(f"""
{repo.git} push origin main
""")

# fetch the repo via nix
fetched1 = client.succeed(f"""
nix eval --impure --raw --expr "(builtins.fetchGit {repo.remote}).outPath"
""")

# check if the committed file is there
client.succeed(f"""
test -f {fetched1}/new-york-state
""")

# check if the revision is the same
rev1_fetched = client.succeed(f"""
nix eval --impure --raw --expr "(builtins.fetchGit {repo.remote}).rev"
""").strip()
assert rev1 == rev1_fetched, f"rev1: {rev1} != rev1_fetched: {rev1_fetched}"
'';
}
36 changes: 30 additions & 6 deletions tests/nixos/fetch-git/testsupport/gitea-repo.nix
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
{ lib, ... }:
let
inherit (lib) mkOption types;
inherit (lib)
mkIf
mkOption
types
;

boolPyLiteral = b: if b then "True" else "False";

testCaseExtension = { config, ... }: {
setupScript = ''
repo = Repo("${config.name}")
'';
options = {
repo.enable = mkOption {
type = types.bool;
default = true;
description = "Whether to provide a repo variable - automatic repo creation.";
};
repo.private = mkOption {
type = types.bool;
default = false;
description = "Whether the repo should be private.";
};
};
config = mkIf config.repo.enable {
setupScript = ''
repo = Repo("${config.name}", private=${boolPyLiteral config.repo.private})
'';
};
};
in
{
Expand All @@ -16,16 +36,20 @@ in
};
config = {
setupScript = ''
def boolToJSON(b):
return "true" if b else "false"

class Repo:
"""
A class to create a git repository on the gitea server and locally.
"""
def __init__(self, name):
def __init__(self, name, private=False):
self.name = name
self.path = "/tmp/repos/" + name
self.remote = "http://gitea:3000/test/" + name
self.remote_ssh = "ssh://gitea/root/" + name
self.git = f"git -C {self.path}"
self.private = private
self.create()

def create(self):
Expand All @@ -37,7 +61,7 @@ in
gitea.succeed(f"""
curl --fail -X POST http://{gitea_admin}:{gitea_admin_password}@gitea:3000/api/v1/user/repos \
-H 'Accept: application/json' -H 'Content-Type: application/json' \
-d {shlex.quote( f'{{"name":"{self.name}", "default_branch": "main"}}' )}
-d {shlex.quote( f'{{"name":"{self.name}", "default_branch": "main", "private": {boolToJSON(self.private)}}}' )}
""")
# setup git remotes on client
client.succeed(f"""
Expand Down
Loading