Reusable repo template for homelab services on git.aridgwayweb.com. Bakes in (all verified live on armistace/wedding-photos): - Hard commit guard: shared pre-commit hook (git config core.hooksPath ~/dev/git-hooks) + master branch protection (push whitelist [armistace], merge whitelist [hermes, armistace]). - Gitea Actions CI (.gitea/workflows/build_push.yml): test + build-deploy, persistent remote buildkit cache, registry push, idempotent deploy that preserves hand-provisioned Secrets, cluster injection from repo secrets/vars via scripts/reconcile-cluster-inject.sh. - Persistent buildkit cache (ci/buildkit/): single-replica Longhorn backing. - scripts/reconcile-cluster-inject.sh: reconcile live Secret/ConfigMap from Gitea secrets/vars without clobbering hand-provisioned values. - RUNBOOK.md: handoff-complete ops doc. Placeholders (<APP> <OWNER> <NS> <KEY_*>) are filled per-service on repo creation.
106 lines
4.6 KiB
YAML
106 lines
4.6 KiB
YAML
# Gitea Actions workflow for a single-service homelab app.
|
|
#
|
|
# THIS IS A TEMPLATE. On repo creation from this template, Gitea copies this file
|
|
# verbatim. Fill in the placeholders below (<APP>, <OWNER>, <NS>) to match your
|
|
# service. See RUNBOOK.md §2 for the one-time per-repo setup (repo secrets/vars).
|
|
#
|
|
# Proven patterns baked in (all verified live on armistace/wedding-photos):
|
|
# - buildx driver: remote -> persistent buildkit daemon (NOT docker-container,
|
|
# which can't reach Docker Hub on this runner). endpoint MUST be top-level,
|
|
# not under driver-opts (that yields "no remote endpoint provided").
|
|
# - Kubeconfig from CI secret, kubectl installed in-job.
|
|
# - Deploy does NOT delete the namespace (preserves hand-provisioned Secrets).
|
|
# - reconcile-cluster-inject.sh keeps the live Secret/ConfigMap in sync with
|
|
# repo secrets/vars without clobbering hand-provisioned values.
|
|
# - linux/amd64 only (arm64 via qemu OOMs the buildkit pod on the NUCs).
|
|
# - Do NOT use ghcr.io images in COPY --from (buildkit can't reach it); use the
|
|
# ECR base and install uv via pip.
|
|
|
|
name: build-and-deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v5
|
|
- name: Unit tests
|
|
working-directory: backend
|
|
run: |
|
|
uv python install 3.12
|
|
uv sync --all-groups --frozen
|
|
uv run python -m pytest -q
|
|
|
|
build-deploy:
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
container: catthehacker/ubuntu:act-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Create Kubeconfig
|
|
run: |
|
|
mkdir -p $HOME/.kube
|
|
echo "${{ secrets.KUBEC_CONFIG_BUILDX_NEW_2 }}" > $HOME/.kube/config
|
|
|
|
- name: Set up Docker Buildx (remote -> persistent buildkit daemon)
|
|
uses: docker/setup-buildx-action@v3
|
|
with:
|
|
driver: remote
|
|
# endpoint must be TOP-LEVEL (the action passes it positionally to
|
|
# `buildx create --driver remote <endpoint>`). Under driver-opts it
|
|
# fails with "ERROR: no remote endpoint provided".
|
|
endpoint: tcp://buildkit.gitea-runner.svc:1234
|
|
|
|
- name: Login to Gitea registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: git.aridgwayweb.com
|
|
username: <OWNER>
|
|
password: ${{ secrets.REG_PASSWORD }}
|
|
|
|
- name: Build & push image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./backend
|
|
push: true
|
|
platforms: linux/amd64
|
|
tags: |
|
|
git.aridgwayweb.com/<OWNER>/<APP>:latest
|
|
git.aridgwayweb.com/<OWNER>/<APP>:${{ github.sha }}
|
|
|
|
- name: Deploy
|
|
run: |
|
|
echo "Installing Kubectl"
|
|
apt-get update
|
|
apt-get install -y apt-transport-https ca-certificates curl gnupg
|
|
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
|
|
chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
|
|
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | tee /etc/apt/sources.list.d/kubernetes.list
|
|
chmod 644 /etc/apt/sources.list.d/kubernetes.list
|
|
apt-get update
|
|
apt-get install -y kubectl
|
|
# Do NOT delete the namespace — that destroys the live Secret (DB creds,
|
|
# JWT, etc.) which is provisioned by hand (see RUNBOOK.md).
|
|
kubectl apply -f kube/<NS>_namespace.yaml
|
|
kubectl create secret docker-registry regcred --docker-server=git.aridgwayweb.com --docker-username=<OWNER> --docker-password='${{ secrets.REG_PASSWORD }}' --docker-email=<OWNER>@aridgwayweb.com --namespace=<NS> --dry-run=client -o yaml | kubectl apply -f -
|
|
# Apply everything EXCEPT the placeholder Secret (which would clobber the live one).
|
|
kubectl apply -f kube/<NS>_configmap.yaml
|
|
kubectl apply -f kube/<APP>-backend_deploy.yaml
|
|
kubectl apply -f kube/<APP>-backend_service.yaml
|
|
# Reconcile the live Secret + ConfigMap from Gitea repo secrets/vars so
|
|
# every env key the deployments reference exists (prevents
|
|
# CreateContainerConfigError). See scripts/reconcile-cluster-inject.sh.
|
|
export <KEY_1>="${{ secrets.<KEY_1> }}"
|
|
export <KEY_2>="${{ secrets.<KEY_2> }}"
|
|
bash scripts/reconcile-cluster-inject.sh <NS>-secret <NS>-config <NS>
|
|
kubectl rollout status deployment/<APP>-backend -n <NS> --timeout=180s
|