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 - 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 type=sha,format=long - 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: Set up kustomize env: KUSTOMIZE_VERSION: v5.7.1 run: | mkdir -p "${RUNNER_TEMP}/bin" curl -fsSL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" \ | tar -xz -C "${RUNNER_TEMP}/bin" kustomize echo "${RUNNER_TEMP}/bin" >> "${GITHUB_PATH}" - name: Pin image to this commit working-directory: k8s run: kustomize edit set image "jesseinit-portfolio=${REGISTRY}/${IMAGE_NAME}:sha-${GITHUB_SHA}" - name: Apply manifests run: kubectl apply -k k8s/ - 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 kubectl rollout undo "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" || true exit 1 fi - name: Verify deployment run: | kubectl get deployments,pods,services,ingress -n "${NAMESPACE}" echo "### Deployed sha-${GITHUB_SHA} to ${NAMESPACE}" >> "${GITHUB_STEP_SUMMARY}"