forked from lix-project/lix
Merge remote-tracking branch 'upstream/master' into indexed-store-path-outputs
This commit is contained in:
commit
41e755bee4
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
|||
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
# required to find all branches
|
||||
|
|
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
|
@ -14,10 +14,10 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- uses: cachix/install-nix-action@v17
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
if: needs.check_cachix.outputs.secret == 'true'
|
||||
|
@ -46,11 +46,11 @@ jobs:
|
|||
outputs:
|
||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- uses: cachix/install-nix-action@v17
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
|
@ -67,9 +67,9 @@ jobs:
|
|||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v3
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- uses: cachix/install-nix-action@v17
|
||||
with:
|
||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||
|
@ -83,10 +83,10 @@ jobs:
|
|||
needs.check_cachix.outputs.secret == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- uses: cachix/install-nix-action@v17
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
|
|
2
.github/workflows/hydra_status.yml
vendored
2
.github/workflows/hydra_status.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
if: github.repository_owner == 'NixOS'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: bash scripts/check-hydra-status.sh
|
||||
|
|
|
@ -41,5 +41,9 @@
|
|||
As before, the old output will continue to work, but `nix flake check` will
|
||||
issue a warning about it.
|
||||
|
||||
* `nix run` is now stricter wrt what it accepts:
|
||||
* Members of `apps` are now required to be apps (as defined in [the manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-run.html#apps))
|
||||
* Member of `packages` or `legacyPackages` cannot be of type "app" when used by `nix run`.
|
||||
|
||||
* Add experimental *indexed store derivations* installable syntax, part of the
|
||||
the `computed-derivations` experimental feature.
|
||||
|
|
|
@ -15,7 +15,7 @@ function _complete_nix {
|
|||
else
|
||||
COMPREPLY+=("$completion")
|
||||
fi
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null)
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
|
|
|
@ -423,6 +423,18 @@ EOF
|
|||
fi
|
||||
done
|
||||
|
||||
if [ "$(uname -s)" = "Linux" ] && [ ! -e /run/systemd/system ]; then
|
||||
warning <<EOF
|
||||
We did not detect systemd on your system. With a multi-user install
|
||||
without systemd you will have to manually configure your init system to
|
||||
launch the Nix daemon after installation.
|
||||
EOF
|
||||
if ! ui_confirm "Do you want to proceed with a multi-user installation?"; then
|
||||
failure <<EOF
|
||||
You have aborted the installation.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
setup_report() {
|
||||
|
|
|
@ -85,11 +85,12 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
|||
{
|
||||
std::optional<Path> file;
|
||||
std::optional<std::string> expr;
|
||||
bool readOnlyMode = false;
|
||||
|
||||
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
||||
OperateOn operateOn = OperateOn::Output;
|
||||
|
||||
SourceExprCommand();
|
||||
SourceExprCommand(bool supportReadOnlyMode = false);
|
||||
|
||||
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
||||
ref<Store> store, std::vector<std::string> ss);
|
||||
|
@ -128,13 +129,13 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
|||
{
|
||||
std::shared_ptr<Installable> installable;
|
||||
|
||||
InstallableCommand();
|
||||
InstallableCommand(bool supportReadOnlyMode = false);
|
||||
|
||||
void prepare() override;
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||
{
|
||||
return parseFlakeRef(_installable, absPath("."));
|
||||
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "registry.hh"
|
||||
#include "flake/flakeref.hh"
|
||||
#include "store-api.hh"
|
||||
#include "command.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -59,6 +60,9 @@ MixEvalArgs::MixEvalArgs()
|
|||
fetchers::Attrs extraAttrs;
|
||||
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
||||
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeFlakeRef(openStore(), prefix);
|
||||
}}
|
||||
});
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
#include "globals.hh"
|
||||
#include "installables.hh"
|
||||
#include "util.hh"
|
||||
#include "command.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "common-eval-args.hh"
|
||||
|
@ -99,6 +101,14 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
lockFlags.inputOverrides.insert_or_assign(
|
||||
flake::parseInputPath(inputPath),
|
||||
parseFlakeRef(flakeRef, absPath("."), true));
|
||||
}},
|
||||
.completer = {[&](size_t n, std::string_view prefix) {
|
||||
if (n == 0) {
|
||||
if (auto flakeRef = getFlakeRefForCompletion())
|
||||
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
||||
} else if (n == 1) {
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
}
|
||||
}}
|
||||
});
|
||||
|
||||
|
@ -129,7 +139,7 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
});
|
||||
}
|
||||
|
||||
SourceExprCommand::SourceExprCommand()
|
||||
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||
{
|
||||
addFlag({
|
||||
.longName = "file",
|
||||
|
@ -157,6 +167,17 @@ SourceExprCommand::SourceExprCommand()
|
|||
.category = installablesCategory,
|
||||
.handler = {&operateOn, OperateOn::Derivation},
|
||||
});
|
||||
|
||||
if (supportReadOnlyMode) {
|
||||
addFlag({
|
||||
.longName = "read-only",
|
||||
.description =
|
||||
"Do not instantiate each evaluated derivation. "
|
||||
"This improves performance, but can cause errors when accessing "
|
||||
"store paths of derivations during evaluation.",
|
||||
.handler = {&readOnlyMode, true},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
||||
|
@ -182,6 +203,8 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
|||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
{
|
||||
if (file) {
|
||||
completionType = ctAttrs;
|
||||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
Expr *e = state->parseExprFromFile(
|
||||
|
@ -210,13 +233,14 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
|||
Value v2;
|
||||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
std::string name = i.name;
|
||||
if (name.find(searchWord) == 0) {
|
||||
completions->add(i.name);
|
||||
if (prefix_ == "")
|
||||
completions->add(name);
|
||||
else
|
||||
completions->add(prefix_ + "." + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,10 +268,11 @@ void completeFlakeRefWithFragment(
|
|||
if (hash == std::string::npos) {
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
} else {
|
||||
completionType = ctAttrs;
|
||||
|
||||
auto fragment = prefix.substr(hash + 1);
|
||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||
// FIXME: do tilde expansion.
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
|
||||
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
|
||||
|
||||
auto evalCache = openEvalCache(*evalState,
|
||||
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
|
||||
|
@ -259,8 +284,6 @@ void completeFlakeRefWithFragment(
|
|||
flake. */
|
||||
attrPathPrefixes.push_back("");
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
||||
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
||||
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
||||
|
@ -334,16 +357,16 @@ DerivedPath Installable::toDerivedPath()
|
|||
return std::move(buildables[0]);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
Installable::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache =
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
|
||||
[&]() { return toValue(state).first; });
|
||||
return {{evalCache->getRoot(), ""}};
|
||||
return {evalCache->getRoot()};
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
||||
ref<eval_cache::AttrCursor>
|
||||
Installable::getCursor(EvalState & state)
|
||||
{
|
||||
auto cursors = getCursors(state);
|
||||
|
@ -586,27 +609,9 @@ InstallableFlake::InstallableFlake(
|
|||
|
||||
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
auto attr = getCursor(*state);
|
||||
|
||||
auto cache = openEvalCache(*state, lockedFlake);
|
||||
auto root = cache->getRoot();
|
||||
|
||||
Suggestions suggestions;
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
debug("trying flake output attribute '%s'", attrPath);
|
||||
|
||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
||||
parseAttrPath(*state, attrPath),
|
||||
true
|
||||
);
|
||||
|
||||
if (!attrOrSuggestions) {
|
||||
suggestions += attrOrSuggestions.getSuggestions();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto attr = *attrOrSuggestions;
|
||||
auto attrPath = attr->getAttrPathStr();
|
||||
|
||||
if (!attr->isDerivation())
|
||||
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
||||
|
@ -618,11 +623,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
|||
attr->getAttr(state->sOutputName)->getString()
|
||||
};
|
||||
|
||||
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
||||
}
|
||||
|
||||
throw Error(suggestions, "flake '%s' does not provide attribute %s",
|
||||
flakeRef, showAttrPaths(getActualAttrPaths()));
|
||||
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
||||
}
|
||||
|
||||
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||
|
@ -634,33 +635,10 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
|||
|
||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
|
||||
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
|
||||
|
||||
auto emptyArgs = state.allocBindings(0);
|
||||
|
||||
Suggestions suggestions;
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
try {
|
||||
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
||||
state.forceValue(*v, pos);
|
||||
return {v, pos};
|
||||
} catch (AttrPathNotFound & e) {
|
||||
suggestions += e.info().suggestions;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error(
|
||||
suggestions,
|
||||
"flake '%s' does not provide attribute %s",
|
||||
flakeRef,
|
||||
showAttrPaths(getActualAttrPaths())
|
||||
);
|
||||
return {&getCursor(state)->forceValue(), noPos};
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableFlake::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache = openEvalCache(state,
|
||||
|
@ -668,21 +646,55 @@ InstallableFlake::getCursors(EvalState & state)
|
|||
|
||||
auto root = evalCache->getRoot();
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res;
|
||||
std::vector<ref<eval_cache::AttrCursor>> res;
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||
if (attr) res.push_back({*attr, attrPath});
|
||||
if (attr) res.push_back(ref(*attr));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
|
||||
auto cache = openEvalCache(state, lockedFlake);
|
||||
auto root = cache->getRoot();
|
||||
|
||||
Suggestions suggestions;
|
||||
|
||||
auto attrPaths = getActualAttrPaths();
|
||||
|
||||
for (auto & attrPath : attrPaths) {
|
||||
debug("trying flake output attribute '%s'", attrPath);
|
||||
|
||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
||||
parseAttrPath(state, attrPath),
|
||||
true
|
||||
);
|
||||
|
||||
if (!attrOrSuggestions) {
|
||||
suggestions += attrOrSuggestions.getSuggestions();
|
||||
continue;
|
||||
}
|
||||
|
||||
return *attrOrSuggestions;
|
||||
}
|
||||
|
||||
throw Error(
|
||||
suggestions,
|
||||
"flake '%s' does not provide attribute %s",
|
||||
flakeRef,
|
||||
showAttrPaths(attrPaths));
|
||||
}
|
||||
|
||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||
{
|
||||
if (!_lockedFlake) {
|
||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||
lockFlagsApplyConfig.applyNixConfig = true;
|
||||
if (!_lockedFlake) {
|
||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||
}
|
||||
return _lockedFlake;
|
||||
|
@ -707,6 +719,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
|||
{
|
||||
std::vector<std::shared_ptr<Installable>> result;
|
||||
|
||||
if (readOnlyMode) {
|
||||
settings.readOnlyMode = true;
|
||||
}
|
||||
|
||||
if (file || expr) {
|
||||
if (file && expr)
|
||||
throw UsageError("'--file' and '--expr' are exclusive");
|
||||
|
@ -988,7 +1004,7 @@ InstallablesCommand::InstallablesCommand()
|
|||
void InstallablesCommand::prepare()
|
||||
{
|
||||
if (_installables.empty() && useDefaultInstallables())
|
||||
// FIXME: commands like "nix install" should not have a
|
||||
// FIXME: commands like "nix profile install" should not have a
|
||||
// default, probably.
|
||||
_installables.push_back(".");
|
||||
installables = parseInstallables(getStore(), _installables);
|
||||
|
@ -998,13 +1014,14 @@ std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
|
|||
{
|
||||
if (_installables.empty()) {
|
||||
if (useDefaultInstallables())
|
||||
return parseFlakeRef(".", absPath("."));
|
||||
return parseFlakeRefWithFragment(".", absPath(".")).first;
|
||||
return {};
|
||||
}
|
||||
return parseFlakeRef(_installables.front(), absPath("."));
|
||||
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
|
||||
}
|
||||
|
||||
InstallableCommand::InstallableCommand()
|
||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||
: SourceExprCommand(supportReadOnlyMode)
|
||||
{
|
||||
expectArgs({
|
||||
.label = "installable",
|
||||
|
|
|
@ -80,10 +80,10 @@ struct Installable
|
|||
return {};
|
||||
}
|
||||
|
||||
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||
getCursors(EvalState & state);
|
||||
|
||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
||||
virtual ref<eval_cache::AttrCursor>
|
||||
getCursor(EvalState & state);
|
||||
|
||||
virtual FlakeRef nixpkgsFlakeRef() const
|
||||
|
@ -180,9 +180,15 @@ struct InstallableFlake : InstallableValue
|
|||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||
exists. */
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
getCursors(EvalState & state) override;
|
||||
|
||||
/* Get a cursor to the first attrpath in getActualAttrPaths() that
|
||||
exists, or throw an exception with suggestions if none exists. */
|
||||
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
|
||||
|
||||
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
||||
|
||||
FlakeRef nixpkgsFlakeRef() const override;
|
||||
|
|
|
@ -306,9 +306,9 @@ Value * EvalCache::getRootValue()
|
|||
return *value;
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> EvalCache::getRoot()
|
||||
ref<AttrCursor> EvalCache::getRoot()
|
||||
{
|
||||
return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt);
|
||||
return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
|
||||
}
|
||||
|
||||
AttrCursor::AttrCursor(
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
EvalState & state,
|
||||
RootLoader rootLoader);
|
||||
|
||||
std::shared_ptr<AttrCursor> getRoot();
|
||||
ref<AttrCursor> getRoot();
|
||||
};
|
||||
|
||||
enum AttrType {
|
||||
|
@ -104,6 +104,8 @@ public:
|
|||
|
||||
ref<AttrCursor> getAttr(std::string_view name);
|
||||
|
||||
/* Get an attribute along a chain of attrsets. Note that this does
|
||||
not auto-call functors or functions. */
|
||||
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
|
||||
|
||||
std::string getString();
|
||||
|
|
|
@ -238,9 +238,18 @@ std::optional<std::string> Input::getRef() const
|
|||
|
||||
std::optional<Hash> Input::getRev() const
|
||||
{
|
||||
if (auto s = maybeGetStrAttr(attrs, "rev"))
|
||||
return Hash::parseAny(*s, htSHA1);
|
||||
return {};
|
||||
std::optional<Hash> hash = {};
|
||||
|
||||
if (auto s = maybeGetStrAttr(attrs, "rev")) {
|
||||
try {
|
||||
hash = Hash::parseAnyPrefixed(*s);
|
||||
} catch (BadHash &e) {
|
||||
// Default to sha1 for backwards compatibility with existing flakes
|
||||
hash = Hash::parseAny(*s, htSHA1);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Input::getRevCount() const
|
||||
|
|
|
@ -28,9 +28,7 @@ static std::string readHead(const Path & path)
|
|||
|
||||
static bool isNotDotGitDirectory(const Path & path)
|
||||
{
|
||||
static const std::regex gitDirRegex("^(?:.*/)?\\.git$");
|
||||
|
||||
return not std::regex_match(path, gitDirRegex);
|
||||
return baseNameOf(path) != ".git";
|
||||
}
|
||||
|
||||
struct GitInputScheme : InputScheme
|
||||
|
@ -189,8 +187,16 @@ struct GitInputScheme : InputScheme
|
|||
if (submodules) cacheType += "-submodules";
|
||||
if (allRefs) cacheType += "-all-refs";
|
||||
|
||||
auto checkHashType = [&](const std::optional<Hash> & hash)
|
||||
{
|
||||
if (hash.has_value() && !(hash->type == htSHA1 || hash->type == htSHA256))
|
||||
throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(Base16, true));
|
||||
};
|
||||
|
||||
auto getLockedAttrs = [&]()
|
||||
{
|
||||
checkHashType(input.getRev());
|
||||
|
||||
return Attrs({
|
||||
{"type", cacheType},
|
||||
{"name", name},
|
||||
|
|
|
@ -178,9 +178,11 @@ struct MercurialInputScheme : InputScheme
|
|||
auto files = tokenizeString<std::set<std::string>>(
|
||||
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
||||
|
||||
Path actualPath(absPath(actualUrl));
|
||||
|
||||
PathFilter filter = [&](const Path & p) -> bool {
|
||||
assert(hasPrefix(p, actualUrl));
|
||||
std::string file(p, actualUrl.size() + 1);
|
||||
assert(hasPrefix(p, actualPath));
|
||||
std::string file(p, actualPath.size() + 1);
|
||||
|
||||
auto st = lstat(p);
|
||||
|
||||
|
@ -193,7 +195,7 @@ struct MercurialInputScheme : InputScheme
|
|||
return files.count(file);
|
||||
};
|
||||
|
||||
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||
|
||||
return {std::move(storePath), input};
|
||||
}
|
||||
|
@ -201,8 +203,17 @@ struct MercurialInputScheme : InputScheme
|
|||
|
||||
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
|
||||
|
||||
auto checkHashType = [&](const std::optional<Hash> & hash)
|
||||
{
|
||||
if (hash.has_value() && hash->type != htSHA1)
|
||||
throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(Base16, true));
|
||||
};
|
||||
|
||||
|
||||
auto getLockedAttrs = [&]()
|
||||
{
|
||||
checkHashType(input.getRev());
|
||||
|
||||
return Attrs({
|
||||
{"type", "hg"},
|
||||
{"name", name},
|
||||
|
|
|
@ -31,6 +31,8 @@ struct BuildResult
|
|||
ResolvesToAlreadyValid,
|
||||
NoSubstituters,
|
||||
} status = MiscFailure;
|
||||
|
||||
// FIXME: include entire ErrorInfo object.
|
||||
std::string errorMsg;
|
||||
|
||||
std::string toString() const {
|
||||
|
|
|
@ -1371,8 +1371,7 @@ void DerivationGoal::done(
|
|||
{
|
||||
buildResult.status = status;
|
||||
if (ex)
|
||||
// FIXME: strip: "error: "
|
||||
buildResult.errorMsg = ex->what();
|
||||
buildResult.errorMsg = fmt("%s", normaltxt(ex->info().msg));
|
||||
if (buildResult.status == BuildResult::TimedOut)
|
||||
worker.timedOut = true;
|
||||
if (buildResult.status == BuildResult::PermanentFailure)
|
||||
|
|
|
@ -704,6 +704,9 @@ void LocalDerivationGoal::startBuilder()
|
|||
|
||||
/* Run the builder. */
|
||||
printMsg(lvlChatty, "executing builder '%1%'", drv->builder);
|
||||
printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args));
|
||||
for (auto & i : drv->env)
|
||||
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
|
||||
|
||||
/* Create the log file. */
|
||||
Path logFile = openLogFile();
|
||||
|
|
|
@ -31,7 +31,7 @@ struct FileTransferSettings : Config
|
|||
R"(
|
||||
The timeout (in seconds) for establishing connections in the
|
||||
binary cache substituter. It corresponds to `curl`’s
|
||||
`--connect-timeout` option.
|
||||
`--connect-timeout` option. A value of 0 means no limit.
|
||||
)"};
|
||||
|
||||
Setting<unsigned long> stalledDownloadTimeout{
|
||||
|
@ -123,8 +123,6 @@ public:
|
|||
|
||||
template<typename... Args>
|
||||
FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args);
|
||||
|
||||
virtual const char* sname() const override { return "FileTransferError"; }
|
||||
};
|
||||
|
||||
bool isUri(std::string_view s);
|
||||
|
|
|
@ -215,7 +215,6 @@ void handleSQLiteBusy(const SQLiteBusy & e)
|
|||
if (now > lastWarned + 10) {
|
||||
lastWarned = now;
|
||||
logWarning({
|
||||
.name = "Sqlite busy",
|
||||
.msg = hintfmt(e.what())
|
||||
});
|
||||
}
|
||||
|
|
|
@ -127,9 +127,9 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
if (flag.handler.arity == ArityAny) break;
|
||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||
}
|
||||
if (flag.completer)
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
if (flag.completer)
|
||||
flag.completer(n, *prefix);
|
||||
}
|
||||
args.push_back(*pos++);
|
||||
|
@ -146,6 +146,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||
completions->add("--" + name, flag->description);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto i = longFlags.find(std::string(*pos, 2));
|
||||
if (i == longFlags.end()) return false;
|
||||
|
@ -187,10 +188,12 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
{
|
||||
std::vector<std::string> ss;
|
||||
for (const auto &[n, s] : enumerate(args)) {
|
||||
ss.push_back(s);
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
ss.push_back(*prefix);
|
||||
if (exp.completer)
|
||||
if (auto prefix = needsCompletion(s))
|
||||
exp.completer(n, *prefix);
|
||||
} else
|
||||
ss.push_back(s);
|
||||
}
|
||||
exp.handler.fun(ss);
|
||||
expectedArgs.pop_front();
|
||||
|
@ -279,21 +282,22 @@ static void _completePath(std::string_view prefix, bool onlyDirs)
|
|||
{
|
||||
completionType = ctFilenames;
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
||||
int flags = GLOB_NOESCAPE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
if (onlyDirs)
|
||||
flags |= GLOB_ONLYDIR;
|
||||
#endif
|
||||
if (glob((std::string(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||
// using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~<Tab> expands to /home/user/
|
||||
if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
||||
if (onlyDirs) {
|
||||
auto st = lstat(globbuf.gl_pathv[i]);
|
||||
auto st = stat(globbuf.gl_pathv[i]);
|
||||
if (!S_ISDIR(st.st_mode)) continue;
|
||||
}
|
||||
completions->add(globbuf.gl_pathv[i]);
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
|
||||
void completePath(size_t, std::string_view prefix)
|
||||
|
@ -322,11 +326,6 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
|||
.optional = true,
|
||||
.handler = {[=](std::string s) {
|
||||
assert(!command);
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, *prefix))
|
||||
completions->add(name);
|
||||
}
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end()) {
|
||||
std::set<std::string> commandNames;
|
||||
|
@ -337,6 +336,11 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
|||
}
|
||||
command = {s, i->second()};
|
||||
command->second->parent = this;
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, prefix))
|
||||
completions->add(name);
|
||||
}}
|
||||
});
|
||||
|
||||
|
|
|
@ -21,12 +21,9 @@ const std::string & BaseError::calcWhat() const
|
|||
if (what_.has_value())
|
||||
return *what_;
|
||||
else {
|
||||
err.name = sname();
|
||||
|
||||
std::ostringstream oss;
|
||||
showErrorInfo(oss, err, loggerSettings.showTrace);
|
||||
what_ = oss.str();
|
||||
|
||||
return *what_;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ struct Trace {
|
|||
|
||||
struct ErrorInfo {
|
||||
Verbosity level;
|
||||
std::string name; // FIXME: rename
|
||||
hintformat msg;
|
||||
std::optional<ErrPos> errPos;
|
||||
std::list<Trace> traces;
|
||||
|
@ -162,8 +161,6 @@ public:
|
|||
: err(e)
|
||||
{ }
|
||||
|
||||
virtual const char* sname() const { return "BaseError"; }
|
||||
|
||||
#ifdef EXCEPTION_NEEDS_THROW_SPEC
|
||||
~BaseError() throw () { };
|
||||
const char * what() const throw () { return calcWhat().c_str(); }
|
||||
|
@ -190,7 +187,6 @@ public:
|
|||
{ \
|
||||
public: \
|
||||
using superClass::superClass; \
|
||||
virtual const char* sname() const override { return #newClass; } \
|
||||
}
|
||||
|
||||
MakeError(Error, BaseError);
|
||||
|
@ -210,8 +206,6 @@ public:
|
|||
auto hf = hintfmt(args...);
|
||||
err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
|
||||
}
|
||||
|
||||
virtual const char* sname() const override { return "SysError"; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -50,10 +50,6 @@ public:
|
|||
ExperimentalFeature missingFeature;
|
||||
|
||||
MissingExperimentalFeature(ExperimentalFeature);
|
||||
virtual const char * sname() const override
|
||||
{
|
||||
return "MissingExperimentalFeature";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ static std::pair<std::optional<HashType>, bool> getParsedTypeAndSRI(std::string_
|
|||
{
|
||||
bool isSRI = false;
|
||||
|
||||
// Parse the has type before the separater, if there was one.
|
||||
// Parse the hash type before the separator, if there was one.
|
||||
std::optional<HashType> optParsedType;
|
||||
{
|
||||
auto hashRaw = splitPrefixTo(rest, ':');
|
||||
|
|
|
@ -93,13 +93,11 @@ public:
|
|||
|
||||
std::string gitRev() const
|
||||
{
|
||||
assert(type == htSHA1);
|
||||
return to_string(Base16, false);
|
||||
}
|
||||
|
||||
std::string gitShortRev() const
|
||||
{
|
||||
assert(type == htSHA1);
|
||||
return std::string(to_string(Base16, false), 0, 7);
|
||||
}
|
||||
|
||||
|
|
|
@ -357,7 +357,7 @@ Sink & operator << (Sink & sink, const Error & ex)
|
|||
sink
|
||||
<< "Error"
|
||||
<< info.level
|
||||
<< info.name
|
||||
<< "Error" // removed
|
||||
<< info.msg.str()
|
||||
<< 0 // FIXME: info.errPos
|
||||
<< info.traces.size();
|
||||
|
@ -426,11 +426,10 @@ Error readError(Source & source)
|
|||
auto type = readString(source);
|
||||
assert(type == "Error");
|
||||
auto level = (Verbosity) readInt(source);
|
||||
auto name = readString(source);
|
||||
auto name = readString(source); // removed
|
||||
auto msg = readString(source);
|
||||
ErrorInfo info {
|
||||
.level = level,
|
||||
.name = name,
|
||||
.msg = hintformat(std::move(format("%s") % msg)),
|
||||
};
|
||||
auto havePos = readNum<size_t>(source);
|
||||
|
|
|
@ -178,7 +178,7 @@ namespace nix {
|
|||
}
|
||||
|
||||
TEST(parseURL, parseFileURLWithQueryAndFragment) {
|
||||
auto s = "file:///none/of/your/business";
|
||||
auto s = "file:///none/of//your/business";
|
||||
auto parsed = parseURL(s);
|
||||
|
||||
ParsedURL expected {
|
||||
|
@ -186,7 +186,7 @@ namespace nix {
|
|||
.base = "",
|
||||
.scheme = "file",
|
||||
.authority = "",
|
||||
.path = "/none/of/your/business",
|
||||
.path = "/none/of//your/business",
|
||||
.query = (StringMap) { },
|
||||
.fragment = "",
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncod
|
|||
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
||||
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
||||
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
||||
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
|
||||
const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
|
||||
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
||||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||
|
||||
|
|
|
@ -198,6 +198,17 @@ std::string_view baseNameOf(std::string_view path)
|
|||
}
|
||||
|
||||
|
||||
std::string expandTilde(std::string_view path)
|
||||
{
|
||||
// TODO: expand ~user ?
|
||||
auto tilde = path.substr(0, 2);
|
||||
if (tilde == "~/" || tilde == "~")
|
||||
return getHome() + std::string(path.substr(1));
|
||||
else
|
||||
return std::string(path);
|
||||
}
|
||||
|
||||
|
||||
bool isInDir(std::string_view path, std::string_view dir)
|
||||
{
|
||||
return path.substr(0, 1) == "/"
|
||||
|
@ -213,6 +224,15 @@ bool isDirOrInDir(std::string_view path, std::string_view dir)
|
|||
}
|
||||
|
||||
|
||||
struct stat stat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st))
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
struct stat lstat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
|
|
|
@ -68,6 +68,9 @@ Path dirOf(const PathView path);
|
|||
following the final `/' (trailing slashes are removed). */
|
||||
std::string_view baseNameOf(std::string_view path);
|
||||
|
||||
/* Perform tilde expansion on a path. */
|
||||
std::string expandTilde(std::string_view path);
|
||||
|
||||
/* Check whether 'path' is a descendant of 'dir'. Both paths must be
|
||||
canonicalized. */
|
||||
bool isInDir(std::string_view path, std::string_view dir);
|
||||
|
@ -77,6 +80,7 @@ bool isInDir(std::string_view path, std::string_view dir);
|
|||
bool isDirOrInDir(std::string_view path, std::string_view dir);
|
||||
|
||||
/* Get status of `path'. */
|
||||
struct stat stat(const Path & path);
|
||||
struct stat lstat(const Path & path);
|
||||
|
||||
/* Return true iff the given path exists. */
|
||||
|
|
|
@ -61,14 +61,18 @@ std::string resolveString(Store & store, const std::string & toResolve, const Bu
|
|||
|
||||
UnresolvedApp Installable::toApp(EvalState & state)
|
||||
{
|
||||
auto [cursor, attrPath] = getCursor(state);
|
||||
auto cursor = getCursor(state);
|
||||
auto attrPath = cursor->getAttrPath();
|
||||
|
||||
auto type = cursor->getAttr("type")->getString();
|
||||
|
||||
std::string expected = !attrPath.empty() && attrPath[0] == "apps" ? "app" : "derivation";
|
||||
if (type != expected)
|
||||
throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected);
|
||||
|
||||
if (type == "app") {
|
||||
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
|
||||
|
||||
|
||||
std::vector<StorePathWithOutputs> context2;
|
||||
for (auto & [path, name] : context)
|
||||
context2.push_back({path, {name}});
|
||||
|
@ -101,7 +105,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
|||
}
|
||||
|
||||
else
|
||||
throw Error("attribute '%s' has unsupported type '%s'", attrPath, type);
|
||||
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
|
||||
}
|
||||
|
||||
// FIXME: move to libcmd
|
||||
|
|
|
@ -16,7 +16,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
|||
std::optional<std::string> apply;
|
||||
std::optional<Path> writeTo;
|
||||
|
||||
CmdEval()
|
||||
CmdEval() : InstallableCommand(true /* supportReadOnlyMode */)
|
||||
{
|
||||
addFlag({
|
||||
.longName = "raw",
|
||||
|
|
|
@ -705,7 +705,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|||
defaultTemplateAttrPathsPrefixes,
|
||||
lockFlags);
|
||||
|
||||
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
||||
auto cursor = installable.getCursor(*evalState);
|
||||
|
||||
auto templateDirAttr = cursor->getAttr("path");
|
||||
auto templateDir = templateDirAttr->getString();
|
||||
|
|
|
@ -177,8 +177,8 @@ Currently the `type` attribute can be one of the following:
|
|||
attribute `url`.
|
||||
|
||||
In URL form, the schema must be `http://`, `https://` or `file://`
|
||||
URLs and the extension must be `.zip`, `.tar`, `.tar.gz`, `.tar.xz`,
|
||||
`.tar.bz2` or `.tar.zst`.
|
||||
URLs and the extension must be `.zip`, `.tar`, `.tgz`, `.tar.gz`,
|
||||
`.tar.xz`, `.tar.bz2` or `.tar.zst`.
|
||||
|
||||
* `github`: A more efficient way to fetch repositories from
|
||||
GitHub. The following attributes are required:
|
||||
|
|
|
@ -165,8 +165,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
|||
}
|
||||
};
|
||||
|
||||
for (auto & [cursor, prefix] : installable->getCursors(*state))
|
||||
visit(*cursor, parseAttrPath(*state, prefix), true);
|
||||
for (auto & cursor : installable->getCursors(*state))
|
||||
visit(*cursor, cursor->getAttrPath(), true);
|
||||
|
||||
if (!json && !results)
|
||||
throw Error("no results for the given search term(s)!");
|
||||
|
|
|
@ -7,7 +7,9 @@ fi
|
|||
|
||||
clearStore
|
||||
|
||||
repo=$TEST_ROOT/hg
|
||||
# Intentionally not in a canonical form
|
||||
# See https://github.com/NixOS/nix/issues/6195
|
||||
repo=$TEST_ROOT/./hg
|
||||
|
||||
rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix
|
||||
|
||||
|
@ -28,6 +30,12 @@ echo world > $repo/hello
|
|||
hg commit --cwd $repo -m 'Bla2'
|
||||
rev2=$(hg log --cwd $repo -r tip --template '{node}')
|
||||
|
||||
# Fetch an unclean branch.
|
||||
echo unclean > $repo/hello
|
||||
path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
||||
[[ $(cat $path/hello) = unclean ]]
|
||||
hg revert --cwd $repo --all
|
||||
|
||||
# Fetch the default branch.
|
||||
path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
||||
[[ $(cat $path/hello) = world ]]
|
||||
|
|
29
tests/flakes-run.sh
Normal file
29
tests/flakes-run.sh
Normal file
|
@ -0,0 +1,29 @@
|
|||
source common.sh
|
||||
|
||||
clearStore
|
||||
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
|
||||
cp ./shell-hello.nix ./config.nix $TEST_HOME
|
||||
cd $TEST_HOME
|
||||
|
||||
cat <<EOF > flake.nix
|
||||
{
|
||||
outputs = {self}: {
|
||||
packages.$system.pkgAsPkg = (import ./shell-hello.nix).hello;
|
||||
packages.$system.appAsApp = self.packages.$system.appAsApp;
|
||||
|
||||
apps.$system.pkgAsApp = self.packages.$system.pkgAsPkg;
|
||||
apps.$system.appAsApp = {
|
||||
type = "app";
|
||||
program = "\${(import ./shell-hello.nix).hello}/bin/hello";
|
||||
};
|
||||
};
|
||||
}
|
||||
EOF
|
||||
nix run --no-write-lock-file .#appAsApp
|
||||
nix run --no-write-lock-file .#pkgAsPkg
|
||||
|
||||
! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'"
|
||||
! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'"
|
||||
|
||||
clearStore
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
nix_tests = \
|
||||
flakes.sh \
|
||||
flakes-run.sh \
|
||||
ca/gc.sh \
|
||||
gc.sh \
|
||||
remote-store.sh \
|
||||
|
|
Loading…
Reference in a new issue