Les proves de la lliçó anterior responen a la pregunta "funciona?". Queda una altra pregunta, diferent i també important: "és aquest codi una cosa amb la qual puguem conviure d'aquí a un any?". Hi ha una família sencera de problemes que cap prova no detecta —codi incoherent, variables sense fer servir, promeses sense await, funcions de dues-centes línies, la mateixa lògica copiada en tres llocs— i que es paga a cada lectura futura. L'anàlisi estàtica examina el codi sense executar-lo i en detecta bona part en segons. En aquesta lliçó veurem per què aquesta feina pertany al pipeline i no a la revisió humana, en què es diferencien exactament Prettier, ESLint i tsc --noEmit amb exemples concrets del que cadascun atrapa i els altres no, com es configura el job qualitat de Reservalia amb --max-warnings 0, què és un quality gate i per què el criteri guanyador és "nou codi net" en comptes d'arreglar tot el deute de cop. Acabarem amb els ganxos de pre-commit i les convencions de commit. L'anàlisi de vulnerabilitats i el SAST de seguretat no hi entren: són la lliçó 04-03.
Contingut
- Per què l'anàlisi estàtica pertany al pipeline
- Formatatge, linting i comprovació de tipus: tres eines, tres problemes
- La configuració de Reservalia i el job
qualitat - Anàlisi més profunda: complexitat, duplicació i code smells
- Quality gates i el criteri de "nou codi net"
- Ganxos de pre-commit amb Husky i lint-staged
- Convencions de commit i la seva verificació automàtica
- Com començar en un projecte amb milers d'avisos
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Per què l'anàlisi estàtica pertany al pipeline
La Marta revisa un pull request d'en Diego i escriu sis comentaris: quatre sobre cometes simples davant de dobles i sagnat, un sobre una variable sense fer servir i un que assenyala que la nova ruta no valida l'identificador del negoci i permetria llegir cites alienes.
Els cinc primers comentaris són malbaratament. Els pot produir una màquina en dos segons, sense cansar-se, sense excepcions i sense que ningú s'ho prengui com una cosa personal. El sisè és la raó per la qual existeix la revisió humana.
El principi. Automatitza tot allò objectivable perquè la revisió humana pugui parlar de disseny, de seguretat i de si el problema s'ha entès bé.
Hi ha tres raons addicionals, i totes tres importen. Coherència sense discussions: quan l'eina decideix el format, deixa d'existir el debat —cap configuració de Prettier no és objectivament millor que una altra; el que és valuós és que n'hi hagi una—. Sense biaix ni cansament: una persona que revisa el cinquè PR del dia ja no veu les variables sense fer servir; l'eina veu sempre el mateix. Retroalimentació immediata i sense cost emocional: que un linter t'expliqui que falta un await és informació; que t'ho digui un company en un comentari públic és informació i una petita factura social.
- Formatatge, linting i comprovació de tipus: tres eines, tres problemes
Es confonen constantment. Són tres capes independents i complementàries.
| Prettier (formatatge) | ESLint (linting) | tsc --noEmit (tipus) |
|
|---|---|---|---|
| Pregunta que respon | Està escrit de manera uniforme? | Hi ha patrons problemàtics? | Encaixen els tipus? |
| Com decideix | Reimprimeix el codi des del seu AST | Regles sobre l'AST | Sistema de tipus complet |
| Corregeix sol? | Sempre | A vegades (--fix) |
Mai |
| És opinable? | Sí, però tant se val quina triïs | Sí, es configura per equip | No: és correcte o no ho és |
| Velocitat a Reservalia | ~2 s | ~15 s | ~20 s |
Vegem què detecta cadascun i els altres no.
Només Prettier. Aquest codi és perfectament vàlid i no té cap error de tipus ni cap mala pràctica; simplement està escrit de manera diferent a tota la resta del repositori:
Prettier el reescriu sense preguntar. Ni ESLint ni tsc no dirien res.
Només ESLint. Aquí no hi ha cap error de tipus i el format és impecable, però el codi està malament:
async function cancelarCita(id: number) {
enviarCorreuCancellacio(id); // ← retorna Promise i ningu no l espera
await marcarCancellada(id);
}tsc no protesta: cridar una funció asíncrona sense await és legal. Prettier tampoc: el format està bé. La regla @typescript-eslint/no-floating-promises ho detecta a l'instant, i és una fallada real: si l'enviament del correu falla, l'error es perd en el buit i ningú no se n'assabenta.
Només tsc. Aquí el format és correcte i no hi ha cap patró sospitós, però el programa està trencat:
import type { Cita } from '@reservalia/tipus-compartits';
function resum(cita: Cita): string {
return `${cita.negoci.nom} · ${cita.iniciHora}`; // ← el camp es diu 'inici'
}Només el sistema de tipus sap que Cita no té un camp iniciHora. És exactament el tipus d'error que en producció es manifesta com un undefined a la interfície del client.
La conclusió pràctica és que les tres capes són necessàries i que cap no substitueix les altres. I n'hi ha una quarta, complementària, que no hi competeix: l'anàlisi de complexitat i duplicació de l'apartat 4.
- La configuració de Reservalia i el job
qualitat
qualitatPrettier es configura a .prettierrc.json, a l'arrel del monorepo:
Quatre decisions que ningú no tornarà a discutir. L'important no és el seu contingut, sinó que estiguin escrites al repositori.
ESLint viu a eslint.config.js. Aquest és el nucli de la configuració de Reservalia:
export default [{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: { parser: tsParser, parserOptions: { project: './tsconfig.json' } },
rules: {
'@typescript-eslint/no-floating-promises': 'error', // 1
'@typescript-eslint/no-explicit-any': 'warn', // 2
'no-unused-vars': 'error',
'complexity': ['warn', 12], // 3
'max-lines-per-function': ['warn', 80],
'eqeqeq': 'error', // 4
},
}];no-floating-promisesés la regla de l'exemple anterior. En una aplicació plena d'operacions asíncrones —correus, base de dades, passarel·la de pagament— és la que atrapa més errors reals.no-explicit-anycom a avís, no error: convertir-la en error de cop trencaria el projecte sencer. El pla per pujar-la aerrorés l'apartat 8.complexityimax-lines-per-functionsón senyals de disseny, no de correcció; per això són avisos.eqeqeqprohibeix==. En JavaScript,'0' == 0és cert inull == undefinedtambé: comparacions que semblen innocents i no ho són.
I ara el job del pipeline:
qualitat:
name: Qualitat
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
# ... checkout, setup-node i npm ci ...
- name: Format
run: npx prettier --check . # 1
- name: Linting
run: npm run lint # 2 → eslint ... --max-warnings 0
- name: Comprovacio de tipus
run: npm run typecheck # 3 → tsc --noEmitprettier --check, no--write. En CI volem comprovar, no modificar: si el pipeline reformatés i tornés a empènyer, canviaria el commit que s'està verificant i crearia un embolic de traçabilitat. El missatge d'error indica el fitxer i l'ordre exacta per arreglar-ho en local.--max-warnings 0és la política més important del job, i ja venia alspackage.jsonde la lliçó 01-04. Sense ella, ESLint acaba amb codi de sortida 0 encara que hi hagi 300 avisos, el pipeline es posa verd i els avisos s'acumulen fins que ningú no els mira. Amb ella, el nombre d'avisos no pot créixer: cada avís nou posa el PR en vermell. La distinciówarn/errorcontinua sent útil per classificar la gravetat, però al pipeline tots dos bloquegen.typecheckva a part delbuild.tsc --noEmitnomés comprova i no escriu res, així que pot córrer en paral·lel amb les proves i dona retroalimentació abans.
Els tres passos són ràpids: el job qualitat complet triga menys d'un minut, i com que és un job independent, corre en paral·lel amb test i build.
- Anàlisi més profunda: complexitat, duplicació i code smells
Els linters miren fitxer a fitxer. Hi ha problemes que només es veuen amb perspectiva de projecte:
| Mesura | Què indica | Llindar orientatiu |
|---|---|---|
| Complexitat ciclomàtica | Nombre de camins possibles dins d'una funció | > 10-15 convé dividir |
| Complexitat cognitiva | Quant costa entendre-la (penalitza l'imbricament) | > 15 és difícil de llegir |
| Duplicació | Percentatge de línies repetides al projecte | > 3-5 % mereix atenció |
| Deute tècnic estimat | Temps que costaria arreglar el que s'ha detectat | Serveix per comparar mòduls |
| Code smells | Patrons que no són errors però avisen | La seva tendència, no el seu valor |
Un exemple real de Reservalia. La funció calcularForats va començar amb 20 línies; entre horaris partits, festius, durades variables i bloquejos manuals, ha arribat a 140 línies i a una complexitat ciclomàtica de 23. No falla cap prova —de fet està ben coberta— però cada canvi nou triga el doble i fa el doble de por. Això és exactament el que mesura la complexitat: el cost futur de tocar-la.
Compte amb els llindars: són senyals per conversar, no veritats. Una funció de complexitat 18 que tradueix una taula de tarifes pot ser perfectament llegible, i una de complexitat 8 amb tres nivells d'imbricament i noms críptics pot ser infernal.
- Quality gates i el criteri de "nou codi net"
Un quality gate és un conjunt de condicions que un canvi ha de complir per considerar-se acceptable. Eines com SonarQube (autogestionat) o SonarCloud (SaaS) l'avaluen i retornen un veredicte que el pipeline pot fer servir per bloquejar el merge.
La configuració mínima és un fitxer sonar-project.properties a l'arrel:
sonar.projectKey=reservalia_monorepo
sonar.sources=apps/api/src,apps/web/src,packages/tipus-compartits/src
sonar.tests=apps/api/tests,apps/web/tests
sonar.javascript.lcov.reportPaths=apps/api/coverage/lcov.info
sonar.qualitygate.wait=truesonar.qualitygate.wait=true fa que el step esperi el veredicte en comptes d'acabar tan bon punt puja les dades; sense això, el pipeline passaria sempre i el gate no bloquejaria res. El pas al job qualitat necessita un secret:
- name: Analisi de qualitat
uses: SonarSource/sonarcloud-github-action@v2
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}I aquí arriba la decisió que separa les adopcions que funcionen de les que fracassen. Reservalia té tres anys de codi i, el primer cop que passen Sonar, apareixen 1.847 avisos. Hi ha dos camins:
| Estratègia | Què exigeix | Resultat real |
|---|---|---|
| Arreglar tot el deute | Aturar-se setmanes a netejar 1.847 avisos | No es fa mai; el gate es desactiva |
| Nou codi net | Només el codi que aquest PR toca ha de complir | S'adopta el mateix dia i el deute baixa sol |
El criteri de "nou codi net" (clean as you code) aplica el gate únicament a les línies afegides o modificades al PR. Les seves condicions típiques:
- 0 errors nous i 0 vulnerabilitats noves al codi nou.
- Cobertura del codi nou ≥ 80 %.
- Duplicació del codi nou < 3 %.
Els avantatges són enormes: és assolible des del primer dia, l'equip no s'ha d'aturar, i com que cada PR toca codi antic per modificar-lo, el deute es redueix de manera natural per les zones que de debò es toquen —que són les que importen—. Un mòdul que ningú no ha obert en dos anys pot tenir 300 avisos; si ningú no el toca, no molesten ningú.
- Ganxos de pre-commit amb Husky i lint-staged
Un ganxo de pre-commit executa comprovacions a la teva màquina abans de crear el commit. El seu valor és el cicle curt: descobrir una fallada de format en 2 segons en comptes de en 3 minuts de pipeline. S'instal·la amb npm i -D husky lint-staged, npx husky init i un .husky/pre-commit que contingui npx lint-staged. La configuració va al package.json de l'arrel:
"lint-staged": {
"*.{ts,tsx}": ["prettier --write", "eslint --max-warnings 0 --fix"],
"*.{json,md,yml}": ["prettier --write"]
}La clau és al nom: lint-staged només processa els fitxers que has posat a l'índex, no tot el repositori. Per això triga un parell de segons en comptes de vint, i per això la gent no el desactiva.
Ara l'advertiment important: els ganxos locals NO substitueixen MAI el control del pipeline. Tres motius:
- Es poden saltar.
git commit --no-verifyels omet, i amb pressa s'utilitza. - Són a la màquina de cadascú. Qui cloni el repositori i no executi la instal·lació no té ganxos, i no se n'assabenta.
- Només veuen el que has tocat tu. Un canvi a
tipus-compartitspot trencarapps/websense que cap fitxer d'apps/websigui al teu índex.
El model correcte: els ganxos són una comoditat (retroalimentació ràpida i opcional); el pipeline és el control (obligatori i no evitable). Si n'has de triar només un, tria el pipeline.
- Convencions de commit i la seva verificació automàtica
Un historial de missatges tipus arranjaments, wip, ara sí no serveix de res. Commits convencionals imposa un format mínim:
<tipus>(<ambit>): <descripcio>
feat(cites): permetre reserves recurrents setmanals
fix(agenda): no oferir forats que se solapin amb el descans
chore(deps): actualitzar vitest a 1.6.0Els tipus habituals són feat, fix, chore, docs, test, refactor i perf. La verificació s'automatitza amb commitlint, tant en local (ganxo commit-msg) com al pipeline sobre els commits del PR. La configuració cap en un fitxer:
Per què molestar-s'hi? Perquè un format llegible per màquines habilita tres coses concretes que veurem més endavant: generar el changelog automàticament, deduir la versió següent (feat → menor, fix → pedaç, BREAKING CHANGE → major) —totes dues a la lliçó 02-06— i filtrar l'historial per respondre a "quines correccions van entrar en producció aquesta setmana?".
Un matís d'estil que convé fixar: si l'equip fa servir squash en fusionar (lliçó 02-07), el missatge que compta és el títol del PR, no els commits intermedis. En aquest cas, valida el títol del PR i deixa lliures els commits de treball.
- Com començar en un projecte amb milers d'avisos
Reservalia té 1.847 avisos de Sonar i uns 400 d'ESLint. Aquest és el pla que funciona, en quatre passos:
Pas 1: congelar la situació. Activa les eines amb --max-warnings 0 sobre una línia base existent, de manera que el número no pugui créixer. ESLint permet generar un fitxer de referència amb els avisos actuals; alternativament, comença aplicant les regles només a les carpetes ja netes i incorpora-hi les altres per fases.
Pas 2: formatar-ho tot d'una vegada. Executa npx prettier --write . en un commit propi, sense cap canvi funcional, i afegeix el seu SHA a .git-blame-ignore-revs perquè git blame continuï sent útil:
npx prettier --write .
git commit -am "chore: aplicar Prettier a tot el repositori"
git rev-parse HEAD >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revsÉs l'únic canvi massiu recomanable, perquè és mecànic i verificable: les proves continuen passant i cap línia no canvia de significat.
Pas 3: activar el gate només sobre el codi nou. El criteri de l'apartat 5. Des d'aquell moment, tot el que hi entra està net.
Pas 4: apujar el llistó per esglaons. Cada mes, una regla passa de warn a error quan el seu nombre d'incompliments ja és a prop de zero. A Reservalia l'ordre acordat és: no-floating-promises (ja en error), després no-unused-vars, després no-explicit-any.
El que no funciona: la setmana de neteja general. Produeix un diff de 20.000 línies impossible de revisar, un risc de regressió alt, i al cap de tres mesos el problema ha tornat perquè res no impedia que tornés.
Errors Comuns i Consells
Error 1: revisar format als pull requests. Si el teu equip discuteix cometes als comentaris de revisió, no li falta disciplina: li falta Prettier al pipeline.
Error 2: linter sense --max-warnings 0. El pipeline surt verd amb centenars d'avisos a dins i tothom aprèn a ignorar-los. És la manera més silenciosa de tenir anàlisi estàtica que no serveix de res.
Error 3: que el pipeline reformati i empenyi el resultat. Canvia el commit que s'està verificant, dispara noves execucions i enreda la traçabilitat de l'artefacte. En CI, --check; en local, --write.
Error 4: confiar només en els ganxos de pre-commit. Se salten amb --no-verify, no existeixen per a qui no els va instal·lar i només veuen els fitxers de l'índex.
Error 5: exigir el 100 % de netedat des del primer dia. Un gate inassolible es desactiva en dues setmanes i s'endú per davant també la part que sí que funcionava.
Consell 1: la mateixa configuració en local i en CI. Un .prettierrc.json i un eslint.config.js versionats, i les mateixes ordres npm als dos llocs. Si l'editor de la Marta formata diferent del pipeline, la guerra és eterna.
Consell 2: mesura el deute com a tendència. El nombre absolut d'avisos no diu res; que baixi mes a mes, sí. Publica'l al resum del job amb $GITHUB_STEP_SUMMARY.
Consell 3: quan desactivis una regla, deixa escrit per què. Un // eslint-disable-next-line sense explicació és una decisió perduda; amb una frase, és una decisió documentada.
Exercicis
Exercici 1
Per a cada fragment, digues quina eina el detecta (Prettier, ESLint o tsc --noEmit) i per què les altres dues no:
// A
const total = preu * quantitat
+ descompte;
// B
function buscar(id: string) { return cites.find(c => c.id === id); } // c.id es number
// C
cites.forEach(async (c) => { await notificar(c); }); // el forEach no esperaExercici 2
L'equip activa Sonar i apareixen 1.847 avisos. El responsable proposa: "dues setmanes d'aturada per deixar-ho a zero, i a partir d'aquí el gate exigeix zero avisos a tot el projecte". Argumenta-hi en contra i proposa un pla alternatiu amb passos concrets.
Exercici 3
En Diego proposa treure el job qualitat del pipeline perquè "ja tenim Husky amb lint-staged en local, i així estalviem un minut per PR". Dona tres arguments pels quals la proposta és perillosa.
Solucions
Solució 1.
- A → Prettier. És un problema purament de format: falta un punt i coma i el salt de línia és arbitrari. El codi és vàlid i amb tipus correctes, així que
tsccalla; no hi ha cap patró problemàtic, així que ESLint calla. - B →
tsc --noEmit. Compararc.id(número) ambid(cadena) mitjançant===dona semprefalse. Només el sistema de tipus coneix la forma deCita; el format és correcte i la construcció és idiomàtica, així que ni Prettier ni ESLint no hi tenen res a dir. (Amb la informació de tipus activada, alguna regla de@typescript-eslinttambé ho pot detectar, precisament perquè fa servir el mateix motor de tipus.) - C → ESLint.
forEachno espera les funcions asíncrones: el bucle acaba abans que s'enviï cap notificació. És codi vàlid (tsccalla) i ben formatat (Prettier calla), però està malament. Ho detectano-misused-promises.
Solució 2. Arguments en contra: (a) dues setmanes sense lliurar valor són un cost enorme i difícil de justificar; (b) un diff de milers de línies és irrevisable, i el risc d'introduir regressions en zones sense cobertura és alt; (c) exigir zero avisos a tot el projecte converteix qualsevol PR que fregui un fitxer antic en una tasca de neteja no planificada, cosa que empeny l'equip a esquivar el gate; (d) bona part d'aquests avisos són en codi que ningú no toca, així que arreglar-los no aporta valor real.
Pla alternatiu: (1) aplicar Prettier a tot en un commit mecànic i afegir-lo a .git-blame-ignore-revs; (2) activar --max-warnings 0 sobre la línia base actual perquè el número no pugui créixer; (3) configurar el quality gate en mode nou codi net amb cobertura ≥ 80 % i 0 errors nous; (4) pujar una regla de warn a error cada mes, començant per les de més impacte; (5) publicar la tendència mensual d'avisos perquè la millora sigui visible.
Solució 3. Tres arguments: (1) els ganxos se salten amb git commit --no-verify i no existeixen per a qui cloni el repositori sense instal·lar-los, així que no són un control sinó una comoditat; (2) lint-staged només analitza els fitxers de l'índex, de manera que un canvi a packages/tipus-compartits que trenqui apps/web passaria desapercebut —només un typecheck complet al pipeline ho detecta—; (3) sense el check obligatori al PR, la regla de protecció de branca de la 02-07 no té res a exigir, i el criteri de qualitat passa a dependre de la bona voluntat de cada persona. A més, l'estalvi és fals: el job qualitat corre en paral·lel amb test i build, així que no afegeix cap minut al temps total del pipeline.
Conclusió
El pipeline de Reservalia ja no només comprova que el codi funcioni, sinó que sigui sostenible:
- L'anàlisi estàtica pertany al pipeline i no a la revisió humana: automatitzar allò objectivable allibera la revisió per parlar de disseny, de seguretat i de si el problema s'ha entès bé.
- Prettier, ESLint i
tsc --noEmitresolen tres problemes diferents i cap no substitueix els altres: format uniforme, patrons problemàtics com una promesa senseawait, i coherència de tipus com un camp que no existeix. - El job
qualitatexecutaprettier --check(mai--writeen CI),npm run lintamb--max-warnings 0—la política que impedeix que el deute creixi— inpm run typecheck, en menys d'un minut i en paral·lel ambtestibuild. - L'anàlisi profunda aporta complexitat, duplicació i code smells: mesures del cost futur de tocar el codi, útils com a senyal per conversar i no com a veritat absoluta.
- Els quality gates només s'adopten si són assolibles. El criteri guanyador és "nou codi net": aplicar les condicions únicament a les línies que el PR afegeix o modifica, amb la qual cosa el deute es redueix sol per les zones que de debò es toquen.
- Els ganxos de pre-commit (Husky + lint-staged) donen retroalimentació en dos segons, però se salten amb
--no-verify, no existeixen per a qui no els va instal·lar i només veuen l'índex: són comoditat, mai control. - Les convencions de commit verificades amb commitlint fan l'historial llegible per màquines, i això habilita el changelog automàtic i el versionat deduït dels commits.
- I hi ha un pla realista per a un projecte amb 1.847 avisos: congelar la línia base, formatar-ho tot en un commit mecànic, aplicar el gate només al codi nou i apujar el llistó per esglaons.
Reservalia ja té tres jobs —qualitat, test i build— i una certesa raonable sobre cada commit. El que encara no té és alguna cosa per guardar: la imatge que construeix el job build s'evapora en acabar el runner. A la lliçó següent, Artefactes, Versionat i Promoció, veurem el principi de construir una vegada, desplegar moltes vegades, per què un artefacte immutable és la base de la traçabilitat, quina estratègia de versionat triar i per què latest és perillosa, com es promociona el mateix reservalia/api:a3f9c21 de dev a staging i a prod, i com saber des d'una màquina en producció quin commit està executant. I afegirem al ci.yml el job publicar.
Curs de CI/CD: Integració i Desplegament Continu
Mòdul 1: Introducció al CI/CD
- Conceptes Bàsics de CI/CD
- Beneficis del CI/CD
- Eines Populars de CI/CD
- El Projecte del Curs: l'Aplicació que Automatitzarem
- Mètriques DORA: Com es Mesura el Lliurament de Programari
Mòdul 2: Integració Contínua (CI)
- Introducció a la Integració Contínua
- Configuració d'un Entorn de CI
- Automatització de la Construcció
- Proves Automatitzades
- Qualitat de Codi i Anàlisi Estàtica
- Artefactes, Versionat i Promoció
- Integració amb el Control de Versions
Mòdul 3: Desplegament Continu (CD)
- Introducció al Desplegament Continu
- Automatització del Desplegament
- Infraestructura com a Codi i Entorns Reproduïbles
- Estratègies de Desplegament
- Feature Flags, Rollback i Recuperació davant Errors
- Monitoratge i Retroalimentació
Mòdul 4: Pràctiques Avançades de CI/CD
- Pipelines de CI/CD
- Gestió de Dependències
- Seguretat en CI/CD
- Escalabilitat i Rendiment
- Pipeline as Code: Plantilles, Reutilització i Proves del Pipeline
- Bases de Dades al Pipeline: Migracions Segures
Mòdul 5: Implementació de CI/CD en Projectes Reals
- Cas d'Estudi: Projecte Web
- Cas d'Estudi: Aplicació Mòbil
- Cas d'Estudi: Microserveis
- Cas d'Estudi: Modernitzar un Projecte Legacy
Mòdul 6: Eines i Tecnologies
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker i Kubernetes
- GitHub Actions a Fons
- Comparativa i Criteris per Triar Eina
Mòdul 7: Exercicis Pràctics
- Exercici 1: Configuració d'un Pipeline Bàsic
- Exercici 2: Integració de Proves Automatitzades
- Exercici 3: Desplegament en un Entorn de Producció
- Exercici 4: Monitoratge i Retroalimentació
- Exercici 5: Enfortir el Pipeline amb Seguretat i Secrets
- Projecte Final: Pipeline Complet d'Extrem a Extrem
