Files
k8s-game-2048/.github/workflows/deploy-prod.yml
Greg 9fdcc9574a 🎯 Update all workflows to test canonical Knative domains
 Improvements:
- Prioritize canonical domain testing over custom domains
- Add fallback testing for both canonical and custom domains
- More reliable smoke tests using direct Knative service URLs
- Separate performance testing for canonical vs custom domains
- Enhanced auto-promotion pipeline with canonical domain validation

🧪 Testing Strategy:
- Primary: Test canonical domains (game-2048-*.*.wa.darknex.us)
- Secondary: Verify custom domains work via redirects
- Fallback: Test both domains in smoke tests for reliability

🔗 Canonical Domains:
- Dev: game-2048-dev.game-2048-dev.dev.wa.darknex.us
- Staging: game-2048-staging.game-2048-staging.staging.wa.darknex.us
- Prod: game-2048-prod.game-2048-prod.wa.darknex.us

This ensures tests are more reliable since canonical domains are always accessible
while custom domains may have redirect complexity.
2025-06-30 23:04:01 -07:00

263 lines
9.1 KiB
YAML

name: Deploy to Production
on:
workflow_dispatch:
inputs:
image_tag:
description: 'Image tag to deploy (default: latest)'
required: false
default: 'latest'
confirmation:
description: 'Type "DEPLOY" to confirm production deployment'
required: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ghndrx/k8s-game-2048
jobs:
deploy-prod:
name: Deploy to Production
runs-on: ubuntu-latest
environment: production
if: ${{ github.event.inputs.confirmation == 'DEPLOY' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v3
with:
version: 'latest'
- name: Configure kubectl
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
chmod 600 ~/.kube/config
- name: Set image tag
run: |
IMAGE_TAG="${{ github.event.inputs.image_tag || 'latest' }}"
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
echo "Deploying image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$IMAGE_TAG"
- name: Deploy to production
run: |
echo "🚀 Deploying to production environment..."
# Apply namespace
kubectl apply -f manifests/prod/namespace.yml
# Ensure GHCR secret exists
if kubectl get secret ghcr-secret -n default &>/dev/null; then
echo "🔐 Copying GHCR secret to prod namespace..."
kubectl get secret ghcr-secret -o yaml | \
sed 's/namespace: default/namespace: game-2048-prod/' | \
sed '/resourceVersion:/d' | \
sed '/uid:/d' | \
sed '/creationTimestamp:/d' | \
kubectl apply -f -
fi
# Update image in service and deploy
kubectl patch ksvc game-2048-prod -n game-2048-prod --type merge -p '{"spec":{"template":{"spec":{"containers":[{"image":"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}","imagePullPolicy":"Always"}]}}}}'
echo "⏳ Waiting for deployment to be ready..."
kubectl wait --for=condition=Ready ksvc/game-2048-prod -n game-2048-prod --timeout=300s || echo "⚠️ Service may still be starting"
- name: Verify deployment
run: |
echo "📊 Deployment status:"
kubectl get ksvc -n game-2048-prod
echo ""
echo "✅ Production deployment completed!"
echo "🌐 Available at: https://2048.wa.darknex.us"
- name: Run smoke test
run: |
echo "🧪 Running smoke test..."
sleep 30
for i in {1..5}; do
echo "Attempt $i/5..."
# Test canonical domain first
if curl -s --max-time 30 https://game-2048-prod.game-2048-prod.wa.darknex.us/ | grep -q "2048"; then
echo "✅ Canonical domain smoke test passed!"
break
# Fallback to custom domain
elif curl -s --max-time 30 https://2048.wa.darknex.us/ | grep -q "2048"; then
echo "✅ Custom domain smoke test passed!"
break
elif [ $i -eq 5 ]; then
echo "⚠️ Smoke test failed after 5 attempts"
exit 1
else
echo "Retrying in 30 seconds..."
sleep 30
fi
done
- name: Create production deployment summary
run: |
echo "## 🚀 Production Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Environment | **Production** |" >> $GITHUB_STEP_SUMMARY
echo "| Image | \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Domain | https://2048.wa.darknex.us |" >> $GITHUB_STEP_SUMMARY
echo "| Status | ✅ **LIVE** |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🎉 Production is Live!" >> $GITHUB_STEP_SUMMARY
echo "- 🎮 [Play the game](https://2048.wa.darknex.us)" >> $GITHUB_STEP_SUMMARY
echo "- 🧪 [Run smoke tests](https://github.com/${{ github.repository }}/actions/workflows/smoke-test.yml)" >> $GITHUB_STEP_SUMMARY
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Set up kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Configure kubectl
run: |
echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig
export KUBECONFIG=kubeconfig
- name: Update image in manifests
run: |
TAG="${{ github.event.release.tag_name || github.event.inputs.tag }}"
sed -i "s|ghcr.io/ghndrx/k8s-game-2048:v1.0.0|${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}|g" manifests/prod/service.yml
- name: Deploy to production with blue-green strategy
run: |
export KUBECONFIG=kubeconfig
# Deploy new revision with 0% traffic
kubectl apply -f manifests/prod/
# Wait for new revision to be ready
kubectl wait --for=condition=Ready ksvc/game-2048-prod -n game-2048-prod --timeout=300s
# Get the latest revision name
LATEST_REVISION=$(kubectl get ksvc game-2048-prod -n game-2048-prod -o jsonpath='{.status.latestReadyRevisionName}')
# Gradually shift traffic (10%, 50%, 100%)
kubectl patch ksvc game-2048-prod -n game-2048-prod --type='merge' -p='{"spec":{"traffic":[{"revisionName":"'$LATEST_REVISION'","percent":10},{"latestRevision":false,"percent":90}]}}'
sleep 60
kubectl patch ksvc game-2048-prod -n game-2048-prod --type='merge' -p='{"spec":{"traffic":[{"revisionName":"'$LATEST_REVISION'","percent":50},{"latestRevision":false,"percent":50}]}}'
sleep 60
kubectl patch ksvc game-2048-prod -n game-2048-prod --type='merge' -p='{"spec":{"traffic":[{"latestRevision":true,"percent":100}]}}'
- name: Run production health checks
run: |
# Wait for traffic to stabilize
sleep 60
# Test the production URL
curl -f https://2048.wa.darknex.us/ || exit 1
# Additional health checks can be added here
- name: Get service URL
id: get-url
run: |
export KUBECONFIG=kubeconfig
SERVICE_URL=$(kubectl get ksvc game-2048-prod -n game-2048-prod -o jsonpath='{.status.url}')
echo "service_url=$SERVICE_URL" >> $GITHUB_OUTPUT
echo "🚀 Production service deployed at: $SERVICE_URL"
- name: Set up Node.js for testing
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: tests/package.json
- name: Install Playwright dependencies
run: |
cd tests
npm install
npx playwright install --with-deps
- name: Run production smoke tests
run: |
cd tests
BASE_URL=${{ steps.get-url.outputs.service_url }} npx playwright test environment.spec.ts
env:
CI: true
- name: Run full test suite
run: |
cd tests
BASE_URL=${{ steps.get-url.outputs.service_url }} npx playwright test
env:
CI: true
- name: Upload production test results
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-results-production-${{ github.sha }}-${{ github.run_number }}
path: |
tests/playwright-report/
tests/test-results/
retention-days: 90
- name: Upload production screenshots
uses: actions/upload-artifact@v4
if: always()
with:
name: screenshots-production-${{ github.sha }}-${{ github.run_number }}
path: tests/test-results/**/*.png
retention-days: 90
- name: Production health validation
run: |
# Extended health checks for production
echo "🔍 Running production health checks..."
# Test main URL
curl -f https://2048.wa.darknex.us/ || exit 1
# Test health endpoint
curl -f https://2048.wa.darknex.us/health || exit 1
# Check response times
RESPONSE_TIME=$(curl -o /dev/null -s -w '%{time_total}' https://2048.wa.darknex.us/)
echo "Response time: ${RESPONSE_TIME}s"
# Fail if response time > 3 seconds
if (( $(echo "$RESPONSE_TIME > 3.0" | bc -l) )); then
echo "❌ Response time too slow: ${RESPONSE_TIME}s"
exit 1
fi
echo "✅ All production health checks passed!"