Semantic Versioning en CD/CI: La Ciencia Entre Versiones
Índice de contenidos
📐 Teoría: El Contrato Social del Versionado
El Semantic Versioning (SemVer) no es solo una convención de nombrado (X.Y.Z); es un contrato social entre el desarrollador y el consumidor del software (ya sea otro desarrollador o el usuario final).
En el formato MAJOR.MINOR.PATCH:
- MAJOR: Cambios incompatibles (Breaking Changes). El contrato se rompe.
- MINOR: Funcionalidad nueva compatible hacia atrás. El contrato se expande.
- PATCH: Corrección de bugs compatible hacia atrás. El contrato se mantiene.
El Reto en CI/CD (Continuous Delivery)
En un entorno de CD, los humanos no deberían decidir versiones. Si un humano decide “esta es la versión 2.0”, introduce subjetividad y error. La versión debe ser una función determinista del historial de cambios.
Versión(t) = Versión(t-1) + Impacto(Commit_t)
🔄 El Bucle de Retroalimentación de Versiones
Un pipeline de CI/CD moderno para Android debe seguir este bucle estrictamente:
- Code Change: Desarrollador hace commit siguiendo Conventional Commits.
- Analysis: CI analiza el commit. ¿Es
feat? ¿Esfix? ¿EsBREAKING CHANGE? - Calculation: CI calcula la nueva versión basándose en la última etiqueta (tag) y el impacto del cambio.
- Release: CI genera el artefacto (APK/AAB) con esa versión.
- Tagging: CI etiqueta el commit con la nueva versión, cerrando el ciclo.
🛠️ Implementación Práctica en GitHub Actions
Vamos a construir un pipeline que implemente esta teoría usando la herramienta semantic-release o equivalentes.
Paso 1: Análisis de Commits (The Parser)
Necesitamos una herramienta que entienda Conventional Commits. Usaremos PaulHatch/semantic-version.
- name: Calculate Semantic Version
id: semver
uses: PaulHatch/semantic-version@v5.3.0
with:
# Define la raíz del código fuente para ignorar cambios en docs/readme
change_path: "app/src"
# Mapeo de tipos de commit a incrementos de versión
major_pattern: "(MAJOR|BREAKING CHANGE)"
minor_pattern: "feat:"
# Formato de salida
version_format: "${major}.${minor}.${patch}"
Paso 2: Cálculo de VersionCode (Android Specific)
Como vimos en Automatización de Versionado, Android necesita un entero. Aquí aplicamos la teoría de conjuntos: el versionCode debe ser una proyección inyectiva del versionName.
- name: Compute Android Version Code
id: compute_code
run: |
# Extraer componentes semánticos
MAJOR=$(echo ${{ steps.semver.outputs.version }} | cut -d. -f1)
MINOR=$(echo ${{ steps.semver.outputs.version }} | cut -d. -f2)
PATCH=$(echo ${{ steps.semver.outputs.version }} | cut -d. -f3)
# Algoritmo de Posicionamiento Decimal
# Permite: 21 Major, 99 Minor, 99 Patch -> 219999
CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH))
echo "android_code=$CODE" >> $GITHUB_OUTPUT
Paso 3: Inmutabilidad del Artefacto
Una regla de oro en DevOps es: Construye una vez, despliega en todas partes. El APK que se prueba en QA debe ser bit a bit idéntico al que va a Producción.
Esto significa que la versión se inyecta en el momento del build, y ese artefacto “viaja” por los entornos. No reconstruimos para producción.
- name: Build Once
run: ./gradlew bundleRelease -PversionName=${{ steps.semver.outputs.version }} -PversionCode=${{ steps.compute_code.outputs.android_code }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: app-release
path: app/build/outputs/bundle/release/*.aab
⚠️ Manejo de Pre-Releases (Alpha/Beta)
El versionado semántico también cubre ciclos de vida inestables.
1.0.0-alpha.1: Primera alpha.1.0.0-beta.1: Feature freeze.1.0.0-rc.1: Release Candidate.
Estrategia de Ramas
feature/*-> Genera versiones alpha (1.1.0-alpha.x).develop-> Genera versiones beta (1.1.0-beta.x).main-> Genera versiones finales (1.1.0).
Configuración del Action para manejar prereleases:
- name: Determine Prerelease Label
id: prerelease
run: |
if [[ $GITHUB_REF == *"feature/"* ]]; then
echo "label=alpha" >> $GITHUB_OUTPUT
elif [[ $GITHUB_REF == *"develop"* ]]; then
echo "label=beta" >> $GITHUB_OUTPUT
else
echo "label=" >> $GITHUB_OUTPUT
fi
📉 Errores Comunes y Cómo Evitarlos
1. El “Tag Manual”
Desarrollador hace git tag v1.0.0 manualmente.
Problema: Rompe la fuente única de verdad. Si el CI intenta generar v1.0.0 después, fallará.
Solución: Bloquear la creación de tags manuales en GitHub para todos excepto el bot de CI.
2. Commits “Sucios”
Mensajes como “wip”, “fix bug”, “changes”. Problema: El analizador semántico no sabe qué hacer (default a PATCH o ignora). Solución: Usar un commit-msg hook o un lint action que obligue al formato Conventional Commits.
- name: Lint Commit Messages
uses: wagoid/commitlint-github-action@v5
3. Explosión del VersionCode
Si usas timestamps o números de build de GitHub (GITHUB_RUN_NUMBER) directamente.
Problema: Si cambias de proveedor de CI (de GitHub a GitLab), el contador se reinicia y Google Play rechaza actualizaciones (Error: Version code 1 < 500).
Solución: Usar siempre el cálculo derivado de SemVer (Major*10000...). Es agnóstico a la plataforma de CI.
🎯 Conclusión
Implementar Semantic Versioning en tu CI/CD no es burocracia; es ingeniería de confiabilidad. Transformas el acto subjetivo y peligroso de “versionar” en un proceso matemático, determinista y completamente automatizado.
Cuando un manager pregunte “¿Qué hay en la versión 2.1.0?”, no necesitas buscar en emails. El sistema te dirá exactamente: “Contiene todas las feat desde la 2.0.0 y es compatible hacia atrás”. Eso es poder.
También te puede interesar
Automatización de Versionado con GitHub Actions en Android
Descubre cómo automatizar completamente el versionado de tu app Android con GitHub Actions: desde commits hasta Google Play Store, sin intervención manual.
GitHub Actions para Google Play Store: La Guía Completa
Aprende a configurar un pipeline de Continuous Deployment robusto que compile, firme y publique tu App Android automáticamente en Google Play Store.
GitHub Agentic Workflows: la automatización de código con agentes de IA
CI/CD ya automatiza builds y deploys, pero todavía dedicamos horas a triage, fallos de CI y documentación desalineada. GitHub Agentic Workflows lleva la automatización un paso más allá: agentes que operan dentro de límites definidos para encargarse de ese trabajo repetitivo con criterio.