ci: optimize pipeline with .NET image, smoke tests, and auto-rollback
Changes: - Runner now uses mcr.microsoft.com/dotnet/sdk:9.0 image - Simplified test job (build + unit tests only) - Deploy uses git pull instead of rsync for faster updates - Added smoke tests in production: - Health check API - Health check Frontend - GraphQL endpoint validation - Database connectivity check - Subjects query test - Auto-rollback if smoke tests fail Flow: test -> deploy -> smoke-tests -> (rollback on failure) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1913e1d6b1
commit
d1bb921b4b
|
|
@ -1,4 +1,4 @@
|
||||||
name: Test and Deploy to k3s
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
@ -12,32 +12,22 @@ env:
|
||||||
DOMAIN: "academia.ingeniumcodex.com"
|
DOMAIN: "academia.ingeniumcodex.com"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# Job 1: Build y Test (obligatorio)
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Restore & Build
|
||||||
uses: actions/setup-dotnet@v4
|
run: dotnet build --configuration Release --verbosity minimal
|
||||||
with:
|
|
||||||
dotnet-version: "10.0.x"
|
|
||||||
|
|
||||||
- name: Restore dependencies
|
- name: Run Tests
|
||||||
run: dotnet restore
|
run: |
|
||||||
|
dotnet test tests/Domain.Tests --no-build -c Release --verbosity minimal
|
||||||
- name: Build
|
dotnet test tests/Application.Tests --no-build -c Release --verbosity minimal
|
||||||
run: dotnet build --no-restore
|
|
||||||
|
|
||||||
- name: Run Unit Tests
|
|
||||||
run: dotnet test tests/Domain.Tests --no-build --verbosity normal
|
|
||||||
|
|
||||||
- name: Run Application Tests
|
|
||||||
run: dotnet test tests/Application.Tests --no-build --verbosity normal
|
|
||||||
|
|
||||||
- name: Run Integration Tests
|
|
||||||
run: dotnet test tests/Integration.Tests --no-build --verbosity normal
|
|
||||||
|
|
||||||
|
# Job 2: Deploy (siempre después de tests)
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
|
|
@ -52,23 +42,17 @@ jobs:
|
||||||
chmod 600 ~/.ssh/id_rsa
|
chmod 600 ~/.ssh/id_rsa
|
||||||
ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
- name: Sync code to k3s
|
- name: Deploy to K3s
|
||||||
run: |
|
|
||||||
rsync -az --delete \
|
|
||||||
--exclude '.git' \
|
|
||||||
--exclude 'node_modules' \
|
|
||||||
--exclude 'dist' \
|
|
||||||
--exclude 'bin' \
|
|
||||||
--exclude 'obj' \
|
|
||||||
--exclude '.angular' \
|
|
||||||
./ ${{ env.K3S_USER }}@${{ env.K3S_HOST }}:~/academia/
|
|
||||||
|
|
||||||
- name: Build images (parallel)
|
|
||||||
run: |
|
run: |
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||||
|
set -e
|
||||||
cd ~/academia
|
cd ~/academia
|
||||||
export DOCKER_BUILDKIT=1
|
|
||||||
|
|
||||||
|
# Pull latest changes
|
||||||
|
git fetch origin main
|
||||||
|
git reset --hard origin/main
|
||||||
|
|
||||||
|
# Build images
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S docker build \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S docker build \
|
||||||
-f deploy/docker/Dockerfile.api -t student-api:latest . &
|
-f deploy/docker/Dockerfile.api -t student-api:latest . &
|
||||||
PID_API=$!
|
PID_API=$!
|
||||||
|
|
@ -79,55 +63,80 @@ jobs:
|
||||||
|
|
||||||
wait $PID_API || exit 1
|
wait $PID_API || exit 1
|
||||||
wait $PID_FE || exit 1
|
wait $PID_FE || exit 1
|
||||||
ENDSSH
|
|
||||||
|
|
||||||
- name: Import to k3s
|
# Import to k3s
|
||||||
run: |
|
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
||||||
'docker save student-api:latest | k3s ctr images import -'
|
'docker save student-api:latest | k3s ctr images import -'
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S sh -c \
|
||||||
'docker save student-frontend:latest | k3s ctr images import -'
|
'docker save student-frontend:latest | k3s ctr images import -'
|
||||||
ENDSSH
|
|
||||||
|
|
||||||
- name: Setup namespace if needed
|
# Deploy
|
||||||
run: |
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl apply -k deploy/k3s/
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
|
||||||
cd ~/academia/deploy/k3s
|
|
||||||
if ! echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl get ns academia &>/dev/null; then
|
|
||||||
echo "Creating namespace and resources..."
|
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl apply -k .
|
|
||||||
fi
|
|
||||||
ENDSSH
|
|
||||||
|
|
||||||
- name: Deploy
|
|
||||||
run: |
|
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
|
||||||
cd ~/academia/deploy/k3s
|
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl apply -k .
|
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout restart \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout restart \
|
||||||
deployment/student-api deployment/student-frontend -n academia
|
deployment/student-api deployment/student-frontend -n academia
|
||||||
ENDSSH
|
|
||||||
|
|
||||||
- name: Wait rollout
|
# Wait for rollout
|
||||||
run: |
|
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
||||||
deployment/student-api -n academia --timeout=120s
|
deployment/student-api -n academia --timeout=180s
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout status \
|
||||||
deployment/student-frontend -n academia --timeout=60s
|
deployment/student-frontend -n academia --timeout=60s
|
||||||
ENDSSH
|
ENDSSH
|
||||||
|
|
||||||
- name: Health check
|
# Job 3: Smoke Tests en Producción
|
||||||
run: |
|
smoke-tests:
|
||||||
sleep 10
|
runs-on: ubuntu-latest
|
||||||
curl -sf https://${{ env.DOMAIN }}/health || exit 1
|
needs: deploy
|
||||||
curl -sf https://${{ env.DOMAIN }}/ || exit 1
|
steps:
|
||||||
|
- name: Wait for services
|
||||||
|
run: sleep 15
|
||||||
|
|
||||||
- name: Rollback on failure
|
- name: Health Check API
|
||||||
if: failure()
|
run: |
|
||||||
|
response=$(curl -sf https://${{ env.DOMAIN }}/health)
|
||||||
|
echo "Health response: $response"
|
||||||
|
echo "$response" | grep -q '"status":"Healthy"' || exit 1
|
||||||
|
|
||||||
|
- name: Health Check Frontend
|
||||||
|
run: |
|
||||||
|
curl -sf https://${{ env.DOMAIN }}/ | grep -q 'Sistema de Estudiantes' || exit 1
|
||||||
|
|
||||||
|
- name: GraphQL Endpoint Check
|
||||||
|
run: |
|
||||||
|
response=$(curl -sf -X POST https://${{ env.DOMAIN }}/graphql \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query":"{ __typename }"}')
|
||||||
|
echo "GraphQL response: $response"
|
||||||
|
echo "$response" | grep -q '"data"' || exit 1
|
||||||
|
|
||||||
|
- name: Database Connectivity Check
|
||||||
|
run: |
|
||||||
|
response=$(curl -sf https://${{ env.DOMAIN }}/health)
|
||||||
|
echo "$response" | grep -q '"name":"database","status":"Healthy"' || exit 1
|
||||||
|
|
||||||
|
- name: Subjects Query Test
|
||||||
|
run: |
|
||||||
|
response=$(curl -sf -X POST https://${{ env.DOMAIN }}/graphql \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query":"{ subjects { id name credits } }"}')
|
||||||
|
echo "Subjects response: $response"
|
||||||
|
echo "$response" | grep -q '"subjects"' || exit 1
|
||||||
|
|
||||||
|
# Job 4: Rollback automático si smoke tests fallan
|
||||||
|
rollback:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: smoke-tests
|
||||||
|
if: failure()
|
||||||
|
steps:
|
||||||
|
- name: Setup SSH
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.K3S_SSH_KEY }}" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
ssh-keyscan -H ${{ env.K3S_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
|
- name: Rollback Deployments
|
||||||
run: |
|
run: |
|
||||||
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
ssh ${{ env.K3S_USER }}@${{ env.K3S_HOST }} << 'ENDSSH'
|
||||||
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout undo \
|
echo "${{ secrets.K3S_SUDO_PASS }}" | sudo -S kubectl rollout undo \
|
||||||
deployment/student-api deployment/student-frontend -n academia 2>/dev/null || true
|
deployment/student-api deployment/student-frontend -n academia
|
||||||
ENDSSH
|
ENDSSH
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue