El pipeline de Reservalia construeix una imatge de contenidor perfectament correcta a cada pull request… i la llença a les escombraries quan el runner mor. Falta la peça que converteix una verificació en una cosa amb la qual es pot desplegar: desar el resultat, posar-li un nom que signifiqui alguna cosa i fer-lo viatjar entre entorns sense tornar-lo a construir. Aquesta lliçó gira al voltant d'un principi únic —construir una vegada, desplegar moltes vegades— i de tot el que se'n deriva: què és un artefacte immutable, per què reconstruir a cada entorn destrueix la traçabilitat, quines estratègies de versionat existeixen i quan fer servir cadascuna, per què l'etiqueta latest és perillosa dins d'un pipeline, on es desen els artefactes i quant costen, com es promociona el mateix reservalia/api:a3f9c21 de dev a staging i a prod, i com esbrinar des d'una màquina en producció quin commit exacte està executant. El que no veurem és com es desplega aquest artefacte: això és el mòdul 3 sencer.

Contingut

  1. Construir una vegada, desplegar moltes vegades
  2. L'artefacte immutable i l'antipatró de reconstruir per entorn
  3. Estratègies de versionat
  4. Per què latest és perillosa
  5. Versionat automàtic a partir de commits convencionals
  6. On es desen els artefactes: registres i retenció
  7. Promoció entre entorns
  8. Traçabilitat: saber què s'està executant en producció
  9. Signatura i procedència de l'artefacte
  10. El job publicar de Reservalia
  11. Errors Comuns i Consells
  12. Exercicis
  13. Conclusió

  1. Construir una vegada, desplegar moltes vegades

El principi. El pipeline construeix un sol artefacte per commit. Aquest artefacte, sense modificar ni reconstruir, és el que es prova a dev, el que es valida a staging i el que acaba servint peticions a prod. L'única cosa que canvia entre entorns és la configuració injectada des de fora.

Ja va aparèixer a la lliçó 01-04, quan vam definir els tres entorns de Reservalia. Ara podem veure per què és tan important: si reconstrueixes a cada entorn, el que proves i el que despleges són objectes diferents, i totes les proves del món deixen de garantir res sobre producció.

Les diferències no han de ser dramàtiques per fer mal. N'hi ha prou que entre la build de staging (dimarts) i la de prod (dijous) una dependència transitiva hagi publicat un pedaç, o que el runner hagi canviat d'imatge base. Dues builds del mateix commit separades per 48 hores poden no ser el mateix programari.

  1. L'artefacte immutable i l'antipatró de reconstruir per entorn

Un artefacte és la unitat desplegable que produeix el pipeline: a Reservalia, la imatge reservalia/api:a3f9c21 i el paquet de fitxers estàtics d'apps/web. Que sigui immutable significa que, un cop publicat amb una etiqueta, aquell contingut no canvia mai. Si s'ha de corregir alguna cosa, es publica un artefacte nou amb una altra etiqueta.

flowchart LR
    subgraph BE["Construir una vegada"]
        C1["commit a3f9c21"] --> B1["build"] --> A1["reservalia/api:a3f9c21"]
        A1 --> D1[dev] --> S1[staging] --> P1[prod]
    end
    subgraph MALAMENT["Reconstruir per entorn"]
        C2["commit a3f9c21"] --> B2["build dev"] --> D2[dev]
        C2 --> B3["build staging"] --> S2[staging]
        C2 --> B4["build prod"] --> P2["prod ← es el que has provat?"]
    end

El que es perd en reconstruir per entorn:

Es perd Conseqüència pràctica
Traçabilitat "Funciona a staging i falla a prod" passa a ser irresoluble
Validesa de les proves Vas provar l'artefacte A i vas desplegar el B
Temps Tres builds en comptes d'una
Rollback fiable Tornar enrere exigeix reconstruir, i potser ja no surt igual

I el corol·lari incòmode: si l'artefacte és el mateix als tres entorns, no pot contenir res específic d'un entorn. Ni l'URL de la base de dades, ni la clau de la passarel·la de pagament, ni el nivell de log. Tot això entra per variables d'entorn o per un gestor de secrets en el moment d'arrencar. Un artefacte que es construeix amb NODE_ENV=staging cuit a dins no és promocionable.

  1. Estratègies de versionat

Posar nom a l'artefacte no és cosmètic: és el que permet parlar-ne sense ambigüitat.

Estratègia Exemple Avantatge Inconvenient Quan fer-la servir
SemVer 2.4.1 Comunica l'impacte del canvi Requereix decidir el número Llibreries i APIs públiques
SHA del commit a3f9c21 Únic i traçable a l'instant No diu res a l'humà Serveis desplegats de manera contínua
git describe v2.4.0-13-ga3f9c21 Llegible i traçable Requereix etiquetes i fetch-depth: 0 Quan vols totes dues coses
Per data 2026.03.02.1 Ordenable, intuïtiu No identifica el codi Releases periòdiques
Incremental build-1284 Simple Es perd en canviar d'eina Sistemes antics

Reservalia fa servir el SHA curt com a etiqueta primària: reservalia/api:a3f9c21. La raó és que és una plataforma SaaS amb un únic desplegament —no distribueix versions a clients— i el que necessita és respondre en un segon a la pregunta "quin codi s'està executant?". SemVer resoldria un problema que Reservalia no té.

Res no impedeix combinar estratègies: la mateixa imatge pot portar diverses etiquetes que apuntin al mateix contingut.

reservalia/api:a3f9c21           # identitat: el commit exacte (no canvia mai)
reservalia/api:v2.4.0            # release llegible per a les persones
reservalia/api:main              # punter mobil a l ultim main verd

Només la primera és immutable; les altres dues són punters que es poden reassignar.

  1. Per què latest és perillosa

latest no és una versió: és una etiqueta mòbil que apunta a l'últim que algú va pujar. Dins d'un pipeline provoca quatre problemes concrets:

  1. No és reproduïble. docker pull reservalia/api:latest avui i demà poden portar imatges diferents. Si un contenidor es reinicia sol, es pot aixecar amb una altra versió.
  2. Trenca el rollback. "Torna a la versió anterior" no té resposta: latest no guarda historial.
  3. Fa impossible el diagnòstic. Dues màquines del mateix servei poden estar executant codi diferent i totes dues reportar latest.
  4. És una cursa. Dos pipelines simultanis escriuen la mateixa etiqueta i guanya l'últim a acabar, que no ha de ser per força el commit més recent.

La regla: en un pipeline, cada desplegament referencia una etiqueta immutable. Si vols una etiqueta amable per al teu portàtil, endavant; per desplegar, el SHA.

  1. Versionat automàtic a partir de commits convencionals

Les convencions de commit de la lliçó anterior habiliten una cosa molt pràctica: deduir la versió següent llegint l'historial, sense que ningú decideixi res a mà.

Commits des de l'última versió Salt De 2.4.1 a
Només fix:, chore:, docs: Pedaç 2.4.2
Almenys un feat: Menor 2.5.0
Algun amb BREAKING CHANGE: Major 3.0.0

Eines com semantic-release o Changesets fan tres coses en un sol pas: calculen la versió, generen el changelog agrupant els commits per tipus, i creen l'etiqueta de git i la release. El changelog resultant s'escriu sol:

## 2.5.0 (2026-03-02)
### Funcionalitats
* **cites:** permetre reserves recurrents setmanals (a3f9c21)
### Correccions
* **agenda:** no oferir forats solapats amb el descans (b7e2d10)

Reservalia l'adopta en mode mixt i per un motiu molt concret: el SHA continua sent la identitat de l'artefacte —és el que es desplega i el que apareix a /version—, mentre que la versió SemVer i el changelog serveixen per comunicar-se amb les persones: notes de release, avisos als negocis i el missatge de la incidència quan alguna cosa es trenca.

  1. On es desen els artefactes: registres i retenció

No tots els artefactes són iguals ni han de viure al mateix lloc.

Destinació Què desa Vida útil Ús a Reservalia
actions/upload-artifact Fitxers del mateix flux de treball Dies Passar dist/ entre jobs, informes de cobertura
Amazon ECR Imatges de contenidor Mesos o anys reservalia/api:a3f9c21, el que es desplega
GitHub Packages Imatges i paquets npm Configurable Alternativa quan tot viu a GitHub
S3 Fitxers estàtics Anys El dist/ d'apps/web servit per CloudFront

Els artefactes efímers del flux de treball resolen el problema que vam veure a la 02-01: els jobs no comparteixen disc. Si build genera alguna cosa que publicar necessita, cal passar-la explícitament:

      - uses: actions/upload-artifact@v4          # al job build
        with:
          name: web-dist
          path: apps/web/dist
          retention-days: 7                       # ← per defecte son 90

      - uses: actions/download-artifact@v4        # al job que la consumeix
        with: { name: web-dist, path: apps/web/dist }

retention-days mereix atenció perquè l'emmagatzematge es factura. Un informe de cobertura de 40 MB per cada execució, amb 20 execucions diàries i 90 dies de retenció, són uns 72 GB d'escombraries acumulades. Política raonable a Reservalia: 7 dies per a informes i artefactes intermedis; per a les imatges d'ECR, una regla de cicle de vida que conservi les 30 últimes de main i esborri les de branques de PR als 14 dies. El que mai no s'esborra automàticament és una imatge que estigui desplegada en algun entorn.

  1. Promoció entre entorns

Promocionar és declarar que un artefacte ja validat avança a l'entorn següent. No es reconstrueix, no es recompila: se senyala.

flowchart LR
    M["merge a main<br/>commit a3f9c21"] --> B["build + publicar"]
    B --> ECR["ECR<br/>reservalia/api:a3f9c21"]
    ECR --> D["dev<br/>automatic"]
    D -- "proves de fum OK" --> S["staging<br/>automatic"]
    S -- "validacio + aprovacio" --> P["prod"]
    P --> T["etiqueta prod-2026-03-02<br/>sobre la MATEIXA imatge"]

Dues maneres de registrar l'avenç, amb implicacions diferents:

  • Etiquetes addicionals sobre el mateix digest. En promocionar, s'afegeix staging o prod-2026-03-02 a la imatge existent. És visible des del mateix registre i no copia bytes.
  • Metadades externes. Una taula o un fitxer versionat que diu quin SHA hi ha a cada entorn. Encaixa millor amb GitOps i deixa un historial auditable.

El que totes dues comparteixen, i és l'essencial: el contingut no canvia. Un digest —sha256:4f3c…— identifica els bytes exactes, i aquest digest és el mateix a dev, a staging i a prod. Les etiquetes són noms; el digest és la identitat.

Com s'executa cada promoció (aprovacions manuals, desplegament progressiu, rollback) és el contingut del mòdul 3.

  1. Traçabilitat: saber què s'està executant en producció

Són les 23:40, hi ha un incident i la primera pregunta de la Nuria és sempre la mateixa: "quina versió hi ha desplegada?". Ha de poder respondre's en menys d'un minut, des de fora i sense accés privilegiat. Dos mecanismes, complementaris.

Etiquetes OCI a la imatge. Metadades estàndard incrustades a la mateixa imatge:

ARG COMMIT_SHA
ARG DATA_BUILD
LABEL org.opencontainers.image.revision="${COMMIT_SHA}" \
      org.opencontainers.image.created="${DATA_BUILD}" \
      org.opencontainers.image.source="https://github.com/reservalia/reservalia" \
      org.opencontainers.image.version="2.5.0"

Es consulten sense arrencar el contenidor amb docker inspect, i sobreviuen encara que algú reanomeni l'etiqueta.

Un endpoint /version. La manera més ràpida i la que no requereix accés a la infraestructura:

// apps/api/src/rutes/version.ts
export const version = {
  commit:   process.env.COMMIT_SHA  ?? 'desconegut',   // a3f9c21
  version:  process.env.APP_VERSION ?? '0.0.0',        // 2.5.0
  construit:process.env.DATA_BUILD  ?? 'desconegut',   // 2026-03-02T09:14:00Z
  entorn:   process.env.ENTORN      ?? 'desconegut',   // prod
};
// GET /version → { "commit": "a3f9c21", "version": "2.5.0", ... }

Els valors s'injecten al docker build com a ARG i es converteixen en ENV de la imatge. Detall de seguretat: /version no ha de revelar rutes internes, dependències ni configuració; el SHA i la data en són suficients. I detall pràctic: aquest endpoint és el que permet comprovar que un desplegament ha fet efecte. Si després de desplegar b7e2d10 el /version continua dient a3f9c21, el desplegament no ha arribat, per molt que el pipeline sigui verd.

  1. Signatura i procedència de l'artefacte

Que un artefacte sigui immutable no demostra qui el va construir ni a partir de què. Un atacant amb accés al registre podria publicar una imatge amb l'etiqueta esperada.

Dos mecanismes hi responen: la signatura criptogràfica de l'artefacte (amb eines com Sigstore/cosign, que permeten verificar abans de desplegar que la imatge la va signar el teu pipeline) i la procedència o provenance, un document verificable que declara quin commit, quin flux de treball i quin runner van produir aquell digest, en la línia del marc SLSA. Tots dos es recolzen a més en un SBOM, l'inventari de tot el que conté la imatge.

Tots tres pertanyen a la cadena de subministrament de programari i es tracten a la lliçó 04-03, Seguretat en CI/CD. Aquí n'hi ha prou de saber que existeixen i que el pas previ —artefactes immutables i identificats pel seu digest— ja està fet.

  1. El job publicar de Reservalia

  publicar:
    name: Publicar artefacte
    runs-on: ubuntu-22.04
    needs: [qualitat, test, build]                     # 1
    if: github.ref == 'refs/heads/main'                # 2
    permissions:
      id-token: write                                  # 3
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Calcular etiquetes
        id: meta
        run: |                                         # 4
          echo "sha_curt=$(git rev-parse --short=7 HEAD)" >> $GITHUB_OUTPUT
          echo "data=$(date -u +%Y-%m-%dT%H:%M:%SZ)"      >> $GITHUB_OUTPUT

      - name: Autenticar-se a AWS
        uses: aws-actions/configure-aws-credentials@v4  # 5
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_CI }}
          aws-region: eu-west-1

      - uses: aws-actions/amazon-ecr-login@v2
        id: ecr

      - name: Construir i publicar la imatge
        uses: docker/build-push-action@v5
        with:
          context: .
          file: apps/api/Dockerfile
          push: true                                    # 6
          tags: |
            ${{ steps.ecr.outputs.registry }}/reservalia/api:${{ steps.meta.outputs.sha_curt }}
            ${{ steps.ecr.outputs.registry }}/reservalia/api:main
          build-args: |
            COMMIT_SHA=${{ steps.meta.outputs.sha_curt }}
            DATA_BUILD=${{ steps.meta.outputs.data }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
  1. needs: [qualitat, test, build] fa que aquest job esperi que tots tres siguin verds. És la garantia que no es publica res sense verificar.
  2. if: github.ref == 'refs/heads/main': als pull requests es construeix per comprovar, però no es publica. Publicar cada PR ompliria el registre d'imatges que ningú no desplegarà.
  3. permissions: id-token: write habilita OIDC: el runner obté credencials temporals d'AWS assumint un rol, en comptes de desar claus d'accés permanents com a secrets. És la manera correcta d'autenticar-se contra el núvol, i es detalla a la 04-03.
  4. $GITHUB_OUTPUT és el mecanisme per passar valors entre steps del mateix job: el que hi escriguis es llegeix després com steps.meta.outputs.sha_curt.
  5. role-to-assume referencia el secret AWS_ROLE_CI, que conté l'ARN del rol —no una credencial—.
  6. push: true amb dues etiquetes que apunten a la mateixa imatge: el SHA curt, immutable i traçable, i main, un punter mòbil còmode per saber quin és l'últim verd. El que es desplega és sempre la primera.

Amb aquest job, l'estat de Reservalia canvia de manera qualitativa: cada commit que entra a main deixa un artefacte identificat, verificat i desat, llest per desplegar.

Errors Comuns i Consells

Error 1: reconstruir a cada entorn. L'antipatró central de la lliçó. Trenca la traçabilitat, invalida les proves i fa irreproduïble el rollback.

Error 2: desplegar latest. No és reproduïble, no permet tornar enrere i fa impossible saber què s'està executant. Fes servir sempre una etiqueta immutable.

Error 3: ficar configuració d'entorn dins de l'artefacte. Un artefacte amb l'URL de la base de dades de staging cuita a dins ja no es pot promocionar; caldrà reconstruir-lo, i tornem a l'error 1.

Error 4: publicar des dels pull requests. Omple el registre d'imatges que ningú no farà servir i multiplica el cost d'emmagatzematge. Publica només des de main o des d'etiquetes.

Error 5: no fixar polítiques de retenció. El cost creix de manera silenciosa fins que algú mira la factura. Defineix retenció des del primer dia, amb l'excepció del que estigui desplegat.

Consell 1: fes visible la versió. Un endpoint /version amb el SHA és la millor inversió de cinc minuts de tot el mòdul; ho agrairàs a les 23:40 d'un dimarts.

Consell 2: pensa en digests, no en etiquetes. Les etiquetes són noms reassignables; el digest sha256:… és la identitat real. Els sistemes de desplegament seriosos referencien digests.

Consell 3: automatitza el changelog des dels commits. Si ja escrius commits convencionals, generar-lo és gratis i l'equip deixa de mantenir a mà un fitxer que ningú no actualitza.

Exercicis

Exercici 1

Un equip té tres fluxos de treball: deploy-dev.yml, deploy-staging.yml i deploy-prod.yml. Tots tres fan checkout de main, npm ci, npm run build, construeixen la imatge com a app:latest i la despleguen. Enumera quatre problemes i descriu el pipeline correcte.

Exercici 2

Reservalia desplega reservalia/api:a3f9c21 a prod. Al cap de dues hores apareix un error greu. La Nuria vol tornar a la versió anterior, b7e2d10. Explica per què això és trivial amb artefactes immutables i què caldria fer si l'equip fes servir latest.

Exercici 3

Dissenya l'esquema d'etiquetatge d'una imatge per a un producte que que es distribueix a clients, amb releases mensuals SemVer i correccions urgents entremig. Indica quines etiquetes hi poses, quines són immutables i quina faries servir per desplegar.

Solucions

Solució 1. Quatre problemes: (1) es construeix tres vegades, així que el que es desplega a prod no és el que s'ha provat a staging —dues builds separades per dies poden diferir—; (2) latest impedeix saber què s'està executant i fa impossible el rollback; (3) fer checkout de main a cada desplegament significa desplegar el que hi hagi ara a la branca, no el commit validat, de manera que un merge intermedi es cola sense voler en producció; (4) es triplica el temps i el cost de build sense obtenir cap informació addicional.

Pipeline correcte: un sol flux de treball de CI que, en entrar un commit a main, construeix i publica reservalia/api:<sha> després de passar qualitat, test i build. Els tres desplegaments són fluxos de treball separats que reben com a paràmetre el SHA i es limiten a apuntar l'entorn a aquesta imatge ja existent, promocionant el mateix digest de dev a staging i a prod.

Solució 2. Amb artefactes immutables, reservalia/api:b7e2d10 continua existint a ECR sense modificar: el rollback consisteix a dir al servei que faci servir aquella etiqueta i esperar que arrenquin les noves instàncies. Són minuts, no requereix compilar res i el resultat és exactament el programari que estava funcionant abans. És justament el cas que reclamava la frase de la Nuria al mòdul 1 sobre desfer un desplegament en cinc minuts.

Amb latest, en canvi, no hi ha res a què tornar: l'etiqueta apunta a la versió trencada i l'anterior no està identificada. Caldria esbrinar quin commit era el bo (sense registre fiable, probablement mirant l'historial de git a ull), reconstruir-lo —amb el risc que la nova build no sigui idèntica a la que funcionava— i tornar-lo a publicar. En comptes de minuts, mitja hora llarga en plena incidència, i amb incertesa sobre el resultat.

Solució 3. Un esquema raonable:

Etiqueta És immutable? Per a què serveix
app:2.5.0 La release que es comunica al client
app:a3f9c21 Identitat exacta del commit; per a diagnòstic
app:2.5 No Punter a l'últim pedaç d'aquella menor
app:2 No Punter a l'última menor d'aquella major
app:latest No Comoditat per a proves locals; mai per desplegar

Per desplegar es fa servir sempre una etiqueta immutable, preferiblement el SHA o, encara millor, el digest sha256:…. Les etiquetes mòbils 2.5 i 2 són un servei als clients que volen rebre pedaços automàticament, i són una decisió d'ells, no teva. Una correcció urgent sobre 2.5.0 es publica com a 2.5.1 i reassigna els punters 2.5, 2 i latest, sense tocar mai les etiquetes immutables anteriors.

Conclusió

Reservalia ja produeix una cosa que es pot desplegar, i sap exactament què és:

  • Construir una vegada, desplegar moltes vegades. Un artefacte per commit, el mateix a dev, staging i prod; l'única cosa que canvia entre entorns és la configuració injectada des de fora. Reconstruir per entorn trenca la traçabilitat i anul·la el valor de les proves.
  • Un artefacte immutable no canvia mai un cop publicat. Si s'ha de corregir, se'n publica un altre amb una altra etiqueta.
  • De les estratègies de versionat, Reservalia fa servir el SHA curt com a identitat —el que necessita un SaaS de desplegament continu— i reserva SemVer per comunicar-se amb les persones. latest no es desplega mai: no és reproduïble, impedeix el rollback i és una cursa entre pipelines.
  • Els commits convencionals permeten deduir la versió i generar el changelog automàticament, sense que ningú mantingui un fitxer a mà.
  • Els artefactes viuen a llocs diferents segons la seva naturalesa —efímers del flux de treball per passar fitxers entre jobs, ECR per a les imatges desplegables, S3 per a la web— i tots necessiten política de retenció, perquè l'emmagatzematge es factura.
  • Promocionar és senyalar, no reconstruir: el mateix digest avança d'entorn en entorn, registrat amb etiquetes addicionals o amb metadades externes.
  • La traçabilitat es resol amb etiquetes OCI a la imatge i un endpoint /version que retorna el commit; és també la manera de comprovar que un desplegament ha fet efecte.
  • La signatura i la procedència de l'artefacte existeixen i són importants; el seu desenvolupament és la lliçó 04-03.
  • El job publicar depèn de qualitat, test i build, només s'executa a main, s'autentica a AWS per OIDC i puja a ECR la imatge etiquetada amb el SHA i amb main.

Ja tenim els quatre jobs i un artefacte publicat. Falta la peça que connecta tot això amb la manera real de treballar de l'equip. A la lliçó següent, Integració amb el Control de Versions, veurem com el model de branques condiciona la CI —comparant trunk-based development, GitHub Flow i Git Flow—, per què les branques de vida llarga són incompatibles amb la Integració Contínua, com es configuren les regles de protecció de main, CODEOWNERS i la merge queue, com evitar execucions inútils amb paths i concurrency, i quin efecte té cada estratègia de merge sobre la traçabilitat de l'artefacte i sobre el lead time de la 01-05. I tancarem el mòdul amb el ci.yml complet de Reservalia.

Curs de CI/CD: Integració i Desplegament Continu

Mòdul 1: Introducció al CI/CD

Mòdul 2: Integració Contínua (CI)

Mòdul 3: Desplegament Continu (CD)

Mòdul 4: Pràctiques Avançades de CI/CD

Mòdul 5: Implementació de CI/CD en Projectes Reals

Mòdul 6: Eines i Tecnologies

Mòdul 7: Exercicis Pràctics

Mòdul 8: Recursos Addicionals

© Copyright 2026. Tots els drets reservats