mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 14:06:16 +02:00
Merge pull request #10044 from edolstra/empty-git-repos
Handle empty Git repositories / workdirs
This commit is contained in:
commit
d2c6a93bd5
8 changed files with 67 additions and 24 deletions
|
@ -467,13 +467,13 @@ EvalState::~EvalState()
|
||||||
void EvalState::allowPath(const Path & path)
|
void EvalState::allowPath(const Path & path)
|
||||||
{
|
{
|
||||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListInputAccessor>())
|
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListInputAccessor>())
|
||||||
rootFS2->allowPath(CanonPath(path));
|
rootFS2->allowPrefix(CanonPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowPath(const StorePath & storePath)
|
void EvalState::allowPath(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListInputAccessor>())
|
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListInputAccessor>())
|
||||||
rootFS2->allowPath(CanonPath(store->toRealPath(storePath)));
|
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
|
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
|
||||||
|
|
|
@ -51,33 +51,33 @@ void FilteringInputAccessor::checkAccess(const CanonPath & path)
|
||||||
|
|
||||||
struct AllowListInputAccessorImpl : AllowListInputAccessor
|
struct AllowListInputAccessorImpl : AllowListInputAccessor
|
||||||
{
|
{
|
||||||
std::set<CanonPath> allowedPaths;
|
std::set<CanonPath> allowedPrefixes;
|
||||||
|
|
||||||
AllowListInputAccessorImpl(
|
AllowListInputAccessorImpl(
|
||||||
ref<InputAccessor> next,
|
ref<InputAccessor> next,
|
||||||
std::set<CanonPath> && allowedPaths,
|
std::set<CanonPath> && allowedPrefixes,
|
||||||
MakeNotAllowedError && makeNotAllowedError)
|
MakeNotAllowedError && makeNotAllowedError)
|
||||||
: AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError))
|
: AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError))
|
||||||
, allowedPaths(std::move(allowedPaths))
|
, allowedPrefixes(std::move(allowedPrefixes))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
bool isAllowed(const CanonPath & path) override
|
bool isAllowed(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return path.isAllowed(allowedPaths);
|
return path.isAllowed(allowedPrefixes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void allowPath(CanonPath path) override
|
void allowPrefix(CanonPath prefix) override
|
||||||
{
|
{
|
||||||
allowedPaths.insert(std::move(path));
|
allowedPrefixes.insert(std::move(prefix));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<AllowListInputAccessor> AllowListInputAccessor::create(
|
ref<AllowListInputAccessor> AllowListInputAccessor::create(
|
||||||
ref<InputAccessor> next,
|
ref<InputAccessor> next,
|
||||||
std::set<CanonPath> && allowedPaths,
|
std::set<CanonPath> && allowedPrefixes,
|
||||||
MakeNotAllowedError && makeNotAllowedError)
|
MakeNotAllowedError && makeNotAllowedError)
|
||||||
{
|
{
|
||||||
return make_ref<AllowListInputAccessorImpl>(next, std::move(allowedPaths), std::move(makeNotAllowedError));
|
return make_ref<AllowListInputAccessorImpl>(next, std::move(allowedPrefixes), std::move(makeNotAllowedError));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CachingFilteringInputAccessor::isAllowed(const CanonPath & path)
|
bool CachingFilteringInputAccessor::isAllowed(const CanonPath & path)
|
||||||
|
|
|
@ -54,18 +54,19 @@ struct FilteringInputAccessor : InputAccessor
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapping `InputAccessor` that checks paths against an allow-list.
|
* A wrapping `InputAccessor` that checks paths against a set of
|
||||||
|
* allowed prefixes.
|
||||||
*/
|
*/
|
||||||
struct AllowListInputAccessor : public FilteringInputAccessor
|
struct AllowListInputAccessor : public FilteringInputAccessor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Grant access to the specified path.
|
* Grant access to the specified prefix.
|
||||||
*/
|
*/
|
||||||
virtual void allowPath(CanonPath path) = 0;
|
virtual void allowPrefix(CanonPath prefix) = 0;
|
||||||
|
|
||||||
static ref<AllowListInputAccessor> create(
|
static ref<AllowListInputAccessor> create(
|
||||||
ref<InputAccessor> next,
|
ref<InputAccessor> next,
|
||||||
std::set<CanonPath> && allowedPaths,
|
std::set<CanonPath> && allowedPrefixes,
|
||||||
MakeNotAllowedError && makeNotAllowedError);
|
MakeNotAllowedError && makeNotAllowedError);
|
||||||
|
|
||||||
using FilteringInputAccessor::FilteringInputAccessor;
|
using FilteringInputAccessor::FilteringInputAccessor;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "fs-input-accessor.hh"
|
#include "fs-input-accessor.hh"
|
||||||
#include "input-accessor.hh"
|
#include "input-accessor.hh"
|
||||||
#include "filtering-input-accessor.hh"
|
#include "filtering-input-accessor.hh"
|
||||||
|
#include "memory-input-accessor.hh"
|
||||||
#include "cache.hh"
|
#include "cache.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "processes.hh"
|
#include "processes.hh"
|
||||||
|
@ -942,17 +943,21 @@ ref<InputAccessor> GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore)
|
||||||
ref<InputAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
|
ref<InputAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
|
||||||
{
|
{
|
||||||
auto self = ref<GitRepoImpl>(shared_from_this());
|
auto self = ref<GitRepoImpl>(shared_from_this());
|
||||||
|
/* In case of an empty workdir, return an empty in-memory tree. We
|
||||||
|
cannot use AllowListInputAccessor because it would return an
|
||||||
|
error for the root (and we can't add the root to the allow-list
|
||||||
|
since that would allow access to all its children). */
|
||||||
ref<InputAccessor> fileAccessor =
|
ref<InputAccessor> fileAccessor =
|
||||||
AllowListInputAccessor::create(
|
wd.files.empty()
|
||||||
makeFSInputAccessor(path),
|
? makeEmptyInputAccessor()
|
||||||
std::set<CanonPath> { wd.files },
|
: AllowListInputAccessor::create(
|
||||||
std::move(makeNotAllowedError));
|
makeFSInputAccessor(path),
|
||||||
if (exportIgnore) {
|
std::set<CanonPath> { wd.files },
|
||||||
|
std::move(makeNotAllowedError)).cast<InputAccessor>();
|
||||||
|
if (exportIgnore)
|
||||||
return make_ref<GitExportIgnoreInputAccessor>(self, fileAccessor, std::nullopt);
|
return make_ref<GitExportIgnoreInputAccessor>(self, fileAccessor, std::nullopt);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
return fileAccessor;
|
return fileAccessor;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<GitFileSystemObjectSink> GitRepoImpl::getFileSystemObjectSink()
|
ref<GitFileSystemObjectSink> GitRepoImpl::getFileSystemObjectSink()
|
||||||
|
|
|
@ -158,6 +158,8 @@ std::vector<PublicKey> getPublicKeys(const Attrs & attrs)
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
||||||
|
static const Hash nullRev{HashAlgorithm::SHA1};
|
||||||
|
|
||||||
struct GitInputScheme : InputScheme
|
struct GitInputScheme : InputScheme
|
||||||
{
|
{
|
||||||
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
|
||||||
|
@ -708,10 +710,12 @@ struct GitInputScheme : InputScheme
|
||||||
if (auto ref = repo->getWorkdirRef())
|
if (auto ref = repo->getWorkdirRef())
|
||||||
input.attrs.insert_or_assign("ref", *ref);
|
input.attrs.insert_or_assign("ref", *ref);
|
||||||
|
|
||||||
auto rev = repoInfo.workdirInfo.headRev.value();
|
/* Return a rev of 000... if there are no commits yet. */
|
||||||
|
auto rev = repoInfo.workdirInfo.headRev.value_or(nullRev);
|
||||||
|
|
||||||
input.attrs.insert_or_assign("rev", rev.gitRev());
|
input.attrs.insert_or_assign("rev", rev.gitRev());
|
||||||
input.attrs.insert_or_assign("revCount", getRevCount(repoInfo, repoInfo.url, rev));
|
input.attrs.insert_or_assign("revCount",
|
||||||
|
rev == nullRev ? 0 : getRevCount(repoInfo, repoInfo.url, rev));
|
||||||
|
|
||||||
verifyCommit(input, repo);
|
verifyCommit(input, repo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,4 +20,10 @@ ref<MemoryInputAccessor> makeMemoryInputAccessor()
|
||||||
return make_ref<MemoryInputAccessorImpl>();
|
return make_ref<MemoryInputAccessorImpl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<InputAccessor> makeEmptyInputAccessor()
|
||||||
|
{
|
||||||
|
static auto empty = makeMemoryInputAccessor().cast<InputAccessor>();
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,6 @@ struct MemoryInputAccessor : InputAccessor
|
||||||
|
|
||||||
ref<MemoryInputAccessor> makeMemoryInputAccessor();
|
ref<MemoryInputAccessor> makeMemoryInputAccessor();
|
||||||
|
|
||||||
|
ref<InputAccessor> makeEmptyInputAccessor();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,3 +271,28 @@ git -C "$repo" add hello .gitignore
|
||||||
git -C "$repo" commit -m 'Bla1'
|
git -C "$repo" commit -m 'Bla1'
|
||||||
cd "$repo"
|
cd "$repo"
|
||||||
path11=$(nix eval --impure --raw --expr "(builtins.fetchGit ./.).outPath")
|
path11=$(nix eval --impure --raw --expr "(builtins.fetchGit ./.).outPath")
|
||||||
|
|
||||||
|
# Test a workdir with no commits.
|
||||||
|
empty="$TEST_ROOT/empty"
|
||||||
|
git init "$empty"
|
||||||
|
|
||||||
|
emptyAttrs='{ lastModified = 0; lastModifiedDate = "19700101000000"; narHash = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo="; rev = "0000000000000000000000000000000000000000"; revCount = 0; shortRev = "0000000"; submodules = false; }'
|
||||||
|
|
||||||
|
[[ $(nix eval --impure --expr "builtins.removeAttrs (builtins.fetchGit $empty) [\"outPath\"]") = $emptyAttrs ]]
|
||||||
|
|
||||||
|
echo foo > "$empty/x"
|
||||||
|
|
||||||
|
[[ $(nix eval --impure --expr "builtins.removeAttrs (builtins.fetchGit $empty) [\"outPath\"]") = $emptyAttrs ]]
|
||||||
|
|
||||||
|
git -C "$empty" add x
|
||||||
|
|
||||||
|
[[ $(nix eval --impure --expr "builtins.removeAttrs (builtins.fetchGit $empty) [\"outPath\"]") = '{ lastModified = 0; lastModifiedDate = "19700101000000"; narHash = "sha256-wzlAGjxKxpaWdqVhlq55q5Gxo4Bf860+kLeEa/v02As="; rev = "0000000000000000000000000000000000000000"; revCount = 0; shortRev = "0000000"; submodules = false; }' ]]
|
||||||
|
|
||||||
|
# Test a repo with an empty commit.
|
||||||
|
git -C "$empty" rm -f x
|
||||||
|
|
||||||
|
git -C "$empty" config user.email "foobar@example.com"
|
||||||
|
git -C "$empty" config user.name "Foobar"
|
||||||
|
git -C "$empty" commit --allow-empty --allow-empty-message --message ""
|
||||||
|
|
||||||
|
nix eval --impure --expr "let attrs = builtins.fetchGit $empty; in assert attrs.lastModified != 0; assert attrs.rev != \"0000000000000000000000000000000000000000\"; assert attrs.revCount == 1; true"
|
||||||
|
|
Loading…
Reference in a new issue