În prima parte a seriei am construit fundația: un pipeline care face build o singură dată, rulează testele și produce un artifact versionat. Am stabilit și principiul-cheie – build once, deploy many.
Acum vine partea în care acel artifact chiar ajunge undeva. Dar nu direct în producție. Îl trecem controlat prin medii separate – dev, staging, producție – cu o aprobare umană înainte de pasul final.
Întrebarea nu e doar cum ajunge codul în producție, ci cine apasă butonul și când.
🎯 Ce sunt GitHub Environments
Un environment în GitHub e o țintă logică de deploy – dev, staging, production – care are propriile reguli de protecție, propriile secrete și propriile variabile.
Practic, un environment îți dă trei lucruri:
-
Reguli de protecție – cine trebuie să aprobe, cât se așteaptă, de pe ce branch se poate face deploy.
-
Secrete și variabile per-mediu – connection string-ul de staging e altul decât cel de prod, iar fiecare stă doar în environment-ul lui.
-
Istoric de deploy-uri – vezi ce versiune e în fiecare mediu și cine a promovat-o.
🛠️ Cum creezi environments
În repo, mergi la Settings → Environments → New environment. Creează trei: dev, staging și production.
Pentru dev nu pui nicio restricție – vrei feedback rapid. Pentru staging poți pune restricții ușoare. Pentru production pui toate garanțiile: reviewers și branch restrictions. Asta e ideea – cu cât mediul e mai aproape de clienți, cu atât poarta e mai strictă.
🚦 Regulile de protecție (approval gates)
Aici stă toată valoarea. Pe environment-ul production activezi:
Required reviewers
Alegând până la 6 persoane (sau echipe) care trebuie să aprobe înainte ca deploy-ul să pornească. Asta e approval gate-ul: când pipeline-ul ajunge la job-ul de producție, se oprește și așteaptă ca cineva să apese Approve. Până atunci, nu se întâmplă nimic.
Wait timer
O întârziere obligatorie (de la câteva minute până la 30 de zile) înainte ca deploy-ul să continue. Util când vrei o fereastră de siguranță în care cineva poate anula.
Deployment branches
Restrângi din ce branch-uri se poate face deploy în acest mediu. Tipic: doar main ajunge în production. Așa, nimeni nu promovează accidental un branch de feature direct la clienți.
🔐 Secrete și variabile per-mediu
Fiecare environment își ține propriile secrete și variabile. Un secret definit pe production e vizibil doar unui job care rulează în environment-ul production – și doar după ce a trecut de approval gate.
Asta rezolvă elegant o problemă reală: nu mai ai un singur set global de secrete la care are acces orice job. Connection string-ul de producție e izolat în environment-ul lui.
Ce diferă între medii e configurația – secrete, connection strings, feature flags – nu artifactul. Binarul rămâne același.
🧩 Cum referi un environment în workflow
Legi un job de un environment cu proprietatea environment. Când job-ul pornește, GitHub aplică automat regulile de protecție ale acelui mediu:
jobs:
deploy-staging:
runs-on: ubuntu-latest
needs: build-test
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/download-artifact@v4
with:
name: app
path: ./publish
# ... pasul de deploy
Observă actions/download-artifact – nu reconstruim nimic. Luăm exact artifactul produs de job-ul de build și îl punem în staging. Asta e build once, deploy many în practică.
🏗️ Pipeline-ul complet: build → staging → producție
Punem totul cap la cap. Un job de build care produce artifactul, un deploy în staging, apoi un deploy în producție care așteaptă aprobare:
name: CI/CD
on:
push:
branches: [ main ]
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- run: dotnet build --configuration Release
- run: dotnet test --configuration Release --no-build
- run: dotnet publish ./src/Api/Api.csproj -c Release -o ./publish
- uses: actions/upload-artifact@v4
with:
name: app
path: ./publish
deploy-staging:
runs-on: ubuntu-latest
needs: build-test
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/download-artifact@v4
with:
name: app
path: ./publish
- name: Deploy to Azure (staging)
uses: azure/webapps-deploy@v3
with:
app-name: my-app-staging
package: ./publish
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment:
name: production
url: https://example.com
steps:
- uses: actions/download-artifact@v4
with:
name: app
path: ./publish
- name: Deploy to Azure (production)
uses: azure/webapps-deploy@v3
with:
app-name: my-app-prod
package: ./publish
Fluxul e liniar și controlat: build-test produce artifactul, deploy-staging îl pune în staging (needs: build-test), iar deploy-production îl așteaptă pe staging (needs: deploy-staging). Pentru că production are required reviewers, ultimul job se oprește și așteaptă aprobare.
Notă: pasul de autentificare la Azure (login-ul propriu-zis) l-am lăsat deoparte intenționat – în partea 5 vedem cum ne conectăm complet fără parole, cu Managed Identity și OIDC. Aici ne concentrăm pe mecanica mediilor.
👀 Cum arată aprobarea în practică
Când pipeline-ul ajunge la job-ul de producție, în tab-ul Actions apare butonul Review deployments. Reviewer-ul vede ce urmează să fie promovat, poate lăsa un comentariu și apasă Approve and deploy sau Reject.
Până la aprobare, artifactul stă cuminte – testat, gata, dar neinstalat în prod. Exact controlul pe care îl vrei înainte de un release.
✅ Câteva reguli sănătoase
-
dev fără restricții, production cu reviewers și branch restrictions – strictețea crește spre clienți.
-
Ține secretele în environment, nu global – fiecare mediu vede doar ce îi trebuie.
-
Niciodată rebuild per mediu – promovezi mereu același artifact.
-
Folosește url în environment ca să ai link direct către mediul deployat în istoricul rulărilor.
Concluzie
Environments transformă un simplu deploy într-un proces controlat: același artifact urcă prin dev → staging → producție, fiecare mediu cu secretele lui, iar înainte de prod stă un approval gate real. Nu mai ajunge nimic la clienți fără ca cineva să apese conștient butonul.
În partea 3 coborâm un nivel: în loc să creăm resursele Azure manual din portal, le definim în cod cu Bicep – ca deploy-ul să includă și infrastructura, nu doar aplicația.