forked from lix-project/lix
fetchMercurial: set HGPLAIN when invoking hg
Without setting HGPLAIN, the user's environment leaks into hg invocations, which means that the output may not be in the expected format. HGPLAIN is the Mercurial-recommended solution for this in that it's intended for uses by scripts and programs which are looking to parse Mercurial's output in a consistent manner.
This commit is contained in:
parent
1973669e86
commit
226116f482
|
@ -11,6 +11,36 @@ using namespace std::string_literals;
|
||||||
|
|
||||||
namespace nix::fetchers {
|
namespace nix::fetchers {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
RunOptions hgOptions(const Strings & args) {
|
||||||
|
RunOptions opts("hg", args);
|
||||||
|
opts.searchPath = true;
|
||||||
|
|
||||||
|
auto env = getEnv();
|
||||||
|
// Set HGPLAIN: this means we get consistent output from hg and avoids leakage from a user or system .hgrc.
|
||||||
|
env["HGPLAIN"] = "";
|
||||||
|
opts.environment = env;
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// runProgram wrapper that uses hgOptions instead of stock RunOptions.
|
||||||
|
string runHg(const Strings & args, const std::optional<std::string> & input = {})
|
||||||
|
{
|
||||||
|
RunOptions opts = hgOptions(args);
|
||||||
|
opts.input = input;
|
||||||
|
|
||||||
|
auto res = runProgram(opts);
|
||||||
|
|
||||||
|
if (!statusOk(res.first))
|
||||||
|
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
||||||
|
|
||||||
|
return res.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct MercurialInputScheme : InputScheme
|
struct MercurialInputScheme : InputScheme
|
||||||
{
|
{
|
||||||
std::optional<Input> inputFromURL(const ParsedURL & url) override
|
std::optional<Input> inputFromURL(const ParsedURL & url) override
|
||||||
|
@ -100,11 +130,11 @@ struct MercurialInputScheme : InputScheme
|
||||||
assert(sourcePath);
|
assert(sourcePath);
|
||||||
|
|
||||||
// FIXME: shut up if file is already tracked.
|
// FIXME: shut up if file is already tracked.
|
||||||
runProgram("hg", true,
|
runHg(
|
||||||
{ "add", *sourcePath + "/" + std::string(file) });
|
{ "add", *sourcePath + "/" + std::string(file) });
|
||||||
|
|
||||||
if (commitMsg)
|
if (commitMsg)
|
||||||
runProgram("hg", true,
|
runHg(
|
||||||
{ "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg });
|
{ "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +160,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {
|
if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {
|
||||||
|
|
||||||
bool clean = runProgram("hg", true, { "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == "";
|
bool clean = runHg({ "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == "";
|
||||||
|
|
||||||
if (!clean) {
|
if (!clean) {
|
||||||
|
|
||||||
|
@ -143,10 +173,10 @@ struct MercurialInputScheme : InputScheme
|
||||||
if (settings.warnDirty)
|
if (settings.warnDirty)
|
||||||
warn("Mercurial tree '%s' is unclean", actualUrl);
|
warn("Mercurial tree '%s' is unclean", actualUrl);
|
||||||
|
|
||||||
input.attrs.insert_or_assign("ref", chomp(runProgram("hg", true, { "branch", "-R", actualUrl })));
|
input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl })));
|
||||||
|
|
||||||
auto files = tokenizeString<std::set<std::string>>(
|
auto files = tokenizeString<std::set<std::string>>(
|
||||||
runProgram("hg", true, { "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
||||||
|
|
||||||
PathFilter filter = [&](const Path & p) -> bool {
|
PathFilter filter = [&](const Path & p) -> bool {
|
||||||
assert(hasPrefix(p, actualUrl));
|
assert(hasPrefix(p, actualUrl));
|
||||||
|
@ -224,33 +254,33 @@ struct MercurialInputScheme : InputScheme
|
||||||
if (!(input.getRev()
|
if (!(input.getRev()
|
||||||
&& pathExists(cacheDir)
|
&& pathExists(cacheDir)
|
||||||
&& runProgram(
|
&& runProgram(
|
||||||
RunOptions("hg", { "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })
|
hgOptions({ "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })
|
||||||
.killStderr(true)).second == "1"))
|
.killStderr(true)).second == "1"))
|
||||||
{
|
{
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
|
||||||
|
|
||||||
if (pathExists(cacheDir)) {
|
if (pathExists(cacheDir)) {
|
||||||
try {
|
try {
|
||||||
runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
|
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
||||||
}
|
}
|
||||||
catch (ExecError & e) {
|
catch (ExecError & e) {
|
||||||
string transJournal = cacheDir + "/.hg/store/journal";
|
string transJournal = cacheDir + "/.hg/store/journal";
|
||||||
/* hg throws "abandoned transaction" error only if this file exists */
|
/* hg throws "abandoned transaction" error only if this file exists */
|
||||||
if (pathExists(transJournal)) {
|
if (pathExists(transJournal)) {
|
||||||
runProgram("hg", true, { "recover", "-R", cacheDir });
|
runHg({ "recover", "-R", cacheDir });
|
||||||
runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
|
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
||||||
} else {
|
} else {
|
||||||
throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
|
throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
createDirs(dirOf(cacheDir));
|
createDirs(dirOf(cacheDir));
|
||||||
runProgram("hg", true, { "clone", "--noupdate", "--", actualUrl, cacheDir });
|
runHg({ "clone", "--noupdate", "--", actualUrl, cacheDir });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tokens = tokenizeString<std::vector<std::string>>(
|
auto tokens = tokenizeString<std::vector<std::string>>(
|
||||||
runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
|
runHg({ "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
|
||||||
assert(tokens.size() == 3);
|
assert(tokens.size() == 3);
|
||||||
|
|
||||||
input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], htSHA1).gitRev());
|
input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], htSHA1).gitRev());
|
||||||
|
@ -263,7 +293,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
Path tmpDir = createTempDir();
|
Path tmpDir = createTempDir();
|
||||||
AutoDelete delTmpDir(tmpDir, true);
|
AutoDelete delTmpDir(tmpDir, true);
|
||||||
|
|
||||||
runProgram("hg", true, { "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir });
|
runHg({ "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir });
|
||||||
|
|
||||||
deletePath(tmpDir + "/.hg_archival.txt");
|
deletePath(tmpDir + "/.hg_archival.txt");
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ hg init $repo
|
||||||
echo '[ui]' >> $repo/.hg/hgrc
|
echo '[ui]' >> $repo/.hg/hgrc
|
||||||
echo 'username = Foobar <foobar@example.org>' >> $repo/.hg/hgrc
|
echo 'username = Foobar <foobar@example.org>' >> $repo/.hg/hgrc
|
||||||
|
|
||||||
|
# Set ui.tweakdefaults to ensure HGPLAIN is being set.
|
||||||
|
echo 'tweakdefaults = True' >> $repo/.hg/hgrc
|
||||||
|
|
||||||
echo utrecht > $repo/hello
|
echo utrecht > $repo/hello
|
||||||
touch $repo/.hgignore
|
touch $repo/.hgignore
|
||||||
hg add --cwd $repo hello .hgignore
|
hg add --cwd $repo hello .hgignore
|
||||||
|
|
Loading…
Reference in a new issue