Create script to deploy in CI #56

Merged
fabianhauser merged 5 commits from 54-deploy-vms-automatically into main 2025-04-19 17:59:46 +02:00
15 changed files with 219 additions and 59 deletions

View file

@ -25,11 +25,22 @@ jobs:
attic use "$CACHE_REPOSITORY" attic use "$CACHE_REPOSITORY"
- name: Run Builds and Checks - name: Run Builds and Checks
run: nix-fast-build --no-nom --max-jobs 6 --skip-cached --attic-cache "$CACHE_REPOSITORY" run: nix-fast-build --no-nom --max-jobs 6 --skip-cached --attic-cache "$CACHE_REPOSITORY"
- name: Deploy Docs deploy:
if: success() && github.ref == 'refs/heads/main' needs: build
run: | if: success() && github.ref == 'refs/heads/main'
mkdir ~/.ssh/ runs-on: nix
echo -e "Host lindberg-webapps.backplane.net.qo.is\n StrictHostKeyChecking no" >> ~/.ssh/config env:
(umask 0077 && printf "%s\n" "${{ secrets.SSH_DEPLOY_KEY }}" > ~/.ssh/id_ed25519) SSH_DEPLOY_KEY: "${{ secrets.SSH_DEPLOY_KEY }}"
deploy --skip-checks --remote-build .#lindberg-webapps.\"docs-ops.qo.is\" strategy:
# Remote build is neccessary due to non-wheel nix users signing restrictions. However, the build should come from the cache anyway. matrix:
profile:
- docs-ops.qo.is
- system-vm
steps:
- name: Initialize CI
uses: https://git.qo.is/qo.is/actions-nix-init@main
with:
token: ${{ secrets.CI_TOKEN }}
lfs: false
- name: "Deploy profile"
run: "auto-deploy ${{ matrix.profile }}"

View file

@ -4,14 +4,26 @@ Note that you have to be connected to the `vpn.qo.is`
(or execute the deployment from a host that is in the `backplane.net.qo.is` overlay network) (or execute the deployment from a host that is in the `backplane.net.qo.is` overlay network)
and that you need to have SSH root access to the target machines. and that you need to have SSH root access to the target machines.
## Deploy system categories
We currently split out nixosConfigurations into these categories:
- `system-ci`: Systems should be updated separately because they might break automated deployment processes.
- `system-vm`: Virtual systems.
- `system-physical`: Physical systems.
You can roll updates with retries by category with:
```bash
auto-deploy system-vm
auto-deploy system-physical
```
## Deploy to selected target hosts ## Deploy to selected target hosts
```bash ```bash
nix run .#deploy-qois .#<hostname>.system .#<hostname2>.system nix develop
```
## Deploy with extended timeouts (sometimes required for slow APU devices) deploy --skip-checks .#cyprianspitz.system-physical
deploy --skip-checks .#lindberg-build.system-vm
```bash
nix run .#deploy-qois .#calanda.system -- --confirm-timeout 600 --activation-timeout 600
``` ```

View file

@ -12,5 +12,6 @@ in
sshUser = "nginx-${domain}"; sshUser = "nginx-${domain}";
path = deployPkgs.deploy-rs.lib.activate.noop self.packages.${system}.docs; path = deployPkgs.deploy-rs.lib.activate.noop self.packages.${system}.docs;
profilePath = "/var/lib/nginx-${domain}/root"; profilePath = "/var/lib/nginx-${domain}/root";
remoteBuild = true;
}; };
} }

View file

@ -0,0 +1,27 @@
{
deployPkgs,
pkgs,
self,
...
}:
let
inherit (pkgs.lib) pipe filterAttrs mapAttrs;
in
{
nodes = pipe self.nixosConfigurations [
(filterAttrs (_n: v: v.config.qois.git-ci-runner.enable))
(mapAttrs (
host: config: {
hostname = "${host}.backplane.net.qo.is";
profiles.system-ci = {
sshUser = "root";
user = "root";
activationTimeout = 300;
confirmTimeout = 60;
remoteBuild = true;
path = deployPkgs.deploy-rs.lib.activate.nixos config;
};
}
))
];
}

View file

@ -0,0 +1,27 @@
{
deployPkgs,
pkgs,
self,
...
}:
let
inherit (pkgs.lib) pipe filterAttrs mapAttrs;
in
{
nodes = pipe self.nixosConfigurations [
(filterAttrs (_n: v: !v.config.services.qemuGuest.enable && !v.config.qois.git-ci-runner.enable))
(mapAttrs (
host: config: {
hostname = "${host}.backplane.net.qo.is";
profiles.system-physical = {
sshUser = "root";
user = "root";
activationTimeout = 600;
confirmTimeout = 120;
remoteBuild = true;
path = deployPkgs.deploy-rs.lib.activate.nixos config;
};
}
))
];
}

View file

@ -0,0 +1,27 @@
{
deployPkgs,
pkgs,
self,
...
}:
let
inherit (pkgs.lib) pipe filterAttrs mapAttrs;
in
{
nodes = pipe self.nixosConfigurations [
(filterAttrs (_n: v: v.config.services.qemuGuest.enable && !v.config.qois.git-ci-runner.enable))
(mapAttrs (
host: config: {
hostname = "${host}.backplane.net.qo.is";
profiles.system-vm = {
sshUser = "root";
user = "root";
activationTimeout = 300;
confirmTimeout = 60;
remoteBuild = true;
path = deployPkgs.deploy-rs.lib.activate.nixos config;
};
}
))
];
}

View file

@ -1,20 +0,0 @@
{
deployPkgs,
pkgs,
self,
system,
...
}:
{
nodes = pkgs.lib.mapAttrs (host: config: {
hostname = "${host}.backplane.net.qo.is";
profiles.system = {
sshUser = "root";
user = "root";
activationTimeout = 420;
confirmTimeout = 120;
path = deployPkgs.deploy-rs.lib.activate.nixos config;
};
}) self.nixosConfigurations;
}

View file

@ -29,9 +29,9 @@ in
pre-commit-check.enabledPackages pre-commit-check.enabledPackages
++ [ vscodium-with-extensions ] ++ [ vscodium-with-extensions ]
++ (with self.packages.${system}; [ ++ (with self.packages.${system}; [
deploy-qois
sops sops
sops-rekey sops-rekey
auto-deploy
]) ])
++ (with pkgs; [ ++ (with pkgs; [
attic-client attic-client

View file

@ -13,6 +13,10 @@ with lib;
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBS65v7n5ozOUjYGuO/dgLC9C5MUGL5kTnQnvWAYP5B3 ci@git.qo.is"
]; # TODO: Move this key to allow CI deployment for all machines.
boot.loader.grub.enable = true; boot.loader.grub.enable = true;
system.autoUpgrade.allowReboot = true; system.autoUpgrade.allowReboot = true;

View file

@ -0,0 +1,16 @@
{
deploy-rs,
gitMinimal,
writeShellApplication,
lib,
...
}:
writeShellApplication {
name = "auto-deploy";
meta.description = "Deploy machines automatically.";
runtimeInputs = [
deploy-rs
gitMinimal
];
text = lib.readFile ./script.bash;
}

View file

@ -0,0 +1,65 @@
#!/usr/bin/env bash
#### Environment
FLAKE_ROOT="$(git rev-parse --show-toplevel)"
export PROFILE="${1:-}"
if [ -z "${PROFILE}" ]; then
echo "🛑 Error: No deployment profile was specified as first parameter (e.g. \"${0} system-vm\")" 1>&2
exit 1
fi
if [ -z "${SSH_DEPLOY_KEY:-}" ]; then
echo " Info: SSH_DEPLOY_KEY env variable was not set, ignoring."
SSH_KEY_FILE_ARG=""
else
TEMP_KEY_FILE=$(mktemp /dev/shm/ssh_deploy_key.XXXXXXXX)
touch "${TEMP_KEY_FILE}" && chmod 600 "${TEMP_KEY_FILE}"
printf "%s\n" "${SSH_DEPLOY_KEY}" >"${TEMP_KEY_FILE}"
SSH_KEY_FILE_ARG="-i ${TEMP_KEY_FILE}"
# Set up a trap to remove the temporary key file on script exit
trap 'rm -f "${TEMP_KEY_FILE}"' EXIT
trap 'rm -f "${TEMP_KEY_FILE}"' SIGINT
trap 'rm -f "${TEMP_KEY_FILE}"' SIGTERM
trap 'rm -f "${TEMP_KEY_FILE}"' SIGQUIT
fi
HOSTS=$(nix eval --raw "${FLAKE_ROOT}"#deploy.nodes --apply "
nodes: let
inherit (builtins) attrNames filter concatStringsSep;
names = attrNames nodes;
profile = \"${PROFILE}\";
filteredNames = filter (name: nodes.\${name}.profiles ? \${profile}) names;
in concatStringsSep \"\\n\" filteredNames
")
if [ -z "$HOSTS" ]; then
echo "🛑 Error: No deployments matching the profile ${PROFILE} were found." 1>&2
exit 1
fi
KNOWN_HOSTS_FILE=$(nix eval --raw .#nixosConfigurations.lindberg.config.environment.etc."ssh/ssh_known_hosts".source)
#### Helpers
retry() {
local -r -i max_attempts="$1"
shift
local -i attempt_num=1
until "$@"; do
if ((attempt_num == max_attempts)); then
echo "🛑 Error: Attempt $attempt_num failed and there are no more attempts left!" 1>&2
return 1
else
echo "⚠️ Attempt $attempt_num failed! Trying again in $attempt_num seconds..."
sleep $((attempt_num++))
fi
done
}
#### Execution
for HOST in $HOSTS; do
retry 3 deploy \
--skip-checks \
--ssh-opts "-o UserKnownHostsFile=${KNOWN_HOSTS_FILE} ${SSH_KEY_FILE_ARG:-}" \
--targets "${FLAKE_ROOT}#\"${HOST}\".\"${PROFILE}\""
done

View file

@ -1,14 +0,0 @@
{
deploy-rs,
flakeSelf,
writeShellApplication,
...
}:
writeShellApplication {
name = "deploy-qois";
meta.description = "Deploy configuration to specificed targets.";
runtimeInputs = [ deploy-rs ];
text = ''
deploy --remote-build --skip-checks --interactive --targets "''${@:-${flakeSelf}}"
'';
}

View file

@ -3,11 +3,12 @@
"extends": [ "extends": [
"config:recommended" "config:recommended"
], ],
"schedule": [
"* 18-19 * * *"
],
"lockFileMaintenance": { "lockFileMaintenance": {
"enabled": true, "enabled": true,
"extends": [ "automerge": true
"schedule:weekly"
]
}, },
"cloneSubmodules": true, "cloneSubmodules": true,
"nix": { "nix": {

View file

@ -19,11 +19,13 @@
"*.toml" "*.toml"
] ]
++ [ ++ [
".envrc"
"robots.txt"
".vscode/*" ".vscode/*"
"nixos-modules/system/etc/*" "nixos-modules/system/etc/*"
"private"
"private/*" "private/*"
".envrc"
"robots.txt"
]; ];
formatter.jsonfmt.excludes = [ ".vscode/*.json" ]; formatter.jsonfmt.excludes = [ ".vscode/*.json" ];
}; };

View file

@ -22,13 +22,14 @@ Deploy updates:
nix develop nix develop
# Deploy vms # Deploy vms
deploy-qois .#lindberg-nextcloud .#lindberg-build auto-deploy system-vm
# Deploy fast physical hosts # Deploy CI hosts
deploy-qois .#lindberg auto-deploy system-ci
# Deploy physical hosts
auto-deploy system-physical
# Deploy slow physical hosts (maybe do individually)
deploy-qois --confirm-timeout 600 --activation-timeout 600 --targets .#stompert .#stompert
``` ```