Add fetchgit builtin

The function builtins.fetchgit fetches Git repositories at evaluation
time, similar to builtins.fetchTarball. (Perhaps the name should be
changed, being confusing with respect to Nixpkgs's fetchgit function,
with works at build time.)

Example:

  (import (builtins.fetchgit git://github.com/NixOS/nixpkgs) {}).hello

or

  (import (builtins.fetchgit {
    url = git://github.com/NixOS/nixpkgs-channels;
    rev = "nixos-16.03";
  }) {}).hello

Note that the result does not contain a .git directory.
This commit is contained in:
Eelco Dolstra 2016-04-29 20:14:44 +02:00
parent 83258225e6
commit 38539b943a
2 changed files with 78 additions and 1 deletions

View file

@ -0,0 +1,77 @@
#include "primops.hh"
#include "eval-inline.hh"
#include "download.hh"
#include "store-api.hh"
namespace nix {
static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
// FIXME: cut&paste from fetch().
if (state.restricted) throw Error("fetchgit is not allowed in restricted mode");
std::string url;
std::string rev = "master";
state.forceValue(*args[0]);
if (args[0]->type == tAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
string name(attr.name);
if (name == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (name == "rev")
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError(format("unsupported argument %1% to fetchgit, at %3%") % attr.name % attr.pos);
}
if (url.empty())
throw EvalError(format("url argument required, at %1%") % pos);
} else
url = state.forceStringNoCtx(*args[0], pos);
if (!isUri(url))
throw EvalError(format("%s is not a valid URI, at %s") % url % pos);
Path cacheDir = getCacheDir() + "/nix/git";
if (!pathExists(cacheDir)) {
createDirs(cacheDir);
runProgram("git", true, { "init", "--bare", cacheDir });
}
Activity act(*logger, lvlInfo, format("fetching Git repository %s") % url);
std::string localRef = "pid-" + std::to_string(getpid());
Path localRefFile = cacheDir + "/refs/heads/" + localRef;
runProgram("git", true, { "-C", cacheDir, "fetch", url, rev + ":" + localRef });
std::string commitHash = chomp(readFile(localRefFile));
unlink(localRefFile.c_str());
debug(format("got revision %s") % commitHash);
// FIXME: should pipe this, or find some better way to extract a
// revision.
auto tar = runProgram("git", true, { "-C", cacheDir, "archive", commitHash });
Path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runProgram("tar", true, { "x", "-C", tmpDir }, tar);
Path storePath = state.store->addToStore("git-export", tmpDir);
mkString(v, storePath, PathSet({storePath}));
}
static RegisterPrimOp r("__fetchgit", 1, prim_fetchgit);
}

View file

@ -313,7 +313,7 @@ bool isUri(const string & s)
size_t pos = s.find("://");
if (pos == string::npos) return false;
string scheme(s, 0, pos);
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel";
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git";
}