gitea: add actions runner and CI/CD deploy workflow

- enable gitea actions
- add native host runner (nix:host label, capacity 1)
- add gitea-runner system user with persisted state
- add agenix-encrypted CI secrets (deploy key, git-crypt key, runner token)
- authorize CI deploy key for root SSH
- add build-and-deploy workflow triggered on push to main
This commit is contained in:
2026-03-30 17:26:21 -04:00
parent 936efaa21b
commit bedc94cbc0
10 changed files with 132 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
name: Build and Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: nix
steps:
- uses: https://github.com/actions/checkout@v4
with:
fetch-depth: 0
- name: Build NixOS configuration
run: |
nix build .#nixosConfigurations.muffin.config.system.build.toplevel -L
- name: Deploy via deploy-rs
run: |
eval $(ssh-agent -s)
ssh-add /run/agenix/ci-deploy-key
nix run github:serokell/deploy-rs -- .#muffin --ssh-opts="-o StrictHostKeyChecking=no"
- name: Health check
run: |
sleep 10
ssh -i /run/agenix/ci-deploy-key -o StrictHostKeyChecking=no root@server-public \
"systemctl is-active gitea && systemctl is-active caddy && systemctl is-active continuwuity && systemctl is-active coturn"
- name: Notify success
if: success()
run: |
curl -sf -X POST \
"https://ntfy.sigkill.computer/deployments" \
-H "Title: [muffin] Deploy succeeded" \
-H "Priority: default" \
-H "Tags: white_check_mark" \
-d "server-config deployed from commit ${GITHUB_SHA::8}"
- name: Notify failure
if: failure()
run: |
curl -sf -X POST \
"https://ntfy.sigkill.computer/deployments" \
-H "Title: [muffin] Deploy FAILED" \
-H "Priority: urgent" \
-H "Tags: rotating_light" \
-d "server-config deploy failed at commit ${GITHUB_SHA::8}"

View File

@@ -26,6 +26,7 @@
./services/caddy.nix ./services/caddy.nix
./services/immich.nix ./services/immich.nix
./services/gitea.nix ./services/gitea.nix
./services/gitea-actions-runner.nix
./services/minecraft.nix ./services/minecraft.nix
./services/wg.nix ./services/wg.nix
@@ -249,6 +250,14 @@
users.groups.${service_configs.media_group} = { }; users.groups.${service_configs.media_group} = { };
users.users.gitea-runner = {
isSystemUser = true;
group = "gitea-runner";
home = "/var/lib/gitea-runner";
description = "Gitea Actions CI runner";
};
users.groups.gitea-runner = { };
users.users.${username} = { users.users.${username} = {
isNormalUser = true; isNormalUser = true;
extraGroups = [ extraGroups = [

View File

@@ -128,5 +128,28 @@
group = "continuwuity"; group = "continuwuity";
}; };
# CI deploy SSH key
ci-deploy-key = {
file = ../secrets/ci-deploy-key.age;
mode = "0400";
owner = "gitea-runner";
group = "gitea-runner";
};
# Git-crypt symmetric key for dotfiles repo
git-crypt-key-dotfiles = {
file = ../secrets/git-crypt-key-dotfiles.age;
mode = "0400";
owner = "gitea-runner";
group = "gitea-runner";
};
# Gitea Actions runner registration token
gitea-runner-token = {
file = ../secrets/gitea-runner-token.age;
mode = "0400";
owner = "gitea-runner";
group = "gitea-runner";
};
}; };
} }

View File

@@ -24,6 +24,7 @@
# ZFS cache directory - persisting the directory instead of the file # ZFS cache directory - persisting the directory instead of the file
# avoids "device busy" errors when ZFS atomically updates the cache # avoids "device busy" errors when ZFS atomically updates the cache
"/etc/zfs" "/etc/zfs"
"/var/lib/gitea-runner"
]; ];
files = [ files = [

BIN
secrets/ci-deploy-key.age Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,46 @@
{
config,
lib,
pkgs,
service_configs,
...
}:
{
services.gitea-actions-runner.instances.muffin = {
enable = true;
name = "muffin";
url = config.services.gitea.settings.server.ROOT_URL;
tokenFile = config.age.secrets.gitea-runner-token.path;
labels = [ "nix:host" ];
hostPackages = with pkgs; [
bash
coreutils
curl
gawk
git
git-crypt
gnugrep
gnused
jq
nix
nodejs
openssh
];
settings = {
runner = {
capacity = 1;
timeout = "3h";
};
};
};
# Override DynamicUser to use our static gitea-runner user
systemd.services."gitea-runner-muffin" = {
serviceConfig = {
DynamicUser = lib.mkForce false;
User = "gitea-runner";
Group = "gitea-runner";
};
environment.GIT_SSH_COMMAND = "ssh -i /run/agenix/ci-deploy-key -o StrictHostKeyChecking=no";
};
}

View File

@@ -37,6 +37,7 @@
}; };
# only I shall use gitea # only I shall use gitea
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
actions.ENABLED = true;
}; };
}; };

View File

@@ -31,5 +31,8 @@
# used for deploying configs to server # used for deploying configs to server
users.users.root.openssh.authorizedKeys.keys = users.users.root.openssh.authorizedKeys.keys =
config.users.users.${username}.openssh.authorizedKeys.keys; config.users.users.${username}.openssh.authorizedKeys.keys
++ [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC5ZYN6idL/w/mUIfPOH1i+Q/SQXuzAMQUEuWpipx1Pc ci-deploy@muffin"
];
} }