RO EN

CI/CD profesionist cu GitHub Actions (1): anatomia unui pipeline serios

CI/CD profesionist cu GitHub Actions (1): anatomia unui pipeline serios ✨ Imagine generată cu AI
Doru Bulubasa
31 august 2026
26 vizualizări

Mulți developeri spun cu convingere că au „CI/CD” pentru că au un workflow care rulează un build la fiecare push. Sună cam așa:

Am pus un GitHub Actions care face dotnet build când dau push. Deci am CI/CD.

Un astfel de workflow e un început bun, dar nu e nici pe departe CI/CD profesionist. E doar un build automat.

Diferența dintre „am un workflow” și „am un pipeline serios” se vede exact atunci când ceva merge prost: un test care ar fi trebuit să blocheze merge-ul, un deploy care pică peste clienți în orele de vârf, un secret uitat în cod, sau un binar care ajunge în producție diferit de cel pe care l-ai testat.

În seria asta construim, pas cu pas, un pipeline profesionist pentru stack-ul .NET + Azure: medii separate cu aprobări, infrastructură din cod, deploy fără downtime și secrete fără parole. Articolul de față pune fundația – ce înseamnă, de fapt, un pipeline serios și cum arată scheletul lui.


🎯 Ce înseamnă, de fapt, CI/CD

Cei doi termeni sunt tot timpul lipiți, dar rezolvă probleme diferite.

CI – Continuous Integration

Fiecare modificare de cod (push sau pull request) declanșează automat un build și o suită de teste. Scopul: să prinzi problemele cât sunt mici și ieftine, nu peste două săptămâni în producție.

CD – Continuous Delivery / Deployment

Artifactul rezultat din CI e livrat automat mai departe, către medii reale. Aici e o distincție importantă:

  • Continuous Delivery: pipeline-ul pregătește totul pentru deploy, dar pasul spre producție cere o aprobare umană.

  • Continuous Deployment: dacă toate verificările trec, ajunge automat în producție, fără intervenție manuală.

Pentru majoritatea echipelor, varianta sănătoasă e Delivery: automatizezi tot, dar păstrezi un buton de aprobare înainte de prod. Exact asta construim în articolul 2.


⚙️ De ce GitHub Actions

Dacă ești deja pe GitHub, Actions e opțiunea cu cea mai mică fricțiune:

  • Pipeline-ul trăiește în repo, ca fișiere YAML în .github/workflows.

  • Ai runners gata pregătiți (Linux, Windows, macOS), fără să administrezi tu servere.

  • Marketplace cu action-uri reutilizabile pentru aproape orice.

  • Environments, secrets și approval gates native – nu ai nevoie de tool-uri externe.

Restul seriei presupune că lucrezi într-un repo GitHub cu o aplicație .NET și un abonament Azure.


🧩 Anatomia unui workflow

Un workflow GitHub Actions are trei niveluri pe care merită să le ai clare în cap:

  • Workflow – fișierul YAML întreg, declanșat de un eveniment.

  • Job – un grup de pași care rulează pe un runner. Job-urile pot rula în paralel sau în lanț.

  • Step – o singură acțiune: fie o comandă (run), fie un action reutilizabil (uses).

Totul pornește de la un trigger – secțiunea on. Iată cel mai simplu workflow util:

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Build
        run: dotnet build --configuration Release

Ce se întâmplă aici, pe scurt: la fiecare push sau pull request pe main, GitHub pornește un runner Ubuntu, îți clonează codul (checkout), instalează SDK-ul .NET și rulează build-ul. Atât – dar e deja un CI funcțional.


🏗️ Pipeline-ul de bază: build → test → publish

Un build fără teste nu îți dă încredere. Iar un build care nu produce un artifact nu are ce să livreze mai departe. Hai să extindem workflow-ul cât să facă toate cele trei lucruri:

name: CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  build-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Restore
        run: dotnet restore

      - name: Build
        run: dotnet build --configuration Release --no-restore

      - name: Test
        run: dotnet test --configuration Release --no-build --verbosity normal

      - name: Publish
        run: dotnet publish ./src/Api/Api.csproj -c Release -o ./publish

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: app
          path: ./publish

De ce pașii în ordinea asta

  • Restore separat – descarcă pachetele NuGet o singură dată; pașii următori refolosesc rezultatul.

  • Build cu --no-restore – nu mai reface restore-ul, deci e mai rapid și mai predictibil.

  • Test cu --no-build – rulează testele pe binarele deja compilate, nu recompilează. Dacă un test pică, job-ul pică – și pe un pull request asta blochează merge-ul.

  • Publish – produce output-ul gata de rulat (dll-uri, config, dependințe) într-un folder.

  • Upload artifact – urcă acel folder ca artifact al rulării, ca să poată fi luat mai târziu de job-urile de deploy.

Observă că build-ul și testarea stau într-un singur job, secvențial – pentru că fiecare pas depinde de rezultatul celui dinainte. Când vom adăuga deploy-ul, acela va fi un job separat, care așteaptă acest job și descarcă artifactul.


💾 Caching: nu re-descărca NuGet la fiecare rulare

Un pipeline care descarcă toate pachetele NuGet de la zero la fiecare rulare e lent și fragil. setup-dotnet știe să facă cache la pachete direct:

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
          cache: true
          cache-dependency-path: '**/packages.lock.json'

Ca să funcționeze cache-ul, ai nevoie de fișiere de lock. Le activezi punând în proiectele tale:

<PropertyGroup>
  <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>

După primul restore vei avea câte un packages.lock.json lângă fiecare .csproj – îl comiți în repo. De acum, rulările refolosesc pachetele deja descărcate și build-urile devin vizibil mai rapide.


🔁 Ideea centrală a seriei: build once, deploy many

Acesta e principiul care ține toată seria împreună, așa că merită subliniat:

Construiești artifactul o singură dată, apoi promovezi exact același artifact prin dev → staging → producție.

De ce contează atât de mult? Pentru că, dacă reconstruiești aplicația separat pentru fiecare mediu, binarul care ajunge în producție nu mai e cel pe care l-ai testat în staging. O versiune de dependință care se schimbă între timp, un SDK ușor diferit pe runner, o variabilă de build – și ai un artifact subtil diferit în prod.

Regula profesionistă e simplă: build o singură dată în CI, urci artifactul, iar deploy-ul în fiecare mediu înseamnă doar „ia acel artifact și pune-l acolo”. Ce diferă între medii e configurația (connection strings, feature flags), nu binarul.


🔒 Un cuvânt despre securitate

Înainte să mergem mai departe, trei obiceiuri de bază pe care le adoptăm din start:

  • Fixează versiunile action-urilor (actions/checkout@v4), nu folosi referințe care se pot schimba sub tine.

  • Dă pipeline-ului doar permisiunile de care are nevoie, cu blocul permissions în workflow.

  • Niciun secret în cod și niciun password în YAML – în articolul 5 vedem cum ne autentificăm la Azure complet fără parole, prin Managed Identity și OIDC.


🗺️ Ce urmează în serie

Acum că avem scheletul – build o dată, teste care blochează, artifact promovabil – construim peste el:

  • Partea 2 – Environments (dev/staging/prod) cu approval gates: promovezi același artifact prin medii, cu aprobare umană înainte de producție.

  • Partea 3 – Infrastructure as Code cu Bicep: creezi resursele Azure din cod, nu din portal.

  • Partea 4 – Deployment slots pentru zero-downtime: swap între staging și producție fără să pice nimic.

  • Partea 5 – Secrets fără parole: autentificare la Azure prin Managed Identity și OIDC, fără client secrets.


Concluzie

Un pipeline profesionist nu înseamnă mai mult YAML, ci decizii mai bune: construiești o singură dată, lași testele să blocheze ce trebuie blocat, produci un artifact versionat pe care îl promovezi mai departe și pregătești terenul pentru medii, aprobări și deploy sigur.

În articolul următor luăm acest artifact și îl trecem prin dev, staging și producție – cu un approval gate real înainte de prod.