mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-10 08:16:15 +02:00
Add abstract interface for writing files to an Input
This commit is contained in:
parent
bb4d35dcca
commit
f780539406
8 changed files with 102 additions and 84 deletions
|
@ -621,65 +621,53 @@ LockedFlake lockFlake(
|
||||||
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
||||||
|
|
||||||
if (lockFlags.writeLockFile) {
|
if (lockFlags.writeLockFile) {
|
||||||
if (auto sourcePath = topRef.input.getAccessor(state.store).first->root().getPhysicalPath()) {
|
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
||||||
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
if (fetchSettings.warnDirty)
|
||||||
if (fetchSettings.warnDirty)
|
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
||||||
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
} else {
|
||||||
} else {
|
if (!lockFlags.updateLockFile)
|
||||||
if (!lockFlags.updateLockFile)
|
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
||||||
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
|
||||||
|
|
||||||
CanonPath flakeDir(*sourcePath);
|
auto path = flake->path.parent() + "flake.lock";
|
||||||
|
|
||||||
auto relPath = flakeDir + "flake.lock";
|
bool lockFileExists = path.pathExists();
|
||||||
|
|
||||||
auto path = flakeDir + "flake.lock";
|
if (lockFileExists) {
|
||||||
|
auto s = chomp(diff);
|
||||||
|
if (s.empty())
|
||||||
|
warn("updating lock file '%s'", path);
|
||||||
|
else
|
||||||
|
warn("updating lock file '%s':\n%s", path, s);
|
||||||
|
} else
|
||||||
|
warn("creating lock file '%s'", path);
|
||||||
|
|
||||||
bool lockFileExists = pathExists(path.abs());
|
std::optional<std::string> commitMessage = std::nullopt;
|
||||||
|
if (lockFlags.commitLockFile) {
|
||||||
|
std::string cm;
|
||||||
|
|
||||||
if (lockFileExists) {
|
cm = fetchSettings.commitLockFileSummary.get();
|
||||||
auto s = chomp(diff);
|
|
||||||
if (s.empty())
|
|
||||||
warn("updating lock file '%s'", path);
|
|
||||||
else
|
|
||||||
warn("updating lock file '%s':\n%s", path, s);
|
|
||||||
} else
|
|
||||||
warn("creating lock file '%s'", path);
|
|
||||||
|
|
||||||
newLockFile.write(path.abs());
|
if (cm == "")
|
||||||
|
cm = fmt("%s: %s", path.path.rel(), lockFileExists ? "Update" : "Add");
|
||||||
|
|
||||||
std::optional<std::string> commitMessage = std::nullopt;
|
cm += "\n\nFlake lock file updates:\n\n";
|
||||||
if (lockFlags.commitLockFile) {
|
cm += filterANSIEscapes(diff, true);
|
||||||
std::string cm;
|
commitMessage = cm;
|
||||||
|
|
||||||
cm = fetchSettings.commitLockFileSummary.get();
|
|
||||||
|
|
||||||
if (cm == "") {
|
|
||||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
|
||||||
}
|
|
||||||
|
|
||||||
cm += "\n\nFlake lock file updates:\n\n";
|
|
||||||
cm += filterANSIEscapes(diff, true);
|
|
||||||
commitMessage = cm;
|
|
||||||
}
|
|
||||||
|
|
||||||
topRef.input.markChangedFile(
|
|
||||||
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
|
||||||
commitMessage);
|
|
||||||
|
|
||||||
/* Rewriting the lockfile changed the top-level
|
|
||||||
repo, so we should re-read it. FIXME: we could
|
|
||||||
also just clear the 'rev' field... */
|
|
||||||
auto prevLockedRef = flake->lockedRef;
|
|
||||||
flake = std::make_unique<Flake>(getFlake(state, topRef, useRegistries));
|
|
||||||
|
|
||||||
if (lockFlags.commitLockFile &&
|
|
||||||
flake->lockedRef.input.getRev() &&
|
|
||||||
prevLockedRef.input.getRev() != flake->lockedRef.input.getRev())
|
|
||||||
warn("committed new revision '%s'", flake->lockedRef.input.getRev()->gitRev());
|
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef);
|
topRef.input.putFile(path.path, fmt("%s\n", newLockFile), commitMessage);
|
||||||
|
|
||||||
|
/* Rewriting the lockfile changed the top-level
|
||||||
|
repo, so we should re-read it. FIXME: we could
|
||||||
|
also just clear the 'rev' field... */
|
||||||
|
auto prevLockedRef = flake->lockedRef;
|
||||||
|
flake = std::make_unique<Flake>(getFlake(state, topRef, useRegistries));
|
||||||
|
|
||||||
|
if (lockFlags.commitLockFile &&
|
||||||
|
flake->lockedRef.input.getRev() &&
|
||||||
|
prevLockedRef.input.getRev() != flake->lockedRef.input.getRev())
|
||||||
|
warn("committed new revision '%s'", flake->lockedRef.input.getRev()->gitRev());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
|
warn("not writing modified lock file of flake '%s':\n%s", topRef, chomp(diff));
|
||||||
flake->forceDirty = true;
|
flake->forceDirty = true;
|
||||||
|
|
|
@ -190,12 +190,6 @@ std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LockFile::write(const Path & path) const
|
|
||||||
{
|
|
||||||
createDirs(dirOf(path));
|
|
||||||
writeFile(path, fmt("%s\n", *this));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<FlakeRef> LockFile::isUnlocked() const
|
std::optional<FlakeRef> LockFile::isUnlocked() const
|
||||||
{
|
{
|
||||||
std::unordered_set<std::shared_ptr<const Node>> nodes;
|
std::unordered_set<std::shared_ptr<const Node>> nodes;
|
||||||
|
|
|
@ -59,10 +59,6 @@ struct LockFile
|
||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
static LockFile read(const Path & path);
|
|
||||||
|
|
||||||
void write(const Path & path) const;
|
|
||||||
|
|
||||||
/* Check whether this lock file has any unlocked inputs. If so,
|
/* Check whether this lock file has any unlocked inputs. If so,
|
||||||
return one. */
|
return one. */
|
||||||
std::optional<FlakeRef> isUnlocked() const;
|
std::optional<FlakeRef> isUnlocked() const;
|
||||||
|
|
|
@ -187,12 +187,13 @@ void Input::clone(const Path & destDir) const
|
||||||
scheme->clone(*this, destDir);
|
scheme->clone(*this, destDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::markChangedFile(
|
void Input::putFile(
|
||||||
std::string_view file,
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
std::optional<std::string> commitMsg) const
|
std::optional<std::string> commitMsg) const
|
||||||
{
|
{
|
||||||
assert(scheme);
|
assert(scheme);
|
||||||
return scheme->markChangedFile(*this, file, commitMsg);
|
return scheme->putFile(*this, path, contents, commitMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Input::getName() const
|
std::string Input::getName() const
|
||||||
|
@ -278,9 +279,13 @@ Input InputScheme::applyOverrides(
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputScheme::markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg)
|
void InputScheme::putFile(
|
||||||
|
const Input & input,
|
||||||
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg) const
|
||||||
{
|
{
|
||||||
assert(false);
|
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputScheme::clone(const Input & input, const Path & destDir)
|
void InputScheme::clone(const Input & input, const Path & destDir)
|
||||||
|
|
|
@ -79,8 +79,9 @@ public:
|
||||||
|
|
||||||
void clone(const Path & destDir) const;
|
void clone(const Path & destDir) const;
|
||||||
|
|
||||||
void markChangedFile(
|
void putFile(
|
||||||
std::string_view file,
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
std::optional<std::string> commitMsg) const;
|
std::optional<std::string> commitMsg) const;
|
||||||
|
|
||||||
std::string getName() const;
|
std::string getName() const;
|
||||||
|
@ -130,7 +131,11 @@ struct InputScheme
|
||||||
|
|
||||||
virtual void clone(const Input & input, const Path & destDir);
|
virtual void clone(const Input & input, const Path & destDir);
|
||||||
|
|
||||||
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
|
virtual void putFile(
|
||||||
|
const Input & input,
|
||||||
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg) const;
|
||||||
|
|
||||||
/* Note: the default implementations of fetchToStore() and
|
/* Note: the default implementations of fetchToStore() and
|
||||||
getAccessor() are defined using the other, so implementations
|
getAccessor() are defined using the other, so implementations
|
||||||
|
|
|
@ -245,17 +245,28 @@ struct GitInputScheme : InputScheme
|
||||||
runProgram("git", true, args);
|
runProgram("git", true, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
void putFile(
|
||||||
|
const Input & input,
|
||||||
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg) const
|
||||||
{
|
{
|
||||||
auto repoInfo = getRepoInfo(input);
|
auto repoInfo = getRepoInfo(input);
|
||||||
assert(repoInfo.isLocal);
|
if (!repoInfo.isLocal)
|
||||||
|
throw Error("cannot commit '%s' to Git repository '%s' because it's not a working tree", path, input.to_string());
|
||||||
|
|
||||||
|
auto absPath = CanonPath(repoInfo.url) + path;
|
||||||
|
|
||||||
|
// FIXME: make sure that absPath is not a symlink that escapes
|
||||||
|
// the repo.
|
||||||
|
writeFile(absPath.abs(), contents);
|
||||||
|
|
||||||
runProgram("git", true,
|
runProgram("git", true,
|
||||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "add", "--force", "--intent-to-add", "--", std::string(file) });
|
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "add", "--force", "--intent-to-add", "--", std::string(path.rel()) });
|
||||||
|
|
||||||
if (commitMsg)
|
if (commitMsg)
|
||||||
runProgram("git", true,
|
runProgram("git", true,
|
||||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(file), "-m", *commitMsg });
|
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-m", *commitMsg });
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RepoInfo
|
struct RepoInfo
|
||||||
|
@ -292,7 +303,7 @@ struct GitInputScheme : InputScheme
|
||||||
std::string gitDir = ".git";
|
std::string gitDir = ".git";
|
||||||
};
|
};
|
||||||
|
|
||||||
RepoInfo getRepoInfo(const Input & input)
|
RepoInfo getRepoInfo(const Input & input) const
|
||||||
{
|
{
|
||||||
auto checkHashType = [&](const std::optional<Hash> & hash)
|
auto checkHashType = [&](const std::optional<Hash> & hash)
|
||||||
{
|
{
|
||||||
|
|
|
@ -116,18 +116,29 @@ struct MercurialInputScheme : InputScheme
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
void putFile(
|
||||||
|
const Input & input,
|
||||||
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg) const
|
||||||
{
|
{
|
||||||
auto [isLocal, path] = getActualUrl(input);
|
auto [isLocal, repoPath] = getActualUrl(input);
|
||||||
assert(isLocal);
|
if (!isLocal)
|
||||||
|
throw Error("cannot commit '%s' to Mercurial repository '%s' because it's not a working tree", path, input.to_string());
|
||||||
|
|
||||||
|
auto absPath = CanonPath(repoPath) + path;
|
||||||
|
|
||||||
|
// FIXME: make sure that absPath is not a symlink that escapes
|
||||||
|
// the repo.
|
||||||
|
writeFile(absPath.abs(), contents);
|
||||||
|
|
||||||
// FIXME: shut up if file is already tracked.
|
// FIXME: shut up if file is already tracked.
|
||||||
runHg(
|
runHg(
|
||||||
{ "add", path + "/" + file });
|
{ "add", absPath.abs() });
|
||||||
|
|
||||||
if (commitMsg)
|
if (commitMsg)
|
||||||
runHg(
|
runHg(
|
||||||
{ "commit", path + "/" + file, "-m", *commitMsg });
|
{ "commit", absPath.abs(), "-m", *commitMsg });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
||||||
|
|
|
@ -80,12 +80,20 @@ struct PathInputScheme : InputScheme
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
|
void putFile(
|
||||||
|
const Input & input,
|
||||||
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg) const
|
||||||
{
|
{
|
||||||
// nothing to do
|
auto absPath = CanonPath(getAbsPath(input)) + path;
|
||||||
|
|
||||||
|
// FIXME: make sure that absPath is not a symlink that escapes
|
||||||
|
// the repo.
|
||||||
|
writeFile(absPath.abs(), contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
CanonPath getAbsPath(ref<Store> store, const Input & input) const
|
CanonPath getAbsPath(const Input & input) const
|
||||||
{
|
{
|
||||||
auto path = getStrAttr(input.attrs, "path");
|
auto path = getStrAttr(input.attrs, "path");
|
||||||
|
|
||||||
|
@ -97,7 +105,7 @@ struct PathInputScheme : InputScheme
|
||||||
|
|
||||||
std::pair<ref<InputAccessor>, Input> getAccessor(ref<Store> store, const Input & input) override
|
std::pair<ref<InputAccessor>, Input> getAccessor(ref<Store> store, const Input & input) override
|
||||||
{
|
{
|
||||||
auto absPath = getAbsPath(store, input);
|
auto absPath = getAbsPath(input);
|
||||||
auto input2(input);
|
auto input2(input);
|
||||||
input2.attrs.emplace("path", (std::string) absPath.abs());
|
input2.attrs.emplace("path", (std::string) absPath.abs());
|
||||||
return {makeFSInputAccessor(absPath), std::move(input2)};
|
return {makeFSInputAccessor(absPath), std::move(input2)};
|
||||||
|
@ -109,7 +117,7 @@ struct PathInputScheme : InputScheme
|
||||||
locked, so just use the path as its fingerprint. Maybe we
|
locked, so just use the path as its fingerprint. Maybe we
|
||||||
should restrict this to CA paths but that's not
|
should restrict this to CA paths but that's not
|
||||||
super-important. */
|
super-important. */
|
||||||
auto path = getAbsPath(store, input);
|
auto path = getAbsPath(input);
|
||||||
if (store->isInStore(path.abs()))
|
if (store->isInStore(path.abs()))
|
||||||
return path.abs();
|
return path.abs();
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
Loading…
Reference in a new issue