Merge remote-tracking branch 'upstream/master' into errors-phase-2
This commit is contained in:
commit
4335ba999b
|
@ -282,7 +282,10 @@ struct GitInput : Input
|
|||
// FIXME: git stderr messes up our progress indicator, so
|
||||
// we're using --quiet for now. Should process its stderr.
|
||||
try {
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", *input->ref, *input->ref) });
|
||||
auto fetchRef = input->ref->compare(0, 5, "refs/") == 0
|
||||
? *input->ref
|
||||
: "refs/heads/" + *input->ref;
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) });
|
||||
} catch (Error & e) {
|
||||
if (!pathExists(localRefFile)) throw;
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", actualUrl);
|
||||
|
@ -418,7 +421,7 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
auto input = std::make_unique<GitInput>(parseURL(getStrAttr(attrs, "url")));
|
||||
if (auto ref = maybeGetStrAttr(attrs, "ref")) {
|
||||
if (!std::regex_match(*ref, refRegex))
|
||||
if (std::regex_search(*ref, badGitRefRegex))
|
||||
throw BadURL("invalid Git branch/tag name '%s'", *ref);
|
||||
input->ref = *ref;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,8 @@ DownloadFileResult downloadFile(
|
|||
info.narHash = hashString(htSHA256, *sink.s);
|
||||
info.narSize = sink.s->size();
|
||||
info.ca = makeFixedOutputCA(FileIngestionMethod::Flat, hash);
|
||||
store->addToStore(info, sink.s, NoRepair, NoCheckSigs);
|
||||
auto source = StringSource { *sink.s };
|
||||
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
||||
storePath = std::move(info.path);
|
||||
}
|
||||
|
||||
|
|
|
@ -113,9 +113,12 @@ void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
|||
diskCache->upsertNarInfo(getUri(), hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
}
|
||||
|
||||
void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
// FIXME: See if we can use the original source to reduce memory usage.
|
||||
auto nar = make_ref<std::string>(narSource.drain());
|
||||
|
||||
if (!repair && isValidPath(info.path)) return;
|
||||
|
||||
/* Verify that all references are valid. This may do some .narinfo
|
||||
|
@ -347,7 +350,8 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
|||
|
||||
ValidPathInfo info(makeFixedOutputPath(method, h, name));
|
||||
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
auto source = StringSource { *sink.s };
|
||||
addToStore(info, source, repair, CheckSigs, nullptr);
|
||||
|
||||
return std::move(info.path);
|
||||
}
|
||||
|
@ -361,7 +365,8 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
|||
if (repair || !isValidPath(info.path)) {
|
||||
StringSink sink;
|
||||
dumpString(s, sink);
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
auto source = StringSource { *sink.s };
|
||||
addToStore(info, source, repair, CheckSigs, nullptr);
|
||||
}
|
||||
|
||||
return std::move(info.path);
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||
{ unsupported("queryPathFromHashPart"); }
|
||||
|
||||
void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
void addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "serialise.hh"
|
||||
#include "store-api.hh"
|
||||
#include "archive.hh"
|
||||
#include "worker-protocol.hh"
|
||||
|
@ -100,7 +101,9 @@ StorePaths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> acces
|
|||
if (readInt(source) == 1)
|
||||
readString(source);
|
||||
|
||||
addToStore(info, tee.source.data, NoRepair, checkSigs, accessor);
|
||||
// Can't use underlying source, which would have been exhausted
|
||||
auto source = StringSource { *tee.source.data };
|
||||
addToStore(info, source, NoRepair, checkSigs, accessor);
|
||||
|
||||
res.push_back(info.path.clone());
|
||||
}
|
||||
|
|
|
@ -844,21 +844,6 @@ std::string makeFixedOutputCA(FileIngestionMethod recursive, const Hash & hash)
|
|||
}
|
||||
|
||||
|
||||
void Store::addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
addToStore(info, make_ref<std::string>(narSource.drain()), repair, checkSigs, accessor);
|
||||
}
|
||||
|
||||
void Store::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
StringSource source(*nar);
|
||||
addToStore(info, source, repair, checkSigs, accessor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -450,12 +450,7 @@ public:
|
|||
/* Import a path into the store. */
|
||||
virtual void addToStore(const ValidPathInfo & info, Source & narSource,
|
||||
RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,
|
||||
std::shared_ptr<FSAccessor> accessor = 0);
|
||||
|
||||
// FIXME: remove
|
||||
virtual void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,
|
||||
std::shared_ptr<FSAccessor> accessor = 0);
|
||||
std::shared_ptr<FSAccessor> accessor = 0) = 0;
|
||||
|
||||
/* Copy the contents of a path to the store and register the
|
||||
validity the resulting path. The resulting path is returned.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
|
|
130
src/libutil/tests/lru-cache.cc
Normal file
130
src/libutil/tests/lru-cache.cc
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include "lru-cache.hh"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* size
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(LRUCache, sizeOfEmptyCacheIsZero) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
ASSERT_EQ(c.size(), 0);
|
||||
}
|
||||
|
||||
TEST(LRUCache, sizeOfSingleElementCacheIsOne) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("foo", "bar");
|
||||
ASSERT_EQ(c.size(), 1);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* upsert / get
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(LRUCache, getFromEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
auto val = c.get("x");
|
||||
ASSERT_EQ(val.has_value(), false);
|
||||
}
|
||||
|
||||
TEST(LRUCache, getExistingValue) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("foo", "bar");
|
||||
auto val = c.get("foo");
|
||||
ASSERT_EQ(val, "bar");
|
||||
}
|
||||
|
||||
TEST(LRUCache, getNonExistingValueFromNonEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("foo", "bar");
|
||||
auto val = c.get("another");
|
||||
ASSERT_EQ(val.has_value(), false);
|
||||
}
|
||||
|
||||
TEST(LRUCache, upsertOnZeroCapacityCache) {
|
||||
LRUCache<std::string, std::string> c(0);
|
||||
c.upsert("foo", "bar");
|
||||
auto val = c.get("foo");
|
||||
ASSERT_EQ(val.has_value(), false);
|
||||
}
|
||||
|
||||
TEST(LRUCache, updateExistingValue) {
|
||||
LRUCache<std::string, std::string> c(1);
|
||||
c.upsert("foo", "bar");
|
||||
|
||||
auto val = c.get("foo");
|
||||
ASSERT_EQ(val.value_or("error"), "bar");
|
||||
ASSERT_EQ(c.size(), 1);
|
||||
|
||||
c.upsert("foo", "changed");
|
||||
val = c.get("foo");
|
||||
ASSERT_EQ(val.value_or("error"), "changed");
|
||||
ASSERT_EQ(c.size(), 1);
|
||||
}
|
||||
|
||||
TEST(LRUCache, overwriteOldestWhenCapacityIsReached) {
|
||||
LRUCache<std::string, std::string> c(3);
|
||||
c.upsert("one", "eins");
|
||||
c.upsert("two", "zwei");
|
||||
c.upsert("three", "drei");
|
||||
|
||||
ASSERT_EQ(c.size(), 3);
|
||||
ASSERT_EQ(c.get("one").value_or("error"), "eins");
|
||||
|
||||
// exceed capacity
|
||||
c.upsert("another", "whatever");
|
||||
|
||||
ASSERT_EQ(c.size(), 3);
|
||||
// Retrieving "one" makes it the most recent element thus
|
||||
// two will be the oldest one and thus replaced.
|
||||
ASSERT_EQ(c.get("two").has_value(), false);
|
||||
ASSERT_EQ(c.get("another").value(), "whatever");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* clear
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(LRUCache, clearEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.clear();
|
||||
ASSERT_EQ(c.size(), 0);
|
||||
}
|
||||
|
||||
TEST(LRUCache, clearNonEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("one", "eins");
|
||||
c.upsert("two", "zwei");
|
||||
c.upsert("three", "drei");
|
||||
ASSERT_EQ(c.size(), 3);
|
||||
c.clear();
|
||||
ASSERT_EQ(c.size(), 0);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* erase
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(LRUCache, eraseFromEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
ASSERT_EQ(c.erase("foo"), false);
|
||||
ASSERT_EQ(c.size(), 0);
|
||||
}
|
||||
|
||||
TEST(LRUCache, eraseMissingFromNonEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("one", "eins");
|
||||
ASSERT_EQ(c.erase("foo"), false);
|
||||
ASSERT_EQ(c.size(), 1);
|
||||
ASSERT_EQ(c.get("one").value_or("error"), "eins");
|
||||
}
|
||||
|
||||
TEST(LRUCache, eraseFromNonEmptyCache) {
|
||||
LRUCache<std::string, std::string> c(10);
|
||||
c.upsert("one", "eins");
|
||||
ASSERT_EQ(c.erase("one"), true);
|
||||
ASSERT_EQ(c.size(), 0);
|
||||
ASSERT_EQ(c.get("one").value_or("empty"), "empty");
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
namespace nix {
|
||||
|
||||
std::regex refRegex(refRegexS, std::regex::ECMAScript);
|
||||
std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript);
|
||||
std::regex revRegex(revRegexS, std::regex::ECMAScript);
|
||||
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
|
||||
|
||||
|
|
|
@ -49,6 +49,12 @@ const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRege
|
|||
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
|
||||
extern std::regex refRegex;
|
||||
|
||||
// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||
// This is because of the definition of a ref in refs.c in https://github.com/git/git
|
||||
// See tests/fetchGitRefs.sh for the full definition
|
||||
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
|
||||
extern std::regex badGitRefRegex;
|
||||
|
||||
// A Git revision (a SHA-1 commit hash).
|
||||
const static std::string revRegexS = "[0-9a-fA-F]{40}";
|
||||
extern std::regex revRegex;
|
||||
|
|
|
@ -50,8 +50,10 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
|||
info.narSize = sink.s->size();
|
||||
info.ca = makeFixedOutputCA(FileIngestionMethod::Recursive, info.narHash);
|
||||
|
||||
if (!dryRun)
|
||||
store->addToStore(info, sink.s);
|
||||
if (!dryRun) {
|
||||
auto source = StringSource { *sink.s };
|
||||
store->addToStore(info, source);
|
||||
}
|
||||
|
||||
logger->stdout("%s", store->printStorePath(info.path));
|
||||
}
|
||||
|
|
111
tests/fetchGitRefs.sh
Normal file
111
tests/fetchGitRefs.sh
Normal file
|
@ -0,0 +1,111 @@
|
|||
source common.sh
|
||||
|
||||
if [[ -z $(type -p git) ]]; then
|
||||
echo "Git not installed; skipping Git tests"
|
||||
exit 99
|
||||
fi
|
||||
|
||||
clearStore
|
||||
|
||||
repo="$TEST_ROOT/git"
|
||||
|
||||
rm -rf "$repo" "${repo}-tmp" "$TEST_HOME/.cache/nix"
|
||||
|
||||
git init "$repo"
|
||||
git -C "$repo" config user.email "foobar@example.com"
|
||||
git -C "$repo" config user.name "Foobar"
|
||||
|
||||
echo utrecht > "$repo"/hello
|
||||
git -C "$repo" add hello
|
||||
git -C "$repo" commit -m 'Bla1'
|
||||
|
||||
path=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"master\"; }).outPath")
|
||||
|
||||
# Test various combinations of ref names
|
||||
# (taken from the git project)
|
||||
|
||||
# git help check-ref-format
|
||||
# Git imposes the following rules on how references are named:
|
||||
#
|
||||
# 1. They can include slash / for hierarchical (directory) grouping, but no slash-separated component can begin with a dot . or end with the sequence .lock.
|
||||
# 2. They must contain at least one /. This enforces the presence of a category like heads/, tags/ etc. but the actual names are not restricted. If the --allow-onelevel option is used, this rule is waived.
|
||||
# 3. They cannot have two consecutive dots .. anywhere.
|
||||
# 4. They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
|
||||
# 5. They cannot have question-mark ?, asterisk *, or open bracket [ anywhere. See the --refspec-pattern option below for an exception to this rule.
|
||||
# 6. They cannot begin or end with a slash / or contain multiple consecutive slashes (see the --normalize option below for an exception to this rule)
|
||||
# 7. They cannot end with a dot ..
|
||||
# 8. They cannot contain a sequence @{.
|
||||
# 9. They cannot be the single character @.
|
||||
# 10. They cannot contain a \.
|
||||
|
||||
valid_ref() {
|
||||
{ set +x; printf >&2 '\n>>>>>>>>>> valid_ref %s\b <<<<<<<<<<\n' $(printf %s "$1" | sed -n -e l); set -x; }
|
||||
git check-ref-format --branch "$1" >/dev/null
|
||||
git -C "$repo" branch "$1" master >/dev/null
|
||||
path1=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = ''$1''; }).outPath")
|
||||
[[ $path1 = $path ]]
|
||||
git -C "$repo" branch -D "$1" >/dev/null
|
||||
}
|
||||
|
||||
invalid_ref() {
|
||||
{ set +x; printf >&2 '\n>>>>>>>>>> invalid_ref %s\b <<<<<<<<<<\n' $(printf %s "$1" | sed -n -e l); set -x; }
|
||||
# special case for a sole @:
|
||||
# --branch @ will try to interpret @ as a branch reference and not fail. Thus we need --allow-onelevel
|
||||
if [ "$1" = "@" ]; then
|
||||
(! git check-ref-format --allow-onelevel "$1" >/dev/null 2>&1)
|
||||
else
|
||||
(! git check-ref-format --branch "$1" >/dev/null 2>&1)
|
||||
fi
|
||||
nix --debug eval --raw "(builtins.fetchGit { url = $repo; ref = ''$1''; }).outPath" 2>&1 | grep 'error: invalid Git branch/tag name' >/dev/null
|
||||
}
|
||||
|
||||
|
||||
valid_ref 'foox'
|
||||
valid_ref '1337'
|
||||
valid_ref 'foo.baz'
|
||||
valid_ref 'foo/bar/baz'
|
||||
valid_ref 'foo./bar'
|
||||
valid_ref 'heads/foo@bar'
|
||||
valid_ref "$(printf 'heads/fu\303\237')"
|
||||
valid_ref 'foo-bar-baz'
|
||||
valid_ref '$1'
|
||||
valid_ref 'foo.locke'
|
||||
|
||||
invalid_ref 'refs///heads/foo'
|
||||
invalid_ref 'heads/foo/'
|
||||
invalid_ref '///heads/foo'
|
||||
invalid_ref '.foo'
|
||||
invalid_ref './foo'
|
||||
invalid_ref './foo/bar'
|
||||
invalid_ref 'foo/./bar'
|
||||
invalid_ref 'foo/bar/.'
|
||||
invalid_ref 'foo bar'
|
||||
invalid_ref 'foo?bar'
|
||||
invalid_ref 'foo^bar'
|
||||
invalid_ref 'foo~bar'
|
||||
invalid_ref 'foo:bar'
|
||||
invalid_ref 'foo[bar'
|
||||
invalid_ref 'foo/bar/.'
|
||||
invalid_ref '.refs/foo'
|
||||
invalid_ref 'refs/heads/foo.'
|
||||
invalid_ref 'heads/foo..bar'
|
||||
invalid_ref 'heads/foo?bar'
|
||||
invalid_ref 'heads/foo.lock'
|
||||
invalid_ref 'heads///foo.lock'
|
||||
invalid_ref 'foo.lock/bar'
|
||||
invalid_ref 'foo.lock///bar'
|
||||
invalid_ref 'heads/v@{ation'
|
||||
invalid_ref 'heads/foo\.ar' # should fail due to \
|
||||
invalid_ref 'heads/foo\bar' # should fail due to \
|
||||
invalid_ref "$(printf 'heads/foo\t')" # should fail because it has a TAB
|
||||
invalid_ref "$(printf 'heads/foo\177')"
|
||||
invalid_ref '@'
|
||||
|
||||
invalid_ref 'foo/*'
|
||||
invalid_ref '*/foo'
|
||||
invalid_ref 'foo/*/bar'
|
||||
invalid_ref '*'
|
||||
invalid_ref 'foo/*/*'
|
||||
invalid_ref '*/foo/*'
|
||||
invalid_ref '/foo'
|
||||
invalid_ref ''
|
|
@ -18,6 +18,7 @@ nix_tests = \
|
|||
nar-access.sh \
|
||||
structured-attrs.sh \
|
||||
fetchGit.sh \
|
||||
fetchGitRefs.sh \
|
||||
fetchGitSubmodules.sh \
|
||||
fetchMercurial.sh \
|
||||
signing.sh \
|
||||
|
|
Loading…
Reference in a new issue