Compare commits
2 Commits
cloneable-
...
nix-init-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc711c6956 | ||
|
|
401b09b7fe |
138
src/nix/flake.cc
138
src/nix/flake.cc
@@ -638,142 +638,6 @@ struct CmdFlakeCheck : FlakeCommand
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|
||||||
{
|
|
||||||
std::string templateUrl = "templates";
|
|
||||||
Path destDir;
|
|
||||||
|
|
||||||
const Strings attrsPathPrefixes{"templates."};
|
|
||||||
const LockFlags lockFlags{ .writeLockFile = false };
|
|
||||||
|
|
||||||
CmdFlakeInitCommon()
|
|
||||||
{
|
|
||||||
addFlag({
|
|
||||||
.longName = "template",
|
|
||||||
.shortName = 't',
|
|
||||||
.description = "The template to use.",
|
|
||||||
.labels = {"template"},
|
|
||||||
.handler = {&templateUrl},
|
|
||||||
.completer = {[&](size_t, std::string_view prefix) {
|
|
||||||
completeFlakeRefWithFragment(
|
|
||||||
getEvalState(),
|
|
||||||
lockFlags,
|
|
||||||
attrsPathPrefixes,
|
|
||||||
{"defaultTemplate"},
|
|
||||||
prefix);
|
|
||||||
}}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(nix::ref<nix::Store> store) override
|
|
||||||
{
|
|
||||||
auto flakeDir = absPath(destDir);
|
|
||||||
|
|
||||||
auto evalState = getEvalState();
|
|
||||||
|
|
||||||
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
|
|
||||||
|
|
||||||
auto installable = InstallableFlake(nullptr,
|
|
||||||
evalState, std::move(templateFlakeRef),
|
|
||||||
Strings{templateName == "" ? "defaultTemplate" : templateName},
|
|
||||||
Strings(attrsPathPrefixes), lockFlags);
|
|
||||||
|
|
||||||
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
|
||||||
|
|
||||||
auto templateDir = cursor->getAttr("path")->getString();
|
|
||||||
|
|
||||||
assert(store->isInStore(templateDir));
|
|
||||||
|
|
||||||
std::vector<Path> files;
|
|
||||||
|
|
||||||
std::function<void(const Path & from, const Path & to)> copyDir;
|
|
||||||
copyDir = [&](const Path & from, const Path & to)
|
|
||||||
{
|
|
||||||
createDirs(to);
|
|
||||||
|
|
||||||
for (auto & entry : readDirectory(from)) {
|
|
||||||
auto from2 = from + "/" + entry.name;
|
|
||||||
auto to2 = to + "/" + entry.name;
|
|
||||||
auto st = lstat(from2);
|
|
||||||
if (S_ISDIR(st.st_mode))
|
|
||||||
copyDir(from2, to2);
|
|
||||||
else if (S_ISREG(st.st_mode)) {
|
|
||||||
auto contents = readFile(from2);
|
|
||||||
if (pathExists(to2)) {
|
|
||||||
auto contents2 = readFile(to2);
|
|
||||||
if (contents != contents2)
|
|
||||||
throw Error("refusing to overwrite existing file '%s'", to2);
|
|
||||||
} else
|
|
||||||
writeFile(to2, contents);
|
|
||||||
}
|
|
||||||
else if (S_ISLNK(st.st_mode)) {
|
|
||||||
auto target = readLink(from2);
|
|
||||||
if (pathExists(to2)) {
|
|
||||||
if (readLink(to2) != target)
|
|
||||||
throw Error("refusing to overwrite existing symlink '%s'", to2);
|
|
||||||
} else
|
|
||||||
createSymlink(target, to2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw Error("file '%s' has unsupported type", from2);
|
|
||||||
files.push_back(to2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
copyDir(templateDir, flakeDir);
|
|
||||||
|
|
||||||
if (pathExists(flakeDir + "/.git")) {
|
|
||||||
Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" };
|
|
||||||
for (auto & s : files) args.push_back(s);
|
|
||||||
runProgram("git", true, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CmdFlakeInit : CmdFlakeInitCommon
|
|
||||||
{
|
|
||||||
std::string description() override
|
|
||||||
{
|
|
||||||
return "create a flake in the current directory from a template";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string doc() override
|
|
||||||
{
|
|
||||||
return
|
|
||||||
#include "flake-init.md"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
CmdFlakeInit()
|
|
||||||
{
|
|
||||||
destDir = ".";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CmdFlakeNew : CmdFlakeInitCommon
|
|
||||||
{
|
|
||||||
std::string description() override
|
|
||||||
{
|
|
||||||
return "create a flake in the specified directory from a template";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string doc() override
|
|
||||||
{
|
|
||||||
return
|
|
||||||
#include "flake-new.md"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
CmdFlakeNew()
|
|
||||||
{
|
|
||||||
expectArgs({
|
|
||||||
.label = "dest-dir",
|
|
||||||
.handler = {&destDir},
|
|
||||||
.completer = completePath
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CmdFlakeClone : FlakeCommand
|
struct CmdFlakeClone : FlakeCommand
|
||||||
{
|
{
|
||||||
Path destDir;
|
Path destDir;
|
||||||
@@ -1125,8 +989,6 @@ struct CmdFlake : NixMultiCommand
|
|||||||
{"metadata", []() { return make_ref<CmdFlakeMetadata>(); }},
|
{"metadata", []() { return make_ref<CmdFlakeMetadata>(); }},
|
||||||
{"info", []() { return make_ref<CmdFlakeInfo>(); }},
|
{"info", []() { return make_ref<CmdFlakeInfo>(); }},
|
||||||
{"check", []() { return make_ref<CmdFlakeCheck>(); }},
|
{"check", []() { return make_ref<CmdFlakeCheck>(); }},
|
||||||
{"init", []() { return make_ref<CmdFlakeInit>(); }},
|
|
||||||
{"new", []() { return make_ref<CmdFlakeNew>(); }},
|
|
||||||
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
|
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
|
||||||
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
|
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
|
||||||
{"show", []() { return make_ref<CmdFlakeShow>(); }},
|
{"show", []() { return make_ref<CmdFlakeShow>(); }},
|
||||||
|
|||||||
@@ -106,26 +106,28 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::vector<std::string>> aliases = {
|
std::map<std::vector<std::string>, std::vector<std::string>> aliases = {
|
||||||
{"add-to-store", {"store", "add-path"}},
|
{{"add-to-store"}, {"store", "add-path"}},
|
||||||
{"cat-nar", {"nar", "cat"}},
|
{{"cat-nar"}, {"nar", "cat"}},
|
||||||
{"cat-store", {"store", "cat"}},
|
{{"cat-store"}, {"store", "cat"}},
|
||||||
{"copy-sigs", {"store", "copy-sigs"}},
|
{{"copy-sigs"}, {"store", "copy-sigs"}},
|
||||||
{"dev-shell", {"develop"}},
|
{{"dev-shell"}, {"develop"}},
|
||||||
{"diff-closures", {"store", "diff-closures"}},
|
{{"diff-closures"}, {"store", "diff-closures"}},
|
||||||
{"dump-path", {"store", "dump-path"}},
|
{{"dump-path"}, {"store", "dump-path"}},
|
||||||
{"hash-file", {"hash", "file"}},
|
{{"hash-file"}, {"hash", "file"}},
|
||||||
{"hash-path", {"hash", "path"}},
|
{{"hash-path"}, {"hash", "path"}},
|
||||||
{"ls-nar", {"nar", "ls"}},
|
{{"ls-nar"}, {"nar", "ls"}},
|
||||||
{"ls-store", {"store", "ls"}},
|
{{"ls-store"}, {"store", "ls"}},
|
||||||
{"make-content-addressable", {"store", "make-content-addressable"}},
|
{{"make-content-addressable"}, {"store", "make-content-addressable"}},
|
||||||
{"optimise-store", {"store", "optimise"}},
|
{{"optimise-store"}, {"store", "optimise"}},
|
||||||
{"ping-store", {"store", "ping"}},
|
{{"ping-store"}, {"store", "ping"}},
|
||||||
{"sign-paths", {"store", "sign"}},
|
{{"sign-paths"}, {"store", "sign"}},
|
||||||
{"to-base16", {"hash", "to-base16"}},
|
{{"to-base16"}, {"hash", "to-base16"}},
|
||||||
{"to-base32", {"hash", "to-base32"}},
|
{{"to-base32"}, {"hash", "to-base32"}},
|
||||||
{"to-base64", {"hash", "to-base64"}},
|
{{"to-base64"}, {"hash", "to-base64"}},
|
||||||
{"verify", {"store", "verify"}},
|
{{"verify"}, {"store", "verify"}},
|
||||||
|
{{"flake", "init"}, {"init"}},
|
||||||
|
{{"flake", "new"}, {"new"}},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool aliasUsed = false;
|
bool aliasUsed = false;
|
||||||
@@ -134,14 +136,33 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||||||
{
|
{
|
||||||
if (aliasUsed || command || pos == args.end()) return pos;
|
if (aliasUsed || command || pos == args.end()) return pos;
|
||||||
auto arg = *pos;
|
auto arg = *pos;
|
||||||
auto i = aliases.find(arg);
|
|
||||||
if (i == aliases.end()) return pos;
|
// Loop through the aliases to see whether the current cli corresponds
|
||||||
warn("'%s' is a deprecated alias for '%s'",
|
// to one of them.
|
||||||
arg, concatStringsSep(" ", i->second));
|
for (auto & [from, to] : aliases) {
|
||||||
pos = args.erase(pos);
|
auto i = pos;
|
||||||
for (auto j = i->second.rbegin(); j != i->second.rend(); ++j)
|
bool isCurrentAlias = true;
|
||||||
pos = args.insert(pos, *j);
|
// Is the current alias a prefix of the args?
|
||||||
aliasUsed = true;
|
for (auto & fromItem : from) {
|
||||||
|
if (i == args.end() || *i != fromItem) {
|
||||||
|
// The current alias doesn’t match the args
|
||||||
|
isCurrentAlias = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// If we went through to the end of the previous loop, then we match
|
||||||
|
// the currently considered alias.
|
||||||
|
// So rewrite the alias in the current args.
|
||||||
|
if (isCurrentAlias) {
|
||||||
|
warn("'%s' is a deprecated alias for '%s'",
|
||||||
|
concatStringsSep(" ", from), concatStringsSep(" ", to));
|
||||||
|
pos = args.erase(pos, i);
|
||||||
|
pos = args.insert(pos, to.begin(), to.end());
|
||||||
|
aliasUsed = true;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
150
src/nix/new.cc
Normal file
150
src/nix/new.cc
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include "command.hh"
|
||||||
|
#include "common-args.hh"
|
||||||
|
#include "shared.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
|
#include "fs-accessor.hh"
|
||||||
|
#include "eval-cache.hh"
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
using namespace nix::flake;
|
||||||
|
|
||||||
|
struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||||
|
{
|
||||||
|
std::string templateUrl = "templates";
|
||||||
|
Path destDir;
|
||||||
|
|
||||||
|
const Strings attrsPathPrefixes{"templates."};
|
||||||
|
const LockFlags lockFlags{ .writeLockFile = false };
|
||||||
|
|
||||||
|
CmdFlakeInitCommon()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "template",
|
||||||
|
.shortName = 't',
|
||||||
|
.description = "The template to use.",
|
||||||
|
.labels = {"template"},
|
||||||
|
.handler = {&templateUrl},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
completeFlakeRefWithFragment(
|
||||||
|
getEvalState(),
|
||||||
|
lockFlags,
|
||||||
|
attrsPathPrefixes,
|
||||||
|
{"defaultTemplate"},
|
||||||
|
prefix);
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(nix::ref<nix::Store> store) override
|
||||||
|
{
|
||||||
|
auto flakeDir = absPath(destDir);
|
||||||
|
|
||||||
|
auto evalState = getEvalState();
|
||||||
|
|
||||||
|
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
|
||||||
|
|
||||||
|
auto installable = InstallableFlake(nullptr,
|
||||||
|
evalState, std::move(templateFlakeRef),
|
||||||
|
Strings{templateName == "" ? "defaultTemplate" : templateName},
|
||||||
|
Strings(attrsPathPrefixes), lockFlags);
|
||||||
|
|
||||||
|
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
||||||
|
|
||||||
|
auto templateDir = cursor->getAttr("path")->getString();
|
||||||
|
|
||||||
|
assert(store->isInStore(templateDir));
|
||||||
|
|
||||||
|
std::vector<Path> files;
|
||||||
|
|
||||||
|
std::function<void(const Path & from, const Path & to)> copyDir;
|
||||||
|
copyDir = [&](const Path & from, const Path & to)
|
||||||
|
{
|
||||||
|
createDirs(to);
|
||||||
|
|
||||||
|
for (auto & entry : readDirectory(from)) {
|
||||||
|
auto from2 = from + "/" + entry.name;
|
||||||
|
auto to2 = to + "/" + entry.name;
|
||||||
|
auto st = lstat(from2);
|
||||||
|
if (S_ISDIR(st.st_mode))
|
||||||
|
copyDir(from2, to2);
|
||||||
|
else if (S_ISREG(st.st_mode)) {
|
||||||
|
auto contents = readFile(from2);
|
||||||
|
if (pathExists(to2)) {
|
||||||
|
auto contents2 = readFile(to2);
|
||||||
|
if (contents != contents2)
|
||||||
|
throw Error("refusing to overwrite existing file '%s'", to2);
|
||||||
|
} else
|
||||||
|
writeFile(to2, contents);
|
||||||
|
}
|
||||||
|
else if (S_ISLNK(st.st_mode)) {
|
||||||
|
auto target = readLink(from2);
|
||||||
|
if (pathExists(to2)) {
|
||||||
|
if (readLink(to2) != target)
|
||||||
|
throw Error("refusing to overwrite existing symlink '%s'", to2);
|
||||||
|
} else
|
||||||
|
createSymlink(target, to2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw Error("file '%s' has unsupported type", from2);
|
||||||
|
files.push_back(to2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
copyDir(templateDir, flakeDir);
|
||||||
|
|
||||||
|
if (pathExists(flakeDir + "/.git")) {
|
||||||
|
Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" };
|
||||||
|
for (auto & s : files) args.push_back(s);
|
||||||
|
runProgram("git", true, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdFlakeInit : CmdFlakeInitCommon
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "create a flake in the current directory from a template";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "init.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CmdFlakeInit()
|
||||||
|
{
|
||||||
|
destDir = ".";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdFlakeNew : CmdFlakeInitCommon
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "create a flake in the specified directory from a template";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "new.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CmdFlakeNew()
|
||||||
|
{
|
||||||
|
expectArgs({
|
||||||
|
.label = "dest-dir",
|
||||||
|
.handler = {&destDir},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto r0 = registerCommand<CmdFlakeInit>("init");
|
||||||
|
static auto r1 = registerCommand<CmdFlakeNew>("new");
|
||||||
|
|
||||||
Reference in New Issue
Block a user