Reservalia té cinc workflows, setze jobs i un problema que cap lliçó anterior no ha tocat: el bloc de checkout + setup-node + npm ci apareix copiat sis vegades. Quan a la 04-04 es va millorar la memòria cau es va haver d'aplicar el canvi sis vegades, i el dia que algú toqui una còpia i oblidi les altres cinc, el pipeline tindrà comportaments diferents segons el workflow sense que ningú sàpiga per què. I ve el pitjor: el mòdul 5 hi afegirà una aplicació mòbil i uns microserveis que necessiten exactament el mateix. La idea central d'aquesta lliçó és fàcil d'enunciar i difícil d'aplicar: el pipeline és codi de producció i mereix el mateix tracte —revisió, versionat, refactorització i, sobretot, proves—. Veurem els tres mecanismes de reutilització de GitHub Actions i quan fer servir cadascun, extraurem de debò una composite action i un workflow reutilitzable, reescriurem el ci.yml a sobre, discutirem el repositori central de plantilles i els seus riscos, fixarem convencions de llegibilitat i acabarem amb el que gairebé ningú no fa: provar el pipeline abans de fusionar-lo.

Contingut

  1. El pipeline és codi, i avui no es tracta com a tal
  2. El problema real de Reservalia, explicat amb números
  3. Els tres mecanismes de reutilització comparats
  4. Extreure una composite action: preparar-node
  5. Extreure un workflow reutilitzable: reusable-build-publicar.yml
  6. El ci.yml de Reservalia: abans i després
  7. Un repositori central de workflows i els seus riscos
  8. Convencions perquè un pipeline sigui llegible
  9. Provar el pipeline
  10. Migració progressiva sense congelar l'equip
  11. Errors Comuns i Consells
  12. Exercicis
  13. Conclusió

  1. El pipeline és codi, i avui no es tracta com a tal

Compara com tracta Reservalia el seu codi d'aplicació i com tracta el seu YAML:

Pràctica apps/api .github/workflows/
Control de versions i revisió en PR Sí (CODEOWNERS)
Format i lint automàtics Prettier + ESLint No
Proves automatitzades Unitàries i integració No
Refactorització quan fa mal Habitual Mai
Reutilització del que es repeteix Funcions i paquets Copiar i enganxar
Es desplega després de verificar Es fusiona i es mira què passa

Les quatre files en negreta són la lliçó. I no és una qüestió de polidesa: el pipeline és el sistema que decideix què arriba a producció, així que una fallada en ell té el mateix abast que una fallada a l'aplicació, amb l'agreujant que el pipeline no té qui el verifiqui. Quan la 04-01 va diagnosticar "la lògica de negoci amagada al YAML" i "el pipeline que només entén la Nuria" en descrivia els símptomes; aquesta lliçó en dona el tractament.

  1. El problema real de Reservalia, explicat amb números

Bloc duplicat On apareix Vegades
checkout + setup-node + npm ci ci.yml (×4 jobs), nocturn.yml, cd.yml (job web) 6
Credencials OIDC + login a ECR ci.yml (publicar), cd.yml (×3), rollback.yml 5
docker build amb memòria cau de registre ci.yml (×2), nocturn.yml 3
Smoke test contra /version cd.yml (×3), rollback.yml 4

Quatre conseqüències concretes, cap de teòrica. Tota millora costa ×6: la memòria cau de capes de la 04-04 es va aplicar bé a ci.yml i es va oblidar a nocturn.yml, que continua trigant dos minuts de més. Les còpies divergeixen: dos jobs ja fan servir actions/checkout@v4 i un es va quedar a @v3. La revisió es torna inútil, perquè un diff de 200 línies de YAML repetit no es llegeix, s'aprova. I afegir un servei nou és copiar 150 línies i confiar a no haver-se oblidat res. La regla que s'aplica al codi serveix igual aquí: a la tercera repetició, extreu. Amb dues còpies, l'abstracció prematura sol sortir malament; amb sis, el cost ja s'està pagant cada dia.

  1. Els tres mecanismes de reutilització comparats

Composite action Reusable workflow Matrix
Què encapsula Una seqüència de steps Un o diversos jobs complets El mateix job amb dades diferents
On viu .github/actions/<nom>/action.yml o un repositori propi .github/workflows/<nom>.yml Dins del job
S'invoca amb uses: dins d'un job uses: a nivell de job strategy: matrix
Defineix runs-on? No: hereta el del job que la crida : defineix els seus propis jobs No
Rep / retorna inputs / outputs inputs, secrets / outputs Valors de la matriu / res
Accés a secrets Només els que li passis com a input Explícits o amb secrets: inherit
Quan fer-lo servir Repetir passos dins de jobs diferents Repetir jobs sencers o pipelines complets Repetir el mateix amb dades diferents

La regla d'elecció, en una frase: si el que es repeteix són passos dins d'un job, composite action; si és el job sencer o un pipeline complet, reusable workflow; si és la mateixa feina amb paràmetres diferents, matrix. I una limitació que sorprèn i convé conèixer abans de dissenyar res: una composite action no pot definir services:, així que el PostgreSQL de les proves no s'hi pot encapsular; ha de quedar-se al job o pujar a un reusable workflow.

  1. Extreure una composite action: preparar-node

Comencem pel que més es repeteix. El bloc que apareix sis vegades és aquest:

      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version-file: .nvmrc, cache: npm }
      - run: npm ci

I es converteix en una acció local:

# .github/actions/preparar-node/action.yml
name: Preparar Node
description: Checkout, Node des de .nvmrc amb memoria cau de npm, i instal·lacio reproduible.

inputs:
  fetch-depth:
    description: Profunditat de l historial. 0 per a l historial complet.
    required: false
    default: '1'                       # 1

runs:
  using: composite                     # 2
  steps:
    - uses: actions/checkout@v4
      with: { fetch-depth: '${{ inputs.fetch-depth }}' }
    - uses: actions/setup-node@v4
      with: { node-version-file: .nvmrc, cache: npm }
    - name: Instal·lar dependencies
      shell: bash                      # 3
      run: npm ci
  1. Els inputs amb default són el que fa usable l'acció. El cas normal no necessita configurar res; el job seguretat, que necessita l'historial complet per a gitleaks, passa fetch-depth: 0. Sense aquell input caldria triar entre duplicar l'acció o portar sempre l'historial sencer, que és lent.
  2. using: composite distingeix una acció de passos d'una acció de JavaScript o de contenidor. I shell: bash és obligatori (3) a cada run d'una composite action: la seva absència és l'error número u en escriure la primera, i el missatge resultant no és especialment clar.

L'ús queda així en qualsevol job:

      - uses: ./.github/actions/preparar-node                       # 1 · cas normal
      - uses: ./.github/actions/preparar-node                       # al job seguretat
        with: { fetch-depth: '0' }
  1. La ruta comença per ./ perquè és una acció local, del mateix repositori: GitHub descarrega el repositori per resoldre-la, de manera que el patró funciona encara que el checkout el faci la mateixa acció. Si visqués en un altre repositori es referenciaria com reservalia/accions/preparar-node@v1 i se li aplicaria la regla de fixar per SHA de la 04-03.

  1. Extreure un workflow reutilitzable: reusable-build-publicar.yml

La composite action resol els steps repetits, però build i publicar són jobs sencers que es repeteixen amb variacions entre ci.yml i nocturn.yml, i que el mòdul 5 voldrà reutilitzar per al mòbil i els microserveis. Això és un reusable workflow:

# .github/workflows/reusable-build-publicar.yml
name: Reusable · build i publicar

on:
  workflow_call:                       # 1 · el que el fa invocable
    inputs:
      dockerfile:    { type: string,  required: true }
      repositori:    { type: string,  required: true }    # p. ex. reservalia/api
      publicar:      { type: boolean, required: false, default: false }
    secrets:
      AWS_ROLE:      { required: true }                    # 2 · explicit, no inherit
    outputs:                                               # 3
      digest: { description: Digest de la imatge, value: '${{ jobs.construir.outputs.digest }}' }

jobs:
  construir:
    runs-on: ubuntu-22.04
    timeout-minutes: 15
    permissions: { contents: read, id-token: write }
    outputs:
      digest: ${{ steps.imatge.outputs.digest }}
    steps:
      - uses: ./.github/actions/preparar-node
      - run: npm run build

      - uses: aws-actions/configure-aws-credentials@v4
        if: inputs.publicar                                # 4
        with: { role-to-assume: '${{ secrets.AWS_ROLE }}', aws-region: eu-west-1 }
      - uses: aws-actions/amazon-ecr-login@v2
        if: inputs.publicar

      - uses: docker/setup-buildx-action@v3
      - id: imatge
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ${{ inputs.dockerfile }}
          push: ${{ inputs.publicar }}
          tags: ${{ vars.ECR_REGISTRY }}/${{ inputs.repositori }}:${{ github.sha }}
          cache-from: type=registry,ref=${{ vars.ECR_REGISTRY }}/${{ inputs.repositori }}:cache
          cache-to:   type=registry,ref=${{ vars.ECR_REGISTRY }}/${{ inputs.repositori }}:cache,mode=max
  1. on: workflow_call converteix el fitxer en una cosa invocable des d'un altre workflow, i els inputs amb tipus són un contracte verificat: passar una cadena on s'espera un booleà falla en analitzar el fitxer, abans d'executar res.
  2. Els secrets es declaren un a un. Existeix secrets: inherit, que passa tots els del cridador, i és còmode i mala idea: trenca el mínim privilegi de la 04-03 i fa impossible saber a què té accés el workflow llegint-ne la capçalera.
  3. Els outputs del workflow reutilitzable s'alimenten dels outputs dels seus jobs, i són el que permet que el cridador rebi el digest i el promocioni: el mecanisme de la 04-01, un nivell més amunt. if: inputs.publicar (4) permet a més un sol workflow per a dos comportaments: en un pull request es construeix sense publicar i sense demanar credencials; a main es publica. Un paràmetre en comptes de dos fitxers gairebé iguals.

  1. El ci.yml de Reservalia: abans i després

Abans, els jobs build i publicar ocupaven unes 45 línies amb el bloc de preparació repetit a cadascun:

  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version-file: .nvmrc, cache: npm }
      - run: npm ci
      - run: npm run build
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v5
        with: { context: ., file: apps/api/Dockerfile, push: false, cache-from: '…' }

  publicar:
    needs: [qualitat, test, build]
    if: github.ref == 'refs/heads/main'
    permissions: { contents: read, id-token: write }
    steps:
      - uses: actions/checkout@v4          # ← una altra vegada
      - uses: actions/setup-node@v4        # ← una altra vegada
        with: { node-version-file: .nvmrc, cache: npm }
      - run: npm ci                        # ← i credencials, login a ECR,
      # … build i push duplicats respecte al job anterior

Després, els dos jobs es converteixen en dues crides:

jobs:
  qualitat:
    runs-on: ubuntu-22.04
    steps:
      - uses: ./.github/actions/preparar-node          # 1
      - run: npx prettier --check .
      - run: npm run lint
      - run: npm run typecheck

  build:                                                # 2
    uses: ./.github/workflows/reusable-build-publicar.yml
    with: { dockerfile: apps/api/Dockerfile, repositori: reservalia/api, publicar: false }
    secrets: { AWS_ROLE: '${{ secrets.AWS_ROLE_CI }}' }

  publicar:
    needs: [qualitat, test, build, seguretat]
    if: github.ref == 'refs/heads/main'
    uses: ./.github/workflows/reusable-build-publicar.yml
    with: { dockerfile: apps/api/Dockerfile, repositori: reservalia/api, publicar: true }
    secrets: { AWS_ROLE: '${{ secrets.AWS_ROLE_CI }}' }
  1. Un uses: substitueix tres steps, als sis llocs on eren.
  2. Un job que crida un reusable workflow no té steps ni runs-on: els defineix el workflow cridat. Té, això sí, needs, if, with i secrets. És el que més confon al principi, perquè sembla un job i es comporta com una crida a funció.

Balanç: ci.yml passa d'unes 180 línies a unes 95, la memòria cau es configura en un sol lloc, i afegir el microservei del mòdul 5 serà afegir un job de quatre línies amb un altre dockerfile i un altre repositori. Amb un avís honest: la indirecció té un cost real de llegibilitat —per saber què fa build ara cal obrir un altre fitxer, i a la interfície els jobs apareixen imbricats—. Extreure una cosa que només es fa servir una vegada empitjora el pipeline; la regla de la tercera repetició evita justament això.

  1. Un repositori central de workflows i els seus riscos

Quan el mòdul 5 afegeixi el mòbil i els microserveis, la pregunta següent és si aquelles plantilles han de viure a cada repositori o en un de central, tipus reservalia/accions.

Plantilles a cada repositori Repositori central
Duplicació Alta entre repositoris Cap
Autonomia de cada equip Total Limitada
Propagar una millora Repositori a repositori Una vegada, per a tothom
Radi d'un error Un repositori Tots alhora
Qui el manté Cada equip Cal un propietari amb nom

El risc és a la fila del radi d'error: un canvi a la plantilla central arriba a tots els equips alhora i, si trenca alguna cosa, trenca tots els pipelines de l'empresa un dimarts al matí. La mitigació és la mateixa que per a qualsevol dependència compartida, com vam veure a la 04-02: versionar per etiqueta i no consumir main.

  build:
    uses: reservalia/accions/.github/workflows/build-node.yml@v2   # 1 · etiqueta, no main
  1. Amb @v2, un canvi a la plantilla no arriba fins que cada equip puja la seva referència. Consumir @main significa que qualsevol commit del repositori central es desplega instantàniament a tots els pipelines, sense revisió ni proves per part de qui ho pateix. La convenció pràctica és una etiqueta major mòbil (v2 apuntant sempre a l'última 2.x) per rebre correccions compatibles, i un canvi explícit d'etiqueta per a les majors. I el repositori central necessita el seu propi pipeline: proves, revisió i un CHANGELOG que expliqui què canvia a cada versió, perquè és una llibreria encara que no ho sembli.

  1. Convencions perquè un pipeline sigui llegible

L'antipatró "el pipeline que només entén la Nuria" de la 04-01 es combat amb quatre convencions barates. Noms explícits, en jobs i en steps: name: Publicar imatge a ECR en lloc de publicar-2; els noms són el que es veu a la interfície quan alguna cosa falla i als checks obligatoris, així que un nom dolent costa un clic cada vegada, per sempre. env centralitzat a la capçalera: regió, zona horària, versions d'eines i noms de recursos en un únic bloc a nivell de workflow, en comptes de repetits a cada step; canviar de regió ha de ser canviar una línia. Fitxers curts amb una responsabilitat: si un workflow supera les 150 línies o barreja dos propòsits, probablement són dos workflows, que és la mateixa heurística que aplicaries a un mòdul de codi. I comentaris que expliquin el perquè, mai el què: # instal·la dependències sobre un npm ci és soroll; el que cal escriure és la raó no evident:

      # cancel-in-progress: false a main expressament: cada commit de main produeix
      # un artefacte publicable i cancel·lar deixaria commits sense imatge (veure 02-07).
      concurrency: { group: cd-main, cancel-in-progress: false }

  1. Provar el pipeline

Aquí hi ha el forat més gran, i el que dona títol a la lliçó. Avui, l'única manera que té Reservalia de saber si un canvi al pipeline funciona és fusionar-lo i mirar. Això és provar en producció, amb la particularitat que la producció del pipeline és la capacitat de l'equip de lliurar programari: si es trenca, ningú no desplega.

Nivell Què comprova Cost Quan s'executa
Lint i validació d'esquema (actionlint) Sintaxi, expressions, referències a jobs inexistents Segons A cada PR
Execució local (act) Que els steps fan el que creus Minuts Al portàtil
Branca de prova El flux complet amb disparadors reals Una execució Abans de fusionar
Servei de baix risc primer El canvi contra una cosa real que no és crítica Un desplegament En desplegar el canvi

actionlint és l'eina de més rendiment i s'afegeix al job qualitat en dues línies: descarregar el binari amb l'script oficial i executar ./actionlint -color.

Detecta el que l'editor no veu: una expressió ${{ }} mal formada, un needs: [buidl] amb una errada que deixaria el job sense executar-se en silenci, un runs-on inexistent, un shell que falta en una composite action, i fins i tot errors de shell dins dels blocs run, perquè incorpora shellcheck. És la diferència entre assabentar-se'n en vint segons i assabentar-se'n quan el workflow ja és a main.

act executa els workflows localment en contenidors. Serveix per iterar sobre la lògica d'un job sense gastar vint minuts per intent, i té límits que convé conèixer per no confiar-s'hi: no reprodueix els entorns de GitHub, ni les regles de protecció, ni el token OIDC, ni els services amb exactitud. La regla pràctica: act serveix per depurar, no per validar. La branca de prova cobreix el que act no pot: es crea prova/pipeline-plantilles, s'apunten els disparadors a aquella branca i s'executa el flux real, amb els seus entorns i els seus permisos, contra recursos de dev. És l'única manera de verificar de debò un canvi a cd.yml. I finalment, el desplegament del canvi a un servei de baix risc primer. Quan les plantilles siguin centrals, la versió nova s'aplica abans a un servei intern la caiguda del qual no noti cap client, es deixa una setmana i només aleshores es propaga. És exactament el canari de la 03-04 aplicat al pipeline, i per la mateixa raó: és l'únic desplegament progressiu possible quan el radi d'impacte és tota l'organització.

  1. Migració progressiva sense congelar l'equip

La temptació de reescriure els cinc workflows en un pull request gegant s'ha de resistir: seria un PR de 800 línies impossible de revisar que, si surt malament, deixa l'equip sense pipeline i sense saber quina part ha fallat. El pla de Reservalia són cinc passos, cap de bloquejant:

  1. Afegir actionlint primer, sobre els workflows actuals. És la xarxa que farà segurs els quatre passos següents, i troba tres errades el primer dia.
  2. Extreure preparar-node i fer-la servir en un sol job, qualitat. Un PR de vint línies, revisable, amb risc acotat a un job.
  3. Propagar-la als cinc jobs restants, un o dos per PR, verificant cada vegada. Aquí és on es corregeixen les divergències acumulades, com el checkout@v3 oblidat.
  4. Extreure reusable-build-publicar.yml i fer-lo servir primer a nocturn.yml, el workflow de menys risc: si falla no bloqueja ningú i s'arregla al matí.
  5. Migrar ci.yml i cd.yml, només quan els passos anteriors portin un parell de setmanes funcionant.

Dos principis sostenen el pla. Cada pas deixa el sistema funcionant: en cap moment hi ha un estat intermedi trencat, i es pot parar al pas 3 durant un mes si sorgeix alguna cosa urgent. I es comença pel de menys risc i més repetició, que és on el benefici arriba abans i l'error costa menys. És la mateixa estratègia que aplicaries a refactoritzar un mòdul de producció, perquè el problema és exactament el mateix.

Errors Comuns i Consells

Error 1: extreure a la primera repetició. Una abstracció construïda sobre un sol cas acaba plena de paràmetres per acomodar el segon; espera a la tercera. Error 2: oblidar shell: bash als run d'una composite action, amb un missatge d'error poc clar.

Error 3: fer servir secrets: inherit per comoditat. Passa tots els secrets del cridador i trenca el mínim privilegi; declara els que calguin. Error 4: consumir plantilles centrals des de main, amb la qual cosa un commit aliè canvia el pipeline de tothom sense avís. Error 5: posar steps en un job que crida un reusable workflow: no és un job normal, és una crida, i falla en analitzar el fitxer.

Error 6: sobreabstreure fins que entendre per què ha fallat el pipeline exigeixi obrir quatre fitxers; de vegades deu línies duplicades i clares són millors que una plantilla amb nou paràmetres. Error 7: fusionar un canvi de pipeline sense haver-lo executat mai, que és provar en producció amb l'equip sencer de conillet d'índies. Error 8: reescriure els cinc workflows en un PR, impossible de revisar i de diagnosticar si alguna cosa va malament. Consell 1: afegeix actionlint avui, encara que no hagis de refactoritzar res; és la millor relació benefici/esforç de tota la lliçó. Consell 2: versiona les plantilles per etiqueta i publica un CHANGELOG. Consell 3: documenta al README de les plantilles com es fan servir i com es proven, o tornaràs a tenir un sistema que només entén una persona. Consell 4: aplica al pipeline les mateixes revisions que al codi, començant per CODEOWNERS sobre .github/.

Exercicis

Exercici 1

En aquests tres casos, decideix quin mecanisme de reutilització faries servir i justifica-ho: (a) quatre jobs repeteixen els mateixos cinc steps de preparació; (b) tres repositoris diferents necessiten el mateix pipeline complet de construcció i publicació d'imatges; (c) cal executar la mateixa suite de proves contra Node 18, 20 i 22.

Exercici 2

Un equip extreu les seves plantilles a empresa/workflows i tots els repositoris les consumeixen amb @main. Un dimarts al matí algú fusiona un canvi a la plantilla i tots els pipelines de l'empresa fallen. Explica què va fallar en el disseny, quines pràctiques d'aquesta lliçó ho haurien evitat i com ho arreglaries, distingint la resposta immediata de l'estructural.

Exercici 3

Dissenya l'estratègia de proves per a un canvi a cd.yml que afegeix una verificació de signatura amb cosign abans de desplegar a prod. Indica què comprovaries a cada nivell, en quin ordre i què faries si alguna cosa falla a l'últim.

Solucions

Solució 1. (a) Composite action. El que es repeteix són steps dins de jobs que continuen sent diferents entre si; una composite action s'insereix amb un uses: i hereta el runs-on del job que la crida, que és justament el que es vol. Un reusable workflow seria excessiu, perquè obligaria a convertir cada job en una crida i a redefinir la resta del seu contingut.

(b) Reusable workflow, allotjat en un repositori central i consumit per etiqueta. El que es repeteix és un pipeline sencer amb els seus jobs, els seus runs-on i els seus permisos, i això una composite action no ho pot encapsular. Els inputs parametritzen el que canvia —dockerfile, repositori d'imatges, si publica— i els secrets es declaren explícitament. (c) Matrix. No hi ha res per extreure: és el mateix job executat tres vegades amb una dada diferent. strategy: matrix: { node: [18, 20, 22] } amb fail-fast: false per veure els tres resultats. Convé a més el job agregador de la 04-04, perquè els checks obligatoris es configuren per nom i els noms inclouen el valor de la matriu.

Solució 2. Què va fallar: consumir una dependència compartida des de la seva branca de desenvolupament. @main significa que qualsevol commit del repositori central entra en producció —la producció de tots els pipelines— sense revisió, sense proves i sense que els consumidors puguin triar quan. És exactament el que a la 04-02 seria no tenir lockfile: totes les dependències en latest. Què ho hauria evitat: (1) versionar per etiqueta (@v2), de manera que un canvi al central no arribi fins que cada equip pugi la seva referència; (2) que el repositori central tingui el seu propi pipeline amb actionlint, validació d'esquema i proves, més revisió obligatòria amb CODEOWNERS; (3) provar el canvi en un repositori de baix risc abans de publicar l'etiqueta, que és el canari de l'apartat 9; i (4) un CHANGELOG que permeti als consumidors saber què canvia abans de pujar de versió.

Resposta immediata: revertir el commit al repositori central, cosa que restableix tots els pipelines alhora, i només després investigar. La temptació d'arreglar cap endavant amb l'equip sencer bloquejat és l'error clàssic de gestió d'incidents, i aquí s'aplica igual que a producció: revertir primer, diagnosticar després. Resposta estructural: publicar una etiqueta v1 amb l'estat bo conegut, migrar tots els repositoris de @main a @v1 i, a partir d'aquí, adoptar el cicle de versionat amb proves.

Solució 3. Cinc nivells, en ordre de cost creixent:

  1. actionlint en local i al PR. Detecta expressions mal formades i errors de shell al bloc run de cosign. Segons, i descarta la meitat dels errors possibles.
  2. Verificació manual de l'ordre fora del pipeline. Executar cosign verify des d'un portàtil contra una imatge real ja publicada, comprovant que falla amb una imatge no signada. Això és clau: una verificació que sempre passa no verifica res, així que cal provar el cas negatiu abans que el positiu.
  3. act per iterar sobre la lògica del step, sabent que no reproduirà l'OIDC ni els entorns: serveix per afinar l'script, no per donar el vistiplau.
  4. Branca de prova amb desplegament real a dev, el primer nivell que exercita el flux complet amb permisos i entorns reals. Es comproven els dos camins: imatge signada, que desplega, i imatge sense signar, que s'atura; sense el cas negatiu, la porta no està provada.
  5. Aplicar el canvi primer a dev i staging durant una setmana, i només després a prod. La verificació de signatura és una porta nova, i el seu mode de fallada més probable no és deixar passar alguna cosa dolenta, sinó bloquejar un desplegament legítim un dia que calgui desplegar de pressa.

Si alguna cosa falla a l'últim nivell —la verificació bloqueja un desplegament legítim a prod—, hi ha dues respostes i només una és correcta. La incorrecta és desactivar la verificació "temporalment", perquè aquella desactivació no es reverteix mai. La correcta és tractar-ho com un incident: fer servir rollback.yml per tornar a la versió anterior si urgeix, i diagnosticar per què la signatura no era vàlida —el rol OIDC va canviar, la identitat del signant no coincideix amb l'expressió regular, la imatge es va republicar sense signar— per arreglar-ne la causa. I preparar-ho per endavant: documentar a SECURITY.md qui pot autoritzar una excepció, amb quina justificació i amb quina data de revisió, perquè aquella decisió no s'improvisi a les onze de la nit.

Conclusió

El pipeline de Reservalia ha deixat de ser sis còpies del mateix YAML. Una composite action, .github/actions/preparar-node, concentra el checkout, la instal·lació de Node des de .nvmrc i l'npm ci amb memòria cau, parametritzada amb fetch-depth perquè el job seguretat obtingui l'historial complet sense duplicar res. Un reusable workflow, .github/workflows/reusable-build-publicar.yml, encapsula la construcció i la publicació amb inputs tipats, secrets declarats un a un —mai inherit— i un output amb el digest, de manera que un únic fitxer cobreix el cas "construir sense publicar" dels pull requests i el cas "publicar" de main. El ci.yml ha passat de 180 línies a 95, la memòria cau es configura en un sol lloc, i el mòbil i els microserveis del mòdul 5 seran quatre línies cadascun. Amb la regla que evita l'excés: a la tercera repetició s'extreu, i una abstracció que només es fa servir una vegada empitjora el pipeline. I sabem triar el mecanisme —composite action per a steps, reusable workflow per a jobs o pipelines complets, matrix per a la mateixa feina amb dades diferents—, quan compensa un repositori central i quin és el seu risc real: que el seu radi d'impacte és tota l'organització, motiu pel qual es consumeix per etiqueta i mai des de main, amb el seu propi pipeline, la seva revisió i el seu CHANGELOG. I tenim convencions que impedeixen tornar al pipeline que només entén una persona: noms explícits, env centralitzat, fitxers curts amb una responsabilitat i comentaris que expliquin el perquè.

El que més canvia, tanmateix, és l'últim. Reservalia ja no fusiona canvis de pipeline per veure què passa: els passa per actionlint a cada pull request —que va trobar tres errades el primer dia, entre elles un needs mal escrit que deixava un job sense executar-se en silenci—, els itera en local amb act sabent que serveix per depurar i no per validar, els executa en una branca de prova amb disparadors i entorns reals, i els aplica primer al servei de menys risc. Tot això per migració progressiva, en cinc passos que deixen el sistema funcionant a cadascun, sense congelar la feina de ningú. Queda el quart front, i és el que més desplegaments trenca. Tot el que s'ha construït en tres mòduls s'apuntala en una propietat que l'esquema de la base de dades no té: un artefacte és immutable i se substitueix per l'anterior en quatre minuts, però una columna esborrada no torna, i el rollback.yml de la 03-05 no sap desfer un DROP COLUMN. La lliçó següent, Bases de Dades al Pipeline: Migracions Segures, aborda l'estat compartit: migracions versionades executades des del pipeline i mai des d'un portàtil, el patró expand and contract desenvolupat pas a pas sobre un cas real de Reservalia, la classificació dels canvis d'esquema en segurs, perillosos i prohibits en calent, els bloquejos que un ALTER TABLE pot provocar en producció, i per què les dades de clients no han d'acabar mai a staging.

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