Produktionsklara CI/CD-pipelines i GitHub Actions.
De flesta CI/CD-pipelines jag öppnar har samma historia. Någon byggde den på en eftermiddag för två år sedan, den fungerade, och sedan har lager efter lager lagts på utan att någon vågat röra grunden. Resultatet är en pipeline som tar elva minuter, faller på ett flaky test ungefär var tredje körning och som ingen riktigt förstår. Det går att göra bättre, och GitHub Actions ger dig nästan allt du behöver om du lägger upp det med eftertanke från början. Jag ska gå igenom hur jag tänker för Next.js, Python och Terraform.
Börja med vad pipelinen ska skydda
Innan jag skriver en rad YAML bestämmer jag vad pipelinen faktiskt ska garantera. En pipeline är ett kontrakt: om den är grön ska jag våga släppa koden. Allt som inte bidrar till det kontraktet är brus som saktar ner dig. För de flesta projekt landar jag i fyra saker som måste hålla innan en merge: koden kompilerar, testerna passerar, linting och typkontroll är rena, och en byggbar artefakt produceras. Allt annat kan köras parallellt eller efter merge.
Den distinktionen avgör vad som blir blockerande. Jag har sett team låta säkerhetsskanningar, tunga end-to-end-tester och bundle-analys blockera varje pull request, vilket gör att en trivial textändring tar tio minuter att få in. Lägg det blockerande på det som verkligen skyddar huvudgrenen, och kör det tyngre arbetet schemalagt eller på natten.
Cache är skillnaden mellan tre och tio minuter
Den enskilt största hävstången för hastighet är caching. Utan cache laddar varje körning ner och bygger om allt från noll. GitHub Actions har inbyggt cache-stöd, och de flesta officiella setup-actions hanterar det åt dig om du ber dem.
- Next.js: cacha din pakethanterares store mot lockfilen, och utnyttja Next bygg-cache mellan körningar så att bara det som ändrats kompileras om.
- Python: cacha det virtuella environmentet eller wheel-cachen mot din lockfil, så slipper du installera om hela beroendeträdet varje gång.
- Terraform: cacha provider-pluginerna så att terraform init inte laddar ner samma binärer vid varje körning.
En sak att vara vaksam på är cache-nycklar. En för bred nyckel ger dig gammal cache som introducerar svårfunna fel, en för smal nyckel träffar nästan aldrig. Jag nycklar alltid mot innehållet i lockfilen, inte mot en branch eller ett datum.
Matriser för att täcka fler kombinationer billigt
När du behöver testa mot flera versioner av en runtime, flera operativsystem eller flera databaser är matrisbyggen rätt verktyg. En matris expanderar ett jobb till en körning per kombination, parallellt. Det är billigt i tid eftersom körningarna går samtidigt, men dyrt i minuter om du inte begränsar dig. För Python testar jag sällan mot fler än två eller tre versioner, oftast den lägsta jag stödjer och den senaste, och låter mellanversionerna vara.
Ett vanligt misstag är att låta hela matrisen vara blockerande när bara en kombination egentligen speglar produktion. Markera de exotiska kombinationerna som tillåtna att misslyckas om de mest är ett tidigt varningssystem, så stoppar de inte din leverans.
Miljöer, godkännanden och hemligheter
GitHub Actions har ett begrepp som heter environments och som är underutnyttjat. En environment kan ha egna hemligheter, egna skyddsregler och ett krav på manuellt godkännande innan en deploy får köras. Det är så jag bygger en grind mellan staging och produktion utan att uppfinna något eget. Produktionsmiljön får ett krav på godkännande, och hemligheterna för produktion ligger bara där, oåtkomliga för pull request-körningar.
Just för Terraform är detta särskilt viktigt, eftersom en apply kan ändra verklig infrastruktur. Jag går allt oftare över till OIDC, där pipelinen byter en kortlivad GitHub-token mot temporära molncredentials, så att det inte finns någon statisk nyckel att läcka. Det ämnet hänger tätt ihop med hur du hanterar hemligheter i övrigt, och jag fördjupar det i min tjänst för DevOps-plattform.
Gör pipelinen läsbar för nästa person
En pipeline lever längre än du tror, och oftast är det inte du som felsöker den klockan halv sex en fredag. Därför lägger jag krut på läsbarhet. Namnge jobb och steg på vanlig svenska eller engelska, inte med kryptiska förkortningar. Bryt ut återkommande logik i composite actions eller reusable workflows så att du inte kopierar samma trettio rader till fem repon.
- Använd concurrency-grupper så att en ny push avbryter en pågående körning på samma branch i stället för att köa.
- Sätt timeouts på jobb så att en hängande process inte äter dina minuter i en timme.
- Lägg fail-fast med eftertanke, ibland vill du se alla fel i matrisen, inte bara det första.
Deploy ska vara tråkigt
Den bästa deployen är den du knappt märker av. När pipelinen väl är grön och godkänd ska driftsättningen vara identisk varje gång. Här lönar det sig att skilja på bygget och deployen: bygg en gång, märk artefakten med commit-hashen, och deploya exakt den artefakten till varje miljö. Att bygga om mellan staging och produktion är hur subtila skillnader smyger sig in. För Next.js betyder det en byggd artefakt, för Terraform en granskad plan som appliceras oförändrad.
Relaterat
- GitOps med ArgoCD vs Flux: Vilket passar er K8s-stack?
- SRE för 5-personers team: Error budgets, SLO:er och on-call light
- Trunk-based development på riktigt: Feature flags med PostHog/Unleash
Vill du se hur en sådan här pipeline ser ut i ett riktigt uppdrag har jag dokumenterat flera case i min casebook.
Vill du ta det vidare?
Om din pipeline tar för lång tid, faller för ofta eller skrämmer teamet hjälper jag gärna till att räta upp den. Hör av dig via kontaktsidan så tittar vi på den tillsammans.
“En pipeline är ett kontrakt: om den är grön ska jag våga släppa koden.”
- Simon Axelsson
Vanliga frågor
- Är GitHub Actions dyrt jämfört med andra CI-system?
- För privata repon betalar du per minut, men med ordentlig caching och vettiga timeouts är kostnaden oftast låg. Self-hosted runners kan sänka den ytterligare om du har stora byggen, men de tillför egen drift.
- Bör jag använda reusable workflows direkt?
- Inte från dag ett. Börja med en tydlig pipeline i ett repo, och bryt ut till reusable workflows först när du märker att du kopierar samma logik till flera projekt.
- Hur undviker jag flaky tester som stoppar leveransen?
- Isolera dem, kör dem i en egen icke-blockerande grupp och åtgärda grundorsaken. Att lägga in automatiska omkörningar döljer problemet och urholkar förtroendet för pipelinen.
