name: CI on: push: pull_request: env: REGISTRY_IMAGE: registry.neko-piranha.ts.net/astral/buzz-sheet DEPLOY_NAMESPACE: buzz-sheet KUBE_API_SERVER: https://100.112.189.33:6443/ jobs: verify: name: Verify runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Check out repository uses: actions/checkout@v4 - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.14 - name: Install dependencies run: bun install --frozen-lockfile - name: Run unit and integration tests env: REDIS_INTEGRATION_URL: ${{ secrets.REDIS_INTEGRATION_URL }} run: bun run test - name: Generate route types and check TypeScript run: bun run typecheck - name: Lint run: bun run lint - name: Set up kubectl uses: azure/setup-kubectl@v4 - name: Validate Kubernetes manifests run: kubectl kustomize k8s/ >/dev/null build-and-deploy: name: Build immutable images and deploy needs: verify if: >- gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' && vars.DEPLOY_ENABLED == 'true' runs-on: ubuntu-latest timeout-minutes: 45 steps: - name: Check out repository uses: actions/checkout@v4 - name: Build and push application image run: | docker build \ --target app \ --build-arg NEXT_DEPLOYMENT_ID=${{ gitea.sha }} \ --build-arg VCS_REF=${{ gitea.sha }} \ --tag ${{ env.REGISTRY_IMAGE }}:${{ gitea.sha }} \ . docker push ${{ env.REGISTRY_IMAGE }}:${{ gitea.sha }} - name: Build and push migration image run: | docker build \ --target migration \ --build-arg VCS_REF=${{ gitea.sha }} \ --tag ${{ env.REGISTRY_IMAGE }}:migrate-${{ gitea.sha }} \ . docker push ${{ env.REGISTRY_IMAGE }}:migrate-${{ gitea.sha }} - name: Set up kubectl uses: azure/setup-kubectl@v4 - name: Configure kubectl env: KUBE_CONFIG_B64: ${{ secrets.KUBE_CONFIG_B64 }} run: | if [ -z "$KUBE_CONFIG_B64" ]; then echo "Missing KUBE_CONFIG_B64 secret for the CI deploy identity" >&2 exit 1 fi kube_dir="$RUNNER_TEMP/buzz-sheet-kube" mkdir -p "$kube_dir" chmod 700 "$kube_dir" export KUBECONFIG="$kube_dir/config" printf '%s' "$KUBE_CONFIG_B64" | base64 --decode >"$KUBECONFIG" chmod 600 "$KUBECONFIG" cluster_name="$(kubectl config view --kubeconfig "$KUBECONFIG" --minify -o jsonpath='{.clusters[0].name}')" kubectl config set-cluster "$cluster_name" \ --kubeconfig "$KUBECONFIG" \ --server "$KUBE_API_SERVER" \ --insecure-skip-tls-verify=true >/dev/null env_file="${GITEA_ENV_FILE:-${GITHUB_ENV:-}}" if [ -n "$env_file" ]; then printf '%s\n' "KUBECONFIG=$KUBECONFIG" >> "$env_file" fi - name: Migrate, then roll out the immutable revision env: REVISION: ${{ gitea.sha }} KUBE_CONFIG_B64: ${{ secrets.KUBE_CONFIG_B64 }} shell: bash run: | set -Eeuo pipefail if [ -z "$KUBE_CONFIG_B64" ]; then echo "Missing KUBE_CONFIG_B64 secret for the CI deploy identity" >&2 exit 1 fi kube_dir="$RUNNER_TEMP/buzz-sheet-kube" mkdir -p "$kube_dir" chmod 700 "$kube_dir" export KUBECONFIG="$kube_dir/config" if ! printf '%s' "$KUBE_CONFIG_B64" | base64 --decode >"$KUBECONFIG"; then echo "KUBE_CONFIG_B64 is not valid base64" >&2 exit 1 fi chmod 600 "$KUBECONFIG" if [ ! -s "$KUBECONFIG" ]; then echo "KUBE_CONFIG_B64 decoded to an empty kubeconfig" >&2 exit 1 fi cluster_name="$(kubectl config view --kubeconfig "$KUBECONFIG" --minify -o jsonpath='{.clusters[0].name}')" if [ -z "$cluster_name" ]; then echo "Decoded kubeconfig has no active cluster" >&2 exit 1 fi kubectl config set-cluster "$cluster_name" \ --kubeconfig "$KUBECONFIG" \ --server "$KUBE_API_SERVER" \ --insecure-skip-tls-verify=true >/dev/null revision_short="${REVISION:0:12}" migration_dir="$RUNNER_TEMP/buzz-sheet-migration-$revision_short" migration_manifest="$RUNNER_TEMP/buzz-sheet-migration-$revision_short.yaml" cp -R k8s/migration "$migration_dir" sed -i "s#newName: .*#newName: $REGISTRY_IMAGE#" "$migration_dir/kustomization.yaml" sed -i "s/newTag: .*/newTag: migrate-$REVISION/" "$migration_dir/kustomization.yaml" kubectl kustomize "$migration_dir" >"$migration_manifest" kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" delete job buzz-sheet-migrate --ignore-not-found migration_resource="$(kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" create --validate=false -f "$migration_manifest" -o name)" if ! kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" wait \ --for=condition=complete \ --timeout=10m \ "$migration_resource"; then kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" logs "$migration_resource" --all-containers=true || true exit 1 fi kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" patch configmap buzz-sheet-config \ --type=merge \ --patch "{\"data\":{\"NEXT_DEPLOYMENT_ID\":\"$REVISION\"}}" kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" set image \ deployment/buzz-sheet \ app="$REGISTRY_IMAGE:$REVISION" kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" set image \ deployment/buzz-sheet-worker \ worker="$REGISTRY_IMAGE:$REVISION" kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" set image \ deployment/buzz-sheet-discord-worker \ discord-worker="$REGISTRY_IMAGE:$REVISION" kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" rollout status \ deployment/buzz-sheet \ --timeout=10m kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" rollout status \ deployment/buzz-sheet-worker \ --timeout=10m kubectl --kubeconfig "$KUBECONFIG" --namespace "$DEPLOY_NAMESPACE" rollout status \ deployment/buzz-sheet-discord-worker \ --timeout=10m