Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.git
.github
.env
.venv
venv
__pycache__
*.pyc
.pytest_cache
.ruff_cache
.coverage
coverage.xml
htmlcov
*.sarif
data
ml/artefactos
tests
Docs
README.md
32 changes: 32 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: 2
updates:
# Dependencias Python (requirements.txt y requirements-dev.txt)
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 5
labels: [dependencies, python]
commit-message:
prefix: "chore(deps)"

# Imagen base del Dockerfile
- package-ecosystem: docker
directory: /
schedule:
interval: weekly
day: monday
labels: [dependencies, docker]
commit-message:
prefix: "chore(docker)"

# Versiones de las actions usadas en los workflows
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
labels: [dependencies, ci]
commit-message:
prefix: "chore(ci)"
35 changes: 34 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,40 @@ jobs:
run: ruff check ml tests scripts

- name: Tests (pytest + cobertura)
run: pytest tests/ -v --cov=ml --cov-report=xml --cov-report=term
run: pytest tests/ -v --cov=ml --cov-report=xml --cov-report=term --cov-fail-under=60

- name: Health check (config)
run: python -m ml.health --json --skip-s3 --skip-artifacts

docker:
name: Imagen Docker (build, smoke test y publicacion)
runs-on: ubuntu-latest
needs: lint-test
permissions:
contents: read
packages: write
env:
IMAGE: ghcr.io/sebithaz-dev/model-training-s3

steps:
- name: Descargar codigo
uses: actions/checkout@v4

- name: Construir imagen (tag = SHA del commit)
run: |
docker build \
--build-arg GIT_SHA=${{ github.sha }} \
-t $IMAGE:${{ github.sha }} .

- name: Smoke test del contenedor (health check de configuracion)
run: |
docker run --rm $IMAGE:${{ github.sha }} \
python -m ml.health --json --skip-s3 --skip-artifacts

- name: Publicar en GHCR (solo push a main)
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker tag $IMAGE:${{ github.sha }} $IMAGE:latest
docker push $IMAGE:${{ github.sha }}
docker push $IMAGE:latest
25 changes: 16 additions & 9 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,15 @@ jobs:
with:
python-version: '3.11'

# Snyk necesita las dependencias instaladas para resolver el arbol completo
- name: Instalar dependencias
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Instalar Snyk CLI
uses: snyk/actions/setup@v1

- name: Snyk Open Source (runtime)
continue-on-error: true
run: >
snyk test --file=requirements.txt
--severity-threshold=high
--sarif-file-output=snyk-sca.sarif
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

- name: Snyk Open Source (dev)
continue-on-error: true
run: >
Expand All @@ -60,6 +57,16 @@ jobs:
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

# Bloqueante: una vulnerabilidad high/critical en dependencias de
# runtime falla el workflow y, con la proteccion de main, bloquea el merge.
- name: Snyk Open Source (runtime)
run: >
snyk test --file=requirements.txt
--severity-threshold=high
--sarif-file-output=snyk-sca.sarif
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

- name: Subir SARIF a GitHub Security
if: always()
continue-on-error: true
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/train.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ on:
- cron: '0 2 * * 0'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: train
cancel-in-progress: false

jobs:
train:
runs-on: ubuntu-latest
Expand Down
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM python:3.11-slim

ARG GIT_SHA=local
LABEL org.opencontainers.image.source="https://github.com/Sebithaz-dev/Model-Training-S3" \
org.opencontainers.image.description="Job de entrenamiento del clasificador Citi Bike" \
org.opencontainers.image.revision="${GIT_SHA}"

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY ml ./ml
COPY config ./config
COPY scripts ./scripts

# Usuario sin privilegios: el job no necesita root
RUN useradd --create-home --uid 10001 trainer \
&& mkdir -p data ml/artefactos \
&& chown -R trainer:trainer /app
USER 10001

CMD ["python", "-m", "scripts.run_pipeline"]
121 changes: 0 additions & 121 deletions EP1_DOY0101_Rubrica.md

This file was deleted.

19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ Las métricas se calculan sobre el test set (80/20 split estratificado) y se gua
- **Triggers:** `pull_request` a `main` (gate de merge, obligatorio verde) y `push` a `main` (verificación post-merge del trunk).
- **Pasos:** checkout → setup Python 3.11 (cache de pip) → instalar `requirements-dev.txt` → `ruff check` → `pytest` con cobertura → health check de configuración.
- **Propósito:** garantizar que todo lo que llega a `main` pasa lint, tests y el health check básico. Bloquea el merge si falla.
- **Política de calidad:** cobertura mínima del 60 % sobre `ml/` (`--cov-fail-under=60`); bajo ese umbral el job falla.
- **Job `docker`** (`needs: lint-test`): construye la imagen con tag = SHA del commit, ejecuta un smoke test (`ml.health`) dentro del contenedor y, en push a `main`, la publica en GHCR como `ghcr.io/sebithaz-dev/model-training-s3:<sha>` y `:latest`.

> **Nota sobre IE3/IE4:** el enunciado pide disparar la CI en "push a `develop` y pull request a `main`". Como este repositorio usa **TBD puro** (sin rama `develop` — ver [justificación](#estrategia-de-branching-trunk-based-development-tbd)), el trigger `push a develop` se sustituye por `push a main`, que cumple el mismo rol en TBD: verificar el trunk tras cada integración.

Expand All @@ -246,7 +248,22 @@ Las métricas se calculan sobre el test set (80/20 split estratificado) y se gua
- **SonarCloud:** ejecuta `pytest --cov=ml --cov-report=xml` y publica cobertura, bugs, vulnerabilidades y code smells.
- **Secrets requeridos:** `SNYK_TOKEN`, `SONAR_TOKEN`.
- **Variables:** `SONAR_ORGANIZATION` (default `sebithaz-dev`), `SONAR_PROJECT_KEY` (default `Sebithaz-dev_Model-Training-S3`).
- **Bloqueo:** Snyk es informativo al inicio (`continue-on-error`); SonarCloud publica el análisis sin esperar el quality gate. Cuando el equipo lo decida, se puede activar `sonar.qualitygate.wait=true`.
- **Bloqueo:** el escaneo de dependencias de runtime (`requirements.txt`) es **bloqueante**: una vulnerabilidad `high` o `critical` falla el workflow. Snyk sobre dependencias de desarrollo y Snyk Code quedan como **alertas** en GitHub Security. SonarCloud espera el resultado del **Quality Gate** (`sonar.qualitygate.wait=true`) y falla si no se cumple. Ambos checks se exigen en la protección de `main`.
- **Dependabot:** `.github/dependabot.yml` abre PRs semanales para dependencias `pip`, la imagen base de Docker y las GitHub Actions.

### Contenedor del job de entrenamiento

El `Dockerfile` empaqueta el job (`python:3.11-slim`, usuario sin privilegios `10001`, sin tests ni datos en la imagen gracias a `.dockerignore`). Su punto de entrada `scripts/run_pipeline.py` descarga el dataset curado, entrena y ejecuta el health check:

- **Con credenciales AWS:** descarga con boto3, sube los artefactos a S3 y valida en modo `--strict`.
- **Sin credenciales:** descarga por la URL pública del bucket y deja los artefactos solo en el contenedor (modo demo).

```bash
docker build -t model-training-s3 .
docker run --rm --env-file .env model-training-s3
```

La orquestación junto al dashboard está en `docker-compose.yml` y `k8s/` del repo [Flask-Dashboard-S3](https://github.com/Sebithaz-dev/Flask-Dashboard-S3).

---

Expand Down
Loading
Loading