# 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 (, , ) 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 `). 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: 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//:latest git.aridgwayweb.com//:${{ 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/_namespace.yaml kubectl create secret docker-registry regcred --docker-server=git.aridgwayweb.com --docker-username= --docker-password='${{ secrets.REG_PASSWORD }}' --docker-email=@aridgwayweb.com --namespace= --dry-run=client -o yaml | kubectl apply -f - # Apply everything EXCEPT the placeholder Secret (which would clobber the live one). kubectl apply -f kube/_configmap.yaml kubectl apply -f kube/-backend_deploy.yaml kubectl apply -f kube/-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 ="${{ secrets. }}" export ="${{ secrets. }}" bash scripts/reconcile-cluster-inject.sh -secret -config kubectl rollout status deployment/-backend -n --timeout=180s