fetchFromGitHub: use the API tarball endpoint

GitHub currently has two kinds of personal access token: "classic" and
"fine-grained".  Fine-grained personal access tokens, as the name
suggests, allow much more control over what the token can and cannot do,
and in particular allow users to specify which repositories the token
should provide access to.

Unfortunately, fine-grained tokens don't allow access to repository
archive tarballs for private repositories at (say)
https://github.com/me-and/private-demo/archive/HEAD.tar.gz.
Fortunately, the GitHub API endpoint does provide this access, and also
works with classic tokens and -- for public repositories -- no token at
all.

To allow folk to use fine-grained access tokens, use the GitHub API for
accessing private repos.  Keep using the existing interface for
non-private repos, as we can only assume an authenticated user for
private repos, and unauthenticated users have restrictive rate limits on
the API interface.

Fixes #321481
This commit is contained in:
Adam Dinwoodie 2024-06-21 11:42:20 +01:00
parent 289a4c6631
commit 1a72fa0e19

View File

@ -43,17 +43,19 @@ let
else if fetchzip ? override then fetchzip.override { withUnzip = false; }
else fetchzip;
privateAttrs = lib.optionalAttrs private {
netrcPhase = ''
if [ -z "''$${varBase}USERNAME" -o -z "''$${varBase}PASSWORD" ]; then
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
exit 1
fi
cat > netrc <<EOF
machine ${githubBase}
login ''$${varBase}USERNAME
password ''$${varBase}PASSWORD
EOF
'';
netrcPhase =
let machineName = if githubBase == "github.com" then "api.github.com" else githubBase;
in ''
if [ -z "''$${varBase}USERNAME" -o -z "''$${varBase}PASSWORD" ]; then
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
exit 1
fi
cat > netrc <<EOF
machine ${machineName}
login ''$${varBase}USERNAME
password ''$${varBase}PASSWORD
EOF
'';
netrcImpureEnvVars = [ "${varBase}USERNAME" "${varBase}PASSWORD" ];
};
@ -66,7 +68,22 @@ let
inherit tag rev deepClone fetchSubmodules sparseCheckout fetchLFS; url = gitRepoUrl;
} // lib.optionalAttrs (leaveDotGit != null) { inherit leaveDotGit; }
else {
url = "${baseUrl}/archive/${revWithTag}.tar.gz";
# Use the API endpoint for private repos, as the archive URI doesn't
# support access with GitHub's fine-grained access tokens.
#
# Use the archive URI for non-private repos, as the API endpoint has
# relatively restrictive rate limits for unauthenticated users.
url =
let
endpoint = "/repos/${owner}/${repo}/tarball/${revWithTag}";
in
if private
then
if githubBase == "github.com"
then "https://api.github.com${endpoint}"
else "https://${githubBase}/api/v3${endpoint}"
else "${baseUrl}/archive/${revWithTag}.tar.gz";
extension = "tar.gz";
passthru = {
inherit gitRepoUrl;