El proyecto contoso-reservas ya existe, pero está vacío. El código de la web de reservas sigue donde estaba: en el portátil de Diego Salas, en una carpeta con control de versiones local que nadie más ve, y en una sucesión de ZIP en la bandeja de entrada de Marta. Esta lección elimina esa situación de raíz.

Azure Repos es el servicio de repositorios Git privados de Azure DevOps. Alojar código en un repositorio remoto es la parte fácil —eso lo hace cualquiera—; lo que de verdad transforma el trabajo del equipo es lo que se construye encima: una estrategia de ramificación que todos entienden, solicitudes de incorporación de cambios que convierten la revisión en conversación registrada, y directivas de rama que hacen que las reglas del equipo dejen de depender de que alguien se acuerde. Al terminar, la rama main de Contoso será, por construcción, siempre desplegable.

Contenido

  1. Git en lo imprescindible: confirmación, rama, fusión y rebase
  2. Crear el repositorio contoso-reservas y clonarlo
  3. Autenticación: Credential Manager, tokens y SSH
  4. Estrategias de ramificación y la que elige Contoso
  5. Solicitudes de incorporación de cambios
  6. Directivas de rama sobre main
  7. Revisores automáticos por ruta: el CODEOWNERS de Azure Repos
  8. Etiquetas y versionado semántico
  9. .gitignore y el error de subir un secreto
  10. Migrar el repositorio local conservando el historial
  11. Git LFS para los recursos gráficos pesados
  12. Comparación con GitHub Repos
  13. Errores Comunes y Consejos
  14. Ejercicios
  15. Conclusión

  1. Git en lo imprescindible: confirmación, rama, fusión y rebase

Esta no es una lección de Git, pero hay cuatro conceptos sin los cuales el resto no se entiende. Una confirmación (commit) es una instantánea completa del proyecto, identificada por un resumen criptográfico y que apunta a la confirmación anterior: la historia de un repositorio es una cadena de instantáneas, no una lista de diferencias. Una rama es solo un puntero móvil a una confirmación —crear una es escribir un fichero de 41 bytes—, y por eso el flujo de trabajo moderno crea y destruye ramas constantemente.

Fusión (merge) y rebase son dos formas de incorporar el trabajo de una rama a otra, y la diferencia importa:

Fusión Rebase
Qué hace Crea una confirmación nueva con dos padres Reescribe las confirmaciones sobre otra base
Historia resultante Ramificada, refleja lo que pasó de verdad Lineal, más fácil de leer
Riesgo Ninguno Nunca sobre ramas ya compartidas
Uso típico en Contoso Nunca a mano: lo hace la solicitud de cambios Actualizar tu rama con main antes de pedir revisión
git switch -c feature/1842-mapa-cabina   # Crear rama desde main y situarse en ella
git commit -am "Anade el mapa de cabina con seleccion de asiento. AB#1842"
git fetch origin && git rebase origin/main   # Reescribir mis confirmaciones sobre la punta de main
git add . && git rebase --continue           # Tras resolver un conflicto: marcarlo y continuar
git push -u origin feature/1842-mapa-cabina  # Publicar la rama en Azure Repos

La regla de oro del rebase: solo sobre tu rama personal y antes de que otros trabajen sobre ella. Rebasar main reescribe la historia de todo el mundo y es la forma más rápida de estropearle el día al equipo.

  1. Crear el repositorio contoso-reservas y clonarlo

Contoso crea cuatro repositorios en el proyecto, con el criterio «un artefacto desplegable, un repositorio»:

Repositorio Contenido Se despliega en
contoso-reservas La web pública app-contoso-reservas-pro/dev
contoso-api-disponibilidad La API de disponibilidad app-contoso-api-disponibilidad-pro/dev
contoso-modelos Biblioteca compartida de modelos de reserva Paquete en el feed (05-05)
contoso-infra Plantillas de Bicep de toda la plataforma Recursos de Azure (05-06)
# Crear los cuatro repositorios con la extension azure-devops de la CLI
for r in contoso-reservas contoso-api-disponibilidad contoso-modelos contoso-infra; do
  az repos create --name "$r"
done
az repos list --query "[].{nombre:name, url:webUrl}" -o table

git clone https://[email protected]/contoso-airlines/contoso-reservas/_git/contoso-reservas

  1. Autenticación: Credential Manager, tokens y SSH

Hay tres formas de que tu Git local se autentique contra Azure Repos, y no son equivalentes:

Método Cómo funciona Recomendación
Git Credential Manager Abre el navegador, autentica con Entra ID (y MFA) y guarda un token de corta vida en el almacén de credenciales del sistema La opción por defecto en portátiles de personas
Token de acceso personal (PAT) Cadena generada a mano, con ámbitos y caducidad, que actúa como contraseña Solo para automatismos que no admiten otra cosa
Claves SSH Par de claves; la pública se registra en tu perfil Buena para equipos Linux o quien prefiere no depender del navegador

Git Credential Manager viene incluido con las instalaciones recientes de Git y respeta el acceso condicional y el MFA de 04-01: la sesión del portátil de Diego caduca cuando lo dice la política de Entra ID, no cuando a alguien se le ocurra. Los tokens de acceso personal son la fuente más común de incidentes de seguridad en Azure DevOps. Si tienes que usar uno: concédele solo los ámbitos necesarios (Code (read) para una herramienta que solo lee) y nunca Full access; ponle la caducidad más corta viable; guárdalo en kv-contoso-pro y no en un fichero de configuración; y ten presente que un PAT hereda todos los permisos de quien lo creó, así que el de un administrador es una llave maestra.

ssh-keygen -t ed25519 -C "[email protected]"   # Generar el par de claves
ssh-add ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub    # La clave publica se pega en el perfil de Azure DevOps
# A partir de ahi se clona por SSH, sin contrasenas ni tokens
git clone [email protected]:v3/contoso-airlines/contoso-reservas/contoso-reservas

Para los pipelines, ninguna de estas tres: usarán la federación de identidad de carga de trabajo de las conexiones de servicio de 05-01. La coherencia es la misma que en el módulo 4: si existe una forma de autenticarse sin secreto, es la que se usa.

  1. Estrategias de ramificación y la que elige Contoso

Estrategia Cómo funciona Ventajas Inconvenientes Encaja con
GitFlow Ramas develop, release/*, hotfix/*, feature/* y main Soporta varias versiones vivas a la vez Complejo, fusiones largas, ralentiza la entrega Software instalable con versiones mantenidas
GitHub Flow main + ramas de característica; se despliega al fusionar Simple, ideal para despliegue continuo Requiere pruebas automáticas sólidas Servicios web con una sola versión viva
Rama de característica con Trunk-Based main siempre desplegable + ramas de vida muy corta (1-2 días) Fusiones triviales, realimentación rápida, mejores métricas DORA Exige disciplina y marcas de característica Equipos que despliegan a diario
Trunk-Based puro Todos confirman directamente en main Máxima velocidad de integración Sin revisión previa; inviable con auditoría PCI DSS Equipos muy maduros con pruebas exhaustivas

Contoso elige rama de característica con Trunk-Based por tres razones concretas: solo hay una versión viva —la que está en Internet—, así que GitFlow resolvería un problema que Contoso no tiene cobrando su complejidad igualmente; la auditoría PCI DSS exige revisión de los cambios, lo que descarta el trunk-based puro sin solicitudes de cambio; y las ramas cortas atacan directamente su peor métrica DORA, el tiempo de entrega del cambio. Convención de nombres acordada: feature/<id>-<descripcion>, bugfix/<id>-<descripcion> y hotfix/<id>-<descripcion>, siempre con el identificador del elemento de trabajo por delante.

gitGraph
    commit id: "v2.4.0"
    branch feature/1842-mapa-cabina
    commit id: "API mapa"
    checkout main
    branch bugfix/1855-asientos
    commit id: "corrige recuento"
    checkout main
    merge bugfix/1855-asientos id: "PR 58"
    checkout feature/1842-mapa-cabina
    commit id: "rebase sobre main"
    checkout main
    merge feature/1842-mapa-cabina id: "PR 61" tag: "v2.5.0"

La regla que hace que esto funcione: una rama abierta más de dos días es una señal de alarma. Si el trabajo es mayor, se parte en incrementos fusionables en main sin activarse todavía, con las marcas de característica de 05-04.

  1. Solicitudes de incorporación de cambios

Una solicitud de incorporación de cambios (pull request) propone fusionar una rama en otra y abre una conversación sobre ella. Es el mecanismo central de calidad y, de paso, el registro documental que pedía el auditor en 05-01.

# Crear la solicitud desde la CLI, vinculando el elemento de trabajo
az repos pr create --repository contoso-reservas \
  --source-branch feature/1842-mapa-cabina --target-branch main \
  --title "Mapa de cabina con seleccion de asiento" \
  --description "Implementa AB#1842. Incluye pruebas del recuento de asientos." \
  --work-items 1842 --reviewers [email protected] \
  --delete-source-branch true --squash true

--delete-source-branch true borra la rama al completarse: sin ello, en seis meses habrá doscientas ramas muertas y nadie sabrá cuáles siguen vivas. --squash true se explica en el apartado siguiente.

Contoso define una plantilla de descripción en .azuredevops/pull_request_template.md, que Azure Repos precarga en cada solicitud nueva:

## Qué cambia y por qué
<!-- Contexto para quien revise: el problema, no solo la solucion -->
## Elemento de trabajo
AB#
## Cómo se ha probado
- [ ] Pruebas unitarias nuevas o actualizadas
- [ ] Probado en `app-contoso-reservas-dev`
## Riesgos y reversión
<!-- Que puede romperse y como se vuelve atras -->
## Lista de comprobación
- [ ] Sin secretos ni cadenas de conexión en el código
- [ ] Migraciones de base de datos compatibles hacia atrás

Cuatro prácticas separan una revisión útil de un trámite. Solicitudes pequeñas: por encima de 400 líneas la calidad de la revisión cae en picado y se aprueba por cansancio, mientras que una de 60 líneas recibe comentarios reales. Cambios sugeridos: Azure Repos permite proponer el texto exacto desde el comentario, y quien lo recibe lo aplica con un clic. Comentar el código, no a la persona: «esta consulta hace una llamada por asiento» en lugar de «has hecho un N+1». Y sobre todo, la conversación es documentación: cuando Marta pregunta «¿por qué 90 segundos de caché y no 300?» y Diego responde con el dato del proveedor de tarifas, esa respuesta queda asociada al cambio para siempre. Es la mejor documentación de arquitectura que existe, porque nadie tiene que acordarse de escribirla aparte.

  1. Directivas de rama sobre main

Aquí es donde el acuerdo verbal se convierte en regla aplicada. Una directiva de rama es una condición que debe cumplirse para poder completar una solicitud de incorporación de cambios. Contoso protege main con seis:

Directiva Configuración en Contoso Qué previene
Número mínimo de revisores 2, y no puede aprobar quien propone Que un cambio entre sin ojos ajenos
Comprobar elementos de trabajo vinculados Obligatorio Cambios sin justificación rastreable
Comprobar resolución de comentarios Todos resueltos Objeciones que se ignoran en silencio
Compilación con validación Pipeline de CI (05-03), obligatoria Fusionar código que no compila o no pasa pruebas
Limitar tipos de fusión Solo combinación en una confirmación (squash) Historia de main ilegible
Revisores automáticos Por ruta (apartado 7) Que nadie con el contexto necesario se entere
COMUN="--repository-id $REPO_ID --branch main --blocking true --enabled true"

# 1. Minimo de 2 revisores, sin autoaprobacion, reiniciando las
#    aprobaciones si tras aprobar llegan confirmaciones nuevas
az repos policy approver-count create $COMUN \
  --minimum-approver-count 2 --creator-vote-counts false \
  --reset-on-source-push true --allow-downvotes false

# 2. Elemento de trabajo vinculado obligatorio (trazabilidad)
az repos policy work-item-linking create $COMUN

# 3. Todos los comentarios resueltos antes de completar
az repos policy comment-required create $COMUN

# 4. Compilacion con validacion: el pipeline de CI debe pasar,
#    y su resultado caduca a las 12 horas (720 minutos)
az repos policy build create $COMUN \
  --build-definition-id $ID_PIPELINE_CI \
  --display-name "CI de Contoso Reservas" \
  --valid-duration 720 --queue-on-source-update-only true

# 5. Solo se admite combinacion en una confirmacion (squash)
az repos policy merge-strategy create $COMUN --allow-squash true \
  --allow-no-fast-forward false --allow-rebase false --allow-rebase-merge false

Tres detalles que la gente pasa por alto. --reset-on-source-push true: si tras aprobar llegan confirmaciones nuevas, las aprobaciones se anulan —sin esto se puede aprobar un cambio de dos líneas y colar después quinientas—. --valid-duration 720: la compilación caduca a las 12 horas, lo que impide fusionar validado contra un main que ya era otra cosa. Y la combinación en una confirmación, que hace que cada solicitud aparezca en main como una única confirmación limpia: la historia pasa a ser la lista de cambios funcionales entregados, justo lo que quiere leer quien investiga una incidencia.

Las directivas se aplican también a los administradores, y esa es su gracia. Existe la opción de conceder «omitir directivas», y la respuesta correcta casi siempre es no concederla: si hace falta saltarse el proceso por una urgencia, es que el proceso está mal calibrado. Para las urgencias reales, Contoso mantiene ramas hotfix/* con su propia directiva de un solo revisor, pasando por el mismo pipeline.

  1. Revisores automáticos por ruta: el CODEOWNERS de Azure Repos

En GitHub, un fichero CODEOWNERS asigna propietarios por carpeta. Azure Repos hace lo mismo con la directiva de revisores automáticamente incluidos, configurada por ruta —no en un fichero del repositorio, sino en la propia directiva—:

# Infraestructura y pipelines: revision obligatoria de Contoso-Infraestructura
az repos policy required-reviewer create $COMUN \
  --message "Cambios de infraestructura: revision de Contoso-Infraestructura" \
  --required-reviewer-ids "Contoso-Infraestructura" \
  --path-filter "/infra/*;/*.bicep;/azure-pipelines*.yml"

# Igual para el esquema de datos, con Contoso-DBA-Reservas y el filtro
# --path-filter "/src/Contoso.Reservas.Datos/Migraciones/*"

Los grupos son los mismos del módulo 4, con lo que la gestión de personas sigue estando en un único sitio. El efecto práctico: Diego ya no puede cambiar el esquema de db-reservas sin que un DBA se entere, y no porque se lo hayan prohibido, sino porque la fusión no se completa.

  1. Etiquetas y versionado semántico

Una etiqueta (tag) marca una confirmación concreta con un nombre permanente. Es lo que permite responder a «qué código exacto es la versión 2.5.0 que está en producción».

Contoso usa versionado semántico MAYOR.MENOR.PARCHE:

Parte Cuándo sube Ejemplo en Contoso
MAYOR Cambio incompatible La API de disponibilidad deja de aceptar el formato de fecha antiguo
MENOR Funcionalidad nueva compatible Se añade el mapa de cabina
PARCHE Corrección compatible Se arregla el recuento de asientos
# Etiqueta anotada (con autor, fecha y mensaje) sobre la confirmacion actual
git tag -a v2.5.0 -m "Mapa de cabina y correccion del recuento de asientos"
git push origin v2.5.0   # Las etiquetas NO viajan con un git push normal
git show v2.5.0 --stat   # Que confirmacion es exactamente la version desplegada

En 05-05 el número de versión de los paquetes se generará automáticamente desde el pipeline, y en 05-04 la etiqueta se creará sola al desplegar en producción, de modo que la correspondencia entre etiqueta y entorno no dependa de que nadie la escriba a mano.

  1. .gitignore y el error de subir un secreto

El fichero .gitignore indica qué no debe entrar nunca en el repositorio:

bin/
obj/
node_modules/
appsettings.Development.local.json   # Configuracion local con secretos
.env
*.pfx                                # Claves privadas: jamas al repositorio
*.pem
.vs/

Ahora el error grave. Diego confirma por accidente un appsettings.json con la cadena de conexión de sql-contoso-reservas-dev y la clave de la pasarela de pago. Lo descubre a los diez minutos y borra el fichero en la siguiente confirmación. El secreto sigue comprometido, porque la historia de Git es inmutable y cada confirmación conserva el contenido completo: cualquiera que haya clonado el repositorio —o cualquier copia de seguridad, o cualquier registro de compilación— tiene el valor. Borrarlo después no lo elimina, solo lo esconde de la vista superficial.

El procedimiento correcto, por orden estricto:

  1. Rotar el secreto inmediatamente. Es lo único que de verdad cierra el agujero: generar una clave nueva de la pasarela de pago, actualizarla en kv-contoso-pro y dejar la anterior sin valor. Todo lo demás es limpieza, no remedio.
  2. Comprobar el uso. Revisar los registros de acceso del recurso por si la credencial se usó desde fuera; para esto sirven las alertas de Microsoft Defender for Cloud de 04-05.
  3. Limpiar la historia con git filter-repo, coordinando un reclonado con todo el equipo. Es destructivo y disruptivo, y en repositorios grandes muchas veces no compensa.
  4. Prevenir la reincidencia: .gitignore correcto, revisión en la solicitud de cambios y detección automática.

Azure DevOps incluye detección de secretos que analiza las confirmaciones y avisa —y con GitHub Advanced Security for Azure DevOps, de pago, puede bloquear el envío antes de que el secreto entre—. Un gancho local o un paso de análisis en el pipeline detiene la mayoría de los casos restantes.

La solución de fondo ya la conoces del módulo 4: si no hay secretos, no se pueden filtrar. La aplicación de Contoso se autentica contra SQL y contra el almacenamiento con identidad administrada, y lo poco que sigue siendo secreto de verdad —pasarela-pago-clave, token-meteo— vive en kv-contoso-pro y se referencia con @Microsoft.KeyVault(...). El fichero de configuración del repositorio contiene nombres de recursos, nunca credenciales.

  1. Migrar el repositorio local conservando el historial

El repositorio de Diego tiene tres años de historia que no se pueden perder: es lo que permite saber por qué se escribió cada línea. La migración lo conserva todo:

cd ~/proyectos/contoso-reservas   # El repositorio local existente de Diego
git remote add azure https://[email protected]/contoso-airlines/contoso-reservas/_git/contoso-reservas
git push azure --all       # TODAS las ramas, con su historia intacta
git push azure --tags      # TODAS las etiquetas
git log azure/main --oneline | wc -l   # Comprobar que la historia llego completa

# Si el origen era otro servidor Git, la forma limpia es un clon espejo,
# que incluye todas las referencias y no solo las ramas locales:
git clone --mirror https://servidor-antiguo.contosoairlines.example/reservas.git
cd reservas.git && git push --mirror $URL_AZURE_REPOS

Antes de dar la migración por buena, dos cosas: fijar main como rama predeterminada y dejar el repositorio antiguo en solo lectura. Si sigue aceptando envíos, aparecerán dos historias divergentes y alguien perderá trabajo.

  1. Git LFS para los recursos gráficos pesados

La web incluye imágenes de destinos, mapas de cabina en alta resolución y vídeos de cabecera: binarios de decenas de megabytes. Git guarda cada versión completa de cada binario para siempre, así que un mapa de 30 MB modificado veinte veces son 600 MB permanentes que todo el mundo se descarga al clonar. Git LFS (Large File Storage) sustituye el binario en la historia por un puntero de texto y guarda el contenido aparte, descargando solo la versión que necesitas:

git lfs install                              # Una vez por repositorio y maquina
git lfs track "*.psd" "*.mp4" "recursos/imagenes/**/*.png"
git add .gitattributes                       # El seguimiento se guarda ahi
git commit -m "Configura Git LFS para recursos graficos pesados"

Dos advertencias: LFS hay que configurarlo antes de subir los binarios (hacerlo después exige reescribir la historia), y su almacenamiento consume la cuota facturable de Artifacts de la organización. Los recursos que la web sirve en producción viven en una cuenta de almacenamiento detrás de la CDN de 02-06; al repositorio solo va lo que forma parte del código fuente.

  1. Comparación con GitHub Repos

Azure Repos GitHub Repos
Protección de rama Directivas más granulares, con filtro por ruta y caducidad Reglas de rama, más simples
Propietarios de código Directiva de revisores por ruta Fichero CODEOWNERS versionado
Revisión de código Buena; cambios sugeridos Excelente; el estándar del sector
Seguridad del código Detección de secretos; Advanced Security de pago Advanced Security más maduro; Dependabot
Ecosistema y comunidad Interno Enorme

En la práctica, la experiencia de revisión de GitHub suele resultar más agradable, mientras que las directivas de Azure Repos son más granulares. Contoso se queda en Azure Repos por la integración con Boards y la trazabilidad que exige PCI DSS.

Errores Comunes y Consejos

  • Ramas de larga duración. Una rama de tres semanas garantiza una fusión dolorosa. Si el trabajo es grande, se parte en incrementos fusionables y se oculta con marcas de característica.
  • Rebasar una rama compartida. Reescribe la historia de todos. Rebase solo en tu rama y antes de que alguien más trabaje en ella.
  • Solicitudes de cambio enormes y aprobaciones sin leer. A partir de 400 líneas la revisión es un trámite; y la directiva de dos revisores no vale nada si la segunda firma es automática. Mejor cinco solicitudes de 80 líneas y una revisión honesta.
  • Creer que borrar la confirmación borra el secreto. No lo borra. Rotar siempre, y después limpiar.
  • Conceder «omitir directivas» a los administradores. Convierte la protección en una sugerencia. Si hay urgencias, configura una directiva propia más laxa sobre hotfix/*.
  • Subir binarios pesados sin LFS. El repositorio crece para siempre y el clonado se vuelve insoportable; y recuerda que LFS consume cuota facturable.
  • Consejo: escribe mensajes de confirmación con un título breve en imperativo, un cuerpo que explique por qué —el qué ya se ve en el diff— y la referencia AB#<id>.
  • Consejo: activa el borrado automático de la rama de origen al completar la solicitud y purga periódicamente las ramas sin actividad.

Ejercicios

Ejercicio 1: elegir y justificar la estrategia de ramificación

El equipo de Contoso Millas (centro-coste=CC-2077) mantiene un portal web y, además, una aplicación móvil cuya versión 3.2 sigue instalada en los teléfonos de los clientes y recibe correcciones mientras se desarrolla la 4.0.

  1. ¿Qué estrategia de ramificación le corresponde a cada uno de los dos productos, y por qué son distintas?
  2. Para el portal, define las directivas de rama que pondrías en main con su justificación.
  3. ¿Cómo gestionarías una corrección urgente en el portal un sábado, sin saltarte el proceso?

Ejercicio 2: un secreto en el repositorio

Diego confirma y envía a main un fichero appsettings.json que contiene la clave de la pasarela de pago y la cadena de conexión de sql-contoso-reservas-pro. Se da cuenta veinte minutos después. Ya hay dos compañeros que han hecho git pull.

  1. Enumera las acciones en orden, indicando cuál cierra realmente el riesgo.
  2. ¿Por qué no basta con una confirmación que borre el fichero, ni con revertirla?
  3. ¿Qué tres medidas técnicas evitarían que vuelva a pasar, y qué decisión de arquitectura del módulo 4 lo hace casi imposible?

Ejercicio 3: directivas y revisores por ruta

Contoso quiere que: (a) nadie fusione en main sin dos aprobaciones y sin elemento de trabajo; (b) todo cambio bajo /src/Contoso.Reservas.Datos/Migraciones/ lo revise Contoso-DBA-Reservas; (c) la historia de main quede lineal; (d) una compilación aprobada hace tres días no sirva para fusionar hoy.

  1. Indica qué directiva concreta resuelve cada punto y escribe el comando de la CLI para (b).
  2. ¿Qué ocurre si Marta, que es administradora del proyecto, intenta fusionar sin cumplirlas?

Soluciones

Solución 1:

  1. Para el portal, rama de característica con Trunk-Based: una sola versión viva (la desplegada), despliegue frecuente y ramas de uno o dos días. Para la aplicación móvil, un flujo tipo GitFlow o al menos ramas release/3.2 y release/4.0 de larga duración, porque hay dos versiones vivas simultáneamente: la instalada en los teléfonos, que sigue recibiendo parches, y la que se está desarrollando. La diferencia no es de gusto: en la web tú controlas qué versión ejecuta el cliente; en móvil, no.
  2. Mínimo de 2 revisores sin autoaprobación y con reinicio al enviar cambios; elemento de trabajo vinculado (trazabilidad); comentarios resueltos; compilación con validación con caducidad de 12 horas; solo combinación en una confirmación; y revisores automáticos por ruta para infraestructura y esquema.
  3. Rama hotfix/<id>-<descripcion> desde main, con una directiva propia sobre el patrón hotfix/* que exija un solo revisor pero mantenga la compilación con validación y el elemento de trabajo. Pasa por el mismo pipeline y el mismo despliegue; lo único que se relaja es el número de ojos, no la automatización. Nunca conceder «omitir directivas».

Solución 2:

  1. (a) Rotar la clave de la pasarela de pago y cambiar la credencial de la base de datos —esto y solo esto cierra el riesgo—; actualizar el valor nuevo en kv-contoso-pro. (b) Revisar los registros de acceso y las alertas de Defender for Cloud por si hubo uso indebido. (c) Añadir el fichero a .gitignore y eliminarlo del seguimiento. (d) Valorar reescribir la historia con git filter-repo y coordinar el reclonado, dado que el repositorio es privado y el equipo, pequeño. (e) Comunicarlo al responsable de seguridad: es un incidente, no un despiste.
  2. Porque la historia de Git es inmutable y cada confirmación guarda el contenido completo: el valor sigue siendo recuperable en cualquier clon, copia de seguridad o registro de compilación. Una reversión añade una confirmación nueva, no elimina la anterior. Mientras la credencial siga siendo válida, sigue comprometida.
  3. Detección de secretos activada en el repositorio; un gancho de precommit o un paso de análisis en el pipeline de CI; y .gitignore completo con revisión obligatoria de los ficheros de configuración en la solicitud de cambios. La decisión de arquitectura: autenticación con identidad administrada —sin cadena de conexión con contraseña que filtrar— y lo poco irreductible en Key Vault referenciado con @Microsoft.KeyVault(...). Si no hay secreto en el código, no hay secreto que subir.

Solución 3:

  1. (a) approver-count con --minimum-approver-count 2 --creator-vote-counts false, más work-item-linking. (b) required-reviewer con su filtro de ruta y el grupo de Entra ID: az repos policy required-reviewer create --repository-id $REPO_ID --branch main --blocking true --enabled true --required-reviewer-ids "Contoso-DBA-Reservas" --path-filter "/src/Contoso.Reservas.Datos/Migraciones/*". (c) merge-strategy permitiendo solo --allow-squash true. (d) policy build con --valid-duration 720, que invalida las compilaciones de más de 12 horas.
  2. No puede. Las directivas se aplican por igual a los administradores del proyecto salvo que se conceda explícitamente el permiso de omitir directivas, algo que Contoso no hace. Eso es justamente lo que convierte el acuerdo del equipo en una regla real y lo que satisface al auditor: no hay excepciones por cargo.

Conclusión

El código de Contoso Airlines ya no vive en un portátil ni viaja por correo. Has repasado lo imprescindible de Git —confirmación, rama, fusión y rebase, con la regla de no rebasar nunca ramas compartidas—, has creado los cuatro repositorios del proyecto y has migrado el repositorio local de Diego conservando sus tres años de historia, dejando el origen antiguo en solo lectura para que no aparezcan dos verdades. Sabes autenticarte con Git Credential Manager como opción por defecto, con SSH cuando conviene y con tokens de acceso personal solo cuando no hay alternativa, sabiendo que un PAT hereda todos los permisos de quien lo crea.

Has elegido conscientemente la estrategia de ramificación: rama de característica con Trunk-Based, con ramas de vida muy corta, porque Contoso tiene una sola versión viva, necesita revisión por PCI DSS y su métrica DORA más débil es el tiempo de entrega. Y has convertido el proceso de calidad en algo que no depende de la memoria de nadie: solicitudes de incorporación de cambios con plantilla, revisores y conversación registrada —que es, de paso, la mejor documentación de arquitectura que tendrá el equipo—, y directivas sobre main que exigen dos aprobaciones sin autoaprobación, elemento de trabajo vinculado, comentarios resueltos, compilación con validación vigente y combinación en una sola confirmación, con revisores automáticos por ruta que llevan cada cambio de esquema a Contoso-DBA-Reservas y cada plantilla a Contoso-Infraestructura. Sabes etiquetar con versionado semántico, mantener un .gitignore serio y, sobre todo, sabes que un secreto subido hay que rotarlo: borrar la confirmación no borra nada, y la solución de fondo sigue siendo la del módulo 4, no tener secretos que filtrar.

Queda un cabo suelto muy visible. La directiva más importante que has configurado —la compilación con validación— apunta a un pipeline que todavía no existe: hoy nadie comprueba que el código compila, que las pruebas pasan o que no se ha colado un fallo evidente antes de fusionar. En la próxima lección, Azure Pipelines: integración continua, construirás ese pipeline: agentes hospedados y autohospedados con sus costes reales, la anatomía de un azure-pipelines.yml explicada línea a línea, restauración, compilación, pruebas con publicación de resultados y cobertura, análisis estático, caché, plantillas reutilizables para no repetir el mismo YAML en cada servicio, y los secretos entrando desde Key Vault sin pasar jamás por el repositorio. Al terminarla, la regla «main siempre compila» dejará de ser una intención para ser un hecho verificado en cada cambio.

Curso de Azure

Módulo 1: Introducción a Azure

Módulo 2: Servicios principales de Azure

Módulo 3: Bases de datos de Azure

Módulo 4: Seguridad en Azure

Módulo 5: Azure DevOps

Módulo 6: Servicios avanzados de Azure

Módulo 7: Monitoreo y gestión

Módulo 8: Gestión y optimización de costos

Módulo 9: Estudios de caso y mejores prácticas

© Copyright 2026. Todos los derechos reservados