sapling/eden/fs/testharness/HgRepo.cpp
Thomas Orozco 710c9c475a eden: set hgPath programmatically
Summary:
In Buck v1 the path we receive for hgPath is absolute so we can use it across
chdirs and the likes.

However, in Buck v2, it isn't. This means that using `--hgPath` doesn't work if
we go and chdir later, which we do because we run from the repo directory.

This updates the unit tests to set the path to an absolute value when they
otherwise look for their hg binary (which they do for operations in HgRepo). If
possible I would have preferred to thread this through to the HgImporter
constructor, but we have quite a few overloaded constructors already and this
turned out to be very messy.

Finally, this also updates the tests themselves to set `hgPath` to `false` so
that if we try to actually run it without configuring it, it won't work.

Reviewed By: xavierd

Differential Revision: D29060828

fbshipit-source-id: 17ad615d7ff98e9ce8278386f018167ec589c336
2021-06-15 02:47:41 -07:00

196 lines
5.5 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#ifndef _WIN32
#include "eden/fs/testharness/HgRepo.h"
#include <folly/Exception.h>
#include <folly/File.h>
#include <folly/FileUtil.h>
#include <folly/Portability.h>
#include <folly/String.h>
#include <folly/logging/xlog.h>
#include <sys/stat.h>
#include <unistd.h>
#include "eden/fs/model/Hash.h"
#include "eden/fs/utils/FileUtils.h"
using folly::StringPiece;
using std::string;
using std::vector;
namespace facebook {
namespace eden {
HgRepo::HgRepo(AbsolutePathPiece path, AbsolutePath hgCmd)
: hgCmd_(hgCmd), path_(path) {
XLOG(DBG1) << "Using hg command: " << hgCmd_;
// Set up hgEnv_
std::vector<const char*> passthroughVars{
{"HG_REAL_BIN", "LLVM_PROFILE_FILE", "PATH"}};
hgEnv_.clear();
for (const char* varName : passthroughVars) {
auto value = getenv(varName);
if (value) {
hgEnv_.set(varName, value);
}
}
hgEnv_.set("HGPLAIN", "1");
hgEnv_.set("HGRCPATH", "");
hgEnv_.set("CHGDISABLE", "1");
hgEnv_.set("NOSCMLOG", "1");
hgEnv_.set("LOCALE", "en_US.UTF-8");
hgEnv_.set("LC_ALL", "en_US.UTF-8");
// Trick Mercurial into thinking it's in a test so it doesn't generate
// prod configs.
hgEnv_.set("TESTTMP", "");
}
HgRepo::HgRepo(AbsolutePathPiece path)
: HgRepo(path, findAndConfigureHgBinary()) {}
string HgRepo::hg(vector<string> args) {
auto process = invokeHg(std::move(args));
const auto outputs{process.communicate()};
process.waitChecked();
return outputs.first;
}
SpawnedProcess HgRepo::invokeHg(vector<string> args) {
SpawnedProcess::Options opts;
opts.chdir(path_);
opts.pipeStdout();
return invokeHg(std::move(args), std::move(opts));
}
SpawnedProcess HgRepo::invokeHg(
vector<string> args,
SpawnedProcess::Options&& options) {
args.insert(args.begin(), "hg");
XLOG(DBG1) << "repo " << path_ << " running: " << folly::join(" ", args);
options.environment() = hgEnv_;
options.executablePath(hgCmd_);
return SpawnedProcess(args, std::move(options));
}
void HgRepo::hgInit(std::vector<std::string> extraArgs) {
XLOG(DBG1) << "creating new hg repository at " << path_;
// Invoke SpawnedProcess directly here rather than using our hg() helper
// function. The hg() function requires the repository directory to already
// exist.
std::vector<std::string> args = {"hg", "init", path_.value()};
args.insert(args.end(), extraArgs.begin(), extraArgs.end());
SpawnedProcess::Options opts;
opts.environment() = hgEnv_;
opts.executablePath(hgCmd_);
SpawnedProcess p(args, std::move(opts));
p.waitChecked();
}
void HgRepo::enableTreeManifest(AbsolutePathPiece cacheDirectory) {
appendToHgrc(
"[extensions]\n"
"remotefilelog =\n"
"remotenames =\n"
"treemanifest =\n"
"[treemanifest]\n"
"treeonly = true\n"
"[remotefilelog]\n"
"reponame = test\n"
"cachepath = " +
cacheDirectory.value().str() + "\n");
}
void HgRepo::cloneFrom(
StringPiece serverRepoUrl,
std::vector<std::string> extraArgs) {
XLOG(DBG1) << "cloning new hg repository at " << path_ << " from "
<< serverRepoUrl;
std::vector<std::string> args = {"hg", "clone"};
args.insert(args.end(), extraArgs.begin(), extraArgs.end());
args.push_back(serverRepoUrl.str());
args.push_back(path_.value());
XLOG(DBG1) << "running: " << folly::join(" ", args);
SpawnedProcess::Options opts;
opts.executablePath(hgCmd_);
opts.environment() = hgEnv_;
SpawnedProcess p(args, std::move(opts));
p.waitChecked();
}
void HgRepo::appendToHgrc(folly::StringPiece data) {
auto hgrcPath = path_ + ".hg"_pc + "hgrc"_pc;
folly::File hgrc{hgrcPath.stringPiece(), O_WRONLY | O_APPEND | O_CREAT};
if (folly::writeFull(hgrc.fd(), data.data(), data.size()) < 0) {
folly::throwSystemError("error writing to ", hgrcPath);
}
}
void HgRepo::appendToHgrc(const std::vector<std::string>& lines) {
appendToHgrc(folly::join("\n", lines) + "\n");
}
RootId HgRepo::commit(StringPiece message) {
hg("commit",
"-u",
"Test User <user@example.com>",
"-d",
"2017-01-01 13:00:00",
"-m",
message.str());
auto output = hg("log", "-r.", "-T{node}\\n");
return RootId{Hash{folly::rtrimWhitespace(output)}.toString()};
}
Hash HgRepo::getManifestForCommit(const RootId& commit) {
auto output = hg("log", "-r", commit.value(), "-T{manifest}\\n");
return Hash{folly::rtrimWhitespace(output)};
}
void HgRepo::mkdir(RelativePathPiece path, mode_t permissions) {
auto fullPath = path_ + path;
auto rc = ::mkdir(fullPath.value().c_str(), permissions);
folly::checkUnixError(rc, "mkdir ", fullPath);
}
void HgRepo::writeFile(
RelativePathPiece path,
StringPiece contents,
mode_t permissions) {
// TODO(xavierd): remove permissions from the callers.
(void)permissions;
auto fullPath = path_ + path;
writeFileAtomic(fullPath, contents).value();
}
void HgRepo::symlink(StringPiece contents, RelativePathPiece path) {
auto fullPath = path_ + path;
auto rc = ::symlink(contents.str().c_str(), fullPath.value().c_str());
checkUnixError(rc, "error creating symlink at ", path);
}
bool testEnvironmentSupportsHg() {
// In opt builds, we don't want to optimize away references to HgImporter.cpp,
// since it defines the hgPath gflag. Mask the kIsSanitizeThread constant from
// the optimizer.
static volatile bool kIsSanitizeThread = folly::kIsSanitizeThread;
return !kIsSanitizeThread;
}
} // namespace eden
} // namespace facebook
#endif