name: CI and Deploy on: workflow_dispatch: pull_request: branches: - master push: branches: - master env: REGISTRY: tea.jesseinit.dev IMAGE_NAME: ${{ github.repository_owner }}/personal-site NAMESPACE: personal-site DEPLOYMENT: jesseinit-portfolio permissions: contents: read concurrency: group: deploy-${{ github.ref }} cancel-in-progress: false jobs: ci: name: Build site runs-on: ubuntu-latest timeout-minutes: 15 # The Gitea runner image only caches Node 24; match the Dockerfile's Node 22. container: node:22-bookworm steps: - name: Checkout code uses: actions/checkout@v6 - name: Install dependencies run: npm ci --no-audit --no-fund --no-update-notifier - name: Build run: npm run build - name: Validate Kubernetes manifests run: | curl -fsSL "https://dl.k8s.io/release/v1.34.3/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl chmod +x /usr/local/bin/kubectl kubectl kustomize k8s/ > /dev/null build-image: name: Build and push image needs: ci if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Log in to Gitea Container Registry uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} password: ${{ secrets.REGISTRY_TOKEN }} - name: Extract image metadata id: meta uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=raw,value=latest - name: Build and push image uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest cache-to: type=inline deploy: name: Deploy to Kubernetes needs: build-image runs-on: ubuntu-latest environment: production steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up kubectl uses: azure/setup-kubectl@v5.1.0 with: version: v1.34.3 - name: Configure kubectl env: KUBE_CONFIG_B64: ${{ secrets.KUBE_CONFIG }} run: | set -euo pipefail if [ -z "${KUBE_CONFIG_B64}" ]; then echo "::error::KUBE_CONFIG is not configured in the production environment or repository." exit 1 fi KUBECONFIG_PATH="${RUNNER_TEMP}/personal-site-kubeconfig" umask 077 printf '%s' "${KUBE_CONFIG_B64}" | base64 --decode > "${KUBECONFIG_PATH}" echo "KUBECONFIG=${KUBECONFIG_PATH}" >> "${GITHUB_ENV}" KUBECONFIG="${KUBECONFIG_PATH}" kubectl cluster-info --request-timeout=15s - name: Apply manifests run: kubectl apply -k k8s/ # The spec still says :latest, so force new pods to pull the image just pushed. - name: Restart deployment run: kubectl rollout restart "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" - name: Wait for rollout run: | if ! kubectl rollout status "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --timeout=5m; then kubectl get pods -n "${NAMESPACE}" -o wide kubectl describe pods -n "${NAMESPACE}" | tail -40 exit 1 fi - name: Verify deployment run: | kubectl get deployments,pods,services,ingress -n "${NAMESPACE}" echo "### Deployed ${GITHUB_SHA} to ${NAMESPACE}" >> "${GITHUB_STEP_SUMMARY}"