forked from lix-project/lix
Add date of last commit to SourceInfo
This is primarily useful for version string generation, where we need a monotonically increasing number. The revcount is the preferred thing to use, but isn't available for GitHub flakes (since it requires fetching the entire history). The last commit timestamp OTOH can be extracted from GitHub tarballs.
This commit is contained in:
parent
479757dc15
commit
0f840483c7
|
@ -19,7 +19,8 @@ let
|
||||||
releaseTools.sourceTarball {
|
releaseTools.sourceTarball {
|
||||||
name = "nix-tarball";
|
name = "nix-tarball";
|
||||||
version = builtins.readFile ./.version;
|
version = builtins.readFile ./.version;
|
||||||
versionSuffix = if officialRelease then "" else "pre${toString nix.revCount or 0}_${nix.shortRev or "0000000"}";
|
versionSuffix = if officialRelease then "" else
|
||||||
|
"pre${if nix ? lastModified then builtins.substring 0 8 nix.lastModified else toString nix.revCount or 0}_${nix.shortRev or "0000000"}";
|
||||||
src = nix;
|
src = nix;
|
||||||
inherit officialRelease;
|
inherit officialRelease;
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,9 @@ GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
|
|
||||||
gitInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
|
gitInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
|
||||||
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", uri, "rev-list", "--count", "HEAD" }));
|
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", uri, "rev-list", "--count", "HEAD" }));
|
||||||
|
// FIXME: maybe we should use the timestamp of the last
|
||||||
|
// modified dirty file?
|
||||||
|
gitInfo.lastModified = std::stoull(runProgram("git", true, { "-C", uri, "show", "-s", "--format=%ct", "HEAD" }));
|
||||||
|
|
||||||
return gitInfo;
|
return gitInfo;
|
||||||
}
|
}
|
||||||
|
@ -85,8 +88,9 @@ GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePath(getCacheDir() + "/nix/git");
|
deletePath(getCacheDir() + "/nix/git");
|
||||||
|
deletePath(getCacheDir() + "/nix/gitv2");
|
||||||
|
|
||||||
Path cacheDir = getCacheDir() + "/nix/gitv2/" + hashString(htSHA256, uri).to_string(Base32, false);
|
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, uri).to_string(Base32, false);
|
||||||
Path repoDir;
|
Path repoDir;
|
||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
|
@ -181,6 +185,7 @@ GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
if (store->isValidPath(storePath)) {
|
if (store->isValidPath(storePath)) {
|
||||||
gitInfo.storePath = storePath;
|
gitInfo.storePath = storePath;
|
||||||
gitInfo.revCount = json["revCount"];
|
gitInfo.revCount = json["revCount"];
|
||||||
|
gitInfo.lastModified = json["lastModified"];
|
||||||
return gitInfo;
|
return gitInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +205,7 @@ GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
gitInfo.storePath = store->addToStore(name, tmpDir);
|
gitInfo.storePath = store->addToStore(name, tmpDir);
|
||||||
|
|
||||||
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", repoDir, "rev-list", "--count", gitInfo.rev.gitRev() }));
|
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", repoDir, "rev-list", "--count", gitInfo.rev.gitRev() }));
|
||||||
|
gitInfo.lastModified = std::stoull(runProgram("git", true, { "-C", repoDir, "show", "-s", "--format=%ct", gitInfo.rev.gitRev() }));
|
||||||
|
|
||||||
nlohmann::json json;
|
nlohmann::json json;
|
||||||
json["storePath"] = gitInfo.storePath;
|
json["storePath"] = gitInfo.storePath;
|
||||||
|
@ -207,6 +213,7 @@ GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
json["name"] = name;
|
json["name"] = name;
|
||||||
json["rev"] = gitInfo.rev.gitRev();
|
json["rev"] = gitInfo.rev.gitRev();
|
||||||
json["revCount"] = gitInfo.revCount;
|
json["revCount"] = gitInfo.revCount;
|
||||||
|
json["lastModified"] = gitInfo.lastModified;
|
||||||
|
|
||||||
writeFile(storeLink, json.dump());
|
writeFile(storeLink, json.dump());
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ struct GitInfo
|
||||||
std::string ref;
|
std::string ref;
|
||||||
Hash rev{htSHA1};
|
Hash rev{htSHA1};
|
||||||
uint64_t revCount;
|
uint64_t revCount;
|
||||||
|
time_t lastModified;
|
||||||
};
|
};
|
||||||
|
|
||||||
GitInfo exportGit(ref<Store> store, std::string uri,
|
GitInfo exportGit(ref<Store> store, std::string uri,
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iomanip>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -232,6 +234,18 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & flakeRef, bool
|
||||||
if (evalSettings.pureEval && !impureIsAllowed && !resolvedRef.isImmutable())
|
if (evalSettings.pureEval && !impureIsAllowed && !resolvedRef.isImmutable())
|
||||||
throw Error("requested to fetch mutable flake '%s' in pure mode", resolvedRef);
|
throw Error("requested to fetch mutable flake '%s' in pure mode", resolvedRef);
|
||||||
|
|
||||||
|
auto doGit = [&](const GitInfo & gitInfo) {
|
||||||
|
FlakeRef ref(resolvedRef.baseRef());
|
||||||
|
ref.ref = gitInfo.ref;
|
||||||
|
ref.rev = gitInfo.rev;
|
||||||
|
SourceInfo info(ref);
|
||||||
|
info.storePath = gitInfo.storePath;
|
||||||
|
info.revCount = gitInfo.revCount;
|
||||||
|
info.narHash = state.store->queryPathInfo(info.storePath)->narHash;
|
||||||
|
info.lastModified = gitInfo.lastModified;
|
||||||
|
return info;
|
||||||
|
};
|
||||||
|
|
||||||
// This only downloads only one revision of the repo, not the entire history.
|
// This only downloads only one revision of the repo, not the entire history.
|
||||||
if (auto refData = std::get_if<FlakeRef::IsGitHub>(&resolvedRef.data)) {
|
if (auto refData = std::get_if<FlakeRef::IsGitHub>(&resolvedRef.data)) {
|
||||||
|
|
||||||
|
@ -270,29 +284,13 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & flakeRef, bool
|
||||||
|
|
||||||
// This downloads the entire git history
|
// This downloads the entire git history
|
||||||
else if (auto refData = std::get_if<FlakeRef::IsGit>(&resolvedRef.data)) {
|
else if (auto refData = std::get_if<FlakeRef::IsGit>(&resolvedRef.data)) {
|
||||||
auto gitInfo = exportGit(state.store, refData->uri, resolvedRef.ref, resolvedRef.rev, "source");
|
return doGit(exportGit(state.store, refData->uri, resolvedRef.ref, resolvedRef.rev, "source"));
|
||||||
FlakeRef ref(resolvedRef.baseRef());
|
|
||||||
ref.ref = gitInfo.ref;
|
|
||||||
ref.rev = gitInfo.rev;
|
|
||||||
SourceInfo info(ref);
|
|
||||||
info.storePath = gitInfo.storePath;
|
|
||||||
info.revCount = gitInfo.revCount;
|
|
||||||
info.narHash = state.store->queryPathInfo(info.storePath)->narHash;
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (auto refData = std::get_if<FlakeRef::IsPath>(&resolvedRef.data)) {
|
else if (auto refData = std::get_if<FlakeRef::IsPath>(&resolvedRef.data)) {
|
||||||
if (!pathExists(refData->path + "/.git"))
|
if (!pathExists(refData->path + "/.git"))
|
||||||
throw Error("flake '%s' does not reference a Git repository", refData->path);
|
throw Error("flake '%s' does not reference a Git repository", refData->path);
|
||||||
auto gitInfo = exportGit(state.store, refData->path, {}, {}, "source");
|
return doGit(exportGit(state.store, refData->path, {}, {}, "source"));
|
||||||
FlakeRef ref(resolvedRef.baseRef());
|
|
||||||
ref.ref = gitInfo.ref;
|
|
||||||
ref.rev = gitInfo.rev;
|
|
||||||
SourceInfo info(ref);
|
|
||||||
info.storePath = gitInfo.storePath;
|
|
||||||
info.revCount = gitInfo.revCount;
|
|
||||||
info.narHash = state.store->queryPathInfo(info.storePath)->narHash;
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else abort();
|
else abort();
|
||||||
|
@ -529,6 +527,11 @@ static void emitSourceInfoAttrs(EvalState & state, const SourceInfo & sourceInfo
|
||||||
|
|
||||||
if (sourceInfo.revCount)
|
if (sourceInfo.revCount)
|
||||||
mkInt(*state.allocAttr(vAttrs, state.symbols.create("revCount")), *sourceInfo.revCount);
|
mkInt(*state.allocAttr(vAttrs, state.symbols.create("revCount")), *sourceInfo.revCount);
|
||||||
|
|
||||||
|
if (sourceInfo.lastModified)
|
||||||
|
mkString(*state.allocAttr(vAttrs, state.symbols.create("lastModified")),
|
||||||
|
fmt("%s",
|
||||||
|
std::put_time(std::gmtime(&*sourceInfo.lastModified), "%Y%m%d%H%M%S")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void callFlake(EvalState & state, const ResolvedFlake & resFlake, Value & v)
|
void callFlake(EvalState & state, const ResolvedFlake & resFlake, Value & v)
|
||||||
|
|
|
@ -81,10 +81,22 @@ void writeRegistry(const FlakeRegistry &, const Path &);
|
||||||
|
|
||||||
struct SourceInfo
|
struct SourceInfo
|
||||||
{
|
{
|
||||||
|
// Immutable flakeref that this source tree was obtained from.
|
||||||
FlakeRef resolvedRef;
|
FlakeRef resolvedRef;
|
||||||
|
|
||||||
Path storePath;
|
Path storePath;
|
||||||
|
|
||||||
|
// Number of ancestors of the most recent commit.
|
||||||
std::optional<uint64_t> revCount;
|
std::optional<uint64_t> revCount;
|
||||||
Hash narHash; // store path hash
|
|
||||||
|
// NAR hash of the store path.
|
||||||
|
Hash narHash;
|
||||||
|
|
||||||
|
// A stable timestamp of this source tree. For Git and GitHub
|
||||||
|
// flakes, the commit date (not author date!) of the most recent
|
||||||
|
// commit.
|
||||||
|
std::optional<time_t> lastModified;
|
||||||
|
|
||||||
SourceInfo(const FlakeRef & resolvRef) : resolvedRef(resolvRef) {};
|
SourceInfo(const FlakeRef & resolvRef) : resolvedRef(resolvRef) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -72,14 +73,17 @@ struct CmdFlakeList : EvalCommand
|
||||||
|
|
||||||
static void printSourceInfo(const SourceInfo & sourceInfo)
|
static void printSourceInfo(const SourceInfo & sourceInfo)
|
||||||
{
|
{
|
||||||
std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string());
|
std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string());
|
||||||
if (sourceInfo.resolvedRef.ref)
|
if (sourceInfo.resolvedRef.ref)
|
||||||
std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref);
|
std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref);
|
||||||
if (sourceInfo.resolvedRef.rev)
|
if (sourceInfo.resolvedRef.rev)
|
||||||
std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false));
|
std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false));
|
||||||
if (sourceInfo.revCount)
|
if (sourceInfo.revCount)
|
||||||
std::cout << fmt("Revcount: %s\n", *sourceInfo.revCount);
|
std::cout << fmt("Revisions: %s\n", *sourceInfo.revCount);
|
||||||
std::cout << fmt("Path: %s\n", sourceInfo.storePath);
|
if (sourceInfo.lastModified)
|
||||||
|
std::cout << fmt("Last modified: %s\n",
|
||||||
|
std::put_time(std::localtime(&*sourceInfo.lastModified), "%F %T"));
|
||||||
|
std::cout << fmt("Path: %s\n", sourceInfo.storePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j)
|
static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j)
|
||||||
|
@ -91,14 +95,16 @@ static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j)
|
||||||
j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false);
|
j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false);
|
||||||
if (sourceInfo.revCount)
|
if (sourceInfo.revCount)
|
||||||
j["revCount"] = *sourceInfo.revCount;
|
j["revCount"] = *sourceInfo.revCount;
|
||||||
|
if (sourceInfo.lastModified)
|
||||||
|
j["lastModified"] = *sourceInfo.lastModified;
|
||||||
j["path"] = sourceInfo.storePath;
|
j["path"] = sourceInfo.storePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printFlakeInfo(const Flake & flake)
|
static void printFlakeInfo(const Flake & flake)
|
||||||
{
|
{
|
||||||
std::cout << fmt("ID: %s\n", flake.id);
|
std::cout << fmt("ID: %s\n", flake.id);
|
||||||
std::cout << fmt("Description: %s\n", flake.description);
|
std::cout << fmt("Description: %s\n", flake.description);
|
||||||
std::cout << fmt("Epoch: %s\n", flake.epoch);
|
std::cout << fmt("Epoch: %s\n", flake.epoch);
|
||||||
printSourceInfo(flake.sourceInfo);
|
printSourceInfo(flake.sourceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +120,7 @@ static nlohmann::json flakeToJson(const Flake & flake)
|
||||||
|
|
||||||
static void printNonFlakeInfo(const NonFlake & nonFlake)
|
static void printNonFlakeInfo(const NonFlake & nonFlake)
|
||||||
{
|
{
|
||||||
std::cout << fmt("ID: %s\n", nonFlake.alias);
|
std::cout << fmt("ID: %s\n", nonFlake.alias);
|
||||||
printSourceInfo(nonFlake.sourceInfo);
|
printSourceInfo(nonFlake.sourceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,7 @@ nix flake info --flake-registry $registry $flake1Dir | grep -q 'ID: *flake1'
|
||||||
json=$(nix flake info --flake-registry $registry flake1 --json | jq .)
|
json=$(nix flake info --flake-registry $registry flake1 --json | jq .)
|
||||||
[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]]
|
[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]]
|
||||||
[[ -d $(echo "$json" | jq -r .path) ]]
|
[[ -d $(echo "$json" | jq -r .path) ]]
|
||||||
|
[[ $(echo "$json" | jq -r .lastModified) = $(git -C $flake1Dir log -n1 --format=%ct) ]]
|
||||||
|
|
||||||
# Test 'nix build' on a flake.
|
# Test 'nix build' on a flake.
|
||||||
nix build -o $TEST_ROOT/result --flake-registry $registry flake1:foo
|
nix build -o $TEST_ROOT/result --flake-registry $registry flake1:foo
|
||||||
|
|
Loading…
Reference in a new issue