packages feed

hercules-ci-agent 0.8.1 → 0.8.2

raw patch · 29 files changed

+1248/−745 lines, 29 filesdep ~hercules-ci-api-agentdep ~hercules-ci-api-core

Dependency ranges changed: hercules-ci-api-agent, hercules-ci-api-core

Files

CHANGELOG.md view
@@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.2]++### Added++ - Preparations for the next Nix version++### Fixed++ - #304, `message:epollControl: invalid argument (Bad file descriptor)` in effect task+ ## [0.8.1]  ### Added
cbits/hercules-logger.cxx view
@@ -1,10 +1,5 @@ #include "hercules-logger.hh" -HerculesLogger::HerculesLogger()-{--}- void HerculesLogger::push(std::unique_ptr<LogEntry> entry) {   auto state(state_.lock());   state->queue.push(std::move(entry));@@ -25,6 +20,15 @@     .text = fs.s   })); }++#ifdef NIX_2_4+// TODO structured+void HerculesLogger::logEI(const nix::ErrorInfo & ei) {+  std::stringstream oss;+  showErrorInfo(oss, ei, false);+  log(ei.level, oss.str());+}+#endif  void HerculesLogger::startActivity(nix::ActivityId act, nix::Verbosity lvl, nix::ActivityType type,     const std::string & s, const Fields & fields, nix::ActivityId parent) {
cbits/hercules-logger.hh view
@@ -1,6 +1,11 @@ #pragma once  #include <nix/config.h>++#ifdef NIX_2_4+#include <nix/error.hh>+#endif+ #include <nix/shared.hh> #include <nix/sync.hh> #include <nix/logging.hh>@@ -27,7 +32,7 @@   uint64_t getMs();   public:-  HerculesLogger();+  inline HerculesLogger() {};    struct LogEntry {     int entryType;@@ -45,6 +50,10 @@       const std::string & s, const Fields & fields, nix::ActivityId parent) override;   void stopActivity(nix::ActivityId act) override;   void result(nix::ActivityId act, nix::ResultType type, const Fields & fields) override;++#ifdef NIX_2_4+  void logEI(const nix::ErrorInfo &ei) override;+#endif    std::unique_ptr<LogEntry> pop();   void popMany(int max, std::queue<std::unique_ptr<LogEntry>> &out);
− cbits/hercules-store.cxx
@@ -1,271 +0,0 @@-#include <cstdio>-#include <cstring>-#include <math.h>-#include <nix/config.h>-#include <nix/shared.hh>-#include <nix/store-api.hh>-#include <nix/common-eval-args.hh>-#include <nix/get-drvs.hh>-#include <nix/derivations.hh>-#include <nix/affinity.hh>-#include <nix/globals.hh>--#include "hercules-store.hh"--using namespace nix;--WrappingStore::WrappingStore(const Params& params, ref<Store> storeToWrap)-    : Store(params), wrappedStore(storeToWrap) {}--WrappingStore::~WrappingStore() {}--std::string WrappingStore::getUri() {-  return "wrapped:" + wrappedStore->getUri();-};--bool WrappingStore::isValidPathUncached(const Path& path) {-  return wrappedStore->isValidPath(path);  // not ideal-}--PathSet WrappingStore::queryValidPaths(const PathSet& paths,-                                       SubstituteFlag maybeSubstitute) {-  return wrappedStore->queryValidPaths(paths, maybeSubstitute);-}-PathSet WrappingStore::queryAllValidPaths() {-  return wrappedStore->queryAllValidPaths();-}--// protected:-void WrappingStore::queryPathInfoUncached(-    const Path& path,-    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {-  unsupported("queryPathInfoUncached");-  /*-      Callback<ref<ValidPathInfo>>-     callback2([&callback](std::future<ref<ValidPathInfo>> vpi){-        std::shared_ptr<ValidPathInfo> !@#$%^&*-        callback(vpi);-      });-      return wrappedStore->queryPathInfo(path, callback2); // not ideal-  */-}--// public:--void WrappingStore::queryReferrers(const Path& path, PathSet& referrers) {-  wrappedStore->queryReferrers(path, referrers);-}--PathSet WrappingStore::queryValidDerivers(const Path& path) {-  return wrappedStore->queryValidDerivers(path);-}--PathSet WrappingStore::queryDerivationOutputs(const Path& path) {-  return wrappedStore->queryDerivationOutputs(path);-}--StringSet WrappingStore::queryDerivationOutputNames(const Path& path) {-  return wrappedStore->queryDerivationOutputNames(path);-}--Path WrappingStore::queryPathFromHashPart(const string& hashPart) {-  return wrappedStore->queryPathFromHashPart(hashPart);-}--PathSet WrappingStore::querySubstitutablePaths(const PathSet& paths) {-  return wrappedStore->querySubstitutablePaths(paths);-}--void WrappingStore::querySubstitutablePathInfos(const PathSet& paths,-                                                SubstitutablePathInfos& infos) {-  wrappedStore->querySubstitutablePathInfos(paths, infos);-}--bool WrappingStore::wantMassQuery() {-  return wrappedStore->wantMassQuery();-}--void WrappingStore::addToStore(const ValidPathInfo& info,-                               Source& narSource,-                               RepairFlag repair,-                               CheckSigsFlag checkSigs,-                               std::shared_ptr<FSAccessor> accessor) {-  wrappedStore->addToStore(info, narSource, repair, checkSigs, accessor);-}--void WrappingStore::addToStore(const ValidPathInfo& info,-                               const ref<std::string>& nar,-                               RepairFlag repair,-                               CheckSigsFlag checkSigs,-                               std::shared_ptr<FSAccessor> accessor) {-  wrappedStore->addToStore(info, nar, repair, checkSigs, accessor);-}--Path WrappingStore::addToStore(const string& name,-                               const Path& srcPath,-                               bool recursive,-                               HashType hashAlgo,-                               PathFilter& filter,-                               RepairFlag repair) {-  return wrappedStore->addToStore(name, srcPath, recursive, hashAlgo, filter,-                                  repair);-}--Path WrappingStore::addTextToStore(const string& name,-                                   const string& s,-                                   const PathSet& references,-                                   RepairFlag repair) {-  return wrappedStore->addTextToStore(name, s, references, repair);-}--void WrappingStore::narFromPath(const Path& path, Sink& sink) {-  wrappedStore->narFromPath(path, sink);-}--void WrappingStore::buildPaths(const PathSet& paths, BuildMode buildMode) {-  wrappedStore->buildPaths(paths, buildMode);-}--BuildResult WrappingStore::buildDerivation(const Path& drvPath,-                                           const BasicDerivation& drv,-                                           BuildMode buildMode) {-  return wrappedStore->buildDerivation(drvPath, drv, buildMode);-}--void WrappingStore::ensurePath(const Path& path) {-  wrappedStore->ensurePath(path);-}--void WrappingStore::addTempRoot(const Path& path) {-  wrappedStore->addTempRoot(path);-}--void WrappingStore::addIndirectRoot(const Path& path) {-  wrappedStore->addIndirectRoot(path);-}--void WrappingStore::syncWithGC() {-  wrappedStore->syncWithGC();-}--void WrappingStore::collectGarbage(const GCOptions& options,-                                   GCResults& results) {-  wrappedStore->collectGarbage(options, results);-}--void WrappingStore::optimiseStore() {-  wrappedStore->optimiseStore();-};--bool WrappingStore::verifyStore(bool checkContents, RepairFlag repair) {-  return wrappedStore->verifyStore(checkContents, repair);-};--ref<FSAccessor> WrappingStore::getFSAccessor() {-  return wrappedStore->getFSAccessor();-}--void WrappingStore::addSignatures(const Path& storePath,-                                  const StringSet& sigs) {-  wrappedStore->addSignatures(storePath, sigs);-};--void WrappingStore::computeFSClosure(const PathSet& paths,-                                     PathSet& out,-                                     bool flipDirection,-                                     bool includeOutputs,-                                     bool includeDerivers) {-  wrappedStore->computeFSClosure(paths, out, flipDirection, includeOutputs,-                                 includeDerivers);-}--void WrappingStore::queryMissing(const PathSet& targets,-                                 PathSet& willBuild,-                                 PathSet& willSubstitute,-                                 PathSet& unknown,-                                 unsigned long long& downloadSize,-                                 unsigned long long& narSize) {-  wrappedStore->queryMissing(targets, willBuild, willSubstitute, unknown,-                             downloadSize, narSize);-}--std::shared_ptr<std::string> WrappingStore::getBuildLog(const Path& path) {-  return wrappedStore->getBuildLog(path);-}--void WrappingStore::connect() {-  wrappedStore->connect();-};--int WrappingStore::getPriority() {-  return wrappedStore->getPriority();-}--Path WrappingStore::toRealPath(const Path& storePath) {-  return wrappedStore->toRealPath(storePath);-};--/////--HerculesStore::HerculesStore(const Params& params, ref<Store> storeToWrap)-    : WrappingStore(params, storeToWrap) {}--void HerculesStore::ensurePath(const Path& path) {-  /* We avoid asking substituters for paths, since-     those would yield negative pathInfo caches on remote store.--     Instead, we only assert if path exists in the store.--     Once IFD build is performed, we ask for substitution-     via ensurePath.-  */-  if (!wrappedStore->isValidPath(path)) {-    std::exception_ptr exceptionToThrow(nullptr);-    builderCallback(strdup(path.c_str()), &exceptionToThrow);-    if (exceptionToThrow != nullptr) {-      std::rethrow_exception(exceptionToThrow);-    }-    wrappedStore->ensurePath(path);-  }-  ensuredPaths.insert(path);-};--// Avoid substituting in evaluator, see `ensurePath` for more details-void HerculesStore::queryMissing(const PathSet& targets,-                                 PathSet& willBuild,-                                 PathSet& willSubstitute,-                                 PathSet& unknown,-                                 unsigned long long& downloadSize,-                                 unsigned long long& narSize) {-};--void HerculesStore::buildPaths(const PathSet& paths, BuildMode buildMode) {-  for (Path path : paths) {-    std::exception_ptr exceptionToThrow(nullptr);-    builderCallback(strdup(path.c_str()), &exceptionToThrow);-    if (exceptionToThrow != nullptr) {-      std::rethrow_exception(exceptionToThrow);-    }-  }-}--BuildResult HerculesStore::buildDerivation(const Path& drvPath,-                                           const BasicDerivation& drv,-                                           BuildMode buildMode) {-  unsupported("buildDerivation");--  std::cerr << "building derivation " << drvPath << std::endl;-  auto r = wrappedStore->buildDerivation(drvPath, drv, buildMode);-  std::cerr << "built derivation " << drvPath << std::endl;-  return r;-}--void HerculesStore::printDiagnostics() {-  for (std::string path : ensuredPaths) {-    std::cerr << path << std::endl;-  }-}--void HerculesStore::setBuilderCallback(void (* newBuilderCallback)(const char *, std::exception_ptr *exceptionToThrow)) {-  builderCallback = newBuilderCallback;-}
− cbits/hercules-store.hh
@@ -1,150 +0,0 @@-#include <cstdio>-#include <cstring>-#include <math.h>-#include <nix/config.h>-#include <nix/shared.hh>-#include <nix/store-api.hh>-#include <nix/common-eval-args.hh>-#include <nix/get-drvs.hh>-#include <nix/derivations.hh>-#include <nix/affinity.hh>-#include <nix/globals.hh>-#include "HsFFI.h"--using namespace nix;--class WrappingStore : public Store {- public:-  ref<Store> wrappedStore;--  WrappingStore(const Params & params, ref<Store> storeToWrap);---  virtual ~WrappingStore();--  virtual std::string getUri() override;--protected:--  virtual bool isValidPathUncached(const Path & path) override;--public:--  virtual PathSet queryValidPaths(const PathSet & paths,-      SubstituteFlag maybeSubstitute = NoSubstitute) override;-  virtual PathSet queryAllValidPaths() override;--protected:-  virtual void queryPathInfoUncached(const Path & path,-        Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;--public:--  virtual void queryReferrers(const Path & path,-      PathSet & referrers) override;--  virtual PathSet queryValidDerivers(const Path & path) override;--  virtual PathSet queryDerivationOutputs(const Path & path) override;--  virtual StringSet queryDerivationOutputNames(const Path & path) override;--  virtual Path queryPathFromHashPart(const string & hashPart) override;--  virtual PathSet querySubstitutablePaths(const PathSet & paths) override;--  virtual void querySubstitutablePathInfos(const PathSet & paths,-        SubstitutablePathInfos & infos) override;--  virtual bool wantMassQuery() override;--  virtual void addToStore(const ValidPathInfo & info, Source & narSource,-        RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,-        std::shared_ptr<FSAccessor> accessor = 0) override;--  virtual void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,-        RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,-        std::shared_ptr<FSAccessor> accessor = 0) override;--  virtual Path addToStore(const string & name, const Path & srcPath,-        bool recursive = true, HashType hashAlgo = htSHA256,-        PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;--  virtual Path addTextToStore(const string & name, const string & s,-        const PathSet & references, RepairFlag repair = NoRepair) override;--  virtual void narFromPath(const Path & path, Sink & sink) override;--  virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) override;--  virtual BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,-        BuildMode buildMode = bmNormal) override;--  virtual void ensurePath(const Path & path) override;--  virtual void addTempRoot(const Path & path) override;--  virtual void addIndirectRoot(const Path & path) override;--  virtual void syncWithGC() override;--  virtual void collectGarbage(const GCOptions & options, GCResults & results) override;--  virtual void optimiseStore() override;--  virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) override;--  virtual ref<FSAccessor> getFSAccessor() override;--  virtual void addSignatures(const Path & storePath, const StringSet & sigs) override;--    -  virtual void computeFSClosure(const PathSet & paths,-      PathSet & out, bool flipDirection = false,-      bool includeOutputs = false, bool includeDerivers = false) override;--  virtual void queryMissing(const PathSet & targets,-        PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,-        unsigned long long & downloadSize, unsigned long long & narSize) override;--  virtual std::shared_ptr<std::string> getBuildLog(const Path & path) override;--  virtual void connect() override;--  virtual int getPriority() override;--  virtual Path toRealPath(const Path & storePath) override;-};--class HerculesStore : public WrappingStore {-public:-  PathSet ensuredPaths;-  void (* builderCallback)(const char *, std::exception_ptr *exceptionToThrow);--  HerculesStore(const Params & params, ref<Store> storeToWrap);--  // Overrides--  virtual void ensurePath(const Path & path) override;--  virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) override;--  virtual BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,-        BuildMode buildMode = bmNormal) override;--  virtual void queryMissing(const PathSet& targets,-                            PathSet& willBuild,-                            PathSet& willSubstitute,-                            PathSet& unknown,-                            unsigned long long& downloadSize,-                            unsigned long long& narSize) override;--  // Additions--  void printDiagnostics();--  void setBuilderCallback(void (* newBuilderCallback)(const char *, std::exception_ptr *exceptionToThrow));--  void inhibitBuilds();-  void uninhibitBuilds();-};
+ cbits/nix-2.3/hercules-store.cxx view
@@ -0,0 +1,277 @@+#include <cstdio>+#include <cstring>+#include <math.h>+#include <nix/config.h>+#include <nix/shared.hh>+#include <nix/store-api.hh>+#include <nix/common-eval-args.hh>+#include <nix/get-drvs.hh>+#include <nix/derivations.hh>+#include <nix/affinity.hh>+#include <nix/globals.hh>++#include "hercules-store.hh"++using namespace nix;++WrappingStore::WrappingStore(const Params& params, ref<Store> storeToWrap)+    : Store(params), wrappedStore(storeToWrap) {}++WrappingStore::~WrappingStore() {}++std::string WrappingStore::getUri() {+  return "wrapped:" + wrappedStore->getUri();+};++bool WrappingStore::isValidPathUncached(const Path& path) {+  return wrappedStore->isValidPath(path);  // not ideal+}++PathSet WrappingStore::queryValidPaths(const PathSet& paths,+                                       SubstituteFlag maybeSubstitute) {+  return wrappedStore->queryValidPaths(paths, maybeSubstitute);+}+PathSet WrappingStore::queryAllValidPaths() {+  return wrappedStore->queryAllValidPaths();+}++// protected:+void WrappingStore::queryPathInfoUncached(+    const Path& path,+    Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept {+  unsupported("queryPathInfoUncached");+  /*+      Callback<ref<ValidPathInfo>>+     callback2([&callback](std::future<ref<ValidPathInfo>> vpi){+        std::shared_ptr<ValidPathInfo> !@#$%^&*+        callback(vpi);+      });+      return wrappedStore->queryPathInfo(path, callback2); // not ideal+  */+}++// public:++void WrappingStore::queryReferrers(const Path& path, PathSet& referrers) {+  wrappedStore->queryReferrers(path, referrers);+}++PathSet WrappingStore::queryValidDerivers(const Path& path) {+  return wrappedStore->queryValidDerivers(path);+}++PathSet WrappingStore::queryDerivationOutputs(const Path& path) {+  return wrappedStore->queryDerivationOutputs(path);+}++StringSet WrappingStore::queryDerivationOutputNames(const Path& path) {+  return wrappedStore->queryDerivationOutputNames(path);+}++Path WrappingStore::queryPathFromHashPart(const string& hashPart) {+  return wrappedStore->queryPathFromHashPart(hashPart);+}++PathSet WrappingStore::querySubstitutablePaths(const PathSet& paths) {+  return wrappedStore->querySubstitutablePaths(paths);+}++void WrappingStore::querySubstitutablePathInfos(const PathSet& paths,+                                                SubstitutablePathInfos& infos) {+  wrappedStore->querySubstitutablePathInfos(paths, infos);+}++bool WrappingStore::wantMassQuery() {+  return wrappedStore->wantMassQuery();+}++void WrappingStore::addToStore(const ValidPathInfo& info,+                               Source& narSource,+                               RepairFlag repair,+                               CheckSigsFlag checkSigs,+                               std::shared_ptr<FSAccessor> accessor) {+  wrappedStore->addToStore(info, narSource, repair, checkSigs, accessor);+}++void WrappingStore::addToStore(const ValidPathInfo& info,+                               const ref<std::string>& nar,+                               RepairFlag repair,+                               CheckSigsFlag checkSigs,+                               std::shared_ptr<FSAccessor> accessor) {+  wrappedStore->addToStore(info, nar, repair, checkSigs, accessor);+}++Path WrappingStore::addToStore(const string& name,+                               const Path& srcPath,+                               bool recursive,+                               HashType hashAlgo,+                               PathFilter& filter,+                               RepairFlag repair) {+  return wrappedStore->addToStore(name, srcPath, recursive, hashAlgo, filter,+                                  repair);+}++Path WrappingStore::addTextToStore(const string& name,+                                   const string& s,+                                   const PathSet& references,+                                   RepairFlag repair) {+  return wrappedStore->addTextToStore(name, s, references, repair);+}++void WrappingStore::narFromPath(const Path& path, Sink& sink) {+  wrappedStore->narFromPath(path, sink);+}++void WrappingStore::buildPaths(const PathSet& paths, BuildMode buildMode) {+  wrappedStore->buildPaths(paths, buildMode);+}++BuildResult WrappingStore::buildDerivation(const Path& drvPath,+                                           const BasicDerivation& drv,+                                           BuildMode buildMode) {+  return wrappedStore->buildDerivation(drvPath, drv, buildMode);+}++void WrappingStore::ensurePath(const Path& path) {+  wrappedStore->ensurePath(path);+}++void WrappingStore::addTempRoot(const Path& path) {+  wrappedStore->addTempRoot(path);+}++void WrappingStore::addIndirectRoot(const Path& path) {+  wrappedStore->addIndirectRoot(path);+}++void WrappingStore::syncWithGC() {+  wrappedStore->syncWithGC();+}++void WrappingStore::collectGarbage(const GCOptions& options,+                                   GCResults& results) {+  wrappedStore->collectGarbage(options, results);+}++void WrappingStore::optimiseStore() {+  wrappedStore->optimiseStore();+};++bool WrappingStore::verifyStore(bool checkContents, RepairFlag repair) {+  return wrappedStore->verifyStore(checkContents, repair);+};++ref<FSAccessor> WrappingStore::getFSAccessor() {+  return wrappedStore->getFSAccessor();+}++void WrappingStore::addSignatures(const Path& storePath,+                                  const StringSet& sigs) {+  wrappedStore->addSignatures(storePath, sigs);+};++void WrappingStore::computeFSClosure(const PathSet& paths,+                                     PathSet& out,+                                     bool flipDirection,+                                     bool includeOutputs,+                                     bool includeDerivers) {+  wrappedStore->computeFSClosure(paths, out, flipDirection, includeOutputs,+                                 includeDerivers);+}++void WrappingStore::queryMissing(const PathSet& targets,+                                 PathSet& willBuild,+                                 PathSet& willSubstitute,+                                 PathSet& unknown,+                                 unsigned long long& downloadSize,+                                 unsigned long long& narSize) {+  wrappedStore->queryMissing(targets, willBuild, willSubstitute, unknown,+                             downloadSize, narSize);+}++std::shared_ptr<std::string> WrappingStore::getBuildLog(const Path& path) {+  return wrappedStore->getBuildLog(path);+}++void WrappingStore::connect() {+  wrappedStore->connect();+};++int WrappingStore::getPriority() {+  return wrappedStore->getPriority();+}++Path WrappingStore::toRealPath(const Path& storePath) {+  return wrappedStore->toRealPath(storePath);+};++/////++HerculesStore::HerculesStore(const Params& params, ref<Store> storeToWrap)+    : WrappingStore(params, storeToWrap) {}++void HerculesStore::ensurePath(const Path& path) {+  /* We avoid asking substituters for paths, since+     those would yield negative pathInfo caches on remote store.++     Instead, we only assert if path exists in the store.++     Once IFD build is performed, we ask for substitution+     via ensurePath.+  */+  if (!wrappedStore->isValidPath(path)) {+    std::exception_ptr exceptionToThrow(nullptr);+    auto pathWO = nix::parsePathWithOutputs(*this, path);+    auto pathsPtr = new std::vector<StorePathWithOutputs>{pathWO};+    // TODO place outside the loop+    builderCallback(pathsPtr, &exceptionToThrow);+    if (exceptionToThrow != nullptr) {+      std::rethrow_exception(exceptionToThrow);+    }+    wrappedStore->ensurePath(path);+  }+  ensuredPaths.insert(path);+};++// Avoid substituting in evaluator, see `ensurePath` for more details+void HerculesStore::queryMissing(const PathSet& targets,+                                 PathSet& willBuild,+                                 PathSet& willSubstitute,+                                 PathSet& unknown,+                                 unsigned long long& downloadSize,+                                 unsigned long long& narSize) {+};++void HerculesStore::buildPaths(const PathSet& paths, BuildMode buildMode) {+  for (Path path : paths) {+    std::exception_ptr exceptionToThrow(nullptr);+    auto pathWO = nix::parsePathWithOutputs(*this, path);+    auto pathsPtr = new std::vector<StorePathWithOutputs>{pathWO};+    // TODO place outside the loop+    builderCallback(pathsPtr, &exceptionToThrow);+    if (exceptionToThrow != nullptr) {+      std::rethrow_exception(exceptionToThrow);+    }+  }+}++BuildResult HerculesStore::buildDerivation(const Path& drvPath,+                                           const BasicDerivation& drv,+                                           BuildMode buildMode) {+  unsupported("buildDerivation");++  std::cerr << "building derivation " << drvPath << std::endl;+  auto r = wrappedStore->buildDerivation(drvPath, drv, buildMode);+  std::cerr << "built derivation " << drvPath << std::endl;+  return r;+}++void HerculesStore::printDiagnostics() {+  for (std::string path : ensuredPaths) {+    std::cerr << path << std::endl;+  }+}++void HerculesStore::setBuilderCallback(void (* newBuilderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow)) {+  builderCallback = newBuilderCallback;+}
+ cbits/nix-2.3/hercules-store.hh view
@@ -0,0 +1,151 @@+#include <cstdio>+#include <cstring>+#include <math.h>+#include <nix/config.h>+#include <nix/shared.hh>+#include <nix/store-api.hh>+#include <nix/common-eval-args.hh>+#include <nix/get-drvs.hh>+#include <nix/derivations.hh>+#include <nix/affinity.hh>+#include <nix/globals.hh>+#include <nix-compat.hh>+#include "HsFFI.h"++using namespace nix;++class WrappingStore : public Store {+ public:+  ref<Store> wrappedStore;++  WrappingStore(const Params & params, ref<Store> storeToWrap);+++  virtual ~WrappingStore();++  virtual std::string getUri() override;++protected:++  virtual bool isValidPathUncached(const Path & path) override;++public:++  virtual PathSet queryValidPaths(const PathSet & paths,+      SubstituteFlag maybeSubstitute = NoSubstitute) override;+  virtual PathSet queryAllValidPaths() override;++protected:+  virtual void queryPathInfoUncached(const Path & path,+        Callback<std::shared_ptr<ValidPathInfo>> callback) noexcept override;++public:++  virtual void queryReferrers(const Path & path,+      PathSet & referrers) override;++  virtual PathSet queryValidDerivers(const Path & path) override;++  virtual PathSet queryDerivationOutputs(const Path & path) override;++  virtual StringSet queryDerivationOutputNames(const Path & path) override;++  virtual Path queryPathFromHashPart(const string & hashPart) override;++  virtual PathSet querySubstitutablePaths(const PathSet & paths) override;++  virtual void querySubstitutablePathInfos(const PathSet & paths,+        SubstitutablePathInfos & infos) override;++  virtual bool wantMassQuery() override;++  virtual void addToStore(const ValidPathInfo & info, Source & narSource,+        RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,+        std::shared_ptr<FSAccessor> accessor = 0) override;++  virtual void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,+        RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,+        std::shared_ptr<FSAccessor> accessor = 0) override;++  virtual Path addToStore(const string & name, const Path & srcPath,+        bool recursive = true, HashType hashAlgo = htSHA256,+        PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;++  virtual Path addTextToStore(const string & name, const string & s,+        const PathSet & references, RepairFlag repair = NoRepair) override;++  virtual void narFromPath(const Path & path, Sink & sink) override;++  virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) override;++  virtual BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,+        BuildMode buildMode = bmNormal) override;++  virtual void ensurePath(const Path & path) override;++  virtual void addTempRoot(const Path & path) override;++  virtual void addIndirectRoot(const Path & path) override;++  virtual void syncWithGC() override;++  virtual void collectGarbage(const GCOptions & options, GCResults & results) override;++  virtual void optimiseStore() override;++  virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) override;++  virtual ref<FSAccessor> getFSAccessor() override;++  virtual void addSignatures(const Path & storePath, const StringSet & sigs) override;++    +  virtual void computeFSClosure(const PathSet & paths,+      PathSet & out, bool flipDirection = false,+      bool includeOutputs = false, bool includeDerivers = false) override;++  virtual void queryMissing(const PathSet & targets,+        PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,+        unsigned long long & downloadSize, unsigned long long & narSize) override;++  virtual std::shared_ptr<std::string> getBuildLog(const Path & path) override;++  virtual void connect() override;++  virtual int getPriority() override;++  virtual Path toRealPath(const Path & storePath) override;+};++class HerculesStore : public WrappingStore {+public:+  PathSet ensuredPaths;+  void (* builderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow);++  HerculesStore(const Params & params, ref<Store> storeToWrap);++  // Overrides++  virtual void ensurePath(const Path & path) override;++  virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) override;++  virtual BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,+        BuildMode buildMode = bmNormal) override;++  virtual void queryMissing(const PathSet& targets,+                            PathSet& willBuild,+                            PathSet& willSubstitute,+                            PathSet& unknown,+                            unsigned long long& downloadSize,+                            unsigned long long& narSize) override;++  // Additions++  void printDiagnostics();++  void setBuilderCallback(void (* newBuilderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow));++  void inhibitBuilds();+  void uninhibitBuilds();+};
+ cbits/nix-2.4/hercules-store.cxx view
@@ -0,0 +1,276 @@+#include <cstdio>+#include <cstring>+#include <memory>+#include <math.h>+#include <nix/config.h>+#include <nix/shared.hh>+#include <nix/store-api.hh>+#include <nix/common-eval-args.hh>+#include <nix/get-drvs.hh>+#include <nix/derivations.hh>+#include <nix/affinity.hh>+#include <nix/globals.hh>+#include <nix/callback.hh>++#include "hercules-store.hh"++using namespace nix;++WrappingStore::WrappingStore(const Params& params, ref<Store> storeToWrap)+    : Store(params), wrappedStore(storeToWrap) {}++WrappingStore::~WrappingStore() {}++std::string WrappingStore::getUri() {+  return "wrapped:" + wrappedStore->getUri();+};++bool WrappingStore::isValidPathUncached(const StorePath & path) {+  return wrappedStore->isValidPath(path);  // caches again. Not much we can do.+}++StorePathSet WrappingStore::queryValidPaths(const StorePathSet& paths,+                                       SubstituteFlag maybeSubstitute) {+  return wrappedStore->queryValidPaths(paths, maybeSubstitute);+}+StorePathSet WrappingStore::queryAllValidPaths() {+  return wrappedStore->queryAllValidPaths();+}++// protected:+void WrappingStore::queryPathInfoUncached(const StorePath & path,+      Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept {++  auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));++  wrappedStore->queryPathInfo(path, {[=](std::future<ref<const ValidPathInfo>> vpi){+    (*callbackPtr)(vpi.get().get_ptr());+  }});+}++// public:++void WrappingStore::queryReferrers(const StorePath& path, StorePathSet& referrers) {+  wrappedStore->queryReferrers(path, referrers);+}++StorePathSet WrappingStore::queryValidDerivers(const StorePath& path) {+  return wrappedStore->queryValidDerivers(path);+}++StorePathSet WrappingStore::queryDerivationOutputs(const StorePath& path) {+  return wrappedStore->queryDerivationOutputs(path);+}++std::optional<StorePath> WrappingStore::queryPathFromHashPart(const std::string & hashPart) {+  return wrappedStore->queryPathFromHashPart(hashPart);+}++StorePathSet WrappingStore::querySubstitutablePaths(const StorePathSet& paths) {+  return wrappedStore->querySubstitutablePaths(paths);+}++void WrappingStore::querySubstitutablePathInfos(const StorePathCAMap & paths,+      SubstitutablePathInfos & infos) {+  wrappedStore->querySubstitutablePathInfos(paths, infos);+}++void WrappingStore::addToStore(const ValidPathInfo & info, Source & narSource,+    RepairFlag repair, CheckSigsFlag checkSigs) {+  wrappedStore->addToStore(info, narSource, repair, checkSigs);+}++StorePath WrappingStore::addToStore(const string & name, const Path & srcPath,+      FileIngestionMethod method, HashType hashAlgo,+      PathFilter & filter, RepairFlag repair) {+  return wrappedStore->addToStore(name, srcPath, method, hashAlgo, filter, repair);+}++StorePath WrappingStore::addToStoreFromDump(Source & dump, const string & name,+      FileIngestionMethod method, HashType hashAlgo, RepairFlag repair) {+  return wrappedStore->addToStoreFromDump(dump, name, method, hashAlgo, repair);+}++StorePath WrappingStore::addTextToStore(const string & name, const string & s,+      const StorePathSet & references, RepairFlag repair) {+  return wrappedStore->addTextToStore(name, s, references, repair);+}++void WrappingStore::narFromPath(const StorePath& path, Sink& sink) {+  wrappedStore->narFromPath(path, sink);+}++void WrappingStore::buildPaths(+      const std::vector<DerivedPath> & paths, BuildMode buildMode) {+  wrappedStore->buildPaths(paths, buildMode);+}++BuildResult WrappingStore::buildDerivation(const StorePath& drvPath,+                                           const BasicDerivation& drv,+                                           BuildMode buildMode) {+  return wrappedStore->buildDerivation(drvPath, drv, buildMode);+}++void WrappingStore::ensurePath(const StorePath& path) {+  wrappedStore->ensurePath(path);+}++void WrappingStore::addTempRoot(const StorePath& path) {+  wrappedStore->addTempRoot(path);+}++void WrappingStore::addIndirectRoot(const Path& path) {+  wrappedStore->addIndirectRoot(path);+}++void WrappingStore::syncWithGC() {+  wrappedStore->syncWithGC();+}++void WrappingStore::collectGarbage(const GCOptions& options,+                                   GCResults& results) {+  wrappedStore->collectGarbage(options, results);+}++void WrappingStore::optimiseStore() {+  wrappedStore->optimiseStore();+};++bool WrappingStore::verifyStore(bool checkContents, RepairFlag repair) {+  return wrappedStore->verifyStore(checkContents, repair);+};++ref<FSAccessor> WrappingStore::getFSAccessor() {+  return wrappedStore->getFSAccessor();+}++void WrappingStore::addSignatures(const StorePath& storePath,+                                  const StringSet& sigs) {+  wrappedStore->addSignatures(storePath, sigs);+};++void WrappingStore::computeFSClosure(const StorePathSet& paths,+                                     StorePathSet& out,+                                     bool flipDirection,+                                     bool includeOutputs,+                                     bool includeDerivers) {+  wrappedStore->computeFSClosure(paths, out, flipDirection, includeOutputs,+                                 includeDerivers);+}++void WrappingStore::queryMissing(const std::vector<DerivedPath> & targets,+      StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,+      uint64_t & downloadSize, uint64_t & narSize) {+  wrappedStore->queryMissing(targets, willBuild, willSubstitute, unknown,+                             downloadSize, narSize);+}++std::shared_ptr<std::string> WrappingStore::getBuildLog(const StorePath& path) {+  return wrappedStore->getBuildLog(path);+}++void WrappingStore::connect() {+  wrappedStore->connect();+};++Path WrappingStore::toRealPath(const Path& storePath) {+  return wrappedStore->toRealPath(storePath);+};++Roots WrappingStore::findRoots(bool censor) {+  return wrappedStore->findRoots(censor);+}++unsigned int WrappingStore::getProtocol() {+  return wrappedStore->getProtocol();+}++void WrappingStore::createUser(const std::string & userName, uid_t userId) {+  wrappedStore->createUser(userName, userId);+}++/////++HerculesStore::HerculesStore(const Params& params, ref<Store> storeToWrap)+    : StoreConfig(params)+    , WrappingStore(params, storeToWrap) {}++const std::string HerculesStore::name() {+  return "wrapped " + wrappedStore->name();+}++std::optional<const Realisation> HerculesStore::queryRealisation(const DrvOutput &drvOut) {+  // TODO (ca-derivations) use?+  return wrappedStore->queryRealisation(drvOut);+}++void HerculesStore::ensurePath(const StorePath& path) {+  /* We avoid asking substituters for paths, since+     those would yield negative pathInfo caches on remote store.++     Instead, we only assert if path exists in the store.++     Once IFD build is performed, we ask for substitution+     via ensurePath.+  */+  if (!wrappedStore->isValidPath(path)) {+    std::exception_ptr exceptionToThrow(nullptr);+    // FIXME probably need this+    // builderCallback(path, &exceptionToThrow);+    if (exceptionToThrow != nullptr) {+      std::rethrow_exception(exceptionToThrow);+    }+    wrappedStore->ensurePath(path);+  }+  ensuredPaths.insert(path);+};++// Avoid substituting in evaluator, see `ensurePath` for more details+void HerculesStore::queryMissing(const std::vector<DerivedPath> & targets,+      StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,+      uint64_t & downloadSize, uint64_t & narSize) {+};++void HerculesStore::buildPaths(const std::vector<DerivedPath> & derivedPaths, BuildMode buildMode) {+  std::exception_ptr exceptionToThrow(nullptr);++  // responsibility for delete is transferred to builderCallback+  auto pathsPtr = new std::vector<StorePathWithOutputs>();+  std::vector<StorePathWithOutputs> &paths = *pathsPtr;++  // TODO: don't ignore the Opaques+  for (auto & derivedPath : derivedPaths) {+    std::visit(overloaded {+      [&](DerivedPath::Built built) {+          paths.push_back(StorePathWithOutputs { built.drvPath, built.outputs });+      },+      [&](DerivedPath::Opaque opaque) {+          // TODO: pass this to the callback as well+      },+    }, derivedPath.raw());+  }++  builderCallback(pathsPtr, &exceptionToThrow);++  if (exceptionToThrow != nullptr) {+    std::rethrow_exception(exceptionToThrow);+  }+}++BuildResult HerculesStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,+    BuildMode buildMode) {++  unsupported("buildDerivation");+  // unreachable+  return wrappedStore->buildDerivation(drvPath, drv, buildMode);+}++void HerculesStore::printDiagnostics() {+  for (auto path : ensuredPaths) {+    std::cerr << path.to_string() << std::endl;+  }+}++void HerculesStore::setBuilderCallback(void (* newBuilderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow)) {+  builderCallback = newBuilderCallback;+}
+ cbits/nix-2.4/hercules-store.hh view
@@ -0,0 +1,154 @@+#include <cstdio>+#include <cstring>+#include <math.h>+#include <nix/config.h>+#include <nix/shared.hh>+#include <nix/store-api.hh>+#include <nix/common-eval-args.hh>+#include <nix/get-drvs.hh>+#include <nix/derivations.hh>+#include <nix/affinity.hh>+#include <nix/globals.hh>+#include <nix-compat.hh>+#include "HsFFI.h"++using namespace nix;++class WrappingStore : public Store {+ public:+  ref<Store> wrappedStore;++  WrappingStore(const Params & params, ref<Store> storeToWrap);+++  virtual ~WrappingStore();++  virtual std::string getUri() override;++protected:++  virtual bool isValidPathUncached(const StorePath & path) override;++public:++  virtual StorePathSet queryValidPaths(const StorePathSet & paths,+      SubstituteFlag maybeSubstitute = NoSubstitute) override;+  virtual StorePathSet queryAllValidPaths() override;++protected:+  virtual void queryPathInfoUncached(const StorePath & path,+      Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;++public:++  virtual void queryReferrers(const StorePath & path,+      StorePathSet & referrers) override;++  virtual StorePathSet queryValidDerivers(const StorePath & path) override;++  virtual StorePathSet queryDerivationOutputs(const StorePath & path) override;++  virtual std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;++  virtual StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;++  virtual void querySubstitutablePathInfos(const StorePathCAMap & paths,+      SubstitutablePathInfos & infos) override;++  virtual void addToStore(const ValidPathInfo & info, Source & narSource,+      RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) override;++  virtual StorePath addToStore(const string & name, const Path & srcPath,+      FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,+      PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair) override;++  virtual StorePath addToStoreFromDump(Source & dump, const string & name,+      FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair) override;++  virtual StorePath addTextToStore(const string & name, const string & s,+      const StorePathSet & references, RepairFlag repair = NoRepair) override;++  virtual void narFromPath(const StorePath & path, Sink & sink) override;++  virtual void buildPaths(+      const std::vector<DerivedPath> & paths,+      BuildMode buildMode = bmNormal) override;++  virtual BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,+      BuildMode buildMode = bmNormal) override;++  virtual void ensurePath(const StorePath & path) override;++  virtual void addTempRoot(const StorePath & path) override;++  virtual void addIndirectRoot(const Path & path) override;++  virtual void syncWithGC() override;++  virtual Roots findRoots(bool censor) override;++  virtual void collectGarbage(const GCOptions & options, GCResults & results) override;++  virtual void optimiseStore() override;++  virtual bool verifyStore(bool checkContents, RepairFlag repair = NoRepair) override;++  virtual ref<FSAccessor> getFSAccessor() override;++  virtual void addSignatures(const StorePath & storePath, const StringSet & sigs) override;++  virtual void computeFSClosure(const StorePathSet & paths,+      StorePathSet & out, bool flipDirection = false,+      bool includeOutputs = false, bool includeDerivers = false) override;++  virtual void queryMissing(const std::vector<DerivedPath> & targets,+      StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,+      uint64_t & downloadSize, uint64_t & narSize) override;++  virtual std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;++  virtual unsigned int getProtocol() override;++  virtual void connect() override;++  virtual Path toRealPath(const Path & storePath) override;++  virtual void createUser(const std::string & userName, uid_t userId) override;++};++class HerculesStore : public WrappingStore {+public:+  StorePathSet ensuredPaths;+  void (* builderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow);++  HerculesStore(const Params & params, ref<Store> storeToWrap);++  virtual const std::string name() override;++  // Overrides++  virtual std::optional<const Realisation> queryRealisation(const DrvOutput &) override;++  virtual void ensurePath(const StorePath & path) override;++  virtual void buildPaths(+      const std::vector<DerivedPath> & paths,+      BuildMode buildMode = bmNormal) override;++  virtual BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,+      BuildMode buildMode = bmNormal) override;++  virtual void queryMissing(const std::vector<DerivedPath> & targets,+      StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown,+      uint64_t & downloadSize, uint64_t & narSize) override;++  // Additions++  void printDiagnostics();++  void setBuilderCallback(void (* newBuilderCallback)(std::vector<nix::StorePathWithOutputs>*, std::exception_ptr *exceptionToThrow));++  void inhibitBuilds();+  void uninhibitBuilds();+};
hercules-ci-agent-worker/Hercules/Agent/Worker.hs view
@@ -14,7 +14,6 @@ import Control.Monad.Except import Control.Monad.IO.Unlift import Control.Monad.Trans.Control-import qualified Data.ByteString as BS import qualified Data.Conduit import Data.Conduit.Extras (sinkChan, sinkChanTerminate, sourceChan) import Data.Conduit.Katip.Orphans ()@@ -64,6 +63,9 @@ import Hercules.CNix.Expr.Context (EvalState) import qualified Hercules.CNix.Expr.Raw import Hercules.CNix.Expr.Typed (Value)+import Hercules.CNix.Std.Vector (StdVector)+import qualified Hercules.CNix.Std.Vector as Std.Vector+import Hercules.CNix.Store.Context (NixStorePathWithOutputs) import Hercules.CNix.Util (triggerInterrupt) import Hercules.Error import Katip@@ -82,8 +84,8 @@ import qualified Prelude  data HerculesState = HerculesState-  { drvsCompleted :: TVar (Map Text (UUID, BuildResult.BuildStatus)),-    drvsInProgress :: IORef (Set Text),+  { drvsCompleted :: TVar (Map StorePath (UUID, BuildResult.BuildStatus)),+    drvsInProgress :: IORef (Set StorePath),     herculesStore :: Ptr (Ref HerculesStore),     wrappedStore :: Store,     shortcutChannel :: Chan (Maybe Event)@@ -252,7 +254,8 @@                 logLocM                   DebugS                   "Received remote build result"-              liftIO $ atomically $ modifyTVar (drvsCompleted herculesState) (M.insert path (attempt, result))+              storePath <- liftIO $ CNix.parseStorePath (wrappedStore herculesState) (encodeUtf8 path)+              liftIO $ atomically $ modifyTVar (drvsCompleted herculesState) (M.insert storePath (attempt, result))             _ -> pass     Command.Build build ->       katipAddNamespace "Build" $@@ -371,7 +374,7 @@     Eval.LiteralArg s -> ["--argstr", encodeUtf8 k, s]     Eval.ExprArg s -> ["--arg", encodeUtf8 k, s] -withDrvInProgress :: MonadUnliftIO m => HerculesState -> Text -> m a -> m a+withDrvInProgress :: MonadUnliftIO m => HerculesState -> StorePath -> m a -> m a withDrvInProgress HerculesState {drvsInProgress = ref} drvPath =   bracket acquire release . const   where@@ -435,54 +438,66 @@   UnliftIO unlift <- lift askUnliftIO   let decode = decodeUtf8With lenientDecode   liftIO $-    setBuilderCallback hStore $ \path -> unlift $-      katipAddContext (sl "fullpath" (decode path)) $ do-        logLocM DebugS "Building"-        let (plainDrv, bangOut) = BS.span (/= fromIntegral (ord '!')) path-            outputName = BS.dropWhile (== fromIntegral (ord '!')) bangOut-            plainDrvText = decode plainDrv-        withDrvInProgress st plainDrvText $ do-          liftIO $ writeChan shortcutChan $ Just $ Event.Build plainDrvText (decode outputName) Nothing-          derivation <- liftIO $ getDerivation store plainDrv-          outputPath <- liftIO $ getDerivationOutputPath derivation outputName-          katipAddContext (sl "outputPath" (decode outputPath)) $-            logLocM DebugS "Naively calling ensurePath"-          liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e0 -> do-            katipAddContext (sl "message" (show (e0 :: SomeException) :: Text)) $-              logLocM DebugS "Recovering from failed wrapped.ensurePath"-            (attempt0, result) <--              liftIO $-                atomically $ do-                  c <- readTVar drvsCompl-                  anyAlternative $ M.lookup plainDrvText c-            liftIO $ maybeThrowBuildException result plainDrvText-            liftIO clearSubstituterCaches-            liftIO $ clearPathInfoCache store-            liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e1 -> do-              katipAddContext (sl "message" (show (e1 :: SomeException) :: Text)) $-                logLocM DebugS "Recovering from fresh ensurePath"-              liftIO $ writeChan shortcutChan $ Just $ Event.Build plainDrvText (decode outputName) (Just attempt0)-              -- TODO sync-              result' <--                liftIO $-                  atomically $ do-                    c <- readTVar drvsCompl-                    (attempt1, r) <- anyAlternative $ M.lookup plainDrvText c-                    guard (attempt1 /= attempt0)-                    pure r-              liftIO $ maybeThrowBuildException result' plainDrvText-              liftIO clearSubstituterCaches-              liftIO $ clearPathInfoCache store-              liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e2 ->-                liftIO $-                  throwIO $-                    BuildException-                      plainDrvText-                      ( Just $-                          "It could not be retrieved on the evaluating agent, despite a successful rebuild. Exception: "-                            <> show (e2 :: SomeException)-                      )-        logLocM DebugS "Built"+    setBuilderCallback hStore $+      traverseSPWOs $ \storePathWithOutputs -> unlift $ do+        drvStorePath <- liftIO $ getStorePath storePathWithOutputs+        drvPath <- liftIO $ CNix.storePathToPath store drvStorePath+        let pathText = decode drvPath+        outputs <- liftIO $ getOutputs storePathWithOutputs+        katipAddContext (sl "fullpath" pathText) $+          for_ outputs $ \outputName -> do+            logLocM DebugS "Building"+            withDrvInProgress st drvStorePath $ do+              liftIO $ writeChan shortcutChan $ Just $ Event.Build drvPath (decode outputName) Nothing+              derivation <- liftIO $ getDerivation store drvStorePath+              drvName <- liftIO $ getDerivationNameFromPath drvStorePath+              drvOutputs <- liftIO $ getDerivationOutputs store drvName derivation+              outputPath <-+                case find (\o -> derivationOutputName o == outputName) drvOutputs of+                  Nothing -> panic $ "output " <> show outputName <> " does not exist on " <> pathText+                  Just o -> case derivationOutputPath o of+                    Just x -> pure x+                    Nothing ->+                      -- FIXME ca-derivations+                      panic $ "output path unknown for output " <> show outputName <> " on " <> pathText <> ". ca-derivations is not supported yet."+              katipAddContext (sl "outputPath" (show outputPath :: Text)) $+                logLocM DebugS "Naively calling ensurePath"+              liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e0 -> do+                katipAddContext (sl "message" (show (e0 :: SomeException) :: Text)) $+                  logLocM DebugS "Recovering from failed wrapped.ensurePath"+                (attempt0, result) <-+                  liftIO $+                    atomically $ do+                      c <- readTVar drvsCompl+                      anyAlternative $ M.lookup drvStorePath c+                liftIO $ maybeThrowBuildException result (decode drvPath)+                liftIO clearSubstituterCaches+                liftIO $ clearPathInfoCache store+                liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e1 -> do+                  katipAddContext (sl "message" (show (e1 :: SomeException) :: Text)) $+                    logLocM DebugS "Recovering from fresh ensurePath"+                  liftIO $ writeChan shortcutChan $ Just $ Event.Build drvPath (decode outputName) (Just attempt0)+                  -- TODO sync+                  result' <-+                    liftIO $+                      atomically $ do+                        c <- readTVar drvsCompl+                        (attempt1, r) <- anyAlternative $ M.lookup drvStorePath c+                        guard (attempt1 /= attempt0)+                        pure r+                  liftIO $ maybeThrowBuildException result' (decode drvPath)+                  liftIO clearSubstituterCaches+                  liftIO $ clearPathInfoCache store+                  liftIO (ensurePath (wrappedStore st) outputPath) `catch` \e2 ->+                    liftIO $+                      throwIO $+                        BuildException+                          (decode drvPath)+                          ( Just $+                              "It could not be retrieved on the evaluating agent, despite a successful rebuild. Exception: "+                                <> show (e2 :: SomeException)+                          )+            logLocM DebugS "Built"   withEvalStateConduit store $ \evalState -> do     katipAddContext (sl "storeURI" (decode s)) $       logLocM DebugS "EvalState loaded."@@ -493,16 +508,22 @@       do         imprt <- liftIO $ evalFile evalState (toS $ Eval.file eval)         applied <- liftIO (autoCallFunction evalState imprt args)-        walk evalState args applied+        walk store evalState args applied     yield Event.EvaluationDone +traverseSPWOs :: (StorePathWithOutputs -> IO ()) -> StdVector NixStorePathWithOutputs -> IO ()+traverseSPWOs f v = do+  v' <- Std.Vector.toListFP v+  traverse_ f v'+ walk ::   (MonadUnliftIO m, KatipContext m) =>+  Store ->   Ptr EvalState ->   Value NixAttrs ->   RawValue ->   ConduitT i Event m ()-walk evalState = walk' True [] 10+walk store evalState = walk' True [] 10   where     handleErrors path = Data.Conduit.handleC (yieldAttributeError path)     walk' ::@@ -531,7 +552,8 @@                 isDeriv <- liftIO $ isDerivation evalState v                 if isDeriv                   then do-                    drvPath <- getDrvFile evalState v+                    drvStorePath <- getDrvFile evalState v+                    drvPath <- liftIO $ CNix.storePathToPath store drvStorePath                     typE <- runExceptT do                       isEffect <- liftEitherAs Left =<< liftIO (getAttrBool evalState attrValue "isEffect")                       case isEffect of
hercules-ci-agent-worker/Hercules/Agent/Worker/Build.hs view
@@ -5,7 +5,6 @@  import Conduit import Data.Conduit.Katip.Orphans ()-import Foreign (ForeignPtr) import Hercules.Agent.Worker.Build.Prefetched (buildDerivation) import qualified Hercules.Agent.Worker.Build.Prefetched as Build import qualified Hercules.Agent.WorkerProtocol.Command.Build as Command.Build@@ -17,8 +16,7 @@     getDerivationOutputs,   ) import qualified Hercules.CNix as CNix-import Hercules.CNix.Store (Store, queryPathInfo, validPathInfoNarHash, validPathInfoNarSize)-import Hercules.CNix.Store.Context (Derivation)+import Hercules.CNix.Store (Store, queryPathInfo, validPathInfoNarHash32, validPathInfoNarSize) import Katip import Protolude hiding (yield) @@ -26,8 +24,10 @@ runBuild store build = do   let extraPaths = Command.Build.inputDerivationOutputPaths build       drvPath = encodeUtf8 $ Command.Build.drvPath build-  x <- for extraPaths $ \input -> do-    liftIO $ try $ CNix.ensurePath store input+  drvStorePath <- liftIO $ CNix.parseStorePath store drvPath+  x <- for extraPaths $ \input -> liftIO $ do+    storePath <- CNix.parseStorePath store input+    try $ CNix.ensurePath store storePath   materialize <- case sequenceA x of     Right _ ->       -- no error, proceed with requested materialization setting@@ -36,32 +36,35 @@       CNix.logInfo $ "while retrieving dependencies: " <> toS (displayException e)       CNix.logInfo "unable to retrieve dependency; attempting fallback to local build"       pure True-  derivationMaybe <- liftIO $ Build.getDerivation store drvPath+  drvName <- liftIO $ CNix.getDerivationNameFromPath drvStorePath+  derivationMaybe <- liftIO $ Build.getDerivation store drvStorePath   derivation <- case derivationMaybe of     Just drv -> pure drv-    Nothing -> panic $ "Could not retrieve derivation " <> show drvPath <> " from local store or binary caches."-  nixBuildResult <- liftIO $ buildDerivation store drvPath derivation (extraPaths <$ guard (not materialize))+    Nothing -> panic $ "Could not retrieve derivation " <> show drvStorePath <> " from local store or binary caches."+  nixBuildResult <- liftIO $ buildDerivation store drvStorePath derivation (extraPaths <$ guard (not materialize))   katipAddContext (sl "result" (show nixBuildResult :: Text)) $     logLocM DebugS "Build result"-  buildResult <- liftIO $ enrichResult store derivation nixBuildResult+  buildResult <- liftIO $ enrichResult store drvName derivation nixBuildResult   yield $ Event.BuildResult buildResult  -- TODO: case distinction on BuildStatus enumeration-enrichResult :: Store -> ForeignPtr Derivation -> Build.BuildResult -> IO Event.BuildResult.BuildResult-enrichResult _ _ result@Build.BuildResult {isSuccess = False} =+enrichResult :: Store -> ByteString -> CNix.Derivation -> Build.BuildResult -> IO Event.BuildResult.BuildResult+enrichResult _ _ _ result@Build.BuildResult {isSuccess = False} =   pure $     Event.BuildResult.BuildFailure {errorMessage = Build.errorMessage result}-enrichResult store derivation _ = do-  drvOuts <- getDerivationOutputs derivation+enrichResult store drvName derivation _ = do+  drvOuts <- getDerivationOutputs store drvName derivation   outputInfos <- for drvOuts $ \drvOut -> do-    vpi <- queryPathInfo store (derivationOutputPath drvOut)-    hash_ <- validPathInfoNarHash vpi-    let size = validPathInfoNarSize vpi+    -- FIXME: ca-derivations: always get the built path+    vpi <- for (derivationOutputPath drvOut) (queryPathInfo store)+    hash_ <- traverse validPathInfoNarHash32 vpi+    path <- traverse (CNix.storePathToPath store) (derivationOutputPath drvOut)+    let size = fmap validPathInfoNarSize vpi     pure       Event.BuildResult.OutputInfo         { name = derivationOutputName drvOut,-          path = derivationOutputPath drvOut,-          hash = hash_,-          size = size+          path = fromMaybe "" path,+          hash = fromMaybe "" hash_,+          size = fromMaybe 0 size         }   pure $ Event.BuildResult.BuildSuccess outputInfos
hercules-ci-agent-worker/Hercules/Agent/Worker/Build/Prefetched.hs view
@@ -12,6 +12,7 @@ import qualified Data.ByteString.Char8 as C8 import Foreign (FinalizerPtr, ForeignPtr, alloca, newForeignPtr, nullPtr, peek) import Foreign.C (peekCString)+import Hercules.CNix.Encapsulation import Hercules.CNix.Store import qualified Language.C.Inline.Cpp as C import qualified Language.C.Inline.Cpp.Exceptions as C@@ -37,6 +38,8 @@  C.include "<nix/fs-accessor.hh>" +C.include "<nix-compat.hh>"+ C.include "<hercules-ci-cnix/store.hxx>"  C.using "namespace nix"@@ -90,11 +93,11 @@ nullableForeignPtr _ rawPtr | rawPtr == nullPtr = pure Nothing nullableForeignPtr finalize rawPtr = Just <$> newForeignPtr finalize rawPtr -getDerivation :: Store -> ByteString -> IO (Maybe (ForeignPtr Derivation))+getDerivation :: Store -> StorePath -> IO (Maybe Derivation) getDerivation (Store store) derivationPath =-  nullableForeignPtr finalizeDerivation+  nullableMoveToForeignPtrWrapper     =<< [C.throwBlock| Derivation *{-      std::string derivationPath($bs-ptr:derivationPath, $bs-len:derivationPath);+      StorePath derivationPath = *$fptr-ptr:(nix::StorePath *derivationPath);       std::list<nix::ref<nix::Store>> stores = getDefaultSubstituters();       stores.push_front(*$(refStore* store)); @@ -102,14 +105,7 @@        for (nix::ref<nix::Store> & currentStore : stores) {         try {-          auto accessor = currentStore->getFSAccessor();-          auto drvText = accessor->readFile(derivationPath);--          Path tmpDir = createTempDir();-          AutoDelete delTmpDir(tmpDir, true);-          Path drvTmpPath = tmpDir + "/drv";-          writeFile(drvTmpPath, drvText, 0600);-          derivation = new nix::Derivation(nix::readDerivation(drvTmpPath));+          derivation = new nix::Derivation(currentStore->derivationFromPath(printPath23(*currentStore, derivationPath)));           break;         } catch (nix::Interrupted &e) {           throw e;@@ -126,7 +122,7 @@     }|]  -- | @buildDerivation derivationPath derivationText@-buildDerivation :: Store -> ByteString -> ForeignPtr Derivation -> Maybe [ByteString] -> IO BuildResult+buildDerivation :: Store -> StorePath -> Derivation -> Maybe [ByteString] -> IO BuildResult buildDerivation (Store store) derivationPath derivation extraInputs =   let extraInputsMerged = C8.intercalate "\n" (fromMaybe [] extraInputs)       materializeDerivation = if isNothing extraInputs then 1 else 0@@ -142,13 +138,19 @@       const char *&errorMessage = *$(const char **errorMessagePtr);       time_t &startTime = *$(time_t *startTimePtr);       time_t &stopTime = *$(time_t *stopTimePtr);-      std::string derivationPath($bs-ptr:derivationPath, $bs-len:derivationPath);+      StorePath derivationPath = *$fptr-ptr:(nix::StorePath *derivationPath);        if ($(bool materializeDerivation)) {-        store.ensurePath(derivationPath);-        nix::PathSet paths{derivationPath};+        store.ensurePath(printPath23(store, derivationPath));+#ifdef NIX_2_4+        auto derivation = store.derivationFromPath(derivationPath);+        StorePathWithOutputs storePathWithOutputs { .path = derivationPath, .outputs = derivation.outputNames() };+        std::vector<nix::StorePathWithOutputs> paths{storePathWithOutputs};+#else+        nix::PathSet paths{printPath23(store, derivationPath)};+#endif         try {-          store.buildPaths(paths);+          store.buildPaths(toDerivedPaths24(paths));           status = -1;           success = true;           errorMessage = strdup("");@@ -170,10 +172,15 @@         std::istringstream stream(extraInputsMerged);          while (std::getline(stream, extraInput)) {-          derivation->inputSrcs.insert(extraInput);+#ifdef NIX_2_4+          auto path = store.parseStorePath(extraInput);+#else+          auto path = extraInput;+#endif+          derivation->inputSrcs.insert(path);         } -        nix::BuildResult result = store.buildDerivation(derivationPath, *derivation);+        nix::BuildResult result = store.buildDerivation(printPath23(store, derivationPath), *derivation);          switch (result.status) {           case nix::BuildResult::Built:
hercules-ci-agent-worker/Hercules/Agent/Worker/Effect.hs view
@@ -4,13 +4,12 @@ module Hercules.Agent.Worker.Effect where  import Control.Monad.Catch (MonadThrow)-import GHC.ForeignPtr (ForeignPtr) import Hercules.Agent.Worker.Build.Prefetched (buildDerivation) import qualified Hercules.Agent.Worker.Build.Prefetched as Build import qualified Hercules.Agent.WorkerProtocol.Command.Effect as Command.Effect import Hercules.CNix (Store) import qualified Hercules.CNix as CNix-import Hercules.CNix.Store.Context (Derivation)+import Hercules.CNix.Store (Derivation) import qualified Hercules.Effect as Effect import Katip (KatipContext) import Protolude@@ -22,28 +21,29 @@   dir <- getCurrentDirectory   Effect.runEffect derivation (Command.Effect.token command) (Command.Effect.secretsPath command) (Command.Effect.apiBaseURL command) dir -prepareDerivation :: MonadIO m => Store -> Command.Effect.Effect -> m (ForeignPtr Derivation)+prepareDerivation :: MonadIO m => Store -> Command.Effect.Effect -> m Derivation prepareDerivation store command = do   let extraPaths = Command.Effect.inputDerivationOutputPaths command       drvPath = encodeUtf8 $ Command.Effect.drvPath command-      ensureDeps = for_ extraPaths $ \input ->-        liftIO $ CNix.ensurePath store input+      ensureDeps = for_ extraPaths $ \input -> liftIO $ do+        CNix.ensurePath store =<< CNix.parseStorePath store input+  drvStorePath <- liftIO $ CNix.parseStorePath store drvPath   liftIO $ do     ensureDeps `catch` \e -> do       CNix.logInfo $ "while retrieving dependencies: " <> toS (displayException (e :: SomeException))       CNix.logInfo "unable to retrieve dependency; attempting fallback to local build"-      CNix.ensurePath store drvPath-      derivation <- CNix.getDerivation store drvPath-      depDrvPaths <- CNix.getDerivationInputs derivation+      CNix.ensurePath store drvStorePath+      derivation <- CNix.getDerivation store drvStorePath+      depDrvPaths <- CNix.getDerivationInputs store derivation       for_ depDrvPaths \(depDrv, _outputs) -> do         depDerivation <- CNix.getDerivation store depDrv         _nixBuildResult <- liftIO $ buildDerivation store depDrv depDerivation mempty         pass   derivation <--    liftIO (Build.getDerivation store drvPath) >>= \case+    liftIO (Build.getDerivation store drvStorePath) >>= \case       Just drv -> pure drv       Nothing -> panic $ "Could not retrieve derivation " <> show drvPath <> " from local store or binary caches."-  sources <- liftIO $ CNix.getDerivationSources derivation+  sources <- liftIO $ CNix.getDerivationSources store derivation   for_ sources \src -> do     liftIO $ CNix.ensurePath store src   pure derivation
hercules-ci-agent-worker/Hercules/Agent/Worker/HerculesStore.hs view
@@ -11,9 +11,7 @@ import Control.Exception   ( catch,   )-import qualified Data.ByteString.Unsafe as BS import Data.Coerce (coerce)-import qualified Foreign.C import Foreign.C.String (withCString) import Foreign.StablePtr (castStablePtrToPtr, newStablePtr) import Hercules.Agent.Worker.HerculesStore.Context@@ -21,8 +19,10 @@     HerculesStore,     context,   )-import Hercules.CNix (Store)+import Hercules.CNix.Encapsulation (moveToForeignPtrWrapper) import Hercules.CNix.Expr (Store (Store))+import Hercules.CNix.Expr.Context (NixStorePathWithOutputs)+import Hercules.CNix.Std.Vector (CStdVector, StdVector) import Hercules.CNix.Store.Context (Ref) import qualified Language.C.Inline.Cpp as C import qualified Language.C.Inline.Cpp.Exceptions as C@@ -47,6 +47,8 @@  C.include "<nix/globals.hh>" +C.include "<nix-compat.hh>"+ C.include "hercules-aliases.h"  C.using "namespace nix"@@ -76,21 +78,21 @@   }|]  -- TODO catch pure exceptions from displayException-setBuilderCallback :: Ptr (Ref HerculesStore) -> (ByteString -> IO ()) -> IO ()+setBuilderCallback :: Ptr (Ref HerculesStore) -> (StdVector NixStorePathWithOutputs -> IO ()) -> IO () setBuilderCallback s callback = do   p <--    mkBuilderCallback $ \cstr exceptionToThrowPtr ->-      Control.Exception.catch (BS.unsafePackMallocCString cstr >>= callback) $ \e ->+    mkBuilderCallback $ \sp exceptionToThrowPtr ->+      Control.Exception.catch (callback =<< moveToForeignPtrWrapper sp) $ \e ->         withCString (displayException (e :: SomeException)) $ \renderedException -> do           stablePtr <- castStablePtrToPtr <$> newStablePtr e           [C.block| void {             (*$(exception_ptr *exceptionToThrowPtr)) = std::make_exception_ptr(HaskellException(std::string($(const char* renderedException)), $(void* stablePtr)));           }|]   [C.throwBlock| void {-    (*$(refHerculesStore* s))->setBuilderCallback($(void (*p)(const char *, exception_ptr *) ));+    (*$(refHerculesStore* s))->setBuilderCallback($(void (*p)(std::vector<nix::StorePathWithOutputs>*, exception_ptr *)));   }|] -type BuilderCallback = Foreign.C.CString -> Ptr ExceptionPtr -> IO ()+type BuilderCallback = Ptr (CStdVector NixStorePathWithOutputs) -> Ptr ExceptionPtr -> IO ()  -- Work around a problem in ghcide with foreign imports. #ifndef __GHCIDE__
hercules-ci-agent-worker/Hercules/Agent/Worker/HerculesStore/Context.hs view
@@ -6,6 +6,7 @@  import qualified Data.Map as M import Hercules.CNix.Expr.Context (Ref)+import Hercules.CNix.Std.Vector (stdVectorCtx) import qualified Hercules.CNix.Store.Context as Store import qualified Language.C.Inline.Context as C import qualified Language.C.Inline.Cpp as C@@ -20,6 +21,7 @@ context =   C.cppCtx <> C.fptrCtx     <> C.bsCtx+    <> stdVectorCtx     <> Store.context     <> herculesStoreContext 
hercules-ci-agent.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name:           hercules-ci-agent-version:        0.8.1+version:        0.8.2 synopsis:       Runs Continuous Integration tasks on your machines category:       Nix, CI, Testing, DevOps homepage:       https://docs.hercules-ci.com@@ -14,10 +14,15 @@ extra-source-files:     CHANGELOG.md     cbits/hercules-aliases.h-    cbits/hercules-store.hh     cbits/hercules-logger.hh+    cbits/nix-2.3/hercules-store.hh+    cbits/nix-2.4/hercules-store.hh     testdata/vm-test-run-agent-test.drv +flag nix-2_4+  description: Build for Nix >=2.4pre*+  default: False+ source-repository head   type: git   location: https://github.com/hercules-ci/hercules-ci-agent@@ -183,8 +188,8 @@     , exceptions     , filepath     , hercules-ci-agent-    , hercules-ci-api-core == 0.1.2.0-    , hercules-ci-api-agent == 0.3.1.0+    , hercules-ci-api-core == 0.1.3.0+    , hercules-ci-api-agent == 0.4.0.0     , hostname     , http-client     , http-client-tls@@ -243,8 +248,16 @@   hs-source-dirs:       hercules-ci-agent-worker   cxx-sources:-      cbits/hercules-store.cxx       cbits/hercules-logger.cxx+  if flag(nix-2_4)+    cpp-options:+        -DNIX_2_4+    cxx-sources:+      cbits/nix-2.4/hercules-store.cxx+  else+    cxx-sources:+      cbits/nix-2.3/hercules-store.cxx+   default-extensions: DeriveGeneric DeriveTraversable DisambiguateRecordFields FlexibleContexts InstanceSigs LambdaCase MultiParamTypeClasses NoImplicitPrelude OverloadedStrings RankNTypes TupleSections TypeApplications TypeOperators   ghc-options:     -Werror=incomplete-patterns -Werror=missing-fields@@ -259,6 +272,12 @@     -with-rtsopts=-maxN8   include-dirs:       cbits+  if flag(nix-2_4)+    include-dirs:+      cbits/nix-2.4+  else+    include-dirs:+      cbits/nix-2.3   extra-libraries:       boost_context   build-depends:@@ -352,6 +371,7 @@     , process     , protolude     , safe-exceptions+    , temporary     , text     , transformers-base     , unliftio-core
hercules-ci-agent/Hercules/Agent.hs view
@@ -37,6 +37,7 @@ import qualified Hercules.Agent.Build as Build import Hercules.Agent.CabalInfo (herculesAgentVersion) import qualified Hercules.Agent.Cache as Cache+import qualified Hercules.Agent.Cachix.Env as Cachix.Env import Hercules.Agent.Client   ( lifeCycleClient,     tasksClient,@@ -117,7 +118,9 @@                       ServicePayload.StartEvaluation evalTask ->                         launchTask tasks socket (Task.upcastId $ EvaluateTask.id evalTask) do                           Cache.withCaches do-                            Evaluate.performEvaluation evalTask+                            -- TODO move the store directly under env+                            store <- asks (Cachix.Env.nixStore . Env.cachixEnv)+                            Evaluate.performEvaluation store evalTask                             pure $ TaskStatus.Successful ()                       ServicePayload.StartBuild buildTask ->                         launchTask tasks socket (Task.upcastId $ BuildTask.id buildTask) do
hercules-ci-agent/Hercules/Agent/Build.hs view
@@ -17,6 +17,7 @@ import Hercules.API.TaskStatus (TaskStatus) import qualified Hercules.API.TaskStatus as TaskStatus import qualified Hercules.Agent.Cache as Agent.Cache+import qualified Hercules.Agent.Cachix.Env as Cachix.Env import qualified Hercules.Agent.Client import qualified Hercules.Agent.Config as Config import Hercules.Agent.Env@@ -32,6 +33,7 @@ import qualified Hercules.Agent.WorkerProtocol.Event as Event import qualified Hercules.Agent.WorkerProtocol.Event.BuildResult as BuildResult import qualified Hercules.Agent.WorkerProtocol.LogSettings as LogSettings+import qualified Hercules.CNix.Store as CNix import Hercules.Error (defaultRetry) import qualified Network.URI import Protolude@@ -85,7 +87,8 @@     Just BuildResult.BuildSuccess {outputs = outs'} -> do       let outs = convertOutputs (BuildTask.derivationPath buildTask) outs'       reportOutputInfos buildTask outs-      push buildTask outs+      store <- asks (Cachix.Env.nixStore . Env.cachixEnv)+      push store buildTask outs       reportSuccess buildTask       pure $ TaskStatus.Successful ()     Just BuildResult.BuildFailure {} -> pure $ TaskStatus.Terminated ()@@ -102,12 +105,14 @@         hash = decodeUtf8With lenientDecode $ BuildResult.hash oi       } -push :: BuildTask -> Map Text OutputInfo -> App ()-push buildTask outs = do+push :: CNix.Store -> BuildTask -> Map Text OutputInfo -> App ()+push store buildTask outs = do   let paths = OutputInfo.path <$> toList outs   caches <- activePushCaches   forM_ caches $ \cache -> do-    Agent.Cache.push cache paths 4+    -- TODO preserve StorePath instead+    storePaths <- liftIO $ for paths (CNix.parseStorePath store . encodeUtf8)+    Agent.Cache.push cache storePaths 4     emitEvents buildTask [BuildEvent.Pushed $ Pushed.Pushed {cache = cache}]  reportSuccess :: BuildTask -> App ()
hercules-ci-agent/Hercules/Agent/Cache.hs view
@@ -13,6 +13,9 @@ import qualified Hercules.Agent.Nix as Nix import qualified Hercules.Agent.SecureDirectory as SecureDirectory import qualified Hercules.CNix as CNix+import Hercules.CNix.Std.Set (StdSet, toListFP)+import qualified Hercules.CNix.Std.Set as Std.Set+import Hercules.CNix.Store (StorePath) import qualified Hercules.CNix.Store as Store import qualified Hercules.Formats.NixCache as NixCache import Katip@@ -42,7 +45,7 @@   -- | Cache name   Text ->   -- | Paths, which do not have to form a closure-  [Text] ->+  [StorePath] ->   -- | Number of concurrent upload threads   Int ->   App ()@@ -58,14 +61,13 @@         throwIO $ FatalError $ "Agent does not have a binary cache named " <> show cacheName <> " in its configuration."   fromMaybe failNothing (maybeNix <|> maybeCachix) -nixPush :: NixCache.NixCache -> [Text] -> Int -> App ()-nixPush cacheConf pathsText _concurrency = do-  let paths = map encodeUtf8 pathsText+nixPush :: NixCache.NixCache -> [StorePath] -> Int -> App ()+nixPush cacheConf paths _concurrency = do   keys <- liftIO $ for (NixCache.signingKeys cacheConf) (CNix.parseSecretKey . encodeUtf8)   CNix.withStore $ \store -> do-    (Sum total, Sum signed) <--      mconcat <$> for keys \key -> liftIO $ do-        pathSet <- paths & newPathSetWith+    (Sum total, Sum signed) <- liftIO do+      pathSet <- Std.Set.fromListFP paths+      mconcat <$> for keys \key -> do         signClosure store key pathSet     katipAddContext (sl "num-signatures" signed <> sl "num-paths" total) $       logLocM DebugS "Signed"@@ -73,18 +75,11 @@     CNix.withStoreFromURI (NixCache.storeURI cacheConf) $ \cache -> do       liftIO (CNix.copyClosure store cache paths) -newPathSetWith :: [ByteString] -> IO Store.PathSet-newPathSetWith paths = do-  pathSet <- Store.newEmptyPathSet-  for_ paths \path ->-    Store.addToPathSet path pathSet-  pure pathSet--signClosure :: CNix.Store -> ForeignPtr CNix.SecretKey -> Store.PathSet -> IO (Sum Int, Sum Int)+signClosure :: CNix.Store -> ForeignPtr CNix.SecretKey -> StdSet Store.NixStorePath -> IO (Sum Int, Sum Int) signClosure store key' pathSet = withForeignPtr key' \key -> do-  closure <- Store.computeFSClosure store Store.defaultClosureParams pathSet+  closure <- Store.computeFSClosure store Store.defaultClosureParams pathSet >>= toListFP   closure-    & Store.traversePathSet+    & traverse       ( \path -> do           CNix.signPath store key path       )
hercules-ci-agent/Hercules/Agent/Cachix.hs view
@@ -12,11 +12,12 @@ import Hercules.Agent.Env as Agent.Env hiding (activePushCaches) import qualified Hercules.Agent.EnvironmentInfo as EnvInfo import Hercules.Agent.Log+import Hercules.CNix.Store (StorePath) import Hercules.Error import qualified Hercules.Formats.CachixCache as CachixCache import Protolude -push :: Text -> [Text] -> Int -> App ()+push :: Text -> [StorePath] -> Int -> App () push cache paths workers = withNamedContext "cache" cache $ do   Agent.Cachix.Env     { pushCaches = pushCaches,@@ -36,7 +37,7 @@             pushParamsStore = nixStore,             pushParamsClientEnv = clientEnv,             pushParamsStrategy = \storePath ->-              let ctx = withNamedContext "path" storePath+              let ctx = withNamedContext "path" (show storePath :: Text)                in Cachix.Push.PushStrategy                     { onAlreadyPresent = pass,                       onAttempt = \retryStatus size ->@@ -65,7 +66,7 @@   cks <- asks (Agent.Cachix.cacheKeys . Agent.Env.cachixEnv)   nixInfo <- liftIO EnvInfo.getNixInfo   pure-    ( EnvInfo.nixSubstituters nixInfo+    ( map (decodeUtf8With lenientDecode) (EnvInfo.nixSubstituters nixInfo)         ++ map (\c -> "https://" <> c <> ".cachix.org") (M.keys cks)     ) @@ -73,4 +74,7 @@ getTrustedPublicKeys = do   cks <- asks (Agent.Cachix.cacheKeys . Agent.Env.cachixEnv)   nixInfo <- liftIO EnvInfo.getNixInfo-  pure (EnvInfo.nixTrustedPublicKeys nixInfo ++ concatMap CachixCache.publicKeys cks)+  pure+    ( map (decodeUtf8With lenientDecode) (EnvInfo.nixTrustedPublicKeys nixInfo)+        ++ concatMap CachixCache.publicKeys cks+    )
hercules-ci-agent/Hercules/Agent/EnvironmentInfo.hs view
@@ -1,18 +1,9 @@+{-# LANGUAGE ScopedTypeVariables #-}+ module Hercules.Agent.EnvironmentInfo where -import Control.Lens-  ( to,-    (^..),-    (^?),-  )-import qualified Data.Aeson as Aeson-import Data.Aeson.Lens-  ( key,-    _Array,-    _Number,-    _String,-  )-import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString as BS+import qualified Data.Set as S import qualified Data.Text as T import qualified Hercules.API.Agent.LifeCycle.AgentInfo as AgentInfo import Hercules.Agent.CabalInfo as CabalInfo@@ -20,10 +11,11 @@ import qualified Hercules.Agent.Config as Config import Hercules.Agent.Env as Env import Hercules.Agent.Log+import qualified Hercules.CNix as CNix+import qualified Hercules.CNix.Settings as Settings import qualified Hercules.CNix.Store as Store import Network.HostName (getHostName) import Protolude hiding (to)-import qualified System.Process as Process  extractAgentInfo :: App AgentInfo.AgentInfo extractAgentInfo = do@@ -37,15 +29,15 @@   let s =         AgentInfo.AgentInfo           { hostname = toS hostname,-            agentVersion = CabalInfo.herculesAgentVersion, -- TODO: Add git revision-            nixVersion = nixExeVersion nix,+            agentVersion = CabalInfo.herculesAgentVersion,+            nixVersion = nixLibVersion nix,             nixClientProtocolVersion = nixClientProtocolVersion,             nixDaemonProtocolVersion = nixStoreProtocolVersion,-            platforms = nixPlatforms nix,+            platforms = map fromUtf8Lenient $ nixPlatforms nix,             cachixPushCaches = cachixPushCaches,             pushCaches = pushCaches,-            systemFeatures = nixSystemFeatures nix,-            substituters = nixSubstituters nix, -- TODO: Add cachix substituters+            systemFeatures = map fromUtf8Lenient $ nixSystemFeatures nix,+            substituters = map fromUtf8Lenient $ nixSubstituters nix,             concurrentTasks = fromIntegral $ Config.concurrentTasks cfg,             labels = Config.labels cfg           }@@ -53,72 +45,39 @@   pure s  data NixInfo = NixInfo-  { nixExeVersion :: Text,-    nixPlatforms :: [Text],-    nixSystemFeatures :: [Text],-    nixSubstituters :: [Text],-    nixTrustedPublicKeys :: [Text],-    nixNarinfoCacheNegativeTTL :: Maybe Integer,-    nixNetrcFile :: Maybe Text+  { nixLibVersion :: Text,+    nixPlatforms :: [ByteString],+    nixSystemFeatures :: [ByteString],+    nixSubstituters :: [ByteString],+    nixTrustedPublicKeys :: [ByteString],+    nixNarinfoCacheNegativeTTL :: Word64,+    nixNetrcFile :: Maybe ByteString   }+  deriving (Show) +fromUtf8Lenient :: ByteString -> Text+fromUtf8Lenient = decodeUtf8With lenientDecode+ getNixInfo :: IO NixInfo getNixInfo = do-  let stdinEmpty = ""-  version <- Process.readProcess "nix" ["--version"] stdinEmpty-  rawJson <- Process.readProcess "nix" ["show-config", "--json"] stdinEmpty-  cfg <--    case Aeson.eitherDecode (LBS.fromStrict $ encodeUtf8 $ toS rawJson) of-      Left e -> panic $ "Could not parse nix show-config --json: " <> show e-      Right r -> pure r+  extraPlatforms <- Settings.getExtraPlatforms+  system <- Settings.getSystem+  systemFeatures <- Settings.getSystemFeatures+  substituters <- Settings.getSubstituters+  trustedPublicKeys <- Settings.getTrustedPublicKeys+  narinfoCacheNegativeTTL <- Settings.getNarinfoCacheNegativeTtl+  netrcFile <- Settings.getNetrcFile   pure     NixInfo-      { nixExeVersion = T.dropAround isSpace (toS version),-        nixPlatforms =-          ((cfg :: Aeson.Value) ^.. key "system" . key "value" . _String)-            <> ( cfg-                   ^.. key "extra-platforms"-                     . key "value"-                     . _Array-                     . traverse-                     . _String-               ),-        nixSystemFeatures =-          cfg-            ^.. key "system-features"-              . key "value"-              . _Array-              . traverse-              . _String,-        nixSubstituters =-          cfg-            ^.. key "substituters"-              . key "value"-              . _Array-              . traverse-              . _String-              . to cleanUrl,-        nixTrustedPublicKeys =-          cfg-            ^.. key "trusted-public-keys"-              . key "value"-              . _Array-              . traverse-              . _String-              . to cleanUrl,-        nixNarinfoCacheNegativeTTL =-          cfg-            ^? key "narinfo-cache-negative-ttl"-              . key "value"-              . _Number-              . to floor,-        nixNetrcFile =-          cfg-            ^? key "netrc-file"-              . key "value"-              . _String+      { nixLibVersion = T.dropAround isSpace (fromUtf8Lenient CNix.nixVersion),+        nixPlatforms = toList (S.singleton system <> extraPlatforms),+        nixSystemFeatures = toList systemFeatures,+        nixSubstituters = map cleanUrl substituters,+        nixTrustedPublicKeys = trustedPublicKeys,+        nixNarinfoCacheNegativeTTL = narinfoCacheNegativeTTL,+        nixNetrcFile = guard (netrcFile /= "") $> netrcFile       } -cleanUrl :: Text -> Text-cleanUrl t | "@" `T.isInfixOf` t = "<URI censored; might contain secret>"+cleanUrl :: ByteString -> ByteString+cleanUrl t | "@" `BS.isInfixOf` t = "<URI censored; might contain secret>" cleanUrl t = t
hercules-ci-agent/Hercules/Agent/Evaluate.hs view
@@ -40,9 +40,11 @@ import qualified Hercules.API.Agent.Evaluate.EvaluateTask as EvaluateTask import Hercules.API.Servant (noContent) import qualified Hercules.Agent.Cache as Agent.Cache+import qualified Hercules.Agent.Cachix.Env as Cachix.Env import qualified Hercules.Agent.Client import Hercules.Agent.Env import qualified Hercules.Agent.Env as Env+import Hercules.Agent.Evaluate.TraversalQueue (Queue) import qualified Hercules.Agent.Evaluate.TraversalQueue as TraversalQueue import Hercules.Agent.Files import Hercules.Agent.Log@@ -66,7 +68,7 @@ import qualified Hercules.Agent.WorkerProtocol.Event.Attribute as WorkerAttribute import qualified Hercules.Agent.WorkerProtocol.Event.AttributeError as WorkerAttributeError import qualified Hercules.Agent.WorkerProtocol.LogSettings as LogSettings-import Hercules.CNix (withStore)+import Hercules.CNix.Store (Store, StorePath, parseStorePath) import Hercules.Error (defaultRetry, quickRetry) import qualified Network.HTTP.Client.Conduit as HTTP.Conduit import qualified Network.HTTP.Simple as HTTP.Simple@@ -83,18 +85,19 @@ pushEvalWorkers :: Int pushEvalWorkers = 16 -performEvaluation :: EvaluateTask.EvaluateTask -> App ()-performEvaluation task' =-  withProducer (produceEvaluationTaskEvents task') $ \producer ->+performEvaluation :: Store -> EvaluateTask.EvaluateTask -> App ()+performEvaluation store task' =+  withProducer (produceEvaluationTaskEvents store task') $ \producer ->     withBoundedDelayBatchProducer (1000 * 1000) 1000 producer $ \batchProducer ->       fix $ \continue ->         joinSTM $ listen batchProducer (\b -> withSync b (postBatch task' . catMaybes) *> continue) pure  produceEvaluationTaskEvents ::+  Store ->   EvaluateTask.EvaluateTask ->   (Syncing EvaluateEvent.EvaluateEvent -> App ()) ->   App ()-produceEvaluationTaskEvents task writeToBatch = withWorkDir "eval" $ \tmpdir -> do+produceEvaluationTaskEvents store task writeToBatch = withWorkDir "eval" $ \tmpdir -> do   logLocM DebugS "Retrieving evaluation task"   let emitSingle = writeToBatch . Syncable       sync = syncer writeToBatch@@ -206,7 +209,7 @@               Message.typ = Message.Error,               Message.message = e             }-    Right file -> TraversalQueue.with $ \derivationQueue ->+    Right file -> TraversalQueue.with $ \(derivationQueue :: Queue StorePath) ->       let doIt = do             Async.Lifted.concurrently_ evaluation emitDrvs             -- derivationInfo upload has finished@@ -241,16 +244,17 @@               emit $ EvaluateEvent.PushedAll $ PushedAll.PushedAll {cache = cache}           captureAttrDrvAndEmit msg = do             case msg of-              EvaluateEvent.Attribute ae ->-                addTopDerivation (AttributeEvent.derivationPath ae)+              EvaluateEvent.Attribute ae -> do+                storePath <- liftIO $ parseStorePath store (encodeUtf8 $ AttributeEvent.derivationPath ae)+                addTopDerivation storePath               _ -> pass             emit msg           emitDrvs =-            withStore $ \store -> TraversalQueue.work derivationQueue $ \recurse drvPath -> do+            TraversalQueue.work derivationQueue $ \recurse drvPath -> do               drvInfo <- retrieveDerivationInfo store drvPath-              forM_-                (M.keys $ DerivationInfo.inputDerivations drvInfo)-                recurse -- asynchronously+              for_ (M.keys $ DerivationInfo.inputDerivations drvInfo) \inp -> do+                inputStorePath <- liftIO $ parseStorePath store (encodeUtf8 inp)+                recurse inputStorePath -- asynchronously               emit $ EvaluateEvent.DerivationInfo drvInfo        in doIt @@ -273,7 +277,7 @@   ] ->   (EvaluateEvent.EvaluateEvent -> App ()) ->   -- | Upload a derivation, return when done-  (Text -> App ()) ->+  (StorePath -> App ()) ->   App () ->   Text ->   App ()@@ -339,13 +343,16 @@                       }                 continue               Event.Build drv outputName notAttempt -> do+                store <- asks (Cachix.Env.nixStore . Env.cachixEnv)+                storePath <- liftIO (parseStorePath store drv)+                let drvText = decode drv                 status <--                  withNamedContext "derivation" drv $ do+                  withNamedContext "derivation" (decode drv) $ do                     currentIndex <- liftIO $ atomicModifyIORef buildRequiredIndex (\i -> (i + 1, i))                     emit $                       EvaluateEvent.BuildRequired                         BuildRequired.BuildRequired-                          { BuildRequired.derivationPath = drv,+                          { BuildRequired.derivationPath = drvText,                             BuildRequired.index = currentIndex,                             BuildRequired.outputName = outputName                           }@@ -353,21 +360,21 @@                           caches <- activePushCaches                           forM_ caches $ \cache -> do                             withNamedContext "cache" cache $ logLocM DebugS "Pushing derivations for import from derivation"-                            Agent.Cache.push cache [drv] pushEvalWorkers+                            Agent.Cache.push cache [storePath] pushEvalWorkers                     Async.Lifted.concurrently_-                      (uploadDerivationInfos drv)+                      (uploadDerivationInfos storePath)                       pushDerivations                     emit $                       EvaluateEvent.BuildRequest                         BuildRequest.BuildRequest-                          { derivationPath = drv,+                          { derivationPath = drvText,                             forceRebuild = isJust notAttempt                           }                     flush-                    status <- drvPoller notAttempt drv+                    status <- drvPoller notAttempt drvText                     logLocM DebugS $ "Got derivation status " <> logStr (show status :: Text)                     return status-                writeChan commandChan $ Just $ Command.BuildResult $ uncurry (BuildResult.BuildResult drv) status+                writeChan commandChan $ Just $ Command.BuildResult $ uncurry (BuildResult.BuildResult drvText) status                 continue               Event.Exception e -> panic e               -- Unused during eval
hercules-ci-agent/Hercules/Agent/Nix.hs view
@@ -8,8 +8,6 @@ import Hercules.Agent.Nix.Env as Nix.Env import Protolude import System.Directory (doesFileExist)-import System.Process (readProcess)-import qualified System.Process  withExtraOptions :: [(Text, Text)] -> App a -> App a withExtraOptions extraOpts = local $ \env ->@@ -23,35 +21,10 @@ askExtraOptions :: MonadReader Agent.Env.Env m => m [(Text, Text)] askExtraOptions = asks (extraOptions . nixEnv) -getExtraOptionArguments :: MonadReader Agent.Env.Env m => m [Text]-getExtraOptionArguments = do-  asks (f . extraOptions . nixEnv)-  where-    f = concatMap $ \(opt, val) -> ["--option", opt, val]--readNixProcess ::-  -- | Command-  Text ->-  -- | Options-  [Text] ->-  -- | Paths-  [Text] ->-  -- | Stdin-  Text ->-  App Text-readNixProcess cmd opts paths stdinText = do-  extraOpts <- getExtraOptionArguments-  toS <$> liftIO (readProcess (toS cmd) (map toS (opts <> extraOpts <> ["--"] <> paths)) (toS stdinText))--nixProc :: Text -> [Text] -> [Text] -> App System.Process.CreateProcess-nixProc exe opts args = do-  extraOpts <- getExtraOptionArguments-  pure $ System.Process.proc (toS exe) (map toS (opts <> extraOpts <> ["--"] <> args))- getNetrcLines :: App [Text] getNetrcLines = liftIO $ do   info <- getNixInfo-  let fm = toS <$> nixNetrcFile info+  let fm = toS . decodeUtf8With lenientDecode <$> nixNetrcFile info   fmap fold <$> runMaybeT $ do     f <- MaybeT $ pure fm     exs <- lift $ doesFileExist f
hercules-ci-agent/Hercules/Agent/Nix/Init.hs view
@@ -7,7 +7,8 @@ newEnv :: IO Env newEnv = do   nixInfo <- EnvironmentInfo.getNixInfo-  when (EnvironmentInfo.nixNarinfoCacheNegativeTTL nixInfo /= Just 0) $ do+  print nixInfo+  when (EnvironmentInfo.nixNarinfoCacheNegativeTTL nixInfo /= 0) $ do     putErrText       "\n\       \We have detected that the setting narinfo-cache-negative-ttl is non-zero.\n\
hercules-ci-agent/Hercules/Agent/Nix/RetrieveDerivationInfo.hs view
@@ -1,8 +1,9 @@+{-# LANGUAGE BlockArguments #-}+ module Hercules.Agent.Nix.RetrieveDerivationInfo where  import qualified Data.Map as M import qualified Data.Text as T-import Foreign.ForeignPtr (ForeignPtr) import Hercules.API.Agent.Evaluate.EvaluateEvent.DerivationInfo   ( DerivationInfo (DerivationInfo),   )@@ -13,39 +14,49 @@ retrieveDerivationInfo ::   MonadIO m =>   Store ->-  DerivationInfo.DerivationPathText ->+  StorePath ->   m DerivationInfo retrieveDerivationInfo store drvPath = liftIO $ do-  drv <- getDerivation store (encodeUtf8 drvPath)-  retrieveDerivationInfo' drvPath drv+  drv <- getDerivation store drvPath+  path <- storePathToPath store drvPath+  drvName <- getDerivationNameFromPath drvPath+  retrieveDerivationInfo' store path drvName drv -retrieveDerivationInfo' :: Text -> ForeignPtr Derivation -> IO DerivationInfo-retrieveDerivationInfo' drvPath drv = do-  sourcePaths <- getDerivationSources drv-  inputDrvPaths <- getDerivationInputs drv-  outputs <- getDerivationOutputs drv+retrieveDerivationInfo' :: Store -> ByteString -> ByteString -> Derivation -> IO DerivationInfo+retrieveDerivationInfo' store drvPath drvName drv = do+  sourceStorePaths <- getDerivationSources store drv+  inputDrvStorePaths <- getDerivationInputs store drv+  outputs <- getDerivationOutputs store drvName drv   env <- getDerivationEnv drv   platform <- getDerivationPlatform drv   let requiredSystemFeatures = maybe [] splitFeatures $ M.lookup "requiredSystemFeatures" env       splitFeatures = filter (not . T.null) . T.split (== ' ') . decode       decode = decodeUtf8With lenientDecode+  inputDrvPaths <- for inputDrvStorePaths \(storePath, inputOutputs) -> do+    path <- Hercules.CNix.storePathToPath store storePath+    pure (path, inputOutputs)+  sourcePaths <- for sourceStorePaths (Hercules.CNix.storePathToPath store)+  outputs' <- for outputs \output -> do+    path <- for (derivationOutputPath output) (Hercules.CNix.storePathToPath store)+    let isFixed = case derivationOutputDetail output of+          (DerivationOutputInputAddressed _s) -> False+          (DerivationOutputCAFixed _f _s) -> True+          (DerivationOutputCAFloating _f _h) -> False+          DerivationOutputDeferred -> False+    pure+      ( decode $ derivationOutputName output,+        DerivationInfo.OutputInfo+          { DerivationInfo.path = decode <$> path,+            DerivationInfo.isFixed = isFixed+          }+      )   pure $     DerivationInfo-      { derivationPath = drvPath,+      { derivationPath = decode drvPath,         platform = decode platform,         requiredSystemFeatures = requiredSystemFeatures,         inputDerivations = inputDrvPaths & map (bimap decode (map decode)) & M.fromList,         inputSources = map decode sourcePaths,         outputs =-          outputs-            & map-              ( \output ->-                  ( decode $ derivationOutputName output,-                    DerivationInfo.OutputInfo-                      { DerivationInfo.path = decode $ derivationOutputPath output,-                        DerivationInfo.isFixed = derivationOutputHashAlgo output /= ""-                      }-                  )-              )-            & M.fromList+          outputs' & M.fromList       }
src/Hercules/Agent/WorkerProtocol/Event.hs view
@@ -15,7 +15,7 @@   | AttributeError AttributeError   | EvaluationDone   | Error Text-  | Build Text Text (Maybe UUID)+  | Build ByteString Text (Maybe UUID)   | BuildResult BuildResult   | EffectResult Int   | Exception Text
src/Hercules/Effect.hs view
@@ -8,10 +8,9 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import qualified Data.Map as M-import Foreign (ForeignPtr) import Hercules.Agent.Sensitive (Sensitive (Sensitive, reveal), revealContainer)+import Hercules.CNix (Derivation) import Hercules.CNix.Store (getDerivationArguments, getDerivationBuilder, getDerivationEnv)-import Hercules.CNix.Store.Context (Derivation) import Hercules.Effect.Container (BindMount (BindMount)) import qualified Hercules.Effect.Container as Container import Hercules.Error (escalateAs)@@ -63,7 +62,7 @@                       { data_ = Formats.Secret.data_ secret                       } -runEffect :: (MonadThrow m, KatipContext m) => ForeignPtr Derivation -> Sensitive Text -> FilePath -> Text -> FilePath -> m ExitCode+runEffect :: (MonadThrow m, KatipContext m) => Derivation -> Sensitive Text -> FilePath -> Text -> FilePath -> m ExitCode runEffect derivation token secretsPath apiBaseURL dir = do   drvBuilder <- liftIO $ getDerivationBuilder derivation   drvArgs <- liftIO $ getDerivationArguments derivation
src/Hercules/Effect/Container.hs view
@@ -15,6 +15,7 @@ import Protolude import System.Directory (createDirectory) import System.FilePath ((</>))+import System.IO (hClose) import System.IO.Error (ioeGetErrorType) import System.Posix.IO (closeFd, fdToHandle) import System.Posix.Terminal (openPseudoTerminal)@@ -92,27 +93,21 @@     Right a -> pure a     Left e -> throwIO (FatalError $ "decoding runc config.json template: " <> show e)   let configJson = effectToRuncSpec config template-  BS.writeFile (configJsonPath) (BL.toStrict $ encode configJson)+  BS.writeFile configJsonPath (BL.toStrict $ encode configJson)   createDirectory rootfsPath   createDirectory runcRootPath   name <- do     uuid <- UUID.nextRandom     pure $ "hercules-ci-" <> show uuid-  (exitCode, _) <- bracket-    openPseudoTerminal-    ( \(fd1, fd2) -> handle (\(_e :: SomeException) -> pass) do-        closeFd fd1-        when (fd2 /= fd1) (closeFd fd2)-    )-    $ \(master, terminal) -> do+  (exitCode, _) <- withPseudoTerminalHandles $+    \(master, terminal) -> do       concurrently         ( do-            terminalHandle <- fdToHandle terminal             let createProcSpec =                   (System.Process.proc runcExe ["--root", runcRootPath, "run", name])-                    { std_in = UseHandle terminalHandle, -- can't pass /dev/null :(-                      std_out = UseHandle terminalHandle,-                      std_err = UseHandle terminalHandle,+                    { std_in = UseHandle terminal, -- can't pass /dev/null :(+                      std_out = UseHandle terminal,+                      std_err = UseHandle terminal,                       cwd = Just dir                     }             withCreateProcess createProcSpec \_subStdin _noOut _noErr processHandle -> do@@ -128,9 +123,8 @@                               )         )         ( do-            masterHandle <- fdToHandle master             let shovel =-                  handleEOF (BS.hGetLine masterHandle) >>= \case+                  handleEOF (BS.hGetLine master) >>= \case                     "" -> pass                     someBytes | "@nix" `BS.isPrefixOf` someBytes -> do                       -- TODO use it (example @nix { "action": "setPhase", "phase": "effectPhase" })@@ -142,3 +136,29 @@             shovel         )   pure exitCode++-- | Like 'openPseudoTerminalHandles' but closes the handles after the+-- function is done.+withPseudoTerminalHandles :: ((Handle, Handle) -> IO a) -> IO a+withPseudoTerminalHandles =+  bracket+    openPseudoTerminalHandles+    ( \(master, terminal) -> do+        hClose master `catch` \(_ :: SomeException) -> pass+        hClose terminal `catch` \(_ :: SomeException) -> pass+    )++-- | Like 'openPseudoTerminal' but returning handles, in a resource-safe manner.+openPseudoTerminalHandles :: IO (Handle, Handle)+openPseudoTerminalHandles =+  mask_ do+    (masterFd, terminalFd) <- openPseudoTerminal++    ( do+        master <- fdToHandle masterFd+        terminal <- fdToHandle terminalFd+        pure (master, terminal)+      )+      `onException` do+        closeFd masterFd+        when (terminalFd /= masterFd) (closeFd terminalFd)
test/Hercules/Agent/Nix/RetrieveDerivationInfoSpec.hs view
@@ -1,23 +1,33 @@+{-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-}  module Hercules.Agent.Nix.RetrieveDerivationInfoSpec where +import qualified Data.ByteString as BS import qualified Data.Map as M import Hercules.API.Agent.Evaluate.EvaluateEvent.DerivationInfo import Hercules.Agent.Nix.RetrieveDerivationInfo import Hercules.CNix import Protolude+import System.IO.Temp (withSystemTempDirectory) import Test.Hspec  spec :: Spec spec = do   describe "retrieveDerivationInfo" $ do-    it "parses vm-test-run-agent-test.drv correctly" $ do-      drv <- getDerivationFromFile "testdata/vm-test-run-agent-test.drv"-      d <- retrieveDerivationInfo' "testdata/vm-test-run-agent-test.drv" drv-      derivationPath d `shouldBe` "testdata/vm-test-run-agent-test.drv"-      platform d `shouldBe` "x86_64-linux"-      requiredSystemFeatures d `shouldBe` ["kvm", "nixos-test"]-      inputDerivations d `shouldBe` M.fromList [("/nix/store/0si75icim8ajxcsp25d9c52m42kqg1xj-stdenv-linux.drv", ["out"]), ("/nix/store/1kircip4wskspsqqzxbmh6ss73iqh9ah-bash-4.4-p23.drv", ["out"]), ("/nix/store/5kpp7mly0qad7l451xhr60k0wbv6vivi-jquery-1.11.3.drv", ["out"]), ("/nix/store/id49f28qvggmzpmlxp511v6yhc070lkf-jquery-ui-1.11.4.drv", ["out"]), ("/nix/store/kyxayhm3r6gpzbg1d57fwb8m7gkmpwcy-nixos-test-driver-agent-test.drv", ["out"]), ("/nix/store/qzvim3ca1s5zxbbvpzzipizfnfpdf5r2-libxslt-1.1.33.drv", ["dev"])]-      inputSources d `shouldBe` ["/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh", "/nix/store/fs6a6m9s5n367dslsvsl9lg89h3ns3ya-logfile.css", "/nix/store/jjh6h82n5rhw45badlpbj1yv7y6m48h2-log2html.xsl", "/nix/store/kdangwwh47ngwxk859ibbvkj4vmm9rzr-treebits.js"]-      outputs d `shouldBe` M.fromList [("out", OutputInfo {path = "/nix/store/7vg7ij5933dhg6dh22fvs90bbzmzg8kj-vm-test-run-agent-test", isFixed = False})]+    it "parses vm-test-run-agent-test.drv correctly" $+      withTempStore $ \store -> do+        s <- BS.readFile "testdata/vm-test-run-agent-test.drv"+        drv <- getDerivationFromString store "vm-test-run-agent-test" s+        d <- retrieveDerivationInfo' store "testdata/vm-test-run-agent-test.drv" "vm-test-run-agent-test" drv+        derivationPath d `shouldBe` "testdata/vm-test-run-agent-test.drv"+        platform d `shouldBe` "x86_64-linux"+        requiredSystemFeatures d `shouldBe` ["kvm", "nixos-test"]+        inputDerivations d `shouldBe` M.fromList [("/nix/store/0si75icim8ajxcsp25d9c52m42kqg1xj-stdenv-linux.drv", ["out"]), ("/nix/store/1kircip4wskspsqqzxbmh6ss73iqh9ah-bash-4.4-p23.drv", ["out"]), ("/nix/store/5kpp7mly0qad7l451xhr60k0wbv6vivi-jquery-1.11.3.drv", ["out"]), ("/nix/store/id49f28qvggmzpmlxp511v6yhc070lkf-jquery-ui-1.11.4.drv", ["out"]), ("/nix/store/kyxayhm3r6gpzbg1d57fwb8m7gkmpwcy-nixos-test-driver-agent-test.drv", ["out"]), ("/nix/store/qzvim3ca1s5zxbbvpzzipizfnfpdf5r2-libxslt-1.1.33.drv", ["dev"])]+        inputSources d `shouldBe` ["/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh", "/nix/store/fs6a6m9s5n367dslsvsl9lg89h3ns3ya-logfile.css", "/nix/store/jjh6h82n5rhw45badlpbj1yv7y6m48h2-log2html.xsl", "/nix/store/kdangwwh47ngwxk859ibbvkj4vmm9rzr-treebits.js"]+        outputs d `shouldBe` M.fromList [("out", OutputInfo {path = Just "/nix/store/7vg7ij5933dhg6dh22fvs90bbzmzg8kj-vm-test-run-agent-test", isFixed = False})]++withTempStore :: (Store -> IO a) -> IO a+withTempStore f =+  withSystemTempDirectory "cnix-test-store" \d ->+    withStoreFromURI (toS d) f