mirror of
https://github.com/0xbbjubjub/miniguest.git
synced 2024-09-11 05:55:35 +03:00
tool: rewrite in C++
This commit is contained in:
parent
75ac66c2dd
commit
1da76fbb65
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -13,4 +13,4 @@
|
||||
- [ ] Documented intended functionality
|
||||
- [ ] Added checks for intended functionality
|
||||
- [ ] Changed Nix files are formatted per `nixpkgs-fmt`
|
||||
- [ ] Changed Bash files are formatted per `shfmt`
|
||||
- [ ] Changed C++ files are formatted per `clang-format`
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,3 +2,6 @@
|
||||
result*
|
||||
|
||||
/template/flake.lock
|
||||
|
||||
build/
|
||||
meson-*/
|
||||
|
37
Taskfile.yaml
Normal file
37
Taskfile.yaml
Normal file
@ -0,0 +1,37 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
deps: [build]
|
||||
|
||||
setup:
|
||||
cmds:
|
||||
- meson setup tool/ build/
|
||||
status:
|
||||
- test -d build/
|
||||
|
||||
build:
|
||||
deps: [setup]
|
||||
dir: build/
|
||||
cmds:
|
||||
- meson compile
|
||||
|
||||
fmt:
|
||||
deps: [fmt/nix, fmt/cpp]
|
||||
fmt/nix:
|
||||
- nixpkgs-fmt .
|
||||
fmt/cpp:
|
||||
deps: [setup]
|
||||
cmds:
|
||||
- ninja -C build/ clang-format
|
||||
|
||||
lint:
|
||||
deps: [lint/cpp]
|
||||
lint/cpp:
|
||||
- ninja -C build/ clang-tidy
|
||||
- iwyu_tool.py -p=build/
|
||||
|
||||
clean:
|
||||
- git clean -Xdf build/
|
@ -29,8 +29,7 @@ let
|
||||
inherit name;
|
||||
nodes.machine = {
|
||||
environment.systemPackages = [
|
||||
# wrapper clears PATH to check for implicit dependencies
|
||||
(writeShellScriptBin "miniguest" ''PATH= exec ${miniguest}/bin/miniguest "$@"'')
|
||||
miniguest
|
||||
];
|
||||
system.extraDependencies = [ pinned-nixpkgs (import pinned-nixpkgs { inherit system; }).stdenvNoCC ];
|
||||
virtualisation.memorySize = 1024;
|
||||
@ -64,7 +63,7 @@ lib.optionalAttrs stdenv.isLinux {
|
||||
name = "miniguest-install-rename";
|
||||
testScript = ''
|
||||
machine.succeed("""
|
||||
miniguest install --name=renamed_dummy /tmp/flake1#dummy
|
||||
miniguest install --name renamed_dummy /tmp/flake1#dummy
|
||||
""")
|
||||
assert "foo" in machine.succeed("""
|
||||
cat /etc/miniguests/renamed_dummy/boot/init
|
||||
|
@ -1,30 +1,50 @@
|
||||
imports = [ "git.hooks" ]
|
||||
imports = [
|
||||
"git.hooks",
|
||||
"language.c",
|
||||
"set-boost-root.nix",
|
||||
]
|
||||
|
||||
[devshell]
|
||||
name = "miniguest"
|
||||
|
||||
[[commands]]
|
||||
package = "bash"
|
||||
packages = [
|
||||
"ninja", # for meson
|
||||
]
|
||||
|
||||
[[commands]]
|
||||
package = "nixFlakes"
|
||||
|
||||
[[commands]]
|
||||
name = "task"
|
||||
package = "go-task"
|
||||
|
||||
[[commands]]
|
||||
package = "meson"
|
||||
|
||||
[[commands]]
|
||||
package = "nixpkgs-fmt"
|
||||
category = "formatters"
|
||||
|
||||
[[commands]]
|
||||
package = "shfmt"
|
||||
name = "clang-format"
|
||||
package = "clang-tools"
|
||||
category = "formatters"
|
||||
|
||||
[[commands]]
|
||||
package = "argbash"
|
||||
category = "preprocessors"
|
||||
name = "clang-tidy"
|
||||
package = "clang-tools"
|
||||
category = "linters"
|
||||
|
||||
[[commands]]
|
||||
package = "shellcheck"
|
||||
package = "include-what-you-use"
|
||||
category = "linters"
|
||||
|
||||
[language.c]
|
||||
includes = [
|
||||
"boehmgc",
|
||||
"nixFlakes",
|
||||
"nlohmann_json",
|
||||
]
|
||||
|
||||
[git.hooks]
|
||||
enable = true
|
||||
pre-commit.text = """
|
||||
@ -41,5 +61,5 @@ function find_staged {
|
||||
}
|
||||
|
||||
find_staged '*.nix' | xargs -r nixpkgs-fmt --check || exit
|
||||
find_staged '*.bash' | xargs -r shfmt -d -s || exit
|
||||
find_staged '*.hpp' '*.cpp' | xargs -r clang-format -n -Werror || exit
|
||||
"""
|
||||
|
8
nix/set-boost-root.nix
Normal file
8
nix/set-boost-root.nix
Normal file
@ -0,0 +1,8 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
env = [{
|
||||
name = "BOOST_ROOT";
|
||||
value = pkgs.boost.dev;
|
||||
}];
|
||||
}
|
5
tool/.clang-format
Normal file
5
tool/.clang-format
Normal file
@ -0,0 +1,5 @@
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortBlocksOnASingleLine: Empty
|
0
tool/.clang-tidy
Normal file
0
tool/.clang-tidy
Normal file
53
tool/common.cpp
Normal file
53
tool/common.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include "error.hh"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace nix;
|
||||
|
||||
Context ContextBuilder::build() {
|
||||
if (!symlink_path)
|
||||
fs::create_directory(default_symlinks_dir);
|
||||
if (!profile_path)
|
||||
fs::create_directory(default_profiles_dir);
|
||||
return {symlink_path.value_or(default_symlinks_dir / guest_name),
|
||||
profile_path.value_or(default_profiles_dir / guest_name)};
|
||||
}
|
||||
|
||||
void Context::ensure_symlink() {
|
||||
auto st = fs::symlink_status(symlink_path);
|
||||
if (!fs::exists(st))
|
||||
fs::create_symlink(profile_path, symlink_path);
|
||||
else
|
||||
check_symlink(st);
|
||||
}
|
||||
void Context::check_symlink(const fs::file_status &st) {
|
||||
if (!fs::is_symlink(st) || fs::read_symlink(symlink_path) != profile_path)
|
||||
throw Error(1,
|
||||
"not touching symlink because it's not in an expected state");
|
||||
}
|
||||
void Context::remove_symlink() {
|
||||
auto st = fs::symlink_status(symlink_path);
|
||||
if (fs::exists(st))
|
||||
check_symlink(st);
|
||||
|
||||
fs::remove(symlink_path);
|
||||
}
|
45
tool/common.hpp
Normal file
45
tool/common.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
struct ContextBuilder final {
|
||||
std::string guest_name;
|
||||
std::optional<std::filesystem::path> symlink_path, profile_path;
|
||||
std::filesystem::path default_symlinks_dir = "/etc/miniguests",
|
||||
default_profiles_dir =
|
||||
"/nix/var/nix/profiles/miniguest-profiles";
|
||||
|
||||
struct Context build();
|
||||
};
|
||||
|
||||
struct Context final {
|
||||
const std::filesystem::path symlink_path, profile_path;
|
||||
|
||||
void ensure_symlink();
|
||||
void remove_symlink();
|
||||
|
||||
private:
|
||||
Context(std::filesystem::path symlink_path,
|
||||
std::filesystem::path profile_path)
|
||||
: symlink_path(symlink_path), profile_path(profile_path) {}
|
||||
friend Context ContextBuilder::build();
|
||||
|
||||
void check_symlink(const std::filesystem::file_status &st);
|
||||
};
|
@ -1,36 +0,0 @@
|
||||
source create_arg.bash
|
||||
|
||||
name="$_arg_guest_name"
|
||||
|
||||
set_color_red=$'\e[1m\e[31m'
|
||||
reset_color=$'\e(B\e[m'
|
||||
|
||||
case "${_arg_hypervisor:?}" in
|
||||
libvirt)
|
||||
cat <<EOF
|
||||
# Create $name using:
|
||||
virt-install \\
|
||||
--connect qemu:///system \\
|
||||
-n $name --os-variant nixos-unstable \\
|
||||
--memory 1536 \\
|
||||
--disk none \\
|
||||
--import \\
|
||||
--boot kernel=/etc/miniguests/$name/kernel,initrd=$guests_dir/$name/initrd \\
|
||||
--filesystem /nix/store/,nix-store,readonly=yes,accessmode=squash \\
|
||||
--filesystem /etc/miniguests/$name/boot/,boot,readonly=yes,accessmode=squash \\
|
||||
|
||||
EOF
|
||||
;;
|
||||
lxc)
|
||||
cat <<EOF
|
||||
#$set_color_red WARNING: make sure root is uid-mapped, otherwise you might experience store corruption in the host!$reset_color
|
||||
# Create $name using:
|
||||
lxc-create $name \\
|
||||
-f extra-config \\
|
||||
-t local -- \\
|
||||
-m @lxc_template@/meta.tar.xz \\
|
||||
-f @lxc_template@/rootfs.tar.xz \\
|
||||
|
||||
EOF
|
||||
;;
|
||||
esac
|
109
tool/create.cpp
Normal file
109
tool/create.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "manifest.hpp"
|
||||
|
||||
#include "build-result.hh"
|
||||
#include "command.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "eval-cache.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace nix;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct CmdCreate : virtual EvalCommand, virtual MixProfile {
|
||||
std::string guest_name;
|
||||
std::optional<std::string> hypervisor;
|
||||
|
||||
const std::string set_color_red = "\e[1m\e[31m", reset_color = "\e(B\e[m";
|
||||
|
||||
CmdCreate() {
|
||||
expectArg("name", &guest_name);
|
||||
addFlag({
|
||||
.longName = "type",
|
||||
.shortName = 't',
|
||||
.description = "hypervisor to configure (default: libvirt)",
|
||||
.labels = {"hypervisor"},
|
||||
.handler = {&hypervisor},
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override {
|
||||
return "generate a command to configure the guest in the hypervisor";
|
||||
}
|
||||
|
||||
std::string doc() override {
|
||||
return R""(miniguest create [-h|--help] [-t|--hypervisor <HYPERVISOR>] <guest-name>
|
||||
<guest-name>: name of guest to create
|
||||
-h, --help: Prints help
|
||||
-t, --hypervisor: hypervisor to configure. Can be one of: 'libvirt' and 'lxc' (default: 'libvirt'))"";
|
||||
}
|
||||
|
||||
std::vector<std::string> prepare_command() {
|
||||
if (*hypervisor == "libvirt")
|
||||
return {
|
||||
"virt-install -n " + guest_name,
|
||||
"--connect qemu:///system",
|
||||
"--os-variant nixos-unstable",
|
||||
"--memory 1536",
|
||||
"--disk none",
|
||||
"--import",
|
||||
"--boot kernel=/etc/miniguests/" + guest_name +
|
||||
"/kernel,initrd=/etc/miniguests/" + guest_name + "/initrd",
|
||||
"--filesystem /nix/store/,nix-store,readonly=yes,accessmode=squash",
|
||||
"--filesystem /etc/miniguests/" + guest_name +
|
||||
"/boot/,boot,readonly=yes,accessmode=squash",
|
||||
};
|
||||
|
||||
else if (*hypervisor == "lxc")
|
||||
return {
|
||||
"lxc-create " + guest_name,
|
||||
"-f extra-config",
|
||||
"-t local --",
|
||||
"-m @lxc_template@/meta.tar.xz",
|
||||
"-f @lxc_template@/rootfs.tar.xz",
|
||||
};
|
||||
else
|
||||
throw Error(2, "unknown hypervisor type");
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override {
|
||||
if (!hypervisor)
|
||||
hypervisor = "libvirt";
|
||||
|
||||
auto cmd = prepare_command();
|
||||
|
||||
if (*hypervisor == "lxc")
|
||||
std::cout << "# " << set_color_red
|
||||
<< "WARNING: make sure root is uid-mapped, otherwise you might "
|
||||
"experience store corruption in the host!"
|
||||
<< reset_color << std::endl;
|
||||
std::cout << "# Create " << guest_name << " using:" << std::endl;
|
||||
for (auto &line : cmd)
|
||||
std::cout << line << " \\\n ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
virtual ~CmdCreate() = default;
|
||||
};
|
||||
|
||||
static auto rCmdCreate = registerCommand<CmdCreate>("create");
|
@ -1,5 +0,0 @@
|
||||
# ARG_POSITIONAL_SINGLE(guest-name, name of guest to create)
|
||||
# ARG_HELP(assist in creatig the guest by configuring the hypervisor)
|
||||
# ARG_OPTIONAL_SINGLE(hypervisor, t, hypervisor to configure, libvirt)
|
||||
# ARG_TYPE_GROUP_SET(hypervisors, HYPERVISOR, hypervisor, [libvirt,lxc])
|
||||
# ARGBASH_GO
|
@ -1,60 +0,0 @@
|
||||
# Copyright 2021 Julie Bettens
|
||||
#
|
||||
# This file is part of the Miniguest companion.
|
||||
#
|
||||
# The Miniguest companion is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Miniguest is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
function run_nix {
|
||||
command "$nix" --experimental-features "nix-command flakes ca-references" "$@"
|
||||
}
|
||||
|
||||
flake=
|
||||
guest_name=
|
||||
|
||||
function parse_flake_reference {
|
||||
[[ $1 =~ ^(.*)\#([^\#\"]*)$ ]] || die "cannot parse flake reference"
|
||||
flake="${BASH_REMATCH[1]}"
|
||||
guest_name="${BASH_REMATCH[2]}"
|
||||
}
|
||||
|
||||
function install_profile {
|
||||
[[ $# -eq 2 ]] || die "$FUNCNAME: wrong number of arguments!"
|
||||
local guest_name="$1"
|
||||
local target="$2"
|
||||
run_nix profile install --profile "$profiles_dir/$guest_name" "$target" ||
|
||||
die "unable to install $guest_name!" $?
|
||||
}
|
||||
|
||||
function upgrade_profile {
|
||||
[[ $# -eq 1 ]] || die "$FUNCNAME: wrong number of arguments!"
|
||||
local guest_name="$1"
|
||||
run_nix profile upgrade --profile "$profiles_dir/$guest_name" ||
|
||||
die "unable to upgrade $guest_name!" $?
|
||||
}
|
||||
|
||||
function reset_profile {
|
||||
[[ $# -eq 1 ]] || die "$FUNCNAME: wrong number of arguments!"
|
||||
local guest_name="$1"
|
||||
run_nix profile remove --profile "$profiles_dir/$guest_name" '.*' ||
|
||||
die "unable to remove guest!" $?
|
||||
}
|
||||
|
||||
function have_control_of_symlink {
|
||||
[[ $# -eq 1 ]] || die "$FUNCNAME: wrong number of arguments!"
|
||||
local symlink="$guests_dir/$guest_name"
|
||||
[[ ! -e $symlink || -L $symlink && $(readlink $symlink) -ef "$profiles_dir/$guest_name" ]] || {
|
||||
echo >&2 "not touching $guests_dir/$guest_name because it's not in an expected state"
|
||||
false
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
# Copyright 2021 Julie Bettens
|
||||
#
|
||||
# This file is part of the Miniguest companion.
|
||||
#
|
||||
# The Miniguest companion is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Miniguest is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
source install_arg.bash
|
||||
|
||||
parse_flake_reference "$_arg_flake_reference"
|
||||
profile_name="${_arg_name:-$guest_name}"
|
||||
|
||||
mkdir -p "$guests_dir" || die "" $?
|
||||
mkdir -p "$profiles_dir" || die "" $?
|
||||
|
||||
reset_profile "$profile_name" # FIXME: need an atomic reset-and-install
|
||||
install_profile "$profile_name" "$flake#nixosConfigurations.$guest_name.config.system.build.miniguest"
|
||||
|
||||
have_control_of_symlink "$profile_name" && ln -sf "$profiles_dir/$profile_name" "$guests_dir"
|
99
tool/install.cpp
Normal file
99
tool/install.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "manifest.hpp"
|
||||
|
||||
#include "build-result.hh"
|
||||
#include "command.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "eval-cache.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace nix;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct CmdInstall : virtual InstallableCommand, virtual MixProfile {
|
||||
std::optional<std::string> guest_name;
|
||||
|
||||
CmdInstall() {
|
||||
addFlag({
|
||||
.longName = "name",
|
||||
.shortName = 'n',
|
||||
.description = "Name of the guest (default: attribute name)",
|
||||
.labels = {"name"},
|
||||
.handler = {&guest_name},
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override {
|
||||
return "build the guest and install it into a nix profile";
|
||||
}
|
||||
|
||||
std::string doc() override {
|
||||
return R""(miniguest install [-n|--name <arg>] <flake-reference>
|
||||
<flake-reference>: guest to build
|
||||
-n, --name: name of the profile (no default))"";
|
||||
}
|
||||
|
||||
Strings getDefaultFlakeAttrPaths() override {
|
||||
return {"nixosConfigurations.default"};
|
||||
}
|
||||
Strings getDefaultFlakeAttrPathPrefixes() override {
|
||||
return {"nixosConfigurations."};
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override {
|
||||
auto evalState = getEvalState();
|
||||
|
||||
if (!guest_name)
|
||||
guest_name = installable->getCursor(*evalState)->getAttrPath().back();
|
||||
|
||||
ContextBuilder bld;
|
||||
bld.guest_name = *guest_name;
|
||||
if (profile)
|
||||
bld.profile_path = *profile;
|
||||
|
||||
Context ctx = bld.build();
|
||||
profile = ctx.profile_path.native();
|
||||
|
||||
auto installableFlake =
|
||||
std::static_pointer_cast<InstallableFlake>(installable);
|
||||
for (auto &a : installableFlake->attrPaths)
|
||||
a += ".config.system.build.miniguest";
|
||||
auto result = Installable::build(getEvalStore(), store, Realise::Outputs,
|
||||
{installable});
|
||||
|
||||
auto [attrPath, resolvedRef, drv] = installableFlake->toDerivation();
|
||||
|
||||
ProfileManifest manifest(*getEvalState(), *profile);
|
||||
ProfileElement element;
|
||||
element.source = {installableFlake->flakeRef, resolvedRef, attrPath};
|
||||
element.updateStorePaths(getEvalStore(), store, result);
|
||||
manifest.elements = {element};
|
||||
updateProfile(manifest.build(store));
|
||||
|
||||
ctx.ensure_symlink();
|
||||
}
|
||||
|
||||
virtual ~CmdInstall() = default;
|
||||
};
|
||||
|
||||
static auto rCmdInstall = registerCommand<CmdInstall>("install");
|
@ -1,21 +0,0 @@
|
||||
# Copyright 2021 Julie Bettens
|
||||
#
|
||||
# This file is part of the Miniguest companion.
|
||||
#
|
||||
# The Miniguest companion is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Miniguest is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ARG_HELP(build the guest and install it into a nix profile)
|
||||
# ARG_POSITIONAL_SINGLE(flake-reference, guest to build)
|
||||
# ARG_OPTIONAL_SINGLE(name, n, name of the profile)
|
||||
# ARGBASH_GO
|
@ -1,45 +0,0 @@
|
||||
#! @bash@/bin/sh
|
||||
|
||||
# Copyright 2021 Julie Bettens
|
||||
#
|
||||
# This file is part of the Miniguest companion.
|
||||
#
|
||||
# The Miniguest companion is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Miniguest is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
source main_arg.bash
|
||||
|
||||
guests_dir="$_arg_guests_dir"
|
||||
profiles_dir="/nix/var/nix/profiles/miniguest-profiles"
|
||||
nix="$_arg_nix"
|
||||
|
||||
source functions.bash
|
||||
|
||||
set -- "${_arg_leftovers[@]}" # reset parameters to subcommand arguments
|
||||
case "${_arg_command:?}" in
|
||||
install)
|
||||
source install.bash
|
||||
;;
|
||||
upgrade)
|
||||
source upgrade.bash
|
||||
;;
|
||||
remove)
|
||||
source remove.bash
|
||||
;;
|
||||
create)
|
||||
source create.bash
|
||||
;;
|
||||
template)
|
||||
source template.bash
|
||||
;;
|
||||
esac
|
79
tool/main.cpp
Normal file
79
tool/main.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "command.hh"
|
||||
#include "common-args.hh"
|
||||
#include "shared.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct MiniguestArgs final : virtual MultiCommand, virtual MixCommonArgs {
|
||||
bool helpRequested = false;
|
||||
|
||||
MiniguestArgs()
|
||||
: MultiCommand(RegisterCommand::getCommandsFor({})),
|
||||
MixCommonArgs("miniguest") {
|
||||
addFlag({
|
||||
.longName = "help",
|
||||
.description = "Show usage information.",
|
||||
.handler = {[&]() { helpRequested = true; }},
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override {
|
||||
return "Companion tool for Miniguest lightweight NixOS images";
|
||||
}
|
||||
|
||||
std::string doc() override {
|
||||
std::string doc = "Available subcommands:";
|
||||
for (auto [name, cmd] : commands) {
|
||||
auto line = " " + name + ":";
|
||||
line.resize(16, ' ');
|
||||
line += cmd()->description();
|
||||
doc.push_back('\n');
|
||||
doc.append(line);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
};
|
||||
|
||||
void main0(int argc, char **argv) {
|
||||
initNix();
|
||||
initGC();
|
||||
MiniguestArgs args;
|
||||
|
||||
settings.experimentalFeatures = {Xp::Flakes};
|
||||
|
||||
args.parseCmdline(argvToStrings(argc, argv));
|
||||
if (args.helpRequested) {
|
||||
Args &cmd = args.command ? static_cast<Args &>(*args.command->second)
|
||||
: static_cast<Args &>(args);
|
||||
std::cout << cmd.description() << std::endl;
|
||||
std::cout << cmd.doc() << std::endl;
|
||||
return;
|
||||
}
|
||||
if (!args.command)
|
||||
throw UsageError("no subcommand specified");
|
||||
|
||||
args.command->second->prepare();
|
||||
args.command->second->run();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return nix::handleExceptions(argv[0], [=]() { main0(argc, argv); });
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
# Copyright 2021 Julie Bettens
|
||||
#
|
||||
# This file is part of the Miniguest companion.
|
||||
#
|
||||
# The Miniguest companion is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# Miniguest is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# ARG_POSITIONAL_SINGLE(command, subcommand to run)
|
||||
# ARG_TYPE_GROUP_SET(commands, COMMAND, command, [install,upgrade,remove,create,template,help])
|
||||
# ARG_OPTIONAL_SINGLE(guests-dir, , directory containing guests profiles, /etc/miniguests)
|
||||
# ARG_OPTIONAL_SINGLE(nix, , path to the nix binary, @nixFlakes@/bin/nix)
|
||||
# ARG_LEFTOVERS(subcommand arguments)
|
||||
# ARGBASH_GO
|
257
tool/manifest.hpp
Normal file
257
tool/manifest.hpp
Normal file
@ -0,0 +1,257 @@
|
||||
/** Copyright Nix contributors
|
||||
* Distributed under the GNU LGPL-2.1 license
|
||||
*
|
||||
* from Nix 2.8.1
|
||||
*/
|
||||
#include "archive.hh"
|
||||
#include "builtins/buildenv.hh"
|
||||
#include "command.hh"
|
||||
#include "common-args.hh"
|
||||
#include "derivations.hh"
|
||||
#include "flake/flakeref.hh"
|
||||
#include "names.hh"
|
||||
#include "profiles.hh"
|
||||
#include "shared.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <regex>
|
||||
|
||||
// don't reformat copypasted code
|
||||
// clang-format off
|
||||
|
||||
namespace nix {
|
||||
// from nix/profile.cc
|
||||
struct ProfileElementSource
|
||||
{
|
||||
FlakeRef originalRef;
|
||||
// FIXME: record original attrpath.
|
||||
FlakeRef resolvedRef;
|
||||
std::string attrPath;
|
||||
// FIXME: output names
|
||||
|
||||
bool operator < (const ProfileElementSource & other) const
|
||||
{
|
||||
return
|
||||
std::pair(originalRef.to_string(), attrPath) <
|
||||
std::pair(other.originalRef.to_string(), other.attrPath);
|
||||
}
|
||||
};
|
||||
|
||||
struct ProfileElement
|
||||
{
|
||||
StorePathSet storePaths;
|
||||
std::optional<ProfileElementSource> source;
|
||||
bool active = true;
|
||||
// FIXME: priority
|
||||
|
||||
std::string describe() const
|
||||
{
|
||||
if (source)
|
||||
return fmt("%s#%s", source->originalRef, source->attrPath);
|
||||
StringSet names;
|
||||
for (auto & path : storePaths)
|
||||
names.insert(DrvName(path.name()).name);
|
||||
return concatStringsSep(", ", names);
|
||||
}
|
||||
|
||||
std::string versions() const
|
||||
{
|
||||
StringSet versions;
|
||||
for (auto & path : storePaths)
|
||||
versions.insert(DrvName(path.name()).version);
|
||||
return showVersions(versions);
|
||||
}
|
||||
|
||||
bool operator < (const ProfileElement & other) const
|
||||
{
|
||||
return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
|
||||
}
|
||||
|
||||
void updateStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
const BuiltPaths & builtPaths)
|
||||
{
|
||||
// FIXME: respect meta.outputsToInstall
|
||||
storePaths.clear();
|
||||
for (auto & buildable : builtPaths) {
|
||||
std::visit(overloaded {
|
||||
[&](const BuiltPath::Opaque & bo) {
|
||||
storePaths.insert(bo.path);
|
||||
},
|
||||
[&](const BuiltPath::Built & bfd) {
|
||||
for (auto & output : bfd.outputs)
|
||||
storePaths.insert(output.second);
|
||||
},
|
||||
}, buildable.raw());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ProfileManifest
|
||||
{
|
||||
std::vector<ProfileElement> elements;
|
||||
|
||||
ProfileManifest() { }
|
||||
|
||||
ProfileManifest(EvalState & state, const Path & profile)
|
||||
{
|
||||
auto manifestPath = profile + "/manifest.json";
|
||||
|
||||
if (pathExists(manifestPath)) {
|
||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||
|
||||
auto version = json.value("version", 0);
|
||||
std::string sUrl;
|
||||
std::string sOriginalUrl;
|
||||
switch(version){
|
||||
case 1:
|
||||
sUrl = "uri";
|
||||
sOriginalUrl = "originalUri";
|
||||
break;
|
||||
case 2:
|
||||
sUrl = "url";
|
||||
sOriginalUrl = "originalUrl";
|
||||
break;
|
||||
default:
|
||||
throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);
|
||||
}
|
||||
|
||||
for (auto & e : json["elements"]) {
|
||||
ProfileElement element;
|
||||
for (auto & p : e["storePaths"])
|
||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||
element.active = e["active"];
|
||||
if (e.value(sUrl,"") != "") {
|
||||
element.source = ProfileElementSource{
|
||||
parseFlakeRef(e[sOriginalUrl]),
|
||||
parseFlakeRef(e[sUrl]),
|
||||
e["attrPath"]
|
||||
};
|
||||
}
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
else if (pathExists(profile + "/manifest.nix")) {
|
||||
// FIXME: needed because of pure mode; ugly.
|
||||
state.allowPath(state.store->followLinksToStore(profile));
|
||||
state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
|
||||
|
||||
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
|
||||
|
||||
for (auto & drvInfo : drvInfos) {
|
||||
ProfileElement element;
|
||||
element.storePaths = {drvInfo.queryOutPath()};
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
std::string toJSON(Store & store) const
|
||||
{
|
||||
auto array = nlohmann::json::array();
|
||||
for (auto & element : elements) {
|
||||
auto paths = nlohmann::json::array();
|
||||
for (auto & path : element.storePaths)
|
||||
paths.push_back(store.printStorePath(path));
|
||||
nlohmann::json obj;
|
||||
obj["storePaths"] = paths;
|
||||
obj["active"] = element.active;
|
||||
if (element.source) {
|
||||
obj["originalUrl"] = element.source->originalRef.to_string();
|
||||
obj["url"] = element.source->resolvedRef.to_string();
|
||||
obj["attrPath"] = element.source->attrPath;
|
||||
}
|
||||
array.push_back(obj);
|
||||
}
|
||||
nlohmann::json json;
|
||||
json["version"] = 2;
|
||||
json["elements"] = array;
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
StorePath build(ref<Store> store)
|
||||
{
|
||||
auto tempDir = createTempDir();
|
||||
|
||||
StorePathSet references;
|
||||
|
||||
Packages pkgs;
|
||||
for (auto & element : elements) {
|
||||
for (auto & path : element.storePaths) {
|
||||
if (element.active)
|
||||
pkgs.emplace_back(store->printStorePath(path), true, 5);
|
||||
references.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
buildProfile(tempDir, std::move(pkgs));
|
||||
|
||||
writeFile(tempDir + "/manifest.json", toJSON(*store));
|
||||
|
||||
/* Add the symlink tree to the store. */
|
||||
StringSink sink;
|
||||
dumpPath(tempDir, sink);
|
||||
|
||||
auto narHash = hashString(htSHA256, sink.s);
|
||||
|
||||
ValidPathInfo info {
|
||||
store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references),
|
||||
narHash,
|
||||
};
|
||||
info.references = std::move(references);
|
||||
info.narSize = sink.s.size();
|
||||
info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash };
|
||||
|
||||
StringSource source(sink.s);
|
||||
store->addToStore(info, source);
|
||||
|
||||
return std::move(info.path);
|
||||
}
|
||||
|
||||
static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
|
||||
{
|
||||
auto prevElems = prev.elements;
|
||||
std::sort(prevElems.begin(), prevElems.end());
|
||||
|
||||
auto curElems = cur.elements;
|
||||
std::sort(curElems.begin(), curElems.end());
|
||||
|
||||
auto i = prevElems.begin();
|
||||
auto j = curElems.begin();
|
||||
|
||||
bool changes = false;
|
||||
|
||||
while (i != prevElems.end() || j != curElems.end()) {
|
||||
if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) {
|
||||
std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions());
|
||||
changes = true;
|
||||
++j;
|
||||
}
|
||||
else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) {
|
||||
std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions());
|
||||
changes = true;
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
auto v1 = i->versions();
|
||||
auto v2 = j->versions();
|
||||
if (v1 != v2) {
|
||||
std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2);
|
||||
changes = true;
|
||||
}
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes)
|
||||
std::cout << fmt("%sNo changes.\n", indent);
|
||||
}
|
||||
};
|
||||
}
|
34
tool/meson.build
Normal file
34
tool/meson.build
Normal file
@ -0,0 +1,34 @@
|
||||
project('miniguest', 'cpp',
|
||||
version : '0.2',
|
||||
default_options : ['warning_level=1',
|
||||
'cpp_std=c++17'])
|
||||
|
||||
add_project_arguments('-DSYSTEM="x86_64-linux"', language: 'cpp') # TODO
|
||||
|
||||
|
||||
# Nix dependency
|
||||
nixlibs = [
|
||||
dependency('boost'), # transitive
|
||||
dependency('nlohmann_json'), # transitive
|
||||
|
||||
dependency('nix-main'),
|
||||
dependency('nix-expr'),
|
||||
dependency('nix-cmd'),
|
||||
dependency('nix-store'),
|
||||
]
|
||||
add_project_arguments('-Wno-non-virtual-dtor', language: 'cpp') # present in Nix headers
|
||||
|
||||
|
||||
src = [
|
||||
'main.cpp',
|
||||
'install.cpp',
|
||||
'remove.cpp',
|
||||
'upgrade.cpp',
|
||||
'create.cpp',
|
||||
'common.cpp',
|
||||
]
|
||||
exe = executable('miniguest', src,
|
||||
dependencies : nixlibs,
|
||||
install : true)
|
||||
|
||||
test('basic', exe)
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2021 Julie Bettens
|
||||
# Copyright 2022 Julie Bettens
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
@ -12,41 +12,21 @@
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
{ lib, stdenvNoCC, argbash, bash, coreutils, miniguest-lxc-template, nixFlakes, shellcheck, makeWrapper }:
|
||||
{ lib, stdenv, boost, miniguest-lxc-template, meson, ninja, nixFlakes, nlohmann_json, pkg-config }:
|
||||
|
||||
stdenvNoCC.mkDerivation {
|
||||
stdenv.mkDerivation {
|
||||
pname = "miniguest";
|
||||
version = "0.1.4";
|
||||
version = "0.2";
|
||||
src = builtins.path { name = "source"; path = ./.; };
|
||||
inherit bash nixFlakes;
|
||||
lxc_template = miniguest-lxc-template;
|
||||
|
||||
nativeBuildInputs = [ argbash makeWrapper ];
|
||||
nativeBuildInputs = [ meson ninja nlohmann_json pkg-config ];
|
||||
buildInputs = [ boost nixFlakes ];
|
||||
|
||||
buildPhase = ''
|
||||
for f in *.bash; do
|
||||
postPatch = ''
|
||||
for f in *.cpp; do
|
||||
substituteAllInPlace $f
|
||||
done
|
||||
for f in *_arg.bash; do
|
||||
argbash --strip=all -i "$f"
|
||||
done
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/{libexec/miniguest,bin}
|
||||
mv *.bash $out/libexec/miniguest
|
||||
chmod +x $out/libexec/miniguest/main.bash
|
||||
# keep PATH open ended since Nix pulls from the environment e.g. git
|
||||
makeWrapper $out/libexec/miniguest/main.bash $out/bin/miniguest \
|
||||
--prefix PATH ":" "$out/libexec/miniguest:${coreutils}/bin"
|
||||
'';
|
||||
|
||||
doInstallCheck = true;
|
||||
|
||||
installCheckInputs = [ shellcheck ];
|
||||
|
||||
installCheckPhase = ''
|
||||
shellcheck -e SC2123 -x -s bash $out/bin/miniguest
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
|
@ -1,7 +0,0 @@
|
||||
source remove_arg.bash
|
||||
|
||||
guest_name="$_arg_guest_name"
|
||||
|
||||
reset_profile "$guest_name"
|
||||
|
||||
have_control_of_symlink "$guest_name" && rm -f "$guests_dir/$guest_name"
|
71
tool/remove.cpp
Normal file
71
tool/remove.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "manifest.hpp"
|
||||
|
||||
#include "build-result.hh"
|
||||
#include "command.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "eval-cache.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace nix;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct CmdRemove : virtual EvalCommand, virtual MixProfile {
|
||||
std::string guest_name;
|
||||
|
||||
CmdRemove() {
|
||||
expectArg("name", &guest_name);
|
||||
}
|
||||
|
||||
std::string description() override {
|
||||
return "uninstall a guest";
|
||||
}
|
||||
|
||||
std::string doc() override {
|
||||
return R""(miniguest remove [-h|--help] <guest-name>
|
||||
<guest-name>: name of guest to remove
|
||||
-h, --help: Prints help)"";
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override {
|
||||
ContextBuilder bld;
|
||||
bld.guest_name = guest_name;
|
||||
if (profile)
|
||||
bld.profile_path = *profile;
|
||||
|
||||
Context ctx = bld.build();
|
||||
profile = ctx.profile_path.native();
|
||||
|
||||
// reset profile first
|
||||
ProfileManifest manifest(*getEvalState(), *profile);
|
||||
manifest.elements.clear();
|
||||
updateProfile(manifest.build(store));
|
||||
|
||||
// then clean the symlink
|
||||
ctx.remove_symlink();
|
||||
}
|
||||
|
||||
virtual ~CmdRemove() = default;
|
||||
};
|
||||
|
||||
static auto rCmdRemove = registerCommand<CmdRemove>("remove");
|
@ -1,2 +0,0 @@
|
||||
# ARG_POSITIONAL_SINGLE(guest-name, name of guest to remove)
|
||||
# ARGBASH_GO
|
@ -1,5 +0,0 @@
|
||||
source template_arg.bash
|
||||
|
||||
dest_dir="$_arg_dest_dir"
|
||||
|
||||
run_nix flake new -t github:bbjubjub2494/miniguest "$dest_dir"
|
@ -1,2 +0,0 @@
|
||||
# ARG_POSITIONAL_SINGLE(dest-dir, destination directory)
|
||||
# ARGBASH_GO
|
@ -1,5 +0,0 @@
|
||||
source upgrade_arg.bash
|
||||
|
||||
guest_name="$_arg_guest_name"
|
||||
|
||||
upgrade_profile "$guest_name"
|
86
tool/upgrade.cpp
Normal file
86
tool/upgrade.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
/* Copyright 2022 Julie Bettens
|
||||
*
|
||||
* This file is part of the Miniguest companion.
|
||||
*
|
||||
* The Miniguest companion is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Miniguest is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Miniguest. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "manifest.hpp"
|
||||
|
||||
#include "build-result.hh"
|
||||
#include "command.hh"
|
||||
#include "derived-path.hh"
|
||||
#include "eval-cache.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace nix;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct CmdUpgrade : virtual EvalCommand, virtual MixProfile {
|
||||
std::string guest_name;
|
||||
|
||||
CmdUpgrade() {
|
||||
expectArg("name", &guest_name);
|
||||
}
|
||||
|
||||
std::string description() override {
|
||||
return "upgrade a guest through its flake reference";
|
||||
}
|
||||
|
||||
std::string doc() override {
|
||||
return R""(miniguest upgrade [-h|--help] <guest-name>
|
||||
<guest-name>: name of guest to upgrade
|
||||
-h, --help: Prints help)"";
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override {
|
||||
ContextBuilder bld;
|
||||
bld.guest_name = guest_name;
|
||||
if (profile)
|
||||
bld.profile_path = *profile;
|
||||
|
||||
Context ctx = bld.build();
|
||||
profile = ctx.profile_path.native();
|
||||
|
||||
ProfileManifest manifest(*getEvalState(), *profile);
|
||||
|
||||
for (auto &element : manifest.elements) {
|
||||
if (!element.source || element.source->originalRef.input.isLocked())
|
||||
continue;
|
||||
|
||||
auto installable = std::make_shared<InstallableFlake>(
|
||||
nullptr, getEvalState(), FlakeRef(element.source->originalRef), "",
|
||||
Strings{element.source->attrPath}, Strings{}, flake::LockFlags{});
|
||||
auto [attrPath, resolvedRef, drv] = installable->toDerivation();
|
||||
if (element.source->resolvedRef == resolvedRef)
|
||||
continue;
|
||||
printInfo("upgrading '%s' from flake '%s' to '%s'",
|
||||
element.source->attrPath, element.source->resolvedRef,
|
||||
resolvedRef);
|
||||
auto result = Installable::build(getEvalStore(), store, Realise::Outputs,
|
||||
{installable});
|
||||
|
||||
element.source = {installable->flakeRef, resolvedRef, attrPath};
|
||||
element.updateStorePaths(getEvalStore(), store, result);
|
||||
}
|
||||
updateProfile(manifest.build(store));
|
||||
}
|
||||
|
||||
virtual ~CmdUpgrade() = default;
|
||||
};
|
||||
|
||||
static auto rCmdUpgrade = registerCommand<CmdUpgrade>("upgrade");
|
@ -1,2 +0,0 @@
|
||||
# ARG_POSITIONAL_SINGLE(guest-name, name of guest to upgrade)
|
||||
# ARGBASH_GO
|
Loading…
Reference in New Issue
Block a user