2020-04-08 15:12:22 +03:00
|
|
|
#include "filetransfer.hh"
|
2020-03-30 17:04:18 +03:00
|
|
|
#include "cache.hh"
|
|
|
|
#include "globals.hh"
|
|
|
|
#include "store-api.hh"
|
2020-06-17 22:08:59 +03:00
|
|
|
#include "types.hh"
|
2020-09-21 19:22:45 +03:00
|
|
|
#include "url-parts.hh"
|
2022-05-04 15:32:21 +03:00
|
|
|
#include "git.hh"
|
2022-03-01 03:29:34 +02:00
|
|
|
#include "fetchers.hh"
|
|
|
|
#include "fetch-settings.hh"
|
2023-10-20 20:50:21 +03:00
|
|
|
#include "tarball.hh"
|
2023-12-21 11:28:06 +02:00
|
|
|
#include "tarfile.hh"
|
2023-11-29 13:35:08 +02:00
|
|
|
#include "git-utils.hh"
|
2022-03-01 03:29:34 +02:00
|
|
|
|
2020-09-25 08:49:44 +03:00
|
|
|
#include <optional>
|
2020-03-30 17:04:18 +03:00
|
|
|
#include <nlohmann/json.hpp>
|
2021-10-06 19:32:46 +03:00
|
|
|
#include <fstream>
|
2020-03-30 17:04:18 +03:00
|
|
|
|
|
|
|
namespace nix::fetchers {
|
|
|
|
|
2020-06-17 22:08:59 +03:00
|
|
|
struct DownloadUrl
|
|
|
|
{
|
|
|
|
std::string url;
|
2020-09-25 08:49:44 +03:00
|
|
|
Headers headers;
|
2020-06-17 22:08:59 +03:00
|
|
|
};
|
|
|
|
|
2021-10-06 19:32:46 +03:00
|
|
|
// A github, gitlab, or sourcehut host
|
2023-05-04 01:59:44 +03:00
|
|
|
const static std::string hostRegexS = "[a-zA-Z0-9.-]*"; // FIXME: check
|
2020-09-21 17:29:08 +03:00
|
|
|
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);
|
2020-03-30 17:04:18 +03:00
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
struct GitArchiveInputScheme : InputScheme
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2020-10-06 15:00:42 +03:00
|
|
|
virtual std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const = 0;
|
2020-06-17 22:08:59 +03:00
|
|
|
|
2023-08-01 17:07:20 +03:00
|
|
|
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2023-10-30 16:14:27 +02:00
|
|
|
if (url.scheme != schemeName()) return {};
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
|
|
|
|
auto path = tokenizeString<std::vector<std::string>>(url.path, "/");
|
|
|
|
|
|
|
|
std::optional<Hash> rev;
|
|
|
|
std::optional<std::string> ref;
|
2020-06-04 14:36:30 +03:00
|
|
|
std::optional<std::string> host_url;
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
|
2020-10-06 21:08:51 +03:00
|
|
|
auto size = path.size();
|
|
|
|
if (size == 3) {
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
if (std::regex_match(path[2], revRegex))
|
2023-11-28 15:20:27 +02:00
|
|
|
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
else if (std::regex_match(path[2], refRegex))
|
|
|
|
ref = path[2];
|
|
|
|
else
|
|
|
|
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
|
2020-10-06 21:08:51 +03:00
|
|
|
} else if (size > 3) {
|
|
|
|
std::string rs;
|
|
|
|
for (auto i = std::next(path.begin(), 2); i != path.end(); i++) {
|
|
|
|
rs += *i;
|
|
|
|
if (std::next(i) != path.end()) {
|
|
|
|
rs += "/";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::regex_match(rs, refRegex)) {
|
|
|
|
ref = rs;
|
|
|
|
} else {
|
|
|
|
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs);
|
|
|
|
}
|
|
|
|
} else if (size < 2)
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
throw BadURL("URL '%s' is invalid", url.url);
|
|
|
|
|
|
|
|
for (auto &[name, value] : url.query) {
|
|
|
|
if (name == "rev") {
|
|
|
|
if (rev)
|
|
|
|
throw BadURL("URL '%s' contains multiple commit hashes", url.url);
|
2023-11-28 15:20:27 +02:00
|
|
|
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
}
|
|
|
|
else if (name == "ref") {
|
|
|
|
if (!std::regex_match(value, refRegex))
|
|
|
|
throw BadURL("URL '%s' contains an invalid branch/tag name", url.url);
|
|
|
|
if (ref)
|
|
|
|
throw BadURL("URL '%s' contains multiple branch/tag names", url.url);
|
|
|
|
ref = value;
|
|
|
|
}
|
2020-09-25 08:46:03 +03:00
|
|
|
else if (name == "host") {
|
2020-09-21 17:29:08 +03:00
|
|
|
if (!std::regex_match(value, hostRegex))
|
2020-09-25 08:46:03 +03:00
|
|
|
throw BadURL("URL '%s' contains an invalid instance host", url.url);
|
2020-06-04 14:36:30 +03:00
|
|
|
host_url = value;
|
|
|
|
}
|
2020-05-30 01:59:13 +03:00
|
|
|
// FIXME: barf on unsupported attributes
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ref && rev)
|
2020-06-09 14:45:07 +03:00
|
|
|
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev());
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
|
|
|
|
Input input;
|
2023-10-30 16:14:27 +02:00
|
|
|
input.attrs.insert_or_assign("type", std::string { schemeName() });
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
input.attrs.insert_or_assign("owner", path[0]);
|
|
|
|
input.attrs.insert_or_assign("repo", path[1]);
|
|
|
|
if (rev) input.attrs.insert_or_assign("rev", rev->gitRev());
|
|
|
|
if (ref) input.attrs.insert_or_assign("ref", *ref);
|
2020-09-25 08:46:03 +03:00
|
|
|
if (host_url) input.attrs.insert_or_assign("host", *host_url);
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
|
2024-03-10 14:56:53 +02:00
|
|
|
auto narHash = url.query.find("narHash");
|
|
|
|
if (narHash != url.query.end())
|
|
|
|
input.attrs.insert_or_assign("narHash", narHash->second);
|
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
return input;
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
|
|
|
|
2023-10-30 16:14:27 +02:00
|
|
|
StringSet allowedAttrs() const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2023-10-30 16:14:27 +02:00
|
|
|
return {
|
|
|
|
"owner",
|
|
|
|
"repo",
|
|
|
|
"ref",
|
|
|
|
"rev",
|
|
|
|
"narHash",
|
|
|
|
"lastModified",
|
|
|
|
"host",
|
2024-03-08 13:40:14 +02:00
|
|
|
"treeHash",
|
2023-10-30 16:14:27 +02:00
|
|
|
};
|
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-10-30 16:14:27 +02:00
|
|
|
std::optional<Input> inputFromAttrs(const Attrs & attrs) const override
|
|
|
|
{
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
getStrAttr(attrs, "owner");
|
|
|
|
getStrAttr(attrs, "repo");
|
2020-03-30 17:04:18 +03:00
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
Input input;
|
|
|
|
input.attrs = attrs;
|
|
|
|
return input;
|
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
ParsedURL toURL(const Input & input) const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
auto owner = getStrAttr(input.attrs, "owner");
|
|
|
|
auto repo = getStrAttr(input.attrs, "repo");
|
|
|
|
auto ref = input.getRef();
|
|
|
|
auto rev = input.getRev();
|
2020-03-30 17:04:18 +03:00
|
|
|
auto path = owner + "/" + repo;
|
|
|
|
assert(!(ref && rev));
|
|
|
|
if (ref) path += "/" + *ref;
|
2023-10-13 04:48:15 +03:00
|
|
|
if (rev) path += "/" + rev->to_string(HashFormat::Base16, false);
|
2024-03-10 14:56:53 +02:00
|
|
|
auto url = ParsedURL {
|
2023-10-30 16:14:27 +02:00
|
|
|
.scheme = std::string { schemeName() },
|
2020-03-30 17:04:18 +03:00
|
|
|
.path = path,
|
|
|
|
};
|
2024-03-10 14:56:53 +02:00
|
|
|
if (auto narHash = input.getNarHash())
|
|
|
|
url.query.insert_or_assign("narHash", narHash->to_string(HashFormat::SRI, true));
|
|
|
|
return url;
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
Input applyOverrides(
|
|
|
|
const Input & _input,
|
|
|
|
std::optional<std::string> ref,
|
2022-12-07 13:58:58 +02:00
|
|
|
std::optional<Hash> rev) const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
auto input(_input);
|
2020-06-09 14:45:07 +03:00
|
|
|
if (rev && ref)
|
|
|
|
throw BadURL("cannot apply both a commit hash (%s) and a branch/tag name ('%s') to input '%s'",
|
|
|
|
rev->gitRev(), *ref, input.to_string());
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
if (rev) {
|
|
|
|
input.attrs.insert_or_assign("rev", rev->gitRev());
|
|
|
|
input.attrs.erase("ref");
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
if (ref) {
|
|
|
|
input.attrs.insert_or_assign("ref", *ref);
|
2020-06-09 14:45:07 +03:00
|
|
|
input.attrs.erase("rev");
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
}
|
|
|
|
return input;
|
2020-01-31 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
2020-09-30 13:11:22 +03:00
|
|
|
std::optional<std::string> getAccessToken(const std::string & host) const
|
|
|
|
{
|
2022-03-01 03:29:34 +02:00
|
|
|
auto tokens = fetchSettings.accessTokens.get();
|
2020-09-30 13:11:22 +03:00
|
|
|
if (auto token = get(tokens, host))
|
|
|
|
return *token;
|
|
|
|
return {};
|
2020-09-25 08:49:44 +03:00
|
|
|
}
|
|
|
|
|
2020-09-30 13:11:22 +03:00
|
|
|
Headers makeHeadersWithAuthTokens(const std::string & host) const
|
|
|
|
{
|
2020-09-25 08:49:44 +03:00
|
|
|
Headers headers;
|
|
|
|
auto accessToken = getAccessToken(host);
|
|
|
|
if (accessToken) {
|
|
|
|
auto hdr = accessHeaderFromToken(*accessToken);
|
|
|
|
if (hdr)
|
|
|
|
headers.push_back(*hdr);
|
|
|
|
else
|
|
|
|
warn("Unrecognized access token for host '%s'", host);
|
|
|
|
}
|
|
|
|
return headers;
|
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
struct RefInfo
|
|
|
|
{
|
|
|
|
Hash rev;
|
|
|
|
std::optional<Hash> treeHash;
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;
|
2020-05-29 15:23:32 +03:00
|
|
|
|
2020-06-17 22:08:59 +03:00
|
|
|
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
|
2020-01-21 17:27:53 +02:00
|
|
|
|
2024-02-15 22:58:08 +02:00
|
|
|
struct TarballInfo
|
|
|
|
{
|
|
|
|
Hash treeHash;
|
|
|
|
time_t lastModified;
|
|
|
|
};
|
|
|
|
|
2023-12-21 11:28:06 +02:00
|
|
|
std::pair<Input, TarballInfo> downloadArchive(ref<Store> store, Input input) const
|
2020-01-21 17:27:53 +02:00
|
|
|
{
|
2020-06-18 14:25:08 +03:00
|
|
|
if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD");
|
2020-01-21 17:27:53 +02:00
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
std::optional<Hash> upstreamTreeHash;
|
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
auto rev = input.getRev();
|
2023-11-29 13:35:08 +02:00
|
|
|
if (!rev) {
|
|
|
|
auto refInfo = getRevFromRef(store, input);
|
|
|
|
rev = refInfo.rev;
|
|
|
|
upstreamTreeHash = refInfo.treeHash;
|
|
|
|
debug("HEAD revision for '%s' is %s", input.to_string(), refInfo.rev.gitRev());
|
|
|
|
}
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
|
|
|
|
input.attrs.erase("ref");
|
|
|
|
input.attrs.insert_or_assign("rev", rev->gitRev());
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
auto cache = getCache();
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2024-04-10 22:39:40 +03:00
|
|
|
Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}};
|
|
|
|
Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}};
|
2023-11-29 13:35:08 +02:00
|
|
|
|
|
|
|
if (auto treeHashAttrs = cache->lookup(treeHashKey)) {
|
|
|
|
if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) {
|
|
|
|
auto treeHash = getRevAttr(*treeHashAttrs, "treeHash");
|
|
|
|
auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified");
|
|
|
|
if (getTarballCache()->hasObject(treeHash))
|
2023-12-21 11:28:06 +02:00
|
|
|
return {std::move(input), TarballInfo { .treeHash = treeHash, .lastModified = (time_t) lastModified }};
|
2023-11-29 13:35:08 +02:00
|
|
|
else
|
|
|
|
debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev());
|
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
/* Stream the tarball into the tarball cache. */
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
auto url = getDownloadUrl(input);
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
auto source = sinkToSource([&](Sink & sink) {
|
|
|
|
FileTransferRequest req(url.url);
|
|
|
|
req.headers = url.headers;
|
|
|
|
getFileTransfer()->download(std::move(req), sink);
|
|
|
|
});
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-12-21 11:28:06 +02:00
|
|
|
TarArchive archive { *source };
|
|
|
|
auto parseSink = getTarballCache()->getFileSystemObjectSink();
|
|
|
|
auto lastModified = unpackTarfileToSink(archive, *parseSink);
|
|
|
|
|
|
|
|
TarballInfo tarballInfo {
|
|
|
|
.treeHash = parseSink->sync(),
|
|
|
|
.lastModified = lastModified
|
|
|
|
};
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
cache->upsert(treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}});
|
|
|
|
cache->upsert(lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}});
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2023-12-14 14:38:10 +02:00
|
|
|
#if 0
|
2023-11-29 13:35:08 +02:00
|
|
|
if (upstreamTreeHash != tarballInfo.treeHash)
|
|
|
|
warn(
|
|
|
|
"Git tree hash mismatch for revision '%s' of '%s': "
|
|
|
|
"expected '%s', got '%s'. "
|
|
|
|
"This can happen if the Git repository uses submodules.",
|
|
|
|
rev->gitRev(), input.to_string(), upstreamTreeHash->gitRev(), tarballInfo.treeHash.gitRev());
|
2023-12-14 14:38:10 +02:00
|
|
|
#endif
|
2023-11-29 13:35:08 +02:00
|
|
|
|
|
|
|
return {std::move(input), tarballInfo};
|
|
|
|
}
|
|
|
|
|
2024-05-03 13:14:01 +03:00
|
|
|
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
|
2023-11-29 13:35:08 +02:00
|
|
|
{
|
|
|
|
auto [input, tarballInfo] = downloadArchive(store, _input);
|
|
|
|
|
2024-03-08 13:40:14 +02:00
|
|
|
#if 0
|
2023-11-29 13:35:08 +02:00
|
|
|
input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev());
|
2024-03-08 13:40:14 +02:00
|
|
|
#endif
|
2023-11-29 13:35:08 +02:00
|
|
|
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
|
|
|
|
|
2024-02-14 15:45:19 +02:00
|
|
|
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false);
|
2023-11-29 13:35:08 +02:00
|
|
|
|
|
|
|
accessor->setPathDisplay("«" + input.to_string() + "»");
|
|
|
|
|
|
|
|
return {accessor, input};
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
2023-10-17 15:57:29 +03:00
|
|
|
|
2024-02-16 18:00:07 +02:00
|
|
|
bool isLocked(const Input & input) const override
|
|
|
|
{
|
2024-02-21 13:08:18 +02:00
|
|
|
/* Since we can't verify the integrity of the tarball from the
|
|
|
|
Git revision alone, we also require a NAR hash for
|
|
|
|
locking. FIXME: in the future, we may want to require a Git
|
|
|
|
tree hash instead of a NAR hash. */
|
2024-03-27 15:49:45 +02:00
|
|
|
return input.getRev().has_value()
|
|
|
|
&& (fetchSettings.trustTarballsFromGitForges ||
|
|
|
|
input.getNarHash().has_value());
|
2024-02-16 18:00:07 +02:00
|
|
|
}
|
|
|
|
|
2023-10-30 16:14:27 +02:00
|
|
|
std::optional<ExperimentalFeature> experimentalFeature() const override
|
2023-10-17 15:57:29 +03:00
|
|
|
{
|
|
|
|
return Xp::Flakes;
|
|
|
|
}
|
2023-11-20 21:04:37 +02:00
|
|
|
|
|
|
|
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
|
|
|
|
{
|
|
|
|
if (auto rev = input.getRev())
|
|
|
|
return rev->gitRev();
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
};
|
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
struct GitHubInputScheme : GitArchiveInputScheme
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2023-10-30 16:14:27 +02:00
|
|
|
std::string_view schemeName() const override { return "github"; }
|
2020-05-29 15:23:32 +03:00
|
|
|
|
2020-10-06 15:00:42 +03:00
|
|
|
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
2020-09-30 13:11:22 +03:00
|
|
|
{
|
2020-09-25 08:49:44 +03:00
|
|
|
// Github supports PAT/OAuth2 tokens and HTTP Basic
|
|
|
|
// Authentication. The former simply specifies the token, the
|
|
|
|
// latter can use the token as the password. Only the first
|
|
|
|
// is used here. See
|
|
|
|
// https://developer.github.com/v3/#authentication and
|
|
|
|
// https://docs.github.com/en/developers/apps/authorizing-oath-apps
|
2020-06-17 22:08:59 +03:00
|
|
|
return std::pair<std::string, std::string>("Authorization", fmt("token %s", token));
|
2020-09-01 16:29:04 +03:00
|
|
|
}
|
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
std::string getHost(const Input & input) const
|
|
|
|
{
|
|
|
|
return maybeGetStrAttr(input.attrs, "host").value_or("github.com");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getOwner(const Input & input) const
|
|
|
|
{
|
|
|
|
return getStrAttr(input.attrs, "owner");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getRepo(const Input & input) const
|
|
|
|
{
|
|
|
|
return getStrAttr(input.attrs, "repo");
|
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2022-12-07 13:58:58 +02:00
|
|
|
auto host = getHost(input);
|
2022-05-05 20:46:48 +03:00
|
|
|
auto url = fmt(
|
|
|
|
host == "github.com"
|
|
|
|
? "https://api.%s/repos/%s/%s/commits/%s"
|
|
|
|
: "https://%s/api/v3/repos/%s/%s/commits/%s",
|
2022-12-07 13:58:58 +02:00
|
|
|
host, getOwner(input), getRepo(input), *input.getRef());
|
2020-09-01 16:29:04 +03:00
|
|
|
|
2020-09-25 08:49:44 +03:00
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
2020-09-01 16:29:04 +03:00
|
|
|
|
2020-05-29 15:23:32 +03:00
|
|
|
auto json = nlohmann::json::parse(
|
|
|
|
readFile(
|
|
|
|
store->toRealPath(
|
2024-04-05 17:35:12 +03:00
|
|
|
downloadFile(store, url, "source", headers).storePath)));
|
2023-12-14 14:31:29 +02:00
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
return RefInfo {
|
2023-12-14 14:31:29 +02:00
|
|
|
.rev = Hash::parseAny(std::string { json["sha"] }, HashAlgorithm::SHA1),
|
|
|
|
.treeHash = Hash::parseAny(std::string { json["commit"]["tree"]["sha"] }, HashAlgorithm::SHA1)
|
2023-11-29 13:35:08 +02:00
|
|
|
};
|
2020-05-29 15:23:32 +03:00
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2020-06-17 22:08:59 +03:00
|
|
|
DownloadUrl getDownloadUrl(const Input & input) const override
|
2020-05-29 15:23:32 +03:00
|
|
|
{
|
2022-12-07 13:58:58 +02:00
|
|
|
auto host = getHost(input);
|
|
|
|
|
2022-09-12 23:50:18 +03:00
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
2022-12-07 13:58:58 +02:00
|
|
|
|
2022-09-12 23:50:18 +03:00
|
|
|
// If we have no auth headers then we default to the public archive
|
|
|
|
// urls so we do not run into rate limits.
|
|
|
|
const auto urlFmt =
|
|
|
|
host != "github.com"
|
|
|
|
? "https://%s/api/v3/repos/%s/%s/tarball/%s"
|
|
|
|
: headers.empty()
|
|
|
|
? "https://%s/%s/%s/archive/%s.tar.gz"
|
|
|
|
: "https://api.%s/repos/%s/%s/tarball/%s";
|
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
const auto url = fmt(urlFmt, host, getOwner(input), getRepo(input),
|
2023-10-13 04:48:15 +03:00
|
|
|
input.getRev()->to_string(HashFormat::Base16, false));
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2020-09-30 13:09:18 +03:00
|
|
|
return DownloadUrl { url, headers };
|
2020-05-29 15:23:32 +03:00
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
void clone(const Input & input, const Path & destDir) const override
|
2020-05-29 15:23:32 +03:00
|
|
|
{
|
2022-12-07 13:58:58 +02:00
|
|
|
auto host = getHost(input);
|
2021-09-14 14:38:05 +03:00
|
|
|
Input::fromURL(fmt("git+https://%s/%s/%s.git",
|
2022-12-07 13:58:58 +02:00
|
|
|
host, getOwner(input), getRepo(input)))
|
2021-09-14 14:38:45 +03:00
|
|
|
.applyOverrides(input.getRef(), input.getRev())
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
.clone(destDir);
|
2020-05-29 15:23:32 +03:00
|
|
|
}
|
|
|
|
};
|
2020-03-30 17:04:18 +03:00
|
|
|
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
struct GitLabInputScheme : GitArchiveInputScheme
|
2020-05-29 15:23:32 +03:00
|
|
|
{
|
2023-10-30 16:14:27 +02:00
|
|
|
std::string_view schemeName() const override { return "gitlab"; }
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2020-10-06 15:00:42 +03:00
|
|
|
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
2020-09-30 13:11:22 +03:00
|
|
|
{
|
2020-09-25 08:49:44 +03:00
|
|
|
// Gitlab supports 4 kinds of authorization, two of which are
|
|
|
|
// relevant here: OAuth2 and PAT (Private Access Token). The
|
|
|
|
// user can indicate which token is used by specifying the
|
|
|
|
// token as <TYPE>:<VALUE>, where type is "OAuth2" or "PAT".
|
|
|
|
// If the <TYPE> is unrecognized, this will fall back to
|
|
|
|
// treating this simply has <HDRNAME>:<HDRVAL>. See
|
|
|
|
// https://docs.gitlab.com/12.10/ee/api/README.html#authentication
|
|
|
|
auto fldsplit = token.find_first_of(':');
|
|
|
|
// n.b. C++20 would allow: if (token.starts_with("OAuth2:")) ...
|
|
|
|
if ("OAuth2" == token.substr(0, fldsplit))
|
|
|
|
return std::make_pair("Authorization", fmt("Bearer %s", token.substr(fldsplit+1)));
|
|
|
|
if ("PAT" == token.substr(0, fldsplit))
|
|
|
|
return std::make_pair("Private-token", token.substr(fldsplit+1));
|
|
|
|
warn("Unrecognized GitLab token type %s", token.substr(0, fldsplit));
|
2021-11-14 13:23:46 +02:00
|
|
|
return std::make_pair(token.substr(0,fldsplit), token.substr(fldsplit+1));
|
2020-06-17 22:08:59 +03:00
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
2020-05-29 15:23:32 +03:00
|
|
|
{
|
2020-09-25 08:49:44 +03:00
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
|
|
|
// See rate limiting note below
|
2020-07-13 20:22:59 +03:00
|
|
|
auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s",
|
2020-09-25 08:49:44 +03:00
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());
|
2020-06-17 22:08:59 +03:00
|
|
|
|
2020-09-25 08:49:44 +03:00
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
2020-06-17 22:08:59 +03:00
|
|
|
|
2020-05-29 15:23:32 +03:00
|
|
|
auto json = nlohmann::json::parse(
|
|
|
|
readFile(
|
|
|
|
store->toRealPath(
|
2024-04-05 17:35:12 +03:00
|
|
|
downloadFile(store, url, "source", headers).storePath)));
|
2023-12-14 14:31:29 +02:00
|
|
|
|
2024-05-31 00:20:42 +03:00
|
|
|
if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) {
|
|
|
|
return RefInfo {
|
|
|
|
.rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1)
|
|
|
|
};
|
|
|
|
} if (json.is_array() && json.size() == 0) {
|
|
|
|
throw Error("No commits returned by GitLab API -- does the git ref really exist?");
|
|
|
|
} else {
|
|
|
|
throw Error("Unexpected response received from GitLab: %s", json);
|
|
|
|
}
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
|
|
|
|
2020-06-17 22:08:59 +03:00
|
|
|
DownloadUrl getDownloadUrl(const Input & input) const override
|
2020-03-30 17:04:18 +03:00
|
|
|
{
|
2020-09-25 08:46:03 +03:00
|
|
|
// This endpoint has a rate limit threshold that may be
|
|
|
|
// server-specific and vary based whether the user is
|
|
|
|
// authenticated via an accessToken or not, but the usual rate
|
|
|
|
// is 10 reqs/sec/ip-addr. See
|
|
|
|
// https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits
|
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
2020-06-04 14:36:30 +03:00
|
|
|
auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/archive.tar.gz?sha=%s",
|
2020-09-25 08:46:03 +03:00
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
|
2023-10-13 04:48:15 +03:00
|
|
|
input.getRev()->to_string(HashFormat::Base16, false));
|
2020-03-30 17:04:18 +03:00
|
|
|
|
2020-09-25 08:49:44 +03:00
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
2020-09-30 13:09:18 +03:00
|
|
|
return DownloadUrl { url, headers };
|
2020-05-29 15:23:32 +03:00
|
|
|
}
|
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
void clone(const Input & input, const Path & destDir) const override
|
2020-05-29 15:23:32 +03:00
|
|
|
{
|
2020-09-25 08:46:03 +03:00
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
|
2020-06-04 14:36:30 +03:00
|
|
|
// FIXME: get username somewhere
|
2021-09-14 23:53:31 +03:00
|
|
|
Input::fromURL(fmt("git+https://%s/%s/%s.git",
|
2020-09-25 08:46:03 +03:00
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
2021-09-14 14:38:45 +03:00
|
|
|
.applyOverrides(input.getRef(), input.getRev())
|
Remove TreeInfo
The attributes previously stored in TreeInfo (narHash, revCount,
lastModified) are now stored in Input. This makes it less arbitrary
what attributes are stored where.
As a result, the lock file format has changed. An entry like
"info": {
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github"
},
is now stored as
"locked": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be",
"type": "github",
"lastModified": 1585405475,
"narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE="
},
The 'Input' class is now a dumb set of attributes. All the fetcher
implementations subclass InputScheme, not Input. This simplifies the
API.
Also, fix substitution of flake inputs. This was broken since lazy
flake fetching started using fetchTree internally.
2020-05-30 01:44:11 +03:00
|
|
|
.clone(destDir);
|
2020-03-30 17:04:18 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-10-06 19:32:46 +03:00
|
|
|
struct SourceHutInputScheme : GitArchiveInputScheme
|
|
|
|
{
|
2023-10-30 16:14:27 +02:00
|
|
|
std::string_view schemeName() const override { return "sourcehut"; }
|
2021-10-06 19:32:46 +03:00
|
|
|
|
|
|
|
std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const override
|
|
|
|
{
|
|
|
|
// SourceHut supports both PAT and OAuth2. See
|
|
|
|
// https://man.sr.ht/meta.sr.ht/oauth.md
|
|
|
|
return std::pair<std::string, std::string>("Authorization", fmt("Bearer %s", token));
|
|
|
|
// Note: This currently serves no purpose, as this kind of authorization
|
|
|
|
// does not allow for downloading tarballs on sourcehut private repos.
|
|
|
|
// Once it is implemented, however, should work as expected.
|
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
RefInfo getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
2021-10-06 19:32:46 +03:00
|
|
|
{
|
|
|
|
// TODO: In the future, when the sourcehut graphql API is implemented for mercurial
|
|
|
|
// and with anonymous access, this method should use it instead.
|
|
|
|
|
|
|
|
auto ref = *input.getRef();
|
|
|
|
|
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
|
|
|
|
auto base_url = fmt("https://%s/%s/%s",
|
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"));
|
|
|
|
|
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
|
|
|
|
2022-06-11 22:52:20 +03:00
|
|
|
std::string refUri;
|
2021-10-06 19:32:46 +03:00
|
|
|
if (ref == "HEAD") {
|
|
|
|
auto file = store->toRealPath(
|
2024-04-05 17:35:12 +03:00
|
|
|
downloadFile(store, fmt("%s/HEAD", base_url), "source", headers).storePath);
|
2021-10-06 19:32:46 +03:00
|
|
|
std::ifstream is(file);
|
|
|
|
std::string line;
|
|
|
|
getline(is, line);
|
|
|
|
|
2022-05-04 15:32:21 +03:00
|
|
|
auto remoteLine = git::parseLsRemoteLine(line);
|
|
|
|
if (!remoteLine) {
|
2021-10-06 19:32:46 +03:00
|
|
|
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
|
|
|
|
}
|
2022-06-11 22:52:20 +03:00
|
|
|
refUri = remoteLine->target;
|
2022-04-30 01:30:00 +03:00
|
|
|
} else {
|
2022-06-11 22:52:20 +03:00
|
|
|
refUri = fmt("refs/(heads|tags)/%s", ref);
|
2022-04-30 01:30:00 +03:00
|
|
|
}
|
2022-06-11 22:52:20 +03:00
|
|
|
std::regex refRegex(refUri);
|
2021-10-06 19:32:46 +03:00
|
|
|
|
|
|
|
auto file = store->toRealPath(
|
2024-04-05 17:35:12 +03:00
|
|
|
downloadFile(store, fmt("%s/info/refs", base_url), "source", headers).storePath);
|
2021-10-06 19:32:46 +03:00
|
|
|
std::ifstream is(file);
|
|
|
|
|
|
|
|
std::string line;
|
2022-04-30 01:30:00 +03:00
|
|
|
std::optional<std::string> id;
|
|
|
|
while(!id && getline(is, line)) {
|
2022-05-04 15:32:21 +03:00
|
|
|
auto parsedLine = git::parseLsRemoteLine(line);
|
2022-06-11 22:52:20 +03:00
|
|
|
if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex))
|
2022-05-04 15:32:21 +03:00
|
|
|
id = parsedLine->target;
|
2021-10-06 19:32:46 +03:00
|
|
|
}
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
if (!id)
|
2021-10-06 19:32:46 +03:00
|
|
|
throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
|
|
|
|
|
2023-11-29 13:35:08 +02:00
|
|
|
return RefInfo {
|
2023-12-14 14:31:29 +02:00
|
|
|
.rev = Hash::parseAny(*id, HashAlgorithm::SHA1)
|
2023-11-29 13:35:08 +02:00
|
|
|
};
|
2021-10-06 19:32:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
DownloadUrl getDownloadUrl(const Input & input) const override
|
|
|
|
{
|
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
|
|
|
|
auto url = fmt("https://%s/%s/%s/archive/%s.tar.gz",
|
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
|
2023-10-13 04:48:15 +03:00
|
|
|
input.getRev()->to_string(HashFormat::Base16, false));
|
2021-10-06 19:32:46 +03:00
|
|
|
|
|
|
|
Headers headers = makeHeadersWithAuthTokens(host);
|
|
|
|
return DownloadUrl { url, headers };
|
|
|
|
}
|
|
|
|
|
2022-12-07 13:58:58 +02:00
|
|
|
void clone(const Input & input, const Path & destDir) const override
|
2021-10-06 19:32:46 +03:00
|
|
|
{
|
|
|
|
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
|
|
|
|
Input::fromURL(fmt("git+https://%s/%s/%s",
|
|
|
|
host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
|
|
|
|
.applyOverrides(input.getRef(), input.getRev())
|
|
|
|
.clone(destDir);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
|
|
|
|
static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });
|
2021-10-06 19:32:46 +03:00
|
|
|
static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique<SourceHutInputScheme>()); });
|
2020-03-30 17:04:18 +03:00
|
|
|
|
|
|
|
}
|