hydra
, cgit
and vm-sala
on mothership
This commit is contained in:
parent
58e7ae54f0
commit
026a0d0180
9 changed files with 489 additions and 0 deletions
|
@ -13,6 +13,11 @@
|
||||||
"mosh"
|
"mosh"
|
||||||
"ccr"
|
"ccr"
|
||||||
"nix"
|
"nix"
|
||||||
|
"vm-sala"
|
||||||
|
"hydra"
|
||||||
|
"nix-serve"
|
||||||
|
"cgit"
|
||||||
|
"docker"
|
||||||
];
|
];
|
||||||
|
|
||||||
ccr = {
|
ccr = {
|
||||||
|
|
57
modules/cgit/config.nix
Normal file
57
modules/cgit/config.nix
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
repos-path = "/var/lib/cgit-repos";
|
||||||
|
cgit-setup-repos =
|
||||||
|
pkgs.writers.writePython3 "cgit-setup-repos" {
|
||||||
|
libraries = with pkgs.python3Packages; [PyGithub];
|
||||||
|
} ''
|
||||||
|
from github import Github
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
c = Path("${repos-path}")
|
||||||
|
c.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
with open(c, "w") as f:
|
||||||
|
for repo in Github().get_user("aciceri").get_repos():
|
||||||
|
f.writelines([
|
||||||
|
f"repo.url={repo.name}\n"
|
||||||
|
f"repo.path=/home/ccr/projects/aciceri/{repo.name}/.git\n"
|
||||||
|
f"repo.desc={repo.description}\n"
|
||||||
|
])
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
services.nginx.virtualHosts."git.aciceri.dev" = {
|
||||||
|
cgit = {
|
||||||
|
enable = true;
|
||||||
|
css = "/custom.css";
|
||||||
|
# scan-path = "/home/ccr/projects/aciceri";
|
||||||
|
virtual-root = "/";
|
||||||
|
cache-size = 1000;
|
||||||
|
include = [
|
||||||
|
(builtins.toString (pkgs.writeText "cgit-extra" ''
|
||||||
|
source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py
|
||||||
|
about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh
|
||||||
|
''))
|
||||||
|
repos-path
|
||||||
|
];
|
||||||
|
};
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
# locations."/" = {
|
||||||
|
# proxyPass = "http://127.0.0.1:${builtins.toString config.services.hydra.port}";
|
||||||
|
# };
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.cgit-setup-repos = {
|
||||||
|
description = "Update GitHub personal repos for cgit";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
script = builtins.toString cgit-setup-repos;
|
||||||
|
};
|
||||||
|
}
|
|
@ -24,6 +24,11 @@ with lib; let
|
||||||
pkgs.writeText name (lib.generators.toKeyValue {listsAsDuplicateKeys = true;} values);
|
pkgs.writeText name (lib.generators.toKeyValue {listsAsDuplicateKeys = true;} values);
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
imports = [
|
||||||
|
../nginx-base
|
||||||
|
./config.nix
|
||||||
|
];
|
||||||
|
|
||||||
options.services.nginx.virtualHosts = mkOption {
|
options.services.nginx.virtualHosts = mkOption {
|
||||||
type = types.attrsOf (types.submodule ({config, ...}: let
|
type = types.attrsOf (types.submodule ({config, ...}: let
|
||||||
cfg = config.cgit;
|
cfg = config.cgit;
|
||||||
|
|
8
modules/hydra/config.nix
Normal file
8
modules/hydra/config.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
services.my-hydra.repos = {
|
||||||
|
emacs = {};
|
||||||
|
nixfleet = {};
|
||||||
|
trotten = {};
|
||||||
|
blog = {};
|
||||||
|
};
|
||||||
|
}
|
206
modules/hydra/default.nix
Normal file
206
modules/hydra/default.nix
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.services.my-hydra;
|
||||||
|
toSpec = {
|
||||||
|
name,
|
||||||
|
owner,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
spec = {
|
||||||
|
enabled = 1;
|
||||||
|
hidden = false;
|
||||||
|
description = "Declarative specification jobset automatically generated";
|
||||||
|
checkinterval = 120;
|
||||||
|
schedulingshares = 10000;
|
||||||
|
enableemail = false;
|
||||||
|
emailoverride = "";
|
||||||
|
keepnr = 1;
|
||||||
|
nixexprinput = "src";
|
||||||
|
nixexprpath = "jobsets.nix";
|
||||||
|
inputs = {
|
||||||
|
src = {
|
||||||
|
type = "path";
|
||||||
|
value = pkgs.writeTextFile {
|
||||||
|
name = "src";
|
||||||
|
text = builtins.readFile ./jobsets.nix;
|
||||||
|
destination = "/jobsets.nix";
|
||||||
|
};
|
||||||
|
emailresponsible = false;
|
||||||
|
};
|
||||||
|
repoInfoPath = {
|
||||||
|
type = "path";
|
||||||
|
value = pkgs.writeTextFile {
|
||||||
|
name = "repo";
|
||||||
|
text = builtins.toJSON {
|
||||||
|
inherit name owner;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
emailresponsible = false;
|
||||||
|
};
|
||||||
|
prs = {
|
||||||
|
type = "githubpulls";
|
||||||
|
value = "${owner} ${name}";
|
||||||
|
emailresponsible = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
drv = pkgs.writeTextFile {
|
||||||
|
name = "hydra-jobset-specification-${name}";
|
||||||
|
text = builtins.toJSON spec;
|
||||||
|
destination = "/spec.json";
|
||||||
|
};
|
||||||
|
in "${drv}";
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
./config.nix
|
||||||
|
../nginx-base
|
||||||
|
];
|
||||||
|
|
||||||
|
options.services.my-hydra = {
|
||||||
|
domain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "hydra.aciceri.dev";
|
||||||
|
};
|
||||||
|
repos = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.submodule ({
|
||||||
|
name,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
owner = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "aciceri";
|
||||||
|
};
|
||||||
|
description = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = config.homepage;
|
||||||
|
};
|
||||||
|
homepage = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://github.com/${config.owner}/${config.name}";
|
||||||
|
};
|
||||||
|
reportStatus = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# TODO manage `hydra` user ssh key declaratively
|
||||||
|
nix.extraOptions = ''
|
||||||
|
allowed-uris = https://github.com/ git://git.savannah.gnu.org/
|
||||||
|
'';
|
||||||
|
|
||||||
|
services.hydra = {
|
||||||
|
enable = true;
|
||||||
|
hydraURL = "https://${cfg.domain}";
|
||||||
|
notificationSender = "hydra@mothership.fleet";
|
||||||
|
buildMachinesFiles = [];
|
||||||
|
useSubstitutes = true;
|
||||||
|
extraConfig =
|
||||||
|
''
|
||||||
|
<github_authorization>
|
||||||
|
include ${config.age.secrets.hydra-github-token.path}
|
||||||
|
</github_authorization>
|
||||||
|
''
|
||||||
|
+ (lib.concatMapStrings (repo:
|
||||||
|
lib.optionalString repo.reportStatus
|
||||||
|
''
|
||||||
|
<githubstatus>
|
||||||
|
jobs = ${repo.name}.*
|
||||||
|
excludeBuildFromContext = 1
|
||||||
|
useShortContext = 1
|
||||||
|
</githubstatus>
|
||||||
|
'') (builtins.attrValues cfg.repos));
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.hydra-setup = {
|
||||||
|
description = "Hydra CI setup";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.RemainAfterExit = true;
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
requires = ["hydra-init.service"];
|
||||||
|
after = ["hydra-init.service"];
|
||||||
|
environment = builtins.removeAttrs (config.systemd.services.hydra-init.environment) ["PATH"];
|
||||||
|
script =
|
||||||
|
''
|
||||||
|
PATH=$PATH:${lib.makeBinPath (with pkgs; [yq-go curl config.services.hydra.package])}
|
||||||
|
PASSWORD="$(cat ${config.age.secrets.hydra-admin-password.path})"
|
||||||
|
if [ ! -e ~hydra/.setup-is-complete ]; then
|
||||||
|
hydra-create-user admin \
|
||||||
|
--full-name "Andrea Ciceri" \
|
||||||
|
--email-address "andrea.ciceri@autistici.org" \
|
||||||
|
--password "$PASSWORD" \
|
||||||
|
--role admin
|
||||||
|
touch ~hydra/.setup-is-complete
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --head -X GET --retry 5 --retry-connrefused --retry-delay 1 http://localhost:3000
|
||||||
|
|
||||||
|
CURRENT_REPOS=$(curl -s -H "Accept: application/json" http://localhost:3000 | yq ".[].name")
|
||||||
|
DECLARED_REPOS="${lib.concatStringsSep " " (builtins.attrNames cfg.repos)}"
|
||||||
|
|
||||||
|
curl -H "Accept: application/json" \
|
||||||
|
-H 'Origin: http://localhost:3000' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"username\": \"admin\", \"password\": \"$PASSWORD\"}" \
|
||||||
|
--request "POST" localhost:3000/login \
|
||||||
|
--cookie-jar cookie
|
||||||
|
|
||||||
|
for repo in $CURRENT_REPOS; do
|
||||||
|
echo $repo
|
||||||
|
[[ ! "$DECLARED_REPOS" =~ (\ |^)$repo(\ |$) ]] && \
|
||||||
|
curl -H "Accept: application/json" \
|
||||||
|
--request "DELETE" \
|
||||||
|
--cookie cookie \
|
||||||
|
http://localhost:3000/project/$repo
|
||||||
|
done
|
||||||
|
''
|
||||||
|
+ lib.concatMapStrings (repo: ''
|
||||||
|
curl -H "Accept: application/json" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
--request "PUT" \
|
||||||
|
localhost:3000/project/${repo.name} \
|
||||||
|
--cookie cookie \
|
||||||
|
-d '{
|
||||||
|
"name": "${repo.name}",
|
||||||
|
"displayname": "${repo.name}",
|
||||||
|
"description": "${repo.description}",
|
||||||
|
"homepage": "${repo.homepage}",
|
||||||
|
"owner": "admin",
|
||||||
|
"enabled": true,
|
||||||
|
"visible": true,
|
||||||
|
"declarative": {
|
||||||
|
"file": "spec.json",
|
||||||
|
"type": "path",
|
||||||
|
"value": "${toSpec repo}"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
'') (builtins.attrValues cfg.repos)
|
||||||
|
+ ''
|
||||||
|
rm cookie
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."${cfg.domain}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${builtins.toString config.services.hydra.port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
73
modules/hydra/jobsets.nix
Normal file
73
modules/hydra/jobsets.nix
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
repoInfoPath,
|
||||||
|
prs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
minutes = 60;
|
||||||
|
hours = 60 * minutes;
|
||||||
|
days = 24 * hours;
|
||||||
|
filterAttrs = pred: set:
|
||||||
|
builtins.listToAttrs (builtins.concatMap (name: let
|
||||||
|
v = set.${name};
|
||||||
|
in
|
||||||
|
if pred name v
|
||||||
|
then [
|
||||||
|
{
|
||||||
|
inherit name;
|
||||||
|
value = v;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
else []) (builtins.attrNames set));
|
||||||
|
mapAttrs' = f: set:
|
||||||
|
builtins.listToAttrs (map (attr: f attr set.${attr}) (builtins.attrNames set));
|
||||||
|
|
||||||
|
mkJobset = {
|
||||||
|
enabled ? 1,
|
||||||
|
hidden ? false,
|
||||||
|
type ? 1,
|
||||||
|
description ? "",
|
||||||
|
checkinterval ? 5 * minutes,
|
||||||
|
schedulingshares ? 100,
|
||||||
|
enableemail ? false,
|
||||||
|
emailoverride ? "",
|
||||||
|
keepnr ? 1,
|
||||||
|
flake,
|
||||||
|
} @ args: {inherit enabled hidden type description checkinterval schedulingshares enableemail emailoverride keepnr flake;};
|
||||||
|
|
||||||
|
mkSpec = contents: let
|
||||||
|
escape = builtins.replaceStrings [''"''] [''\"''];
|
||||||
|
contentsJson = builtins.toJSON contents;
|
||||||
|
in
|
||||||
|
builtins.derivation {
|
||||||
|
name = "spec.json";
|
||||||
|
system = "x86_64-linux";
|
||||||
|
preferLocalBuild = true;
|
||||||
|
allowSubstitutes = false;
|
||||||
|
builder = "/bin/sh";
|
||||||
|
args = [
|
||||||
|
(builtins.toFile "builder.sh" ''
|
||||||
|
echo "${escape contentsJson}" > $out
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
repo = builtins.fromJSON (builtins.readFile repoInfoPath);
|
||||||
|
|
||||||
|
pullRequests = builtins.fromJSON (builtins.readFile prs);
|
||||||
|
pullRequestsToBuild = filterAttrs (n: pr: pr.head.repo != null && pr.head.repo.owner.login == repo.owner && pr.head.repo.name == repo.name) pullRequests;
|
||||||
|
in {
|
||||||
|
jobsets = mkSpec ({
|
||||||
|
master = mkJobset {
|
||||||
|
description = "${repo.name}'s master branch";
|
||||||
|
flake = "git+ssh://git@github.com/${repo.owner}/${repo.name}?ref=master";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// (mapAttrs' (n: pr: {
|
||||||
|
name = "pullRequest_${n}";
|
||||||
|
value = mkJobset {
|
||||||
|
description = pr.title;
|
||||||
|
flake = "git+ssh://git@github.com/${repo.owner}/${repo.name}?ref=${pr.head.ref}";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
pullRequests));
|
||||||
|
}
|
19
modules/nginx-base/default.nix
Normal file
19
modules/nginx-base/default.nix
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
security.acme = {
|
||||||
|
acceptTerms = true;
|
||||||
|
defaults.email = "andrea.ciceri@autistici.org";
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
80
|
||||||
|
443
|
||||||
|
];
|
||||||
|
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
recommendedTlsSettings = true;
|
||||||
|
};
|
||||||
|
}
|
29
modules/nix-serve/default.nix
Normal file
29
modules/nix-serve/default.nix
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.services.my-nix-serve;
|
||||||
|
in {
|
||||||
|
imports = [../nginx-base];
|
||||||
|
options.services.my-nix-serve = {
|
||||||
|
domain = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "cache.aciceri.dev";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
services.nix-serve = {
|
||||||
|
enable = true;
|
||||||
|
secretKeyFile = config.age.secrets.cache-private-key.path;
|
||||||
|
# Public key: cache.aciceri.dev:4e9sFjWPUOjGwTJE98PXinJJZLwPz0m5nKsAe63MY3E=
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${cfg.domain}" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${builtins.toString config.services.nix-serve.port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
87
modules/vm-sala/default.nix
Normal file
87
modules/vm-sala/default.nix
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
fleetFlake,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
security.polkit.enable = true;
|
||||||
|
virtualisation.libvirtd.enable = true;
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
2222
|
||||||
|
];
|
||||||
|
|
||||||
|
imports = [../nginx-base];
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."git.slavni.aciceri.dev" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:13000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.vm-sala = let
|
||||||
|
initial-config = fleetFlake.inputs.nixos-generators.nixosGenerate {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
modules = [
|
||||||
|
fleetFlake.inputs.nixos-vscode-server.nixosModule
|
||||||
|
({
|
||||||
|
modulesPath,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
services.vscode-server = {
|
||||||
|
enable = true;
|
||||||
|
enableFHS = true;
|
||||||
|
};
|
||||||
|
system.build.qcow = lib.mkForce (import "${toString modulesPath}/../lib/make-disk-image.nix" {
|
||||||
|
inherit lib config pkgs;
|
||||||
|
diskSize = 50 * 1024;
|
||||||
|
format = "qcow2";
|
||||||
|
partitionTableType = "hybrid";
|
||||||
|
});
|
||||||
|
services.openssh.enable = true;
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim
|
||||||
|
git
|
||||||
|
htop
|
||||||
|
];
|
||||||
|
users.users.root = {
|
||||||
|
password = "password";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
(import "${fleetFlake.outPath}/lib").keys.users.ccr-ssh
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7qikwR0a4LDoMQIVvtX+gyJ41OsAOWe8RcXc4ksIBP9x1nCcSrItlgC2soADB77QGIgyeyLGmnTCtMj5/s8NdREAycPeXLii1WRakbT7oZ/hTEmvAgObpadeYJn3LhaUDNtmsnAqqh2pRpCpSsAdhfIt+YyV4VgRYSfaa12Ozp/H6NI9bJMNttmG8TmY9V4zyskV9bE+up9y8Yuck2bZV/GjQe6UWgxsiC3XPSrFFGuxyaFMRGsc8h86xVwTAmwaHESEFhRvHD4EtdPNss0jqmSI6m4AoSZQ2wq7eiH8ZiYzERF0FnEFf4UsyOTM7j78bfogNLfKrdcEIPLrNNFFc3Iarfe9CJn3DdSnwwPnhFU1MBBXSbGOp1IyN3+gpjHwLMPzozlDAVqOwx6XpnpF78VpeknFBHCbkcKC/R0MXzqf900wH3i2HvfB7v9e9EUFzCQ0vUC+1Og+BFw3F5VSo0QtZyLc4BJ/akBs5mEE6TnuWQa/GhlY8Lz7wbcV1AaBOAQdx+NTbL/+Q31SJ1XsXtGtXCrwMY9noUTyVfpGVXo7Mn4HSslmeQ9SKfYKjyetkBR/1f8a47O3rCggjBy1AlfLjgbERnXy+0Ma4T8lnPZAKt3s9Ya1JupZ7SO7D5j7WfPKP+60c372/RrX1wXsxEeLvBJ0jd8GnSCXDOuvHTQ=="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
format = "qcow";
|
||||||
|
};
|
||||||
|
image = "${initial-config}/nixos.qcow2";
|
||||||
|
start-vm = pkgs.writeShellApplication {
|
||||||
|
name = "start-vm";
|
||||||
|
runtimeInputs = with pkgs; [qemu];
|
||||||
|
text = ''
|
||||||
|
[ ! -f /var/lib/vm-sala/nixos.qcow2 ] && \
|
||||||
|
install ${image} /var/lib/vm-sala
|
||||||
|
|
||||||
|
qemu-system-x86_64 \
|
||||||
|
-enable-kvm \
|
||||||
|
-cpu host \
|
||||||
|
-smp 2 \
|
||||||
|
-m 4096 \
|
||||||
|
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22,hostfwd=tcp::13000-:3000 \
|
||||||
|
-nographic \
|
||||||
|
-drive file=/var/lib/vm-sala/nixos.qcow2
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
after = ["network.target"];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${start-vm}/bin/start-vm";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue