ci: optimize pipeline with .NET image, smoke tests, and auto-rollback
CI/CD Pipeline / test (push) Failing after 45s Details
CI/CD Pipeline / deploy (push) Has been skipped Details
CI/CD Pipeline / smoke-tests (push) Has been skipped Details
CI/CD Pipeline / rollback (push) Has been skipped Details

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:
Andrés Eduardo García Márquez 2026-01-09 07:35:58 -05:00
parent 1913e1d6b1
commit d1bb921b4b
1 changed files with 74 additions and 65 deletions

View File

@ -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