Clase 08 — YAML en CI/CD (GitHub Actions & GitLab CI)
GitHub Actions
Estructura básica
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
nombre_job:
runs-on: ubuntu-latest
steps:
- name: Paso 1
run: echo "Hola"
Eventos (triggers)
on:
push:
branches:
- main
- 'release/**'
paths:
- 'src/**'
- '!src/**/*.md' # Ignorar cambios en .md
tags:
- 'v*'
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
schedule:
- cron: '0 6 * * 1-5' # Lun-Vie a las 6AM UTC
workflow_dispatch:
inputs:
environment:
description: 'Ambiente de deploy'
required: true
default: 'staging'
type: choice
options:
- staging
- production
Ejemplo completo: CI/CD para API Python
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
PYTHON_VERSION: "3.11"
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ─── LINT ───
lint:
name: Lint & Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Verificar formato
run: |
pip install flake8 black isort
black --check .
isort --check-only .
flake8 .
# ─── TEST ───
test:
name: Tests
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: test_db
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Ejecutar tests
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
pytest tests/ --cov=app --cov-report=xml -v
# ─── BUILD ───
build:
name: Build & Push Docker
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login al registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build y Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# ─── DEPLOY ───
deploy-staging:
name: Deploy Staging
runs-on: ubuntu-latest
needs: build
environment:
name: staging
url: https://staging.mi-app.com
steps:
- uses: actions/checkout@v4
- name: Deploy a staging
run: echo "Deploying to staging..."
deploy-production:
name: Deploy Production
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: https://mi-app.com
steps:
- uses: actions/checkout@v4
- name: Deploy a producción
run: echo "Deploying to production..."
Matrix Strategy
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]
exclude:
- os: macos-latest
python-version: "3.10"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: |
pip install -r requirements.txt
pytest tests/
GitLab CI/CD
Estructura básica
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
APP_NAME: mi-app
test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest tests/
Ejemplo completo: GitLab CI/CD
stages:
- lint
- test
- build
- deploy
variables:
PYTHON_VERSION: "3.11"
REGISTRY: $CI_REGISTRY
IMAGE: $CI_REGISTRY_IMAGE
# ─── Templates reutilizables ───
.python_template: &python_template
image: python:${PYTHON_VERSION}
before_script:
- pip install --upgrade pip
- pip install -r requirements.txt
.docker_template: &docker_template
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
.deploy_template: &deploy_template
image: bitnami/kubectl:latest
before_script:
- kubectl config set-cluster k8s --server=$K8S_SERVER
- kubectl config set-credentials deployer --token=$K8S_TOKEN
- kubectl config use-context default
# ─── LINT ───
lint:
<<: *python_template
stage: lint
script:
- pip install flake8 black
- black --check .
- flake8 .
# ─── TEST ───
test:
<<: *python_template
stage: test
services:
- postgres:16-alpine
variables:
POSTGRES_DB: test_db
POSTGRES_USER: test
POSTGRES_PASSWORD: test
DATABASE_URL: "postgresql://test:test@postgres:5432/test_db"
script:
- pip install pytest pytest-cov
- pytest tests/ --cov=app --cov-report=xml -v
# ─── BUILD ───
build:
<<: *docker_template
stage: build
script:
- docker build -t $IMAGE:$CI_COMMIT_SHA -t $IMAGE:latest .
- docker push $IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE:latest
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
# ─── DEPLOY ───
deploy_staging:
<<: *deploy_template
stage: deploy
script:
- kubectl set image deployment/$CI_PROJECT_NAME app=$IMAGE:$CI_COMMIT_SHA
- kubectl rollout status deployment/$CI_PROJECT_NAME --timeout=180s
environment:
name: staging
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
deploy_production:
<<: *deploy_template
stage: deploy
script:
- kubectl set image deployment/$CI_PROJECT_NAME app=$IMAGE:$CI_COMMIT_SHA
- kubectl rollout status deployment/$CI_PROJECT_NAME --timeout=300s
environment:
name: production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
Comparación rápida
| Concepto | GitHub Actions | GitLab CI |
|---|---|---|
| Archivo | .github/workflows/*.yml | .gitlab-ci.yml |
| Ejecución | jobs | stages + jobs |
| Runner | runs-on | image / tags |
| Variables | env / secrets | variables |
| Condicional | if | rules / only |
| Caché | actions/cache | cache |
| Artefactos | actions/upload-artifact | artifacts |
| Manual | workflow_dispatch | when: manual |
| Reutilizar | Reusable workflows | include / anclas |
Ejercicios
- Creá un GitHub Actions workflow para una app Node.js con: lint, test y build de Docker
- Creá un
.gitlab-ci.ymlcon stages de test, build y deploy usando anclas para evitar duplicación - Agregá una matrix strategy para testear en múltiples versiones de Node.js