aboutsummaryrefslogtreecommitdiff
path: root/src/libfetchers
diff options
context:
space:
mode:
authorBen Burdette <bburdette@protonmail.com>2022-04-07 13:42:01 -0600
committerBen Burdette <bburdette@protonmail.com>2022-04-07 13:42:01 -0600
commit1a93ac8133381eb692416c4e46b1706faa5cd89f (patch)
tree9a559f977ad6213c055099f6f2ab6be96f0c551b /src/libfetchers
parentd2ec9b4e15718e42720787140d7825dcbfd73249 (diff)
parent8b1e328d5d0ae7d3a4a8f6012ec065b59674ed4a (diff)
Merge remote-tracking branch 'upstream/master' into upstream-merge
Diffstat (limited to 'src/libfetchers')
-rw-r--r--src/libfetchers/cache.cc8
-rw-r--r--src/libfetchers/cache.hh2
-rw-r--r--src/libfetchers/fetch-settings.cc13
-rw-r--r--src/libfetchers/fetch-settings.hh93
-rw-r--r--src/libfetchers/fetchers.cc20
-rw-r--r--src/libfetchers/fetchers.hh15
-rw-r--r--src/libfetchers/git.cc96
-rw-r--r--src/libfetchers/github.cc114
-rw-r--r--src/libfetchers/indirect.cc2
-rw-r--r--src/libfetchers/mercurial.cc38
-rw-r--r--src/libfetchers/path.cc21
-rw-r--r--src/libfetchers/registry.cc4
-rw-r--r--src/libfetchers/tarball.cc22
13 files changed, 335 insertions, 113 deletions
diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc
index 34ff6f85b..0c8ecac9d 100644
--- a/src/libfetchers/cache.cc
+++ b/src/libfetchers/cache.cc
@@ -52,13 +52,13 @@ struct CacheImpl : Cache
const Attrs & inAttrs,
const Attrs & infoAttrs,
const StorePath & storePath,
- bool immutable) override
+ bool locked) override
{
_state.lock()->add.use()
(attrsToJSON(inAttrs).dump())
(attrsToJSON(infoAttrs).dump())
(store->printStorePath(storePath))
- (immutable)
+ (locked)
(time(0)).exec();
}
@@ -91,7 +91,7 @@ struct CacheImpl : Cache
auto infoJSON = stmt.getStr(0);
auto storePath = store->parseStorePath(stmt.getStr(1));
- auto immutable = stmt.getInt(2) != 0;
+ auto locked = stmt.getInt(2) != 0;
auto timestamp = stmt.getInt(3);
store->addTempRoot(storePath);
@@ -105,7 +105,7 @@ struct CacheImpl : Cache
inAttrsJSON, infoJSON, store->printStorePath(storePath));
return Result {
- .expired = !immutable && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
+ .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
.infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)),
.storePath = std::move(storePath)
};
diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh
index 3db4f081c..3763ee2a6 100644
--- a/src/libfetchers/cache.hh
+++ b/src/libfetchers/cache.hh
@@ -13,7 +13,7 @@ struct Cache
const Attrs & inAttrs,
const Attrs & infoAttrs,
const StorePath & storePath,
- bool immutable) = 0;
+ bool locked) = 0;
virtual std::optional<std::pair<Attrs, StorePath>> lookup(
ref<Store> store,
diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc
new file mode 100644
index 000000000..e7d5244dc
--- /dev/null
+++ b/src/libfetchers/fetch-settings.cc
@@ -0,0 +1,13 @@
+#include "fetch-settings.hh"
+
+namespace nix {
+
+FetchSettings::FetchSettings()
+{
+}
+
+FetchSettings fetchSettings;
+
+static GlobalConfig::Register rFetchSettings(&fetchSettings);
+
+}
diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh
new file mode 100644
index 000000000..04c9feda0
--- /dev/null
+++ b/src/libfetchers/fetch-settings.hh
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "types.hh"
+#include "config.hh"
+#include "util.hh"
+
+#include <map>
+#include <limits>
+
+#include <sys/types.h>
+
+namespace nix {
+
+struct FetchSettings : public Config
+{
+ FetchSettings();
+
+ Setting<StringMap> accessTokens{this, {}, "access-tokens",
+ R"(
+ Access tokens used to access protected GitHub, GitLab, or
+ other locations requiring token-based authentication.
+
+ Access tokens are specified as a string made up of
+ space-separated `host=token` values. The specific token
+ used is selected by matching the `host` portion against the
+ "host" specification of the input. The actual use of the
+ `token` value is determined by the type of resource being
+ accessed:
+
+ * Github: the token value is the OAUTH-TOKEN string obtained
+ as the Personal Access Token from the Github server (see
+ https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps).
+
+ * Gitlab: the token value is either the OAuth2 token or the
+ Personal Access Token (these are different types tokens
+ for gitlab, see
+ https://docs.gitlab.com/12.10/ee/api/README.html#authentication).
+ The `token` value should be `type:tokenstring` where
+ `type` is either `OAuth2` or `PAT` to indicate which type
+ of token is being specified.
+
+ Example `~/.config/nix/nix.conf`:
+
+ ```
+ access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk
+ ```
+
+ Example `~/code/flake.nix`:
+
+ ```nix
+ input.foo = {
+ type = "gitlab";
+ host = "gitlab.mycompany.com";
+ owner = "mycompany";
+ repo = "pro";
+ };
+ ```
+
+ This example specifies three tokens, one each for accessing
+ github.com, gitlab.mycompany.com, and sourceforge.net.
+
+ The `input.foo` uses the "gitlab" fetcher, which might
+ requires specifying the token type along with the token
+ value.
+ )"};
+
+ Setting<bool> allowDirty{this, true, "allow-dirty",
+ "Whether to allow dirty Git/Mercurial trees."};
+
+ Setting<bool> warnDirty{this, true, "warn-dirty",
+ "Whether to warn about dirty Git/Mercurial trees."};
+
+ Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
+ "Path or URI of the global flake registry."};
+
+ Setting<bool> useRegistries{this, true, "use-registries",
+ "Whether to use flake registries to resolve flake references."};
+
+ Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
+ "Whether to accept nix configuration from a flake without prompting."};
+
+ Setting<std::string> commitLockFileSummary{
+ this, "", "commit-lockfile-summary",
+ R"(
+ The commit summary to use when committing changed flake lock files. If
+ empty, the summary is generated based on the action performed.
+ )"};
+};
+
+// FIXME: don't use a global variable.
+extern FetchSettings fetchSettings;
+
+}
diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc
index e158d914b..976f40d3b 100644
--- a/src/libfetchers/fetchers.cc
+++ b/src/libfetchers/fetchers.cc
@@ -24,11 +24,11 @@ static void fixupInput(Input & input)
input.getType();
input.getRef();
if (input.getRev())
- input.immutable = true;
+ input.locked = true;
input.getRevCount();
input.getLastModified();
if (input.getNarHash())
- input.immutable = true;
+ input.locked = true;
}
Input Input::fromURL(const ParsedURL & url)
@@ -124,15 +124,13 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
debug("using substituted/cached input '%s' in '%s'",
to_string(), store->printStorePath(storePath));
- auto actualPath = store->toRealPath(storePath);
-
- return {fetchers::Tree(std::move(actualPath), std::move(storePath)), *this};
+ return {Tree { .actualPath = store->toRealPath(storePath), .storePath = std::move(storePath) }, *this};
} catch (Error & e) {
debug("substitution of input '%s' failed: %s", to_string(), e.what());
}
}
- auto [tree, input] = [&]() -> std::pair<Tree, Input> {
+ auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
try {
return scheme->fetch(store, *this);
} catch (Error & e) {
@@ -141,8 +139,10 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
}
}();
- if (tree.actualPath == "")
- tree.actualPath = store->toRealPath(tree.storePath);
+ Tree tree {
+ .actualPath = store->toRealPath(storePath),
+ .storePath = storePath,
+ };
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true));
@@ -165,7 +165,7 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
input.to_string(), *prevRevCount);
}
- input.immutable = true;
+ input.locked = true;
assert(input.hasAllInfo());
@@ -209,7 +209,7 @@ StorePath Input::computeStorePath(Store & store) const
{
auto narHash = getNarHash();
if (!narHash)
- throw Error("cannot compute store path for mutable input '%s'", to_string());
+ throw Error("cannot compute store path for unlocked input '%s'", to_string());
return store.makeFixedOutputPath(FileIngestionMethod::Recursive, *narHash, getName());
}
diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh
index c43b047a7..bc9a76b0b 100644
--- a/src/libfetchers/fetchers.hh
+++ b/src/libfetchers/fetchers.hh
@@ -16,7 +16,6 @@ struct Tree
{
Path actualPath;
StorePath storePath;
- Tree(Path && actualPath, StorePath && storePath) : actualPath(actualPath), storePath(std::move(storePath)) {}
};
struct InputScheme;
@@ -35,7 +34,7 @@ struct Input
std::shared_ptr<InputScheme> scheme; // note: can be null
Attrs attrs;
- bool immutable = false;
+ bool locked = false;
bool direct = true;
/* path of the parent of this input, used for relative path resolution */
@@ -60,9 +59,9 @@ public:
one that goes through a registry. */
bool isDirect() const { return direct; }
- /* Check whether this is an "immutable" input, that is,
+ /* Check whether this is a "locked" input, that is,
one that contains a commit hash or content hash. */
- bool isImmutable() const { return immutable; }
+ bool isLocked() const { return locked; }
bool hasAllInfo() const;
@@ -70,6 +69,8 @@ public:
bool contains(const Input & other) const;
+ /* Fetch the input into the Nix store, returning the location in
+ the Nix store and the locked input. */
std::pair<Tree, Input> fetch(ref<Store> store) const;
Input applyOverrides(
@@ -131,7 +132,7 @@ struct InputScheme
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
- virtual std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) = 0;
+ virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
};
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
@@ -147,14 +148,14 @@ DownloadFileResult downloadFile(
ref<Store> store,
const std::string & url,
const std::string & name,
- bool immutable,
+ bool locked,
const Headers & headers = {});
std::pair<Tree, time_t> downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,
- bool immutable,
+ bool locked,
const Headers & headers = {});
}
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index 544d2ffbf..f8433bc28 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -6,6 +6,8 @@
#include "url-parts.hh"
#include "pathlocks.hh"
+#include "fetch-settings.hh"
+
#include <sys/time.h>
#include <sys/wait.h>
@@ -172,7 +174,7 @@ struct GitInputScheme : InputScheme
return {isLocal, isLocal ? url.path : url.base};
}
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -187,7 +189,7 @@ struct GitInputScheme : InputScheme
if (submodules) cacheType += "-submodules";
if (allRefs) cacheType += "-all-refs";
- auto getImmutableAttrs = [&]()
+ auto getLockedAttrs = [&]()
{
return Attrs({
{"type", cacheType},
@@ -197,21 +199,18 @@ struct GitInputScheme : InputScheme
};
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
- -> std::pair<Tree, Input>
+ -> std::pair<StorePath, Input>
{
assert(input.getRev());
assert(!_input.getRev() || _input.getRev() == input.getRev());
if (!shallow)
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified"));
- return {
- Tree(store->toRealPath(storePath), std::move(storePath)),
- input
- };
+ return {std::move(storePath), input};
};
if (input.getRev()) {
- if (auto res = getCache()->lookup(store, getImmutableAttrs()))
+ if (auto res = getCache()->lookup(store, getLockedAttrs()))
return makeResult(res->first, std::move(res->second));
}
@@ -223,22 +222,46 @@ struct GitInputScheme : InputScheme
if (!input.getRef() && !input.getRev() && isLocal) {
bool clean = false;
- /* Check whether this repo has any commits. There are
- probably better ways to do this. */
- auto gitDir = actualUrl + "/.git";
- auto commonGitDir = chomp(runProgram(
- "git",
- true,
- { "-C", actualUrl, "rev-parse", "--git-common-dir" }
- ));
- if (commonGitDir != ".git")
- gitDir = commonGitDir;
-
- bool haveCommits = !readDirectory(gitDir + "/refs/heads").empty();
+ auto env = getEnv();
+ // Set LC_ALL to C: because we rely on the error messages from git rev-parse to determine what went wrong
+ // that way unknown errors can lead to a failure instead of continuing through the wrong code path
+ env["LC_ALL"] = "C";
+
+ /* Check whether HEAD points to something that looks like a commit,
+ since that is the refrence we want to use later on. */
+ auto result = runProgram(RunOptions {
+ .program = "git",
+ .args = { "-C", actualUrl, "--git-dir=.git", "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" },
+ .environment = env,
+ .mergeStderrToStdout = true
+ });
+ auto exitCode = WEXITSTATUS(result.first);
+ auto errorMessage = result.second;
+
+ if (errorMessage.find("fatal: not a git repository") != std::string::npos) {
+ throw Error("'%s' is not a Git repository", actualUrl);
+ } else if (errorMessage.find("fatal: Needed a single revision") != std::string::npos) {
+ // indicates that the repo does not have any commits
+ // we want to proceed and will consider it dirty later
+ } else if (exitCode != 0) {
+ // any other errors should lead to a failure
+ throw Error("getting the HEAD of the Git tree '%s' failed with exit code %d:\n%s", actualUrl, exitCode, errorMessage);
+ }
+ bool hasHead = exitCode == 0;
try {
- if (haveCommits) {
- runProgram("git", true, { "-C", actualUrl, "diff-index", "--quiet", "HEAD", "--" });
+ if (hasHead) {
+ // Using git diff is preferrable over lower-level operations here,
+ // because its conceptually simpler and we only need the exit code anyways.
+ auto gitDiffOpts = Strings({ "-C", actualUrl, "diff", "HEAD", "--quiet"});
+ if (!submodules) {
+ // Changes in submodules should only make the tree dirty
+ // when those submodules will be copied as well.
+ gitDiffOpts.emplace_back("--ignore-submodules");
+ }
+ gitDiffOpts.emplace_back("--");
+ runProgram("git", true, gitDiffOpts);
+
clean = true;
}
} catch (ExecError & e) {
@@ -249,10 +272,10 @@ struct GitInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked files. */
- if (!settings.allowDirty)
+ if (!fetchSettings.allowDirty)
throw Error("Git tree '%s' is dirty", actualUrl);
- if (settings.warnDirty)
+ if (fetchSettings.warnDirty)
warn("Git tree '%s' is dirty", actualUrl);
auto gitOpts = Strings({ "-C", actualUrl, "ls-files", "-z" });
@@ -262,9 +285,11 @@ struct GitInputScheme : InputScheme
auto files = tokenizeString<std::set<std::string>>(
runProgram("git", true, gitOpts), "\0"s);
+ Path actualPath(absPath(actualUrl));
+
PathFilter filter = [&](const Path & p) -> bool {
- assert(hasPrefix(p, actualUrl));
- std::string file(p, actualUrl.size() + 1);
+ assert(hasPrefix(p, actualPath));
+ std::string file(p, actualPath.size() + 1);
auto st = lstat(p);
@@ -277,24 +302,21 @@ struct GitInputScheme : InputScheme
return files.count(file);
};
- auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
+ auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter);
// FIXME: maybe we should use the timestamp of the last
// modified dirty file?
input.attrs.insert_or_assign(
"lastModified",
- haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
+ hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
- return {
- Tree(store->toRealPath(storePath), std::move(storePath)),
- input
- };
+ return {std::move(storePath), input};
}
}
if (!input.getRef()) input.attrs.insert_or_assign("ref", isLocal ? readHead(actualUrl) : "master");
- Attrs mutableAttrs({
+ Attrs unlockedAttrs({
{"type", cacheType},
{"name", name},
{"url", actualUrl},
@@ -313,7 +335,7 @@ struct GitInputScheme : InputScheme
} else {
- if (auto res = getCache()->lookup(store, mutableAttrs)) {
+ if (auto res = getCache()->lookup(store, unlockedAttrs)) {
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
@@ -410,7 +432,7 @@ struct GitInputScheme : InputScheme
/* Now that we know the ref, check again whether we have it in
the store. */
- if (auto res = getCache()->lookup(store, getImmutableAttrs()))
+ if (auto res = getCache()->lookup(store, getLockedAttrs()))
return makeResult(res->first, std::move(res->second));
Path tmpDir = createTempDir();
@@ -482,14 +504,14 @@ struct GitInputScheme : InputScheme
if (!_input.getRev())
getCache()->add(
store,
- mutableAttrs,
+ unlockedAttrs,
infoAttrs,
storePath,
false);
getCache()->add(
store,
- getImmutableAttrs(),
+ getLockedAttrs(),
infoAttrs,
storePath,
true);
diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc
index 1c539b80e..58b6e7c04 100644
--- a/src/libfetchers/github.cc
+++ b/src/libfetchers/github.cc
@@ -1,13 +1,16 @@
#include "filetransfer.hh"
#include "cache.hh"
-#include "fetchers.hh"
#include "globals.hh"
#include "store-api.hh"
#include "types.hh"
#include "url-parts.hh"
+#include "fetchers.hh"
+#include "fetch-settings.hh"
+
#include <optional>
#include <nlohmann/json.hpp>
+#include <fstream>
namespace nix::fetchers {
@@ -17,7 +20,7 @@ struct DownloadUrl
Headers headers;
};
-// A github or gitlab host
+// A github, gitlab, or sourcehut host
const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);
@@ -156,7 +159,7 @@ struct GitArchiveInputScheme : InputScheme
std::optional<std::string> getAccessToken(const std::string & host) const
{
- auto tokens = settings.accessTokens.get();
+ auto tokens = fetchSettings.accessTokens.get();
if (auto token = get(tokens, host))
return *token;
return {};
@@ -180,7 +183,7 @@ struct GitArchiveInputScheme : InputScheme
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -192,17 +195,14 @@ struct GitArchiveInputScheme : InputScheme
input.attrs.erase("ref");
input.attrs.insert_or_assign("rev", rev->gitRev());
- Attrs immutableAttrs({
+ Attrs lockedAttrs({
{"type", "git-tarball"},
{"rev", rev->gitRev()},
});
- if (auto res = getCache()->lookup(store, immutableAttrs)) {
+ if (auto res = getCache()->lookup(store, lockedAttrs)) {
input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
- return {
- Tree(store->toRealPath(res->second), std::move(res->second)),
- input
- };
+ return {std::move(res->second), input};
}
auto url = getDownloadUrl(input);
@@ -213,7 +213,7 @@ struct GitArchiveInputScheme : InputScheme
getCache()->add(
store,
- immutableAttrs,
+ lockedAttrs,
{
{"rev", rev->gitRev()},
{"lastModified", uint64_t(lastModified)}
@@ -221,7 +221,7 @@ struct GitArchiveInputScheme : InputScheme
tree.storePath,
true);
- return {std::move(tree), input};
+ return {std::move(tree.storePath), input};
}
};
@@ -348,7 +348,97 @@ struct GitLabInputScheme : GitArchiveInputScheme
}
};
+struct SourceHutInputScheme : GitArchiveInputScheme
+{
+ std::string type() override { return "sourcehut"; }
+
+ 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.
+ }
+
+ Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
+ {
+ // 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);
+
+ std::string ref_uri;
+ if (ref == "HEAD") {
+ auto file = store->toRealPath(
+ downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
+ std::ifstream is(file);
+ std::string line;
+ getline(is, line);
+
+ auto ref_index = line.find("ref: ");
+ if (ref_index == std::string::npos) {
+ throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
+ }
+
+ ref_uri = line.substr(ref_index+5, line.length()-1);
+ } else
+ ref_uri = fmt("refs/(heads|tags)/%s", ref);
+
+ auto file = store->toRealPath(
+ downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
+ std::ifstream is(file);
+
+ std::string line;
+ std::string id;
+ while(getline(is, line)) {
+ // Append $ to avoid partial name matches
+ std::regex pattern(fmt("%s$", ref_uri));
+
+ if (std::regex_search(line, pattern)) {
+ id = line.substr(0, line.find('\t'));
+ break;
+ }
+ }
+
+ if(id.empty())
+ throw BadURL("in '%d', couldn't find ref '%d'", input.to_string(), ref);
+
+ auto rev = Hash::parseAny(id, htSHA1);
+ debug("HEAD revision for '%s' is %s", fmt("%s/%s", base_url, ref), rev.gitRev());
+ return rev;
+ }
+
+ 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"),
+ input.getRev()->to_string(Base16, false));
+
+ Headers headers = makeHeadersWithAuthTokens(host);
+ return DownloadUrl { url, headers };
+ }
+
+ void clone(const Input & input, const Path & destDir) override
+ {
+ 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);
+ }
+};
+
static auto rGitHubInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
static auto rGitLabInputScheme = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });
+static auto rSourceHutInputScheme = OnStartup([] { registerInputScheme(std::make_unique<SourceHutInputScheme>()); });
}
diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc
index 10e59919a..9288fc6cf 100644
--- a/src/libfetchers/indirect.cc
+++ b/src/libfetchers/indirect.cc
@@ -94,7 +94,7 @@ struct IndirectInputScheme : InputScheme
return input;
}
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
{
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
}
diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc
index d52d4641b..8b82e9daa 100644
--- a/src/libfetchers/mercurial.cc
+++ b/src/libfetchers/mercurial.cc
@@ -5,6 +5,8 @@
#include "store-api.hh"
#include "url-parts.hh"
+#include "fetch-settings.hh"
+
#include <sys/time.h>
using namespace std::string_literals;
@@ -26,7 +28,7 @@ static RunOptions hgOptions(const Strings & args)
}
// runProgram wrapper that uses hgOptions instead of stock RunOptions.
-static string runHg(const Strings & args, const std::optional<std::string> & input = {})
+static std::string runHg(const Strings & args, const std::optional<std::string> & input = {})
{
RunOptions opts = hgOptions(args);
opts.input = input;
@@ -143,7 +145,7 @@ struct MercurialInputScheme : InputScheme
return {isLocal, isLocal ? url.path : url.base};
}
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
@@ -165,10 +167,10 @@ struct MercurialInputScheme : InputScheme
/* This is an unclean working tree. So copy all tracked
files. */
- if (!settings.allowDirty)
+ if (!fetchSettings.allowDirty)
throw Error("Mercurial tree '%s' is unclean", actualUrl);
- if (settings.warnDirty)
+ if (fetchSettings.warnDirty)
warn("Mercurial tree '%s' is unclean", actualUrl);
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));
@@ -193,16 +195,13 @@ struct MercurialInputScheme : InputScheme
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
- return {
- Tree(store->toRealPath(storePath), std::move(storePath)),
- input
- };
+ return {std::move(storePath), input};
}
}
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
- auto getImmutableAttrs = [&]()
+ auto getLockedAttrs = [&]()
{
return Attrs({
{"type", "hg"},
@@ -212,32 +211,29 @@ struct MercurialInputScheme : InputScheme
};
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
- -> std::pair<Tree, Input>
+ -> std::pair<StorePath, Input>
{
assert(input.getRev());
assert(!_input.getRev() || _input.getRev() == input.getRev());
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
- return {
- Tree(store->toRealPath(storePath), std::move(storePath)),
- input
- };
+ return {std::move(storePath), input};
};
if (input.getRev()) {
- if (auto res = getCache()->lookup(store, getImmutableAttrs()))
+ if (auto res = getCache()->lookup(store, getLockedAttrs()))
return makeResult(res->first, std::move(res->second));
}
auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef();
- Attrs mutableAttrs({
+ Attrs unlockedAttrs({
{"type", "hg"},
{"name", name},
{"url", actualUrl},
{"ref", *input.getRef()},
});
- if (auto res = getCache()->lookup(store, mutableAttrs)) {
+ if (auto res = getCache()->lookup(store, unlockedAttrs)) {
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
if (!input.getRev() || input.getRev() == rev2) {
input.attrs.insert_or_assign("rev", rev2.gitRev());
@@ -260,7 +256,7 @@ struct MercurialInputScheme : InputScheme
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
}
catch (ExecError & e) {
- string transJournal = cacheDir + "/.hg/store/journal";
+ auto transJournal = cacheDir + "/.hg/store/journal";
/* hg throws "abandoned transaction" error only if this file exists */
if (pathExists(transJournal)) {
runHg({ "recover", "-R", cacheDir });
@@ -283,7 +279,7 @@ struct MercurialInputScheme : InputScheme
auto revCount = std::stoull(tokens[1]);
input.attrs.insert_or_assign("ref", tokens[2]);
- if (auto res = getCache()->lookup(store, getImmutableAttrs()))
+ if (auto res = getCache()->lookup(store, getLockedAttrs()))
return makeResult(res->first, std::move(res->second));
Path tmpDir = createTempDir();
@@ -303,14 +299,14 @@ struct MercurialInputScheme : InputScheme
if (!_input.getRev())
getCache()->add(
store,
- mutableAttrs,
+ unlockedAttrs,
infoAttrs,
storePath,
false);
getCache()->add(
store,
- getImmutableAttrs(),
+ getLockedAttrs(),
infoAttrs,
storePath,
true);
diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc
index 07e543c53..f0ef97da5 100644
--- a/src/libfetchers/path.cc
+++ b/src/libfetchers/path.cc
@@ -1,5 +1,6 @@
#include "fetchers.hh"
#include "store-api.hh"
+#include "archive.hh"
namespace nix::fetchers {
@@ -80,8 +81,9 @@ struct PathInputScheme : InputScheme
// nothing to do
}
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
+ Input input(_input);
std::string absPath;
auto path = getStrAttr(input.attrs, "path");
@@ -111,14 +113,17 @@ struct PathInputScheme : InputScheme
if (storePath)
store->addTempRoot(*storePath);
- if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath))
+ time_t mtime = 0;
+ if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
// FIXME: try to substitute storePath.
- storePath = store->addToStore("source", absPath);
-
- return {
- Tree(store->toRealPath(*storePath), std::move(*storePath)),
- input
- };
+ auto src = sinkToSource([&](Sink & sink) {
+ mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
+ });
+ storePath = store->addToStoreFromDump(*src, "source");
+ }
+ input.attrs.insert_or_assign("lastModified", uint64_t(mtime));
+
+ return {std::move(*storePath), input};
}
};
diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc
index f35359d4b..acd1ff866 100644
--- a/src/libfetchers/registry.cc
+++ b/src/libfetchers/registry.cc
@@ -5,6 +5,8 @@
#include "store-api.hh"
#include "local-fs-store.hh"
+#include "fetch-settings.hh"
+
#include <nlohmann/json.hpp>
namespace nix::fetchers {
@@ -150,7 +152,7 @@ void overrideRegistry(
static std::shared_ptr<Registry> getGlobalRegistry(ref<Store> store)
{
static auto reg = [&]() {
- auto path = settings.flakeRegistry.get();
+ auto path = fetchSettings.flakeRegistry.get();
if (!hasPrefix(path, "/")) {
auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath;
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index c933475ca..dde0ad761 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -13,7 +13,7 @@ DownloadFileResult downloadFile(
ref<Store> store,
const std::string & url,
const std::string & name,
- bool immutable,
+ bool locked,
const Headers & headers)
{
// FIXME: check store
@@ -88,7 +88,7 @@ DownloadFileResult downloadFile(
inAttrs,
infoAttrs,
*storePath,
- immutable);
+ locked);
if (url != res.effectiveUri)
getCache()->add(
@@ -100,7 +100,7 @@ DownloadFileResult downloadFile(
},
infoAttrs,
*storePath,
- immutable);
+ locked);
return {
.storePath = std::move(*storePath),
@@ -113,7 +113,7 @@ std::pair<Tree, time_t> downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,
- bool immutable,
+ bool locked,
const Headers & headers)
{
Attrs inAttrs({
@@ -126,11 +126,11 @@ std::pair<Tree, time_t> downloadTarball(
if (cached && !cached->expired)
return {
- Tree(store->toRealPath(cached->storePath), std::move(cached->storePath)),
+ Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
getIntAttr(cached->infoAttrs, "lastModified")
};
- auto res = downloadFile(store, url, name, immutable, headers);
+ auto res = downloadFile(store, url, name, locked, headers);
std::optional<StorePath> unpackedStorePath;
time_t lastModified;
@@ -160,10 +160,10 @@ std::pair<Tree, time_t> downloadTarball(
inAttrs,
infoAttrs,
*unpackedStorePath,
- immutable);
+ locked);
return {
- Tree(store->toRealPath(*unpackedStorePath), std::move(*unpackedStorePath)),
+ Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
lastModified,
};
}
@@ -202,7 +202,7 @@ struct TarballInputScheme : InputScheme
Input input;
input.attrs = attrs;
- //input.immutable = (bool) maybeGetStrAttr(input.attrs, "hash");
+ //input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
return input;
}
@@ -225,10 +225,10 @@ struct TarballInputScheme : InputScheme
return true;
}
- std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
{
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
- return {std::move(tree), input};
+ return {std::move(tree.storePath), input};
}
};