Add Gitea Actions runner workflow

This commit is contained in:
juvdiaz 2026-05-25 13:20:51 -06:00
parent 7aec70640a
commit e4f4cef272
4 changed files with 211 additions and 1 deletions

View File

@ -0,0 +1,75 @@
name: Homelab Main
"on":
push:
branches:
- main
jobs:
validate-and-deploy:
runs-on: homelab-debian
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify Debian runner guardrails
run: |
set -euo pipefail
test "$(uname -s)" = "Linux"
. /etc/os-release
test "${ID}" = "debian"
sudo -n true
- name: Validate shell, Kubernetes manifests, and OpenTofu stacks
run: |
set -euo pipefail
bash -n lab.sh
mapfile -t manifests < <(find apps -type f \( -name '*.yaml' -o -name '*.yml' \) | sort)
kubectl --kubeconfig "${KUBECONFIG:-/home/jv/.kube/config}" apply --dry-run=server -f "${manifests[@]}"
set +e
kubectl --kubeconfig "${KUBECONFIG:-/home/jv/.kube/config}" diff -f "${manifests[@]}"
diff_status="$?"
set -e
if (( diff_status > 1 )); then
exit "${diff_status}"
fi
for stack in bootstrap/cluster bootstrap/platform bootstrap/apps bootstrap/edge; do
tofu -chdir="${stack}" init -input=false
tofu -chdir="${stack}" fmt -check
tofu -chdir="${stack}" validate
done
- name: Block automatic deploy for high-impact changes
run: |
set -euo pipefail
event_before="$(
python3 -c 'import json, os; p = os.environ.get("GITHUB_EVENT_PATH", ""); print(json.load(open(p, encoding="utf-8")).get("before", "") if p and os.path.exists(p) else "")'
)"
if [[ -n "${event_before}" && ! "${event_before}" =~ ^0+$ ]]; then
changed_files="$(git diff --name-only "${event_before}" HEAD)"
else
changed_files="$(git diff-tree --no-commit-id --name-only -r HEAD)"
fi
printf '%s\n' "${changed_files}"
if printf '%s\n' "${changed_files}" | grep -Eq '^(bootstrap/(cluster|platform|edge)/|lab[.]sh|[.]gitea/workflows/)'; then
echo "High-impact bootstrap, runner, or workflow changes require a manual Debian run."
exit 1
fi
- name: Deploy validated main branch
run: |
set -euo pipefail
./lab.sh up
kubectl --kubeconfig "${KUBECONFIG:-/home/jv/.kube/config}" -n argocd get applications

View File

@ -239,6 +239,46 @@ sudo systemctl start homelab-gitea-backup.service
sudo ls -lh /var/backups/homelab/gitea sudo ls -lh /var/backups/homelab/gitea
``` ```
## Gitea Actions
This repo includes a Gitea Actions workflow at
`.gitea/workflows/homelab-main.yml`. It runs only on pushes to `main` and targets
a repository-scoped Debian host runner with the label `homelab-debian`.
The workflow validates shell syntax, Kubernetes manifests, and all OpenTofu
stacks before deployment. It automatically stops when high-impact files under
`bootstrap/cluster`, `bootstrap/platform`, `bootstrap/edge`, `lab.sh`, or
`.gitea/workflows` change; those changes still require a manual Debian run.
Lower-risk app changes proceed to `./lab.sh up` after validation passes.
Enable Actions for the repository in Gitea, then create a repository-level runner
token from:
```text
https://lab2025.duckdns.org/git/jv/my-homelab-configs/settings/actions/runners
```
Register and start the Debian runner from the Debian server:
```bash
cd ~/my-homelab-configs
GITEA_RUNNER_REGISTRATION_TOKEN='<repo-runner-token>' ./lab.sh install-gitea-runner
```
The runner is installed as `homelab-gitea-runner.service`, runs as user `jv`, and
uses a host label instead of a Docker job container because deployment needs the
Debian host's Docker, OpenTofu, kubeconfig, SSH keys, and local state.
The deployment job is non-interactive. User `jv` must be able to run `sudo -n
true` on the Debian host or the workflow will fail before deployment.
Useful checks:
```bash
systemctl status homelab-gitea-runner.service
journalctl -u homelab-gitea-runner.service -n 100 --no-pager
```
## Destructive Rebuilds ## Destructive Rebuilds
`./lab.sh nuke` resets kubeadm, containerd runtime state, CNI files, Calico `./lab.sh nuke` resets kubeadm, containerd runtime state, CNI files, Calico

View File

@ -43,6 +43,8 @@ spec:
value: "true" value: "true"
- name: GITEA__migrations__ALLOW_LOCALNETWORKS - name: GITEA__migrations__ALLOW_LOCALNETWORKS
value: "true" value: "true"
- name: GITEA__actions__ENABLED
value: "true"
- name: GITEA__repository__DEFAULT_PRIVATE - name: GITEA__repository__DEFAULT_PRIVATE
value: public value: public
- name: GITEA__security__INSTALL_LOCK - name: GITEA__security__INSTALL_LOCK

95
lab.sh
View File

@ -467,6 +467,8 @@ cleanup() {
trap cleanup EXIT trap cleanup EXIT
kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" exec "\${pod}" -c "\${GITEA_CONTAINER}" -- rm -f "\${REMOTE_ARCHIVE}" >/dev/null 2>&1 || true kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" exec "\${pod}" -c "\${GITEA_CONTAINER}" -- rm -f "\${REMOTE_ARCHIVE}" >/dev/null 2>&1 || true
kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" exec "\${pod}" -c "\${GITEA_CONTAINER}" -- \
sh -c 'mkdir -p /data/git/repositories && chown git:git /data/git /data/git/repositories'
kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" exec "\${pod}" -c "\${GITEA_CONTAINER}" -- \ kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" exec "\${pod}" -c "\${GITEA_CONTAINER}" -- \
su-exec git gitea dump -c /data/gitea/conf/app.ini --file "\${REMOTE_ARCHIVE}" su-exec git gitea dump -c /data/gitea/conf/app.ini --file "\${REMOTE_ARCHIVE}"
kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" cp -c "\${GITEA_CONTAINER}" \ kubectl --kubeconfig "\${KUBECONFIG_PATH}" -n "\${GITEA_NAMESPACE}" cp -c "\${GITEA_CONTAINER}" \
@ -516,6 +518,94 @@ backup_gitea() {
sudo /usr/local/sbin/homelab-gitea-backup.sh sudo /usr/local/sbin/homelab-gitea-backup.sh
} }
install_gitea_runner() {
local runner_arch
local runner_home="${GITEA_RUNNER_HOME:-/home/jv/.local/share/gitea-runner/my-homelab-configs}"
local runner_instance="${GITEA_RUNNER_INSTANCE_URL:-https://lab2025.duckdns.org/git/}"
local runner_labels="${GITEA_RUNNER_LABELS:-homelab-debian:host}"
local runner_name="${GITEA_RUNNER_NAME:-homelab-debian-my-homelab-configs}"
local runner_token="${GITEA_RUNNER_REGISTRATION_TOKEN:-${1:-}}"
local runner_user="${GITEA_RUNNER_USER:-jv}"
local runner_version="${GITEA_ACT_RUNNER_VERSION:-0.2.11}"
local missing_packages=""
require_debian_server "install-gitea-runner"
case "$(dpkg --print-architecture)" in
amd64)
runner_arch="linux-amd64"
;;
arm64)
runner_arch="linux-arm64"
;;
*)
echo "Unsupported Debian architecture: $(dpkg --print-architecture)" >&2
exit 1
;;
esac
for package in ca-certificates curl git python3; do
if ! dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "install ok installed"; then
missing_packages="$missing_packages $package"
fi
done
if [[ -n "${missing_packages}" ]]; then
sudo apt-get update
sudo apt-get install -y --no-install-recommends ${missing_packages}
fi
sudo curl -fsSL \
-o /usr/local/bin/act_runner \
"https://gitea.com/gitea/act_runner/releases/download/v${runner_version}/act_runner-${runner_version}-${runner_arch}"
sudo chmod 0755 /usr/local/bin/act_runner
sudo chown root:root /usr/local/bin/act_runner
sudo -u "${runner_user}" mkdir -p "${runner_home}"
if [[ ! -f "${runner_home}/.runner" ]]; then
if [[ -z "${runner_token}" ]]; then
echo "Set GITEA_RUNNER_REGISTRATION_TOKEN to the repository-level runner token from Gitea." >&2
exit 1
fi
sudo -u "${runner_user}" env \
HOME="/home/${runner_user}" \
GITEA_RUNNER_HOME="${runner_home}" \
GITEA_RUNNER_INSTANCE_URL="${runner_instance}" \
GITEA_RUNNER_REGISTRATION_TOKEN="${runner_token}" \
GITEA_RUNNER_NAME="${runner_name}" \
GITEA_RUNNER_LABELS="${runner_labels}" \
bash -lc 'cd "${GITEA_RUNNER_HOME}" && /usr/local/bin/act_runner register --no-interactive --instance "${GITEA_RUNNER_INSTANCE_URL}" --token "${GITEA_RUNNER_REGISTRATION_TOKEN}" --name "${GITEA_RUNNER_NAME}" --labels "${GITEA_RUNNER_LABELS}"'
else
echo "Existing runner registration found at ${runner_home}/.runner; keeping it."
fi
sudo tee /etc/systemd/system/homelab-gitea-runner.service >/dev/null <<SERVICE_EOT
[Unit]
Description=Homelab Gitea Actions runner for my-homelab-configs
After=network-online.target docker.service
Wants=network-online.target
[Service]
Type=simple
User=${runner_user}
Group=${runner_user}
WorkingDirectory=${runner_home}
Environment=HOME=/home/${runner_user}
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/usr/local/bin/act_runner daemon
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
SERVICE_EOT
sudo systemctl daemon-reload
sudo systemctl enable --now homelab-gitea-runner.service >/dev/null
sudo systemctl status homelab-gitea-runner.service --no-pager -l
}
recreate_pods_for_selector() { recreate_pods_for_selector() {
local namespace="$1" local namespace="$1"
local selector="$2" local selector="$2"
@ -818,11 +908,14 @@ case "${1:-}" in
backup-gitea) backup-gitea)
backup_gitea backup_gitea
;; ;;
install-gitea-runner)
install_gitea_runner "${2:-}"
;;
nuke) nuke)
nuke nuke
;; ;;
*) *)
echo "Usage: $0 {up|backup-gitea|nuke}" echo "Usage: $0 {up|backup-gitea|install-gitea-runner|nuke}"
exit 1 exit 1
;; ;;
esac esac