LABS · JULIÁN LÓPEZ JIMÉNEZ · RECURSO GRATUITO
Tres diagnósticos de CI y una práctica real descargable para investigar un archivo ausente, una regresión y un fallo silenciado.
Resuelve causa, evidencia, corrección mínima y prueba funcional antes de abrir la solución. Después realiza la práctica real.
Predice la causa antes de abrir las pistas.
No son resultados de tu equipo o de GitHub
Local: datos.json
Commit: Datos.json
Ubuntu: FileNotFoundError: datos.jsonSepara ejecución, evidencia y decisión de entrega.
El nombre versionado no coincide exactamente con la ruta leída.
Unificar el nombre exacto y usar una ruta derivada de __file__.
El nombre versionado no coincide exactamente con la ruta leída. Unificar el nombre exacto y usar una ruta derivada de __file__. Revisar git ls-files y repetir en checkout limpio, además del caso sensible a mayúsculas. Repite después la práctica real; las notas requieren revisión personal.
Predice la causa antes de abrir las pistas.
No son resultados de tu equipo o de GitHub
El precio de dos unidades a 125 céntimos sale como 125.
pytest: expected 250, got 125; 1 failed.Separa ejecución, evidencia y decisión de entrega.
La implementación ignora la cantidad.
Restaurar precio * cantidad, manteniendo el contrato del test.
La implementación ignora la cantidad. Restaurar precio * cantidad, manteniendo el contrato del test. Repetir el caso de dos unidades y los rechazos de cero y booleanos. Repite después la práctica real; las notas requieren revisión personal.
Predice la causa antes de abrir las pistas.
No son resultados de tu equipo o de GitHub
python -m pytest devuelve 1.
Paso: continue-on-error: true
Trabajo: success.Separa ejecución, evidencia y decisión de entrega.
La política permite continuar tras una prueba fallida.
Retirar continue-on-error del paso que decide la calidad.
La política permite continuar tras una prueba fallida. Retirar continue-on-error del paso que decide la calidad. Introducir el defecto de cantidad y confirmar que package y staging no se ejecutan. Repite después la práctica real; las notas requieren revisión personal.
Qué necesitas: Navegador con JavaScript para diagnosticar, sin cuenta ni tarjeta. Para la práctica gratuita: Python 3.12+ y terminal. Git/GitHub propios solo para el taller remoto; puede requerir cuota según tu plan.
Tu resultado: Distinguir un fallo útil de un falso verde y reproducir tres casos rojo/verde en tu equipo.
El runner solo conoce los archivos del commit. Investiga el nombre y la ruta antes de instalar paquetes. La práctica gratuita reproduce un fichero ausente en cualquier sistema; contrasta Datos.json frente a datos.json en Linux para observar literalmente la distinción de mayúsculas.
python practicar.py rutaEl nombre versionado no coincide exactamente con la ruta leída.
Unificar el nombre exacto y usar una ruta derivada de __file__.Revisar git ls-files y repetir en checkout limpio, además del caso sensible a mayúsculas.
Comprueba: Observa rechazo inicial y éxito al corregir; compara con la evidencia preparada. El guion usa una carpeta temporal y no modifica tu proyecto.
Una función que ignora cantidad puede pasar un ejemplo de una sola unidad. Comprueba dos y tres unidades para distinguirla. El proyecto gratuito es mínimo: función de céntimos y dos tests, sin API ni SQLite. Primero observa el rojo y después conserva el mismo contrato al reparar.
python practicar.py rojoLa implementación ignora la cantidad.
Restaurar precio * cantidad, manteniendo el contrato del test.Repetir el caso de dos unidades y los rechazos de cero y booleanos.
Comprueba: Observa rechazo inicial y éxito al corregir; compara con la evidencia preparada. El guion usa una carpeta temporal y no modifica tu proyecto.
El proceso de pruebas puede terminar con fallo y una política errónea permitir continuar. El guion muestra el código real, explica el falso verde y repara la implementación. No modifica tus workflows ni usa shell para ocultar la salida. En tu repositorio no añadas continue-on-error a las pruebas de calidad.
python practicar.py salidaLa política permite continuar tras una prueba fallida.
Retirar continue-on-error del paso que decide la calidad.Introducir el defecto de cantidad y confirmar que package y staging no se ejecutan.
Comprueba: Observa rechazo inicial y éxito al corregir; compara con la evidencia preparada. El guion usa una carpeta temporal y no modifica tu proyecto.
Crea este archivo con su nombre y ruta exactos dentro de una carpeta nueva. Crea .github/workflows para el YAML; no pegues YAML dentro de Python. El ZIP gratuito contiene la misma estructura.
def total(precio, cantidad):
return precio * cantidad
Comprueba: Conserva el nombre indicado y repite los tres comandos de práctica desde la raíz del proyecto.
Crea este archivo con su nombre y ruta exactos dentro de una carpeta nueva. Crea .github/workflows para el YAML; no pegues YAML dentro de Python. El ZIP gratuito contiene la misma estructura.
"""Casos rojos/verdes aislados; no modifica app.py ni ejecuta shell."""
from pathlib import Path
import argparse
import json
import subprocess
import sys
import tempfile
HERE = Path(__file__).resolve().parent
def require(ok, message):
if not ok:
raise ValueError(message)
def exercise(case):
with tempfile.TemporaryDirectory(prefix='labs-pipeline-') as directory:
folder = Path(directory)
if case in ('rojo', 'salida'):
original = (HERE / 'app.py').read_text(encoding='utf-8')
broken = original.replace('return precio * cantidad', 'return precio')
require(broken != original, 'No se reconoce el ejemplo original de total; usa una copia nueva del laboratorio')
(folder / 'app.py').write_text(broken, encoding='utf-8')
program = 'import app\nif app.total(125,2)!=250: raise AssertionError("expected 250 for two units")'
red = subprocess.run([sys.executable, '-B', '-c', program], cwd=folder, capture_output=True, text=True, timeout=15)
require(red.returncode != 0 and 'AssertionError' in red.stderr, 'No se reprodujo el fallo esperado; revisa la salida y el intérprete')
# Simula la política, no cambia el código de salida del proceso hijo.
print('Fallo real:', red.returncode, red.stderr.strip().splitlines()[-1])
if case == 'salida':
print('Política errónea: ignorar ese fallo permitiría continuar. No lo hagas en CI.')
(folder / 'app.py').write_text(original, encoding='utf-8')
# Evitar caché de timestamp/longitud entre rojo y verde.
green = subprocess.run([sys.executable, '-B', '-c', program], cwd=folder, capture_output=True, text=True, timeout=15)
require(green.returncode == 0, 'La reparación no supera la comprobación: ' + green.stderr)
return {'case':case, 'red':red.returncode, 'green':green.returncode, 'isolated':True}
if case == 'ruta':
(folder / 'Datos.json').write_text('{"unidades":2}', encoding='utf-8')
# Nombre inequívocamente ausente incluso en Windows.
program = 'from pathlib import Path; print(Path("no-versionado.json").read_text())'
red = subprocess.run([sys.executable,'-c',program],cwd=folder,capture_output=True,text=True,timeout=15)
require(red.returncode != 0 and 'FileNotFoundError' in red.stderr, 'No se reprodujo el archivo ausente esperado')
green = subprocess.run([sys.executable,'-c',program.replace('no-versionado.json','Datos.json')],cwd=folder,capture_output=True,text=True,timeout=15)
require(green.returncode == 0 and 'unidades' in green.stdout, 'No se pudo leer el archivo reparado')
return {'case':case,'red':red.returncode,'green':0,'note':'Ausente en ambos SO; la distinción Datos/datos debe comprobarse en Linux.'}
if case == 'entrada':
data = '"; NO_EJECUTAR $(texto)\nñ'
result = subprocess.run([sys.executable,'-X','utf8','-c','import sys; print(sys.argv[1],end="")',data],cwd=folder,capture_output=True,text=True,encoding='utf-8',timeout=15)
require(result.returncode == 0 and result.stdout == data, 'La entrada no se conservó como texto literal')
return {'case':case,'literal':True,'shell':False}
raise ValueError('Caso desconocido')
if __name__ == '__main__':
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('case',choices=['rojo','ruta','salida','entrada'])
args=parser.parse_args()
try:
print(json.dumps(exercise(args.case),ensure_ascii=False))
except (OSError, ValueError, subprocess.TimeoutExpired) as error:
parser.exit(1, 'Práctica no completada: ' + str(error) + '\n')
Comprueba: Conserva el nombre indicado y repite los tres comandos de práctica desde la raíz del proyecto.
Crea este archivo con su nombre y ruta exactos dentro de una carpeta nueva. Crea .github/workflows para el YAML; no pegues YAML dentro de Python. El ZIP gratuito contiene la misma estructura.
import unittest
from app import total
class Precio(unittest.TestCase):
def test_dos(self):
self.assertEqual(total(125,2),250)
def test_tres(self):
self.assertEqual(total(99,3),297)
Comprueba: Conserva el nombre indicado y repite los tres comandos de práctica desde la raíz del proyecto.
Crea este archivo con su nombre y ruta exactos dentro de una carpeta nueva. Crea .github/workflows para el YAML; no pegues YAML dentro de Python. El ZIP gratuito contiene la misma estructura.
# En mi ordenador pasa, en el pipeline falla
Gratis, sin cuenta ni tarjeta para descargar y practicar. Python 3.12+.
En una carpeta nueva, ejecuta `python practicar.py ruta`, `python practicar.py rojo`, `python practicar.py salida` y `python -m unittest -v`.
Cada guion observa primero un rechazo real y después una reparación en una carpeta temporal. No ejecuta una orden remota ni modifica app.py original.
Los mensajes Fallo real y red: 1 son esperados: la práctica termina correctamente solo cuando también obtiene green: 0. Si ves Práctica no completada, lee la causa y usa una copia nueva antes de modificar código. Las comprobaciones no se desactivan con Python optimizado.
En Windows comprueba python --version; si utilizas el lanzador py, sustituye python por py -3.12. La práctica gratuita usa la biblioteca estándar, sin instalar pytest ni dependencias. Los comandos se ejecutan en una terminal, cada uno en su propia línea; no pegues YAML en Python.
El caso ruta usa un archivo ausente en cualquier SO; la distinción de mayúsculas del diagnóstico necesita Linux para contrastarse literalmente.
## Tu primera ejecución real en GitHub
En un repositorio docente propio y sin datos privados, copia app.py, test_total.py y el archivo oculto .github/workflows/gratis.yml a la raíz. Revisa sus permisos y la cuota/visibilidad de tu cuenta antes de hacer push. No subas toda tu carpeta personal ni el repositorio privado de Labs. Cambia `return precio * cantidad` por `return precio`, confirma y abre una PR: el test debe fallar. Restaura la multiplicación en otro commit y contrasta el verde con la misma prueba. No quites tests ni fuerces salida cero.
Guarda el enlace del run, SHA, evento, comando, número de casos y causa corregida. Esto no despliega nada. El playground es diagnóstico preparado, no un runner de GitHub.
Comprueba: Conserva el nombre indicado y repite los tres comandos de práctica desde la raíz del proyecto.
Crea este archivo con su nombre y ruta exactos dentro de una carpeta nueva. Crea .github/workflows para el YAML; no pegues YAML dentro de Python. El ZIP gratuito contiene la misma estructura.
name: Primer rojo y verde
on: [push, pull_request]
permissions:
contents: read
jobs:
pruebas:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
with:
python-version: "3.12"
- run: python -m unittest -v
Comprueba: Conserva el nombre indicado y repite los tres comandos de práctica desde la raíz del proyecto.
Predice qué paso rechazaría el cambio antes de ejecutar cada caso. Repite con un precio y una cantidad distintos.
python practicar.py ruta
python practicar.py rojo
python practicar.py salida
python -m unittest -vEn la práctica real, el rojo es intencional: comprueba también green: 0; Práctica no completada indica que debes revisar la causa. El playground muestra evidencias preparadas; no ejecuta YAML ni GitHub. La práctica real usa subprocesos Python y archivos temporales. Descargar no exige cuenta; guardar es manual, no avanza ni concede XP.
Repite con datos distintos antes de marcarlo como practicado. Esta es una autoevaluación, no una certificación.
El curso añade matriz, artefactos, confianza, API con SQLite, humo y retorno entre releases. La cuenta no es necesaria para completar este recurso.
Ver el curso Tu primer pipeline: de los tests al despliegue y su temarioFuente: Recurso original de Labs. Puedes guardar este HTML, abrirlo sin conexión o imprimirlo como PDF desde tu navegador. No guarda tus respuestas ni el progreso de tu cuenta.