Hi ha una pregunta que aquest curs encara no ha fet i convé fer-la sense embuts: on és el codi de
MercadoFresco? La resposta honesta és que és al portàtil del Luis, a ~/proyectos/tienda, amb un
git init de fa catorze mesos i 1.847 commits que ningú més no ha vist. Hi ha una còpia comprimida en
un USB de setembre. La Marta té una carpeta tienda-v2-FINAL i no sap si és anterior o posterior. I
quan el Luis desplega, fa scp de la carpeta sencera a una instància de l'ASG
asg-mercadofresco-tienda i reinicia el servei a mà.
Tot el que s'ha construït en set mòduls descansa sobre un codi font que viu en un sol disc dur sense còpia fiable. Aquest mòdul ho arregla i comença pel principi: on viu el codi i com es governen els canvis.
Avís de cost. CodeCommit cobra 1 USD per usuari actiu i mes a partir del sisè, amb 50 GB i 10.000 peticions Git incloses: per a l'equip de tres de MercadoFresco, zero. Les connexions de CodeConnections cap a GitHub o GitLab també són gratuïtes; es paga l'ús aigües avall, que veurem a 08-02 i 08-04. Totes les dades i credencials d'aquesta lliçó són fictícies.
Contingut
- El punt de partida: un repositori en un portàtil
- Què aporta un repositori Git gestionat
- La situació de CodeCommit, sense adorns
- Comparativa: CodeCommit, GitHub, GitLab i Bitbucket
- CodeConnections: el pont entre AWS i el teu proveïdor Git
- Els repositoris de MercadoFresco
- Autenticació: HTTPS, SSH, l'ajudant de la CLI i rols
- Polítiques IAM: qui pot empènyer a
main - Estratègia de branques: trunk-based enfront de GitFlow
- El flux de branques de MercadoFresco
- Pull requests: revisió, aprovació i fusió
- Regles d'aprovació i protecció de
main - Convencions: commits, versió semàntica i etiquetes
- El
.gitignorei per què mai no es puja un.env - Notificacions i disparadors
- Migrar el repositori del Luis
- Cost i neteja
- Errors habituals i consells
- Exercicis
- Conclusió
El punt de partida: un repositori en un portàtil
La Marta va fer un inventari del risc abans de tocar res:
| Risc | Situació actual | Conseqüència |
|---|---|---|
| Pèrdua del portàtil | Còpia USB de fa 5 mesos | 5 mesos de feina perduts |
| Dues persones toquen el mateix | S'avisen pel xat | Sobreescriptura silenciosa |
| Saber què hi ha en producció | «El que vaig pujar divendres» | Ningú no reprodueix una fallada |
| Tornar a la versió anterior | Buscar a la paperera | Minuts o hores de caiguda |
| Revisar abans que entri | No es fa | Errors vistos en producció |
| Secrets al codi | Hi ha un .env amb la clau d'RDS |
Fuita en compartir el repositori |
Els quatre primers els resol qualsevol repositori remot. Els tres últims necessiten a més govern: regles sobre qui pot fer què i en quines condicions entra un canvi. Aquesta distinció és la lliçó sencera: allotjar el codi és la part fàcil.
Què aporta un repositori Git gestionat
Git és distribuït i res no obliga a tenir servidor, però tot equip designa una còpia com la de referència, i aquesta còpia ha d'estar disponible, tenir còpia de seguretat i controlar qui hi escriu.
| Aspecte | Servidor propi (EC2 + Gitea/GitLab CE) | Gestionat |
|---|---|---|
| Durabilitat | La de l'EBS; les còpies les fas tu | Replicat entre AZ, sense acció teva |
| Disponibilitat | Una instància = un únic punt de fallada | Acord de servei del proveïdor |
| Xifratge en repòs | El configures tu | Automàtic; a CodeCommit, amb KMS |
| Identitat | Usuaris de sistema o LDAP propi | IAM (CodeCommit) o OIDC/SAML |
| Pedaços i CVE | Teus | Del proveïdor |
| Cost real | Instància + EBS + el teu temps | Per usuari o inclòs |
| Auditoria | Registres del servidor, si els deses | CloudTrail o registre del proveïdor |
Per a tres persones, mantenir un servidor Git és temps que no va al producte: MercadoFresco no el munta.
Mereix un paràgraf el xifratge en repòs de CodeCommit, que connecta amb el mòdul 4: xifra sempre, sense
opció de desactivar-ho, i pot fer-ho amb una clau del client. Aquí això vol dir reutilitzar
alias/mercadofresco-datos, la mateixa clau que ja xifra Aurora i els buckets: una clau, una política, una
rotació i un únic lloc on revocar l'accés. És l'avantatge concret de la integració: no hi ha un
segon sistema de xifratge per governar.
La situació de CodeCommit, sense adorns
Des del juliol del 2024, AWS no admet nous clients a CodeCommit. Si el teu compte ja el feia servir abans, continua funcionant amb suport i correccions de seguretat. Si és nou, la consola no et deixarà crear repositoris. No hi ha data de tancament anunciada, però tampoc funcionalitats noves: la recomanació oficial per a projectes nous és un proveïdor Git extern connectat amb CodeConnections.
Tres conseqüències pràctiques: te'l trobaràs a empreses que el van adoptar entre el 2015 i el 2024 amb centenars de repositoris, així que saber-lo operar és útil; no l'escullis per a un projecte nou, ni encara que el teu compte antic t'ho permeti, perquè estaries construint sobre un servei sense evolució; i el que importa aquí no és el servei, són els conceptes —branques, pull requests, regles d'aprovació, protecció de branca, convencions de commit i de versió són idèntics a GitHub, GitLab o Bitbucket, i són el que consumiran 08-02, 08-03 i 08-04.
La Marta pren la decisió que prendries avui: el codi va a GitHub, i AWS s'hi connecta amb CodeConnections. La lliçó continua explicant CodeCommit perquè te'l trobaràs i perquè el seu model de permisos —IAM pur— és didàcticament molt clar.
Comparativa: CodeCommit, GitHub, GitLab i Bitbucket
| Criteri | CodeCommit | GitHub | GitLab | Bitbucket |
|---|---|---|---|---|
| Nous clients | No admesos | Sí | Sí | Sí |
| Identitat | IAM natiu | Pròpia; SSO de pagament | Pròpia; SSO | Atlassian |
| Revisió de codi | Funcional però pobra | La millor del mercat | Molt completa | Correcta |
| CI/CD propi | No (fa servir CodeBuild) | GitHub Actions | GitLab CI | Bitbucket Pipelines |
| Integració AWS | Nativa total | CodeConnections + OIDC | CodeConnections + OIDC | CodeConnections |
| Autoallotjable | No | Enterprise Server | Sí, self-managed | Data Center |
| Ecosistema | Nul | Enorme | Ampli i integrat | Lligat a Jira |
| Residència del codi | El teu compte i regió AWS | Servidors de GitHub | GitLab o el teu servidor | Atlassian |
| Cost 3 / 30 persones | 0 / ~25 USD mes | 0 / ~120 USD mes | 0 / ~870 USD mes | 0 / ~90 USD mes |
Criteri d'elecció, en l'ordre en què cal aplicar-lo:
- Hi ha exigència legal o contractual que el codi no surti del teu compte o de la UE? Si és innegociable, les teves opcions són CodeCommit (només si ja el tenies) o GitLab autoallotjat. És l'únic criteri que anul·la tots els altres.
- On és ja el teu equip? Moure gent que fa anys que treballa amb GitHub té un cost de fricció diari que es paga per sempre. Aquest criteri decideix la majoria dels casos.
- Faràs servir el CI/CD del proveïdor o el d'AWS? Actions i GitLab CI són excel·lents i despleguen a AWS amb OIDC sense claus; si prefereixes construcció i desplegament dins d'AWS —amb els seus rols, les seves VPC i la seva factura—, CodeConnections dóna el millor de tots dos móns.
- Quant ecosistema necessites? En escaneig de dependències, incidències i taulers, GitHub i GitLab guanyen sense discussió.
MercadoFresco: l'equip ja treballa amb GitHub, no hi ha restricció sobre el codi font (les dades de
clients sí que estan restringides a eu-west-1, però això és una altra cosa) i volen construcció i
desplegament a AWS per reutilitzar rols i alarmes. GitHub + CodeConnections + CodeBuild/CodeDeploy/CodePipeline.
CodeConnections: el pont entre AWS i el teu proveïdor Git
CodeConnections —fins al 2023, AWS CodeStar Connections; veuràs tots dos noms a la documentació i en ARN antics— és un recurs d'AWS que representa una autorització OAuth entre el teu compte i una organització de GitHub, GitLab o Bitbucket. Un cop creada, CodePipeline o CodeBuild llegeixen el repositori i en reben els esdeveniments sense que tu hagis d'emmagatzemar cap token enlloc.
Es crea en dos passos, i el segon és obligatòriament manual:
# Pas 1: crear la connexio. Neix en PENDING i encara no serveix per a res.
aws codeconnections create-connection \
--provider-type GitHub \
--connection-name conn-mercadofresco-github \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=cicd Key=Propietario,Value=marta Key=CentroCoste,Value=plataforma \
--profile mercadofresco-dev --region eu-west-1Pas 2, a la consola i només a la consola. Developer Tools → Settings → Connections, seleccionar la connexió pendent i prémer «Update pending connection». S'obre el flux OAuth de GitHub, s'instal·la l'app AWS Connector for GitHub a l'organització i s'escullen els repositoris autoritzats. Aquest pas no es pot automatitzar per disseny: és una autorització humana en un proveïdor extern, no una crida a l'API d'AWS. Si una plantilla de CloudFormation «crea connexions llestes», crea el recurs i algú l'autoritza a mà una vegada.
Comprova amb aws codeconnections list-connections que l'estat és AVAILABLE abans de continuar: una
connexió en PENDING és la causa número u que l'acció d'origen d'un pipeline falli amb un error
de permisos poc descriptiu, i hi tornarem a 08-04.
Concedeix accés només als repositoris necessaris. GitHub ofereix donar accés a tota l'organització; tria «Only select repositories» i marca els dos de MercadoFresco. És el privilegi mínim de 04-01 aplicat fora d'AWS.
Els repositoris de MercadoFresco
| Repositori | Contingut | Qui el toca | Cadència |
|---|---|---|---|
mercadofresco-tienda |
Web, API, consumidors de cues, codi de les Lambdes, proves, buildspec.yml, appspec.yml |
Luis | Diverses vegades al dia |
mercadofresco-infra |
Definició de la infraestructura, scripts d'operació, runbooks | Marta | Setmanal |
Separar-los permet desplegar l'aplicació vint vegades al dia sense arrossegar canvis d'infraestructura i
sotmetre un canvi de xarxa a una revisió més estricta. El contingut de mercadofresco-infra l'omplirem al
mòdul 9: avui només existeix el repositori.
# A CodeCommit
aws codecommit create-repository \
--repository-name mercadofresco-tienda \
--repository-description "Aplicacio web, API i consumidors de MercadoFresco" \
--kms-key-id alias/mercadofresco-datos \
--tags Proyecto=mercadofresco,Entorno=produccion,Componente=codigo,Propietario=luis,CentroCoste=plataforma \
--profile mercadofresco-dev --region eu-west-1
# A GitHub (l'opcio triada)
gh repo create mercadofresco/mercadofresco-tienda --private
gh repo create mercadofresco/mercadofresco-infra --private--kms-key-id és on es materialitza el xifratge amb clau pròpia: si algun dia revoques aquesta clau, el
repositori deixa de ser llegible, que és exactament el que vols davant d'una fuita. I el repositori és
sempre privat, sense «de moment públic, que ja ho canviarem»: els rastrejadors que busquen
credencials en repositoris públics triguen minuts, no dies.
Autenticació: HTTPS, SSH, l'ajudant de la CLI i rols
Aquí hi ha la diferència conceptual més interessant de CodeCommit: no hi ha usuaris de Git, hi ha identitats d'IAM. No existeix un «usuari luis» del servidor amb la seva contrasenya; existeix el seu usuari o rol d'IAM, i el permís per empènyer és una política. Donar de baixa algú és treure-li IAM, no recordar-se d'un segon sistema.
| Mètode | Com funciona | Quan | Inconvenient |
|---|---|---|---|
| Credencials HTTPS d'IAM | Usuari/contrasenya generats a IAM | Portàtil amb usuari IAM | Material de llarga durada |
| Claus SSH | Clau pública a l'usuari IAM | Portàtil, si prefereixes SSH | Igual |
| Ajudant de credencials | Git demana a la CLI una signatura SigV4 | Recomanat: SSO, MFA, rols | Requereix la CLI |
| Rol IAM | La màquina assumeix un rol, sense credencials | CodeBuild, EC2, Lambda | Només per a màquines |
Fes servir l'ajudant de credencials. La seva virtut és que no desa res: cada vegada que Git necessita
autenticar-se invoca aws codecommit credential-helper, que signa amb les credencials actuals del
perfil. Si vénen d'IAM Identity Center i caduquen al cap de vuit hores, l'accés caduca amb elles. És el
principi de credencials temporals de 04-01 aplicat al repositori.
git config --global credential.helper '!aws --profile mercadofresco-dev codecommit credential-helper $@'
git config --global credential.UseHttpPath true
git clone https://git-codecommit.eu-west-1.amazonaws.com/v1/repos/mercadofresco-tiendacredential.UseHttpPath true és obligatori i s'oblida constantment: sense ell, Git no envia la ruta del
repositori, la signatura es calcula sobre un recurs equivocat i obtens un 403 que no explica res.
A GitHub, l'equivalent modern per a les màquines és OIDC, no una clau d'accés. En lloc de
desar un AWS_ACCESS_KEY_ID com a secret del repositori, s'estableix confiança entre el proveïdor
OIDC de GitHub i un rol del teu compte, amb una condició sobre l'organització i el repositori concrets:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" },
"StringLike": { "token.actions.githubusercontent.com:sub": "repo:mercadofresco/mercadofresco-tienda:*" }
}
}]
}La condició sub impedeix que qualsevol repositori del món assumeixi el teu rol. Un StringLike amb repo:*
és un forat de seguretat greu que s'ha vist en producció més d'una vegada.
Polítiques IAM: qui pot empènyer a main
A CodeCommit la protecció de branca s'expressa com a política d'IAM, mitjançant la condició
codecommit:References. Aquesta política denega empènyer directament a main:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenegarEscrituraDirectaEnMain",
"Effect": "Deny",
"Action": ["codecommit:GitPush", "codecommit:DeleteBranch",
"codecommit:PutFile", "codecommit:DeleteFile"],
"Resource": "arn:aws:codecommit:eu-west-1:111122223333:mercadofresco-tienda",
"Condition": {
"StringEqualsIfExists": { "codecommit:References": ["refs/heads/main"] },
"Null": { "codecommit:References": "false" }
}
}]
}Tres detalls que sovint es copien malament. StringEqualsIfExists limita la denegació al que afecta
refs/heads/main: un push a funcionalidad/franja-horaria no hi encaixa i es permet.
Null: {"codecommit:References": "false"} significa «aplica només si la clau hi és present»; sense ella
denegaries també operacions sense referència de branca, i bloquejaries coses que no volies. I PutFile i
DeleteFile tanquen la porta de la consola web, que permet editar sense passar per Git i és el forat
que més s'oblida —en canvi, no deneguis les accions MergeBranchesBy*: són les que executen la
fusió d'una PR, i bloquejar-les impedeix tancar fins i tot les PR aprovades.
Deny guanya sempre sobre Allow, com vam veure a 04-01: això és efectiu encara que el Luis tingui adjuntada
AWSCodeCommitPowerUser. A GitHub el mecanisme canvia —regles de protecció de branca o rulesets— però
la intenció és idèntica.
Estratègia de branques: trunk-based enfront de GitFlow
| Aspecte | Trunk-based | GitFlow |
|---|---|---|
| Branques de llarga vida | Una: main |
main + develop + release/* + hotfix/* |
| Vida d'una branca de treball | Hores o 1-2 dies | Dies o setmanes |
| Conflictes de fusió | Petits i freqüents | Grans i dolorosos |
| Complexitat mental | Baixa | Alta; cal documentar-la |
| Lliurament continu | És la seva premissa | Malament: la branca de release fa nosa |
| Funcionalitat a mitges | S'amaga amb banderes | S'aïlla a la seva branca |
| Versions simultànies | Difícil | És el que resol bé |
GitFlow es va dissenyar el 2010 per a programari que es publica en versions: un instal·lador, una biblioteca, un producte que el client actualitza quan vol. Allà té sentit mantenir la 1.4 mentre desenvolupes la 1.5. El seu propi autor va publicar després una nota dient que si lliures de forma contínua —un web, un SaaS— probablement no el necessites.
MercadoFresco és una botiga web: només existeix una versió, la que és en producció ara. Una branca
release/* no aporta res i una develop que viu setmanes garanteix fusions doloroses entre tres
persones que toquen els mateixos fitxers.
Decisió de la Marta: trunk-based amb matisos. main sempre desplegable, branques de funcionalitat curtes,
i una branca desarrollo que existeix per una raó temporal: fins que el pipeline de 08-04 sigui madur i les
proves donin confiança, vol un punt on els canvis convisquin un dia abans d'arribar a main. És un
compromís conscient amb data de caducitat declarada: una bastida, no una catedral.
El flux de branques de MercadoFresco
gitGraph
commit id: "v1.4.0"
branch desarrollo
checkout desarrollo
commit id: "sync"
branch funcionalidad/franja-horaria
checkout funcionalidad/franja-horaria
commit id: "model comanda"
commit id: "API + proves"
checkout desarrollo
merge funcionalidad/franja-horaria id: "PR 42"
commit id: "fum dev OK"
checkout main
merge desarrollo tag: "v1.5.0"
branch correccion/iva-canarias
checkout correccion/iva-canarias
commit id: "fix IVA"
checkout main
merge correccion/iva-canarias tag: "v1.5.1"
checkout desarrollo
merge main id: "sync fix"
Les regles escrites, que viuen al CONTRIBUTING.md:
| Branca | Origen | Destinació | Vida | Qui fusiona |
|---|---|---|---|---|
main |
— | — | Permanent | Ningú directament |
desarrollo |
main |
main via PR |
Permanent | Marta, després de revisió |
funcionalidad/<n> |
desarrollo |
desarrollo via PR |
Màx. 3 dies | Autor, 1 aprovació |
correccion/<n> |
main |
main i desarrollo |
Hores | Autor, 1 aprovació |
experimento/<n> |
Qualsevol | S'esborra | Dies | No es fusiona |
El límit de tres dies no és burocràcia: una branca que viu dues setmanes divergeix tant que fusionar-la
deixa de ser una fusió i passa a ser una reintegració; si un canvi no cap en tres dies, es parteix. Les
correccions urgents surten de main, no de desarrollo, que arrossegaria canvis a mitges a
producció; i cal fusionar-les també cap a desarrollo, o la fallada reapareixerà en el següent
lliurament. I les branques s'esborren en fusionar: un repositori amb 140 branques mortes és un repositori
on ningú no troba res.
Pull requests: revisió, aprovació i fusió
Un pull request és la proposta de fusionar una branca en una altra, amb un espai per discutir-la abans que passi. És el que converteix «el Luis va pujar alguna cosa» en «l'equip va acceptar un canvi».
aws codecommit create-pull-request \
--title "Afegir franja horaria de lliurament a la comanda" \
--description "$(cat <<'EOF'
## Que canvia
Afegeix el camp `franja_entrega` (manana|tarde|24h) a la comanda i el propaga a l'esdeveniment
`PedidoConfirmado` com a atribut opcional.
## Per que
Peticio de la Sara: el 31 % de les incidencies de repartiment son per absencia del client.
## Com provar-ho
1. POST /pedidos amb "franja_entrega": "tarde"
2. Comprovar el missatge a cola-mercadofresco-pedidos
3. Comprovar que una comanda SENSE el camp continua funcionant (compatibilitat cap enrere)
## Riscos
Canvi d'esquema a Aurora: columna NULLABLE, sense valor per defecte, sense index nou.
Els consumidors antics ignoren l'atribut. Veure 08-05.
EOF
)" \
--targets repositoryName=mercadofresco-tienda,sourceReference=funcionalidad/franja-horaria,destinationReference=desarrollo \
--profile mercadofresco-dev --region eu-west-1Aquesta descripció és una plantilla real i val la pena copiar-la. Una PR sense descripció obliga el revisor a reconstruir la intenció llegint el diff, la manera més lenta i menys fiable d'entendre un canvi. Les quatre seccions es responen en tres minuts i n'estalvien vint al revisor.
Els comentaris poden ser generals o ancorar-se a una línia concreta del diff amb
post-comment-for-pull-request i el seu paràmetre --location filePath=app/pedidos.py,filePosition=147, que
és on passa la revisió útil: sobre la línia, no en un correu a part.
Les tres estratègies de fusió, idèntiques en qualsevol plataforma:
| Estratègia | Què fa | Història resultant | Quan |
|---|---|---|---|
| Fast-forward | Mou el punter, sense commit nou | Lineal, conserva tots els commits | Branca d'1 commit ja rebasada |
| Squash | Aixafa la branca en un commit | Lineal i neta: 1 PR = 1 commit | Per defecte en funcionalitats |
| Three-way | Commit de fusió amb dos pares | Ramificada, conserva el detall | Entre branques de llarga vida |
MercadoFresco fa servir squash per a les branques de funcionalitat i three-way per a desarrollo → main.
El raonament: els quinze commits de «wip», «arreglo el test» i «ara sí» no aporten res d'aquí a
sis mesos, i un git log de main on cada línia és un canvi complet i descrit és una eina
de diagnòstic excel·lent durant un incident; en canvi, en passar desarrollo a main interessa conservar
els commits individuals per poder revertir-ne un de sol. Una conseqüència del squash que sorprèn la
primera vegada: el commit resultant és nou i no comparteix identificador amb els de la branca, així que la
branca queda «no fusionada» als ulls de Git encara que el seu contingut hi sigui. Esborra-la igualment.
aws codecommit merge-pull-request-by-squash \
--pull-request-id 42 --repository-name mercadofresco-tienda \
--commit-message "Afegir franja horaria de lliurament a la comanda (#42)" \
--profile mercadofresco-dev --region eu-west-1Regles d'aprovació i protecció de main
Una PR que qualsevol fusiona sense que ningú se la miri és un formalisme. El que la converteix en un control real és una plantilla de regla d'aprovació: una política que s'aplica automàticament a les PR que compleixen un criteri i exigeix aprovacions d'un conjunt de persones.
aws codecommit create-approval-rule-template \
--approval-rule-template-name plantilla-mf-main-requiere-aprobacion \
--approval-rule-template-description "Tota PR cap a main necessita 1 aprovacio de l'equip" \
--approval-rule-template-content '{
"Version": "2018-11-08",
"DestinationReferences": ["refs/heads/main"],
"Statements": [{
"Type": "Approvers",
"NumberOfApprovalsNeeded": 1,
"ApprovalPoolMembers": ["arn:aws:sts::111122223333:assumed-role/rol-mercadofresco-desarrollo/*"]
}]
}' \
--profile mercadofresco-dev --region eu-west-1
# La plantilla no fa res fins que s'associa a un repositori
aws codecommit associate-approval-rule-template-with-repository \
--approval-rule-template-name plantilla-mf-main-requiere-aprobacion \
--repository-name mercadofresco-tienda \
--profile mercadofresco-dev --region eu-west-1DestinationReferences limita la regla a les PR cap a main: agilitat en el dia a dia, rigor a la
porta de producció. ApprovalPoolMembers admet el patró assumed-role/<rol>/*, que és el correcte
quan l'equip entra amb SSO i les identitats són sessions de rol, no usuaris IAM.
NumberOfApprovalsNeeded: 1 és el raonable per a tres persones; amb dues, el procés es bloquejaria
així que algú se n'anés de vacances. I l'autor no pot aprovar la seva pròpia PR: CodeCommit ho impedeix
de manera nativa i a GitHub cal activar-ho, però és la meitat del valor del mecanisme.
La segona condició —que la construcció estigui en verd— no es pot expressar aquí, perquè aquestes regles només compten aprovacions humanes. Hi ha dos camins: que CodeBuild aprovi la PR programàticament en acabar bé, o que la comprovació visqui al proveïdor Git com a required status check. Aquesta peça es tanca a 08-04, quan el pipeline existeixi i tingui alguna cosa a dir sobre la qualitat del canvi.
Convencions: commits, versió semàntica i etiquetes
Les convencions semblen cosmètica fins al dia en què un pipeline les llegeix i hi pren decisions.
El format és tipus(àmbit): descripció en imperatiu — feat(pedidos): afegir franja horaria de lliurament, fix(pagos): evitar el doble cobrament quan el proveidor respon 504, chore(deps): pujar boto3:
| Tipus | Significat | Efecte en la versió |
|---|---|---|
feat |
Funcionalitat nova | MINOR |
fix, perf |
Correcció o millora | PATCH |
refactor, docs, test, chore |
Sense canvi funcional | Cap |
Qualsevol amb BREAKING CHANGE: al cos |
Canvi incompatible | MAJOR |
El versionatge semàntic (MAJOR.MINOR.PATCH) és l'altra cara de la mateixa moneda: v1.5.0 enfront de
v1.4.3 diu què cal esperar. En una botiga on l'únic client és el teu frontal, el MAJOR importa poc; així
que publiques una API que consumeix l'ERP del magatzem, importa molt.
git tag -a v1.5.0 -m "Franja horaria de lliurament + memoria cau de cataleg"
git push origin v1.5.0
git log --oneline v1.4.3..v1.5.0El valor operatiu és que l'artefacte desplegat porta aquest número. Quan
mercadofresco-alb-latencia-alta salti a les 19:20 d'un divendres, la primera pregunta serà «quina versió
hi ha en producció?» i la segona «què ha entrat des de l'anterior?». Amb etiquetes es responen en deu
segons; sense, reconstruint dates i esperances.
El .gitignore i per què mai no es puja un .env
Un .gitignore correcte no és higiene: és control de fuites.
# Entorn i secrets - MAI al repositori
.env
.env.*
!.env.example
*.pem
*.key
credenciales*.json
# Python i artefactes de construccio
__pycache__/
.venv/
.pytest_cache/
.coverage
dist/
node_modules/
# Dades locals: poden contenir dades personals
*.csv
*.sql
volcado_aurora_*.dump
!datos/ejemplo_anonimo.csvLa línia !.env.example desexclou una plantilla de variables sense valors, que sí que hi ha de ser perquè
algú nou sàpiga quina configuració cal. I les últimes línies eviten que un bolcat d'Aurora amb
comandes reals acabi al repositori: això és un incident de protecció de dades, no un descuit.
Per què el .env és el pitjor de tots. Al portàtil del Luis n'hi ha un amb la contrasenya de mfadmin,
la clau de la passarel·la de pagaments i el token de l'ERP. Si entra una sola vegada al repositori:
queda per sempre a la història —esborrar-lo al commit següent no l'elimina, es recupera amb
git show, i netejar-lo de debò exigeix reescriure la història i forçar el push trencant tots els
clons—, es replica a cada clon, memòria cau de CI i còpia del proveïdor, i l'única resposta vàlida és
rotar les credencials: si el secret s'ha exposat està compromès, i tant se val quant va durar l'exposició.
La solució la vam construir a 04-03 i aquí només cal fer-la servir: contrasenyes a
mercadofresco/produccion/rds/mfadmin amb rotació automàtica cada 30 dies, i configuració no sensible
a Parameter Store sota /mercadofresco/produccion/. L'aplicació les llegeix en arrencar amb
rol-mercadofresco-tienda. Al repositori no hi ha ni un valor secret, només el nom del secret,
que no és sensible. I la rotació de 04-03 hi guanya un valor que llavors no era evident: posa data de
caducitat automàtica al dany d'una fuita. A 08-02 hi afegirem una xarxa més: una anàlisi que falla la
construcció si detecta alguna cosa amb pinta de credencial.
Notificacions i disparadors
Un repositori que ningú no mira és un repositori on les PR esperen quatre dies.
| Mecanisme | Què és | Casos típics |
|---|---|---|
| Disparadors | Configuració del repositori: en push, invoca SNS o Lambda |
Automatismes lligats al push |
| Notificacions | Regla sobre esdeveniments del servei cap a SNS o un xat | Avisar persones de PR i comentaris |
| EventBridge | Tots els esdeveniments de CodeCommit arriben al bus default |
Encaminament per contingut |
aws codecommit put-repository-triggers \
--repository-name mercadofresco-tienda \
--triggers '[{"name":"aviso-push-main",
"destinationArn":"arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco",
"branches":["main"],"events":["updateReference"]}]' \
--profile mercadofresco-dev --region eu-west-1I una regla d'EventBridge que aprofita tot el de 07-03, amb source: ["aws.codecommit"],
detail-type: ["CodeCommit Pull Request State Change"] i un filtre per detail.event per reaccionar a
pullRequestCreated. Si el que vols és avisar persones, no ho muntis amb un disparador: per a això
hi ha les notificacions, que porten el format fet i s'integren amb Slack sense escriure codi.
Hi ha un antipatró que convé anomenar: fer servir un disparador per llançar el desplegament. És temptador i
funciona el primer dia, però no hi ha construcció, ni proves, ni aprovació, ni artefacte versionat, ni
manera de reintentar. És l'scp del Luis amb més passos. El que ha de reaccionar al push és un pipeline,
i això és 08-04.
Migrar el repositori del Luis
El repositori local té 1.847 commits, tres branques i un .env rastrejat des del segon commit.
Pas 1: inventari. git branch -a, git log --oneline | wc -l i git count-objects -vH per saber
què hi ha i quant pesa.
Pas 2: buscar secrets a la història abans de pujar res. No és opcional.
git log --all --full-history --oneline -- .env
git log --all -p -S "AKIA" --oneline # clau d'acces d'AWS
git log --all -p -S "BEGIN RSA PRIVATE KEY" --onelineEl Luis troba .env en 312 commits. Dos camins: reescriure la història amb git filter-repo si el
repositori serà públic o compartit amb tercers, o pujar i rotar si és privat i l'equip és
petit. MercadoFresco tria la segona opció, però la rotació és innegociable i es fa abans del push, amb
aws secretsmanager rotate-secret --secret-id mercadofresco/produccion/rds/mfadmin i regenerant al
tauler del proveïdor la clau de la passarel·la i el token de l'ERP.
Pas 3: netejar l'arbre de treball.
git rm --cached .env # deixa de rastrejar-lo, no l'esborra del disc
printf '.env\n.env.*\n!.env.example\n' >> .gitignore
git add .gitignore && git commit -m "chore(seguridad): deixar de rastrejar .env i afegir .gitignore"Passos 4 i 5: remot i pujada, amb git remote add origin [email protected]:mercadofresco/mercadofresco-tienda.git
seguit de git push --mirror origin.
--mirror puja totes les referències —branques, etiquetes i notes— tal com són en local, que és el
que vols en una migració inicial. Dos advertiments: és destructiu a la destinació (esborra al
remot el que no existeixi en local, així que només contra un repositori buit i només la primera vegada), i
després no es torna a fer servir mai; el dia a dia és git push origin <branca>. Si vols control fi,
git push origin --all i git push origin --tags.
Pas 6: verificar clonant en net, el pas que la gent es salta i després lamenta. Clona a /tmp,
compta els commits —han de ser els 1.847—, revisa branques i etiquetes, i comprova que .env no hi és.
Passos 7 i 8: crear desarrollo, protegir main al proveïdor (PR obligatòria, una aprovació, sense
force push), i que la Marta i la Sara clonin. El repositori deixa d'estar en un portàtil el dia que hi ha
almenys dues còpies més i algú ha comprovat que funcionen.
Cost i neteja
| Concepte | Cost a MercadoFresco |
|---|---|
| CodeCommit, 3 usuaris i 240 MB | 0 USD (5 usuaris i 50 GB inclosos) |
| CodeConnections i GitHub amb repositoris privats | 0 USD |
| Notificacions via SNS | Cèntims |
| Total del govern del codi | ≈ 0 USD/mes |
La barrera per adoptar això mai no és econòmica: és l'hàbit. El que costa diners comença a 08-02.
# IRREVERSIBLE i sense paperera: clona amb --mirror a un disc abans d'executar-ho
aws codecommit delete-repository --repository-name pruebas-borrar \
--profile mercadofresco-dev --region eu-west-1Errors Habituals i Consells
Triar CodeCommit per a un projecte nou. No admet nous clients i no rep funcionalitats. Aprèn-ne els conceptes, però desplega a GitHub o GitLab.
Oblidar credential.UseHttpPath true. Produeix un 403 en clonar de CodeCommit que sembla un problema
de permisos IAM i no ho és. És el primer lloc on cal mirar.
Deixar la connexió de CodeConnections en PENDING. El recurs existeix, la CLI el llista i tot el que
en depengui falla amb un missatge confús. L'autorització al navegador és obligatòria i manual.
Desar claus de llarga durada com a secrets de GitHub. Fes servir OIDC amb una condició sub que anomeni
la teva organització i el teu repositori; un repo:* és un forat greu.
Denegar les accions MergeBranchesBy* per «protegir main». Ningú no pot fusionar ni tan sols una PR
aprovada, i al cap de dos dies algú demana treure la política sencera. Protegeix l'escriptura directa, no
la fusió governada.
Branques de funcionalitat que viuen tres setmanes. Cada dia extra multiplica el cost de fusió. Si el canvi és gran, parteix-lo en increments compatibles cap enrere. I adoptar GitFlow per costum en un producte de lliurament continu afegeix tres branques de llarga vida a canvi d'un problema que no tens.
Pujar un .env «només un moment per provar». Queda a la història per sempre i l'única resposta
correcta és rotar. No hi ha drecera.
Fusionar amb squash i després intentar revertir un commit intermedi. Ja no existeix: la branca sencera és un sol commit. És el preu conscient del squash.
Consell: escriu el CONTRIBUTING.md el primer dia, i fes servir .github/pull_request_template.md per
precarregar les quatre seccions. Mitja pàgina estalvia les mateixes cinc converses cada mes, i un revisor
amb context revisa el doble de ràpid.
Consell: activa l'esborrat automàtic de branques en fusionar, i etiqueta cada versió que arriba a producció sense excepcions: el dia de l'incident, la diferència entre deu segons i vint minuts de diagnòstic és en aquesta etiqueta.
Exercicis
Exercici 1: triar proveïdor amb criteri
Per a cada escenari, tria entre CodeCommit, GitHub, GitLab autoallotjat o Bitbucket i justifica-ho en tres frases anomenant el criteri decisiu:
(a) Empresa pública espanyola, 40 desenvolupadors. El plec exigeix que el codi resideixi en infraestructura sota el seu control dins de la UE amb auditoria completa d'accessos. Tenen compte AWS des del 2019 amb CodeCommit habilitat i 60 repositoris.
(b) Startup de 6 persones que arrenca avui. Tots vénen de GitHub, volen escaneig de dependències sense muntar-lo i desplegaran a AWS amb Lambda i S3.
(c) Equip de 25 persones en una empresa que fa servir Jira per a tot, amb auditories trimestrals que creuen incidències amb commits.
Exercici 2: la política que va bloquejar l'equip
La Marta aplica això al grup desarrolladores-mercadofresco per protegir main:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "codecommit:GitPush",
"Resource": "arn:aws:codecommit:eu-west-1:111122223333:mercadofresco-tienda",
"Condition": {
"StringNotEqualsIfExists": { "codecommit:References": ["refs/heads/main"] }
}
}]
}L'endemà el Luis no pot empènyer a la seva branca de funcionalitat, però sí que pot empènyer a main.
Respon: (a) què fa exactament i per què produeix aquest efecte; (b) escriu-la correctament;
(c) què li falta per protegir main de debò; (d) quina comprovació hauria detectat la fallada
abans d'aplicar-la.
Exercici 3: la migració amb sorpresa
En executar el pas 2, el Luis troba: un .env en 312 commits amb la contrasenya de mfadmin i la clau
de la passarel·la; un clientes_octubre.csv de 18 MB amb 4.200 noms, adreces i telèfons reals,
afegit fa nou mesos; i una branca experimento/pagos-bizum de fa un any sense fusionar.
Respon: (a) què fas amb cada troballa i en quin ordre; (b) quina fa que reescriure la història deixi de ser opcional, i per què; (c) l'ordre exacta i de què avises l'equip abans; (d) a qui cal informar dins de l'empresa per la segona troballa.
Solucions
Solució 1
(a) CodeCommit, i és l'únic escenari de l'exercici on té sentit. El criteri decisiu és el
primer de la llista: l'exigència legal de residència i control del codi, que anul·la tots els altres.
El compte és anterior al juliol del 2024, així que el servei està disponible; el codi viu al seu compte
d'AWS a eu-west-1, xifrat amb la seva clau de KMS, i CloudTrail dóna l'auditoria que demana el plec.
L'alternativa seria GitLab autoallotjat a la seva VPC, que compleix igual el requisit però els obliga
a operar i pedaçar el servidor: amb 60 repositoris ja migrats no compensa. El que sí que han de fer és
planificar la sortida a mitjà termini, perquè estan sobre un servei sense evolució.
(b) GitHub. Criteri decisiu: on és ja l'equip, reforçat per l'ecosistema. Sis persones que ja coneixen l'eina produeixen des del primer dia; Dependabot dóna l'escaneig de dependències sense muntar res; els repositoris privats són gratuïts a aquesta mida. Per desplegar a AWS, OIDC més CodeConnections ho cobreix tot sense emmagatzemar claus, i no hi ha restricció de residència que ho compliqui.
(c) Bitbucket. Criteri decisiu: la integració amb la resta d'eines. La vinculació nativa entre commits, branques i incidències de Jira és exactament el que consumeix la seva auditoria trimestral; amb GitHub caldria muntar i mantenir aquesta integració. GitLab seria superior en CI/CD, però aquí l'estalvi és al procés, no al pipeline; i amb 25 persones Bitbucket és a més el més barat.
Solució 2
(a) La condició està invertida. StringNotEqualsIfExists amb refs/heads/main significa «denega
quan la referència no sigui main». El resultat és literalment el contrari del desitjat: prohibeix
empènyer a qualsevol branca excepte main i deixa main completament oberta. És un error d'una sola
paraula —Not— amb conseqüències oposades a la intenció, i per això les denegacions amb condicions
negades cal llegir-les dues vegades en veu alta.
(b) La correcta fa servir StringEqualsIfExists per acotar la denegació a main, afegeix Null perquè no
afecti operacions sense referència, i amplia les accions:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenegarEscrituraDirectaEnMain",
"Effect": "Deny",
"Action": ["codecommit:GitPush", "codecommit:DeleteBranch",
"codecommit:PutFile", "codecommit:DeleteFile"],
"Resource": "arn:aws:codecommit:eu-west-1:111122223333:mercadofresco-tienda",
"Condition": {
"StringEqualsIfExists": { "codecommit:References": ["refs/heads/main"] },
"Null": { "codecommit:References": "false" }
}
}]
}(c) DeleteBranch impedeix esborrar main sencera; PutFile i DeleteFile tanquen l'edició des de la
consola web, que salta Git i és el forat més oblidat. Un push --force ja queda cobert perquè IAM no
el distingeix d'un GitPush. Però falta el que cap política no pot donar: una regla d'aprovació
associada al repositori que exigeixi almenys una aprovació a les PR cap a main, més —quan existeixi— la
comprovació que la construcció està en verd. Sense això, la política només obliga a passar per una PR; no
obliga que la PR sigui bona.
(d) El simulador de polítiques d'IAM de 04-01, o millor: aplicar-la primer a un repositori de proves
i executar els quatre casos —push a branca de funcionalitat, push a main, esborrat de branca, edició des
de la consola— comprovant que dos passen i dos fallen. Una denegació amb condicions mai no s'ha
d'estrenar al repositori real: la manera de fallar és que bloqueja l'equip o, pitjor, que no bloqueja res
i ningú no se n'assabenta.
Solució 3
(a) L'ordre importa perquè el primer pas fa irrellevant el temps dels altres. Un: rotar
immediatament els secrets del .env —contrasenya de mfadmin des de Secrets Manager, clau de la
passarel·la regenerada al tauler del proveïdor— abans de tocar el repositori; estan compromesos des de
fa catorze mesos i l'esborrat no els descompromet. Dos: el CSV, decidir la reescriptura i no pujar
res mentrestant. Tres: la branca experimento/pagos-bizum no es migra; si té valor s'exporta
amb git format-patch i es desa fora. Migrar branques mortes és arrossegar soroll.
(b) El CSV. El .env és greu però acotat: es resol rotant, perquè un secret rotat ja no val
res encara que continuï a la història. Un fitxer amb 4.200 noms, adreces i telèfons reals no es
pot rotar. És dada personal sota el RGPD, i el principi de minimització més el dret de supressió
fan inacceptable conservar-lo indefinidament en un repositori de codi, on es replicarà a cada
clon, cada memòria cau de CI i cada còpia del proveïdor. Aquí reescriure deixa de ser una opció tècnica i
passa a ser una obligació.
(c)
cp -r ~/proyectos/tienda ~/proyectos/tienda-copia-seguridad # filter-repo es irreversible
cd ~/proyectos/tienda
git filter-repo --path datos/clientes_octubre.csv --invert-paths
git filter-repo --path .env --invert-paths # de passada, ja que es reescriugit filter-repo canvia el hash dels commits afectats i de tots els seus descendents, així que cal
avisar que qualsevol clon existent queda inservible i s'ha de tornar a clonar: fusionar un clon antic amb la
història reescrita crea un desastre de commits duplicats. Aquí la migració encara no ha passat i només
existeix el clon del Luis, així que l'impacte és nul —raó excel·lent per netejar abans de pujar—. Si ja
fos al remot, a més caldria push --force, esborrar branques i etiquetes obsoletes del remot i
demanar al proveïdor que purgui els objectes inabastables.
(d) Al responsable de protecció de dades (DPO o equivalent). Dades personals de 4.200 clients fora
dels sistemes autoritzats durant nou mesos són un tractament no previst que cal documentar i
avaluar. Potser no és una bretxa notificable a l'AEPD —el repositori era privat i local—, però aquesta
valoració no la fa l'equip tècnic: s'aporta la informació completa (quines dades, quant temps,
qui hi va tenir accés, què s'ha fet) i decideix qui en té la responsabilitat. És el mateix criteri que
vam aplicar a 06-03 amb el clon aurora-mf-pruebas-luis.
Conclusió
El codi de MercadoFresco ha sortit del portàtil del Luis. Ja no és un git init amb una còpia USB de
setembre: és un repositori remot, replicat, xifrat, amb permisos d'IAM en lloc d'usuaris de
sistema i amb almenys tres còpies vives a l'equip. Els quatre primers riscos de l'inventari de la Marta
estan tancats pel simple fet d'haver pujat el codi.
Però l'important d'aquesta lliçó no és l'allotjament, és el govern. Saps que CodeCommit ja no
admet nous clients i per què això no invalida aprendre'l —te'l trobaràs—, i que la resposta
actual és GitHub o GitLab connectats amb CodeConnections, amb la seva autorització manual al navegador
que tantes accions d'origen trenca quan s'oblida. Tens el criteri d'elecció en ordre: primer
l'exigència legal si existeix, després on és ja el teu equip, després on vols que visqui el CI/CD, i per
últim l'ecosistema. Saps autenticar-te amb l'ajudant de credencials —amb credential.UseHttpPath true, que evita el 403 misteriós— i amb OIDC en lloc de claus de llarga durada per a les màquines.
I saps escriure una política que protegeix main sense bloquejar l'equip, i per què la condició Null
acompanya sempre StringEqualsIfExists.
Has triat trunk-based amb una branca desarrollo declarada com a bastida temporal, perquè una botiga
web només té una versió —la que és en producció ara— i GitFlow resol un problema que
MercadoFresco no té. Saps què és una PR que serveix —què, per què, com provar-ho, riscos—, quan fer servir
squash i quan three-way, i com una plantilla de regla d'aprovació converteix la revisió en un
control real gràcies al fet que l'autor no es pot aprovar a si mateix. Tens les convencions que un pipeline
llegirà més endavant: commits convencionals, versió semàntica i etiquetes que responen en deu
segons a «què hi ha en producció». I tens la regla que no admet excepcions: el .env no puja mai,
perquè queda a la història per sempre i l'única resposta vàlida és rotar.
Ara hi ha un repositori amb regles. El que no hi ha és cap garantia que el que hi entra
funcioni. L'aprovació de la PR significa que una persona ha llegit el diff un dimarts a la tarda, la
qual cosa és molt més que res i força menys que suficient. Ningú no ha executat les proves: el Luis les
llança al seu portàtil «gairebé sempre», i l'última vegada que en va fallar una va decidir que era cosa
del seu entorn. Ningú no ha comprovat que les dependències no tinguin vulnerabilitats conegudes, ni que
no s'hagi colat una clau al codi, ni que el projecte tan sols arrenqui en una màquina neta. La frase
«funciona al meu portàtil» continua sent un argument admissible a MercadoFresco, i encara no existeix cap
artefacte: el que es desplega és una carpeta copiada amb scp.
A 08-02, «AWS CodeBuild», això s'acaba. Veurem què és la integració contínua i per què converteix
«funciona al meu portàtil» en una frase sense valor; l'anatomia d'un projecte de construcció amb el seu
entorn, el seu rol de servei, la seva memòria cau i els seus artefactes; un buildspec.yml complet i
comentat amb les fases install, pre_build, build i post_build; com injectar secrets des de
Parameter Store i Secrets Manager sense que apareguin als registres; les proves unitàries amb pytest i els
informes de cobertura com a porta de qualitat i no com a informe que ningú no llegeix; l'escaneig de
dependències i de credencials filtrades; la construcció del paquet que anirà al bucket
mercadofresco-artefactos; i com executar la construcció dins de vpc-mercadofresco per provar contra el
clon aurora-mf-pruebas-luis, amb l'advertiment de cost que això comporta.
Curs d'AWS
Mòdul 1: Introducció a AWS
- Què és AWS?
- Configuració del teu compte d'AWS
- Infraestructura global d'AWS
- Consola d'administració d'AWS
- AWS CLI i SDK
Mòdul 2: Serveis principals d'AWS
Mòdul 3: Xarxes i lliurament de contingut
- Amazon VPC
- Grups de seguretat i llistes de control d'accés
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Mòdul 4: Seguretat i identitat
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager i Parameter Store
- AWS Shield
- AWS WAF
Mòdul 5: Monitoratge i gestió
- Amazon CloudWatch
- AWS X-Ray i traçabilitat distribuïda
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Mòdul 6: Bases de dades
- Com triar la base de dades adequada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Mòdul 7: Integració d'aplicacions
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrons d'integració: idempotència, reintents i cues de missatges fallits
