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
dd5c5ef2dc
commit
a4e928e3ed
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -13,4 +13,4 @@
|
|||||||
- [ ] Documented intended functionality
|
- [ ] Documented intended functionality
|
||||||
- [ ] Added checks for intended functionality
|
- [ ] Added checks for intended functionality
|
||||||
- [ ] Changed Nix files are formatted per `nixpkgs-fmt`
|
- [ ] 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*
|
result*
|
||||||
|
|
||||||
/template/flake.lock
|
/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;
|
inherit name;
|
||||||
nodes.machine = {
|
nodes.machine = {
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
# wrapper clears PATH to check for implicit dependencies
|
miniguest
|
||||||
(writeShellScriptBin "miniguest" ''PATH= exec ${miniguest}/bin/miniguest "$@"'')
|
|
||||||
];
|
];
|
||||||
system.extraDependencies = [ pinned-nixpkgs (import pinned-nixpkgs { inherit system; }).stdenvNoCC ];
|
system.extraDependencies = [ pinned-nixpkgs (import pinned-nixpkgs { inherit system; }).stdenvNoCC ];
|
||||||
virtualisation.memorySize = 1024;
|
virtualisation.memorySize = 1024;
|
||||||
@ -64,7 +63,7 @@ lib.optionalAttrs stdenv.isLinux {
|
|||||||
name = "miniguest-install-rename";
|
name = "miniguest-install-rename";
|
||||||
testScript = ''
|
testScript = ''
|
||||||
machine.succeed("""
|
machine.succeed("""
|
||||||
miniguest install --name=renamed_dummy /tmp/flake1#dummy
|
miniguest install --name renamed_dummy /tmp/flake1#dummy
|
||||||
""")
|
""")
|
||||||
assert "foo" in machine.succeed("""
|
assert "foo" in machine.succeed("""
|
||||||
cat /etc/miniguests/renamed_dummy/boot/init
|
cat /etc/miniguests/renamed_dummy/boot/init
|
||||||
|
@ -1,30 +1,50 @@
|
|||||||
imports = [ "git.hooks" ]
|
imports = [
|
||||||
|
"git.hooks",
|
||||||
|
"language.c",
|
||||||
|
"set-boost-root.nix",
|
||||||
|
]
|
||||||
|
|
||||||
[devshell]
|
[devshell]
|
||||||
name = "miniguest"
|
name = "miniguest"
|
||||||
|
packages = [
|
||||||
[[commands]]
|
"ninja", # for meson
|
||||||
package = "bash"
|
]
|
||||||
|
|
||||||
[[commands]]
|
[[commands]]
|
||||||
package = "nixFlakes"
|
package = "nixFlakes"
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
name = "task"
|
||||||
|
package = "go-task"
|
||||||
|
|
||||||
|
[[commands]]
|
||||||
|
package = "meson"
|
||||||
|
|
||||||
[[commands]]
|
[[commands]]
|
||||||
package = "nixpkgs-fmt"
|
package = "nixpkgs-fmt"
|
||||||
category = "formatters"
|
category = "formatters"
|
||||||
|
|
||||||
[[commands]]
|
[[commands]]
|
||||||
package = "shfmt"
|
name = "clang-format"
|
||||||
|
package = "clang-tools"
|
||||||
category = "formatters"
|
category = "formatters"
|
||||||
|
|
||||||
[[commands]]
|
[[commands]]
|
||||||
package = "argbash"
|
name = "clang-tidy"
|
||||||
category = "preprocessors"
|
package = "clang-tools"
|
||||||
|
category = "linters"
|
||||||
|
|
||||||
[[commands]]
|
[[commands]]
|
||||||
package = "shellcheck"
|
package = "include-what-you-use"
|
||||||
category = "linters"
|
category = "linters"
|
||||||
|
|
||||||
|
[language.c]
|
||||||
|
includes = [
|
||||||
|
"boehmgc",
|
||||||
|
"nixFlakes",
|
||||||
|
"nlohmann_json",
|
||||||
|
]
|
||||||
|
|
||||||
[git.hooks]
|
[git.hooks]
|
||||||
enable = true
|
enable = true
|
||||||
pre-commit.text = """
|
pre-commit.text = """
|
||||||
@ -41,5 +61,5 @@ function find_staged {
|
|||||||
}
|
}
|
||||||
|
|
||||||
find_staged '*.nix' | xargs -r nixpkgs-fmt --check || exit
|
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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis 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 Louis Bettens
|
# Copyright 2022 Louis Bettens
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
# 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
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# 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";
|
pname = "miniguest";
|
||||||
version = "0.1.4";
|
version = "0.2";
|
||||||
src = builtins.path { name = "source"; path = ./.; };
|
src = builtins.path { name = "source"; path = ./.; };
|
||||||
inherit bash nixFlakes;
|
|
||||||
lxc_template = miniguest-lxc-template;
|
lxc_template = miniguest-lxc-template;
|
||||||
|
|
||||||
nativeBuildInputs = [ argbash makeWrapper ];
|
nativeBuildInputs = [ meson ninja nlohmann_json pkg-config ];
|
||||||
|
buildInputs = [ boost nixFlakes ];
|
||||||
|
|
||||||
buildPhase = ''
|
postPatch = ''
|
||||||
for f in *.bash; do
|
for f in *.cpp; do
|
||||||
substituteAllInPlace $f
|
substituteAllInPlace $f
|
||||||
done
|
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; {
|
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 Louis 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:lourkeur/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 Louis 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