ЁЯПл The SchoolтА║ЁЯУо CI/CDтА║ЁЯЫС рдзрдбрд╛ 08 тАФ Environments & рдордВрдЬреБрд░реАрдЪреЗ рдЧреЗрдЯ: рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреА рд╕рд╣реА
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯЫС рдзрдбрд╛ 08 тАФ Environments & рдордВрдЬреБрд░реАрдЪреЗ рдЧреЗрдЯ: рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреА рд╕рд╣реА

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 08 ┬╖ рдорд╛рдЧреАрд▓: lesson-07-build-push-image ┬╖ рдкреБрдвреАрд▓: lesson-09-deploy-strategies


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ07, рдЖрдгрд┐ рддреНрдпрд╛рд╢рд┐рд╡рд╛рдп рд╣рд┐рд░рд╡реА рдЦреВрдг рдЖрдгрд┐ рдореБрдЦреНрдп рд╕реВрдЪрдирд╛ рдлрд▓рдХ рдпрд╛рдВрдЪреНрдпрд╛рдордзрд▓реА рджреЛрди рджрд╛рд░реЗ: рдкреБрдврдЪреЗ рджрд╛рд░ (branch protection) рдЖрдгрд┐ рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреЗ рдХрд╛рд░реНрдпрд╛рд▓рдп (approval gate). рдЦрд▒реНрдпрд╛ files:

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╢рд╛рд│реЗрдд рджреЛрди рд╕реВрдЪрдирд╛ рдлрд▓рдХ рдЖрд╣реЗрдд. рд╢рд┐рдХреНрд╖рдХрд╛рдВрдЪреНрдпрд╛ рдЦреЛрд▓реАрддрд▓рд╛ рд╕рд░рд╛рд╡ рдлрд▓рдХ ЁЯУМ тАФ рдЗрдереЗ courier рдирд╡реА рд╕реВрдЪрдирд╛ рдЖрдзреА рд▓рд╛рд╡рддреЛ, рдореНрд╣рдгрдЬреЗ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рддреА рдиреАрдЯ рд╡рд╛рдЪрддрд╛ рдпреЗрддреЗ рдХрд╛ рддреЗ рддрдкрд╛рд╕реВ рд╢рдХрддреЗ. рд╕рднрд╛рдЧреГрд╣рд╛рддрд▓рд╛ рдореБрдЦреНрдп рдлрд▓рдХ рд╡рд░реНрдЧрд╛рдВрдирд╛ рджрд┐рд╕рддреЛ. CI рдордзреНрдпреЗ рд╣реЗ рдЖрд╣реЗрдд staging рдЖрдгрд┐ production тАФ рдпрд╛ рдХреЛрд░реНрд╕рдордзреНрдпреЗ, рдПрдХрд╛рдЪ cluster рд╡рд░рдЪреЗ рджреЛрди namespaces.

рджреЛрди рдлрд▓рдХрд╛рдВрдЪреНрдпрд╛ рдордзреНрдпреЗ рдЖрд╣реЗ рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреЗ рдХрд╛рд░реНрдпрд╛рд▓рдп ЁЯЫС. рдирд╛рд╡рд╛рдирд┐рд╢реА рд╡реНрдпрдХреНрддреА рд╕рд╣реА рдХрд░реЗрдкрд░реНрдпрдВрдд рд╡реНрд╣реЕрди рд╕реВрдЪрдирд╛ рдШреЗрдКрди рджрд╛рд░рд╛рдд рдерд╛рдВрдмрддреЗ, рдЖрдгрд┐ рддреА рд╕рд╣реА рдПрдХрд╛ рдиреЛрдВрджрд╡рд╣реАрдд рдЬрд╛рддреЗ: рдХреЛрдгреА, рдХреЗрд╡реНрд╣рд╛, рдХреЛрдгрддреА рд╕реВрдЪрдирд╛. рд╣реЗрдЪ approval gate тАФ required reviewer Approve рд╡рд░ click рдХрд░реЗрдкрд░реНрдпрдВрдд run production job рд╡рд░ рдерд╛рдВрдмрддреЛ.

рддреНрдпрд╛рдЪреНрдпрд╛рд╣реА рдЖрдзреА рдПрдХ рджрд╛рд░ рдЖрд╣реЗ. рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХрд╡рд░ тЬЕ рди рдорд┐рд│рд╛рд▓реЗрд▓рд╛ рдЧреГрд╣рдкрд╛рда "copy рдХрд░рд╛рдпрдЪреЗ" рдЯреНрд░реЗрдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВрдЪ рдирдпреЗ. Branch protection рдореБрд│реЗ test (node 22) check рд╣реА merge рдмрдЯрдгрд╛рдЪреА рдЕрдЯ рд╣реЛрддреЗ: рд▓рд╛рд▓ check, merge рдирд╛рд╣реА.

рд╣рд╛ рдорд╛рд░реНрдЧ trunk-based рдЖрд╣реЗ: рдЫреЛрдЯреЗ рдмрджрд▓ рд╡рд╛рд░рдВрд╡рд╛рд░ main рдордзреНрдпреЗ merge рд╣реЛрддрд╛рдд (branches рдЖрдард╡рдбреЗ рдирд╡реНрд╣реЗ, рддрд╛рд╕ рдЬрдЧрддрд╛рдд), рдЖрдгрд┐ build рд╣реЛрдКрди рдкрд╛рдард╡рд▓реА рдЬрд╛рдгрд╛рд░реА рдПрдХрдореЗрд╡ рдЧреЛрд╖реНрдЯ рдореНрд╣рдгрдЬреЗ main. рдЧреЗрдЯ рдореНрд╣рдгрдЬреЗ "рдХреЛрдгрддреА branch" рдирд╛рд╣реА тАФ рддрд░ "рдХреЛрдгрддрд╛ check рдкрд╛рд╕ рдЭрд╛рд▓рд╛, рдЖрдгрд┐ рдХреЛрдгреА рд╕рд╣реА рдХреЗрд▓реА".

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    pr["ЁЯФА pull request<br/>required check: test (node 22)"]
    main["ЁЯМ│ main<br/>protected branch"]
    stg["ЁЯУМ staging job<br/>environment: staging"]
    gate["ЁЯЫС production environment<br/>required reviewers, wait timer<br/>approval recorded"]
    prod["ЁЯУМ production job<br/>environment: production"]
    pr -->|"1 green check, merge"| main
    main -->|"2 ship builds and pushes, then"| stg
    stg -->|"3 pauses here"| gate
    gate -->|"4 a human clicks Approve"| prod

тЭУ рдХрд╛рдп

ЁЯза рджреЛрди рджрд╛рд░реЗ, рджреЛрди рдкреНрд░рд╢реНрди

рджрд╛рд░ рдХреБрдареЗ рдкреНрд░рд╢реНрди рдЙрддреНрддрд░ рдХреЛрдг рджреЗрддреЗ
рдкреБрдврдЪреЗ рджрд╛рд░ pull request тЖТ main рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХрдиреЗ тЬЕ рд╢рд┐рдХреНрдХрд╛ рдорд╛рд░рд▓рд╛ рдХрд╛? robot тАФ required status check
рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдЪреЗ рдХрд╛рд░реНрдпрд╛рд▓рдп staging тЖТ production рд╣реА copy рдореБрдЦреНрдп рдлрд▓рдХрд╛рд╡рд░ рдЬрд╛рд╡реА рдХрд╛? рдирд╛рд╡рд╛рдирд┐рд╢реА рдорд╛рдгреВрд╕ тАФ required reviewer

ЁЯдФ рдХрд╛

рд╣рд┐рд░рд╡реА test рд╕рд╛рдВрдЧрддреЗ рдХреА code рдиреАрдЯ рд╡рд╛рдЧрддреЛ; config, cluster, рдХрд┐рдВрд╡рд╛ рдкреНрд░рддреНрдпрдХреНрд╖ build рдЭрд╛рд▓реЗрд▓реНрдпрд╛ image рдмрджреНрджрд▓ рддреА рдХрд╛рд╣реАрдЪ рд╕рд╛рдВрдЧрдд рдирд╛рд╣реА. рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░рдЪреЗ рдЖрд╢реНрдЪрд░реНрдп staging рд╕реНрд╡рд╕реНрддрд╛рдд рдкрдХрдбрддреЗ. Approval рдореБрд│реЗ рдорд╛рдгрд╕рд╛рд▓рд╛ рдПрдХ рдХреНрд╖рдг рдорд┐рд│рддреЛ рдЖрдгрд┐ рд╢рд╛рд│реЗрд▓рд╛ рдПрдХ рдиреЛрдВрдж рдорд┐рд│рддреЗ: рдореБрдЦреНрдп рдлрд▓рдХ рдмрд┐рдШрдбрд▓рд╛ рдХреА "рдХреЛрдгреА рдХрд╛рдп, рдХреЗрд╡реНрд╣рд╛ approve рдХреЗрд▓реЗ" рд╣реЗ рд╢реЛрдзреВрди рдкрд╛рд╣рд╛рдпрдЪреЗ рдЕрд╕рддреЗ, рд╡рд╛рджрд╛рдЪрд╛ рд╡рд┐рд╖рдп рдирд╕рддреЛ. Branch protection рдореБрд│реЗ рдмрд╛рдХреА рд╕рдЧрд│реЗ рдЯрд┐рдХрддреЗ тАФ рдЬрд░ рди рддрдкрд╛рд╕рд▓реЗрд▓рд╛ code рд╕рд░рд│ main рдордзреНрдпреЗ рд╢рд┐рд░реВ рд╢рдХрдд рдЕрд╕реЗрд▓, рддрд░ production рд╡рд░рдЪреНрдпрд╛ рдЧреЗрдЯрд▓рд╛ рдлрд╛рд░рд╢реА рдХрд┐рдВрдордд рдирд╛рд╣реА. ArgoCD рд╢рд╛рд│реЗрдЪрд╛ рдзрдбрд╛ 03 рд╡реНрд╣реЕрди рдмрджрд▓рдгреНрдпрд╛рдЖрдзреА рдиреЗрдордХреНрдпрд╛ рдпрд╛рдЪ push pipeline рдкрд╛рд╕реВрди рд╕реБрд░реВ рд╣реЛрддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

рджреЛрди jobs, рджреЛрди environments. deploy.yml рдЖрдзреА staging, рдордЧ production рдЪрд╛рд▓рд╡рддреЗ; needs: рддреНрдпрд╛рдВрдЪрд╛ рдХреНрд░рдо рдард░рд╡рддреЗ, environment: рдирд┐рдпрдо рдЬреЛрдбрддреЗ:

  staging:
    runs-on: ubuntu-latest
    environment: staging                          # the practice notice board

  production:
    needs: staging
    runs-on: ubuntu-latest
    environment: production                       # ЁЯЫС required reviewers = the principal's signature

YAML рдордзреНрдпреЗ рдХреБрдареЗрд╣реА "approval рд╕рд╛рдареА рдерд╛рдВрдмрд╛" рдЕрд╕реЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдирд╛рд╣реА. File рдЪреНрдпрд╛ header рдордзреНрдпреЗ рдореНрд╣рдЯрд▓реНрдпрд╛рдкреНрд░рдорд╛рдгреЗ, production environment "must be created once in Settings тЖТ Environments with Required reviewers тАФ THAT checkbox is the approval gate" тАФ рдореНрд╣рдгрдЬреЗ рддреЛ рдПрдХрджрд╛ Settings рдордзреНрдпреЗ рддрдпрд╛рд░ рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ, рдЖрдгрд┐ рддреЛ checkbox рд╣реЗрдЪ approval gate рдЖрд╣реЗ.

рдерд╛рдВрдмрд╛ рдХрд╕рд╛ рдЪрд╛рд▓рддреЛ. Run production job рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд▓рд╛ рдХреА GitHub рддреНрдпрд╛ environment рдЪреЗ protection rules рддрдкрд╛рд╕рддреЛ. Required reviewers рд▓рд╛рд╡рд▓реЗрд▓реЗ рдЕрд╕рддреАрд▓ рддрд░ job Waiting рджрд╛рдЦрд╡рддреЛ рдЖрдгрд┐ run summary рдордзреНрдпреЗ Review deployments рдмрдЯрдг рдпреЗрддреЗ. рдпрд╛рджреАрддрд▓рд╛ reviewer approve рдХрд┐рдВрд╡рд╛ reject рдХрд░рддреЛ, рд╣рд╡реЗ рддрд░ comment рд╕рд╣; approve тЖТ job рддреНрдпрд╛ environment рдЪреНрдпрд╛ secrets рдЖрдгрд┐ variables рд╕рд╣ рд╕реБрд░реВ рд╣реЛрддреЛ, reject тЖТ job fail рд╣реЛрддреЛ. рд╣рд╛ рдирд┐рд░реНрдгрдп run рд╕реЛрдмрдд рдЬрдкрд▓рд╛ рдЬрд╛рддреЛ (summary рдордзреНрдпреЗ рдХреЛрдгреА approve рдХреЗрд▓реЗ рддреЗ рджрд┐рд╕рддреЗ) рдЖрдгрд┐ environment рдЪреНрдпрд╛ deployment history рдордзреНрдпреЗрд╣реА тАФ gh api repos/{owner}/{repo}/actions/runs/<id>/approvals рдиреЗ рд╕реБрджреНрдзрд╛ рд╡рд╛рдЪрддрд╛ рдпреЗрддреЛ.

рд╕реЗрдЯрдЕрдк (рдПрдХрджрд╛рдЪ, fork рдордзреНрдпреЗ). Settings тЖТ Environments тЖТ New environment тЖТ production тЖТ Required reviewers рд╡рд░ рдЦреВрдг рдХрд░рд╛ рдЖрдгрд┐ рд╕реНрд╡рддрдГрд▓рд╛ рдЬреЛрдбрд╛ тЖТ рд╣рд╡реЗ рддрд░ Wait timer тЖТ Deployment branches and tags тЖТ selected branches тЖТ main. staging рд╕реБрджреНрдзрд╛ рддрдпрд╛рд░ рдХрд░рд╛, reviewers рд╢рд┐рд╡рд╛рдп. Public repositories рдирд╛ рд╕рдзреНрдпрд╛рдЪреНрдпрд╛ plans рд╡рд░ protection rules рдорд┐рд│рддрд╛рдд; private рд╕рд╛рдареА paid plan рд▓рд╛рдЧреВ рд╢рдХрддреЛ тАФ GitHub рдЪреЗ docs рддрдкрд╛рд╕рд╛.

рдкреБрдврдЪреЗ рджрд╛рд░. Settings тЖТ Branches (classic) рдХрд┐рдВрд╡рд╛ Settings тЖТ Rules (rulesets) тЖТ main protect рдХрд░рд╛ тЖТ Require status checks to pass before merging тЖТ test (node 22) рдЬреЛрдбрд╛. рдордЧ рд▓рд╛рд▓ check рдЕрд╕рд▓реЗрд▓реНрдпрд╛ pull request рд╡рд░ Merging is blocked рджрд┐рд╕рддреЗ.

рд╡реНрд╣реЕрдирд▓рд╛ рдХреБрдареЗ рдмреЛрд▓рд╛рд╡рд▓реЗ рдЬрд╛рддреЗ. ship.yml рдордзреНрдпреЗ ЁЯЪЪ deliver job (uses: ./.github/workflows/deploy.yml) рдлрдХреНрдд vars.EKS_CLUSTER рд╕реЗрдЯ рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛рдЪ рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЕрд╕рддреЛ тАФ AWS рдирд╕рд▓реЗрд▓реНрдпрд╛ fork рд╡рд░ рддреЛ рдЬрд╛рдгреВрдирдмреБрдЬреВрди skip рд╣реЛрддреЛ.

ЁЯФБ рд╣реЗрдЪ CircleCI рдордзреНрдпреЗ
      - hold-for-approval:               # ЁЯЫС the principal's signature: click Approve in the UI
          type: approval
          requires: [deploy-staging]

Approval job рдореНрд╣рдгрдЬреЗ steps рдирд╕рд▓реЗрд▓реА рдЬрд╛рдЧрд╛ рд░рд╛рдЦрдгрд╛рд░реА рдЦреВрдг; project рд╡рд░ write access рдЕрд╕рд▓реЗрд▓реА рдХреЛрдгреА рд╡реНрдпрдХреНрддреА рддреНрдпрд╛рд╡рд░ click рдХрд░реЗрдкрд░реНрдпрдВрдд workflow рддрд┐рдереЗ рдерд╛рдВрдмрддреЛ, deploy-production рддреНрдпрд╛рд▓рд╛ requires: рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рддреЛ, рдЖрдгрд┐ workflow page рд╡рд░ рдХреЛрдгреА click рдХреЗрд▓реЗ рддреЗ рджрд┐рд╕рддреЗ.

ЁЯжК рд╣реЗрдЪ GitLab CI рдордзреНрдпреЗ
deploy-production:
  <<: *deploy
  needs: [deploy-staging]
  environment: { name: production }
  when: manual                                # ЁЯЫС the principal's signature: a human presses тЦ╢ in the UI

when: manual play рдмрдЯрдг рдХрд╛рдврддреЗ; environment: deployment рд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛ history рд╕рд╣ Operate тЖТ Environments рдЦрд╛рд▓реА рдиреЛрдВрджрд╡рддреЗ. Play рдХреЛрдг рджрд╛рдмреВ рд╢рдХрддреЗ рд╣реЗ рдорд░реНрдпрд╛рджрд┐рдд рдХрд░рдгреЗ рдореНрд╣рдгрдЬреЗ protected environment, рдЬреЗ рд╣реЗ рд▓рд┐рд╣рд┐рддрд╛рдирд╛ paid-tier feature рдЖрд╣реЗ.

ЁЯОй рд╣реЗрдЪ Jenkins рдордзреНрдпреЗ
    stage('ЁЯЫС approve') {
      options { timeout(time: 2, unit: 'DAYS') }
      steps { input message: 'Deploy to production?', ok: 'Approve' }   // the principal's signature
    }

input build рдерд╛рдВрдмрд╡рддреЗ рдЖрдгрд┐ approver рдЪреЗ рдирд╛рд╡ build log рдордзреНрдпреЗ рдпреЗрддреЗ. рджреЛрди рджрд┐рд╡рд╕рд╛рдВрдЪрд╛ timeout рд╡рд┐рд╕рд░рд▓реЗрд▓рд╛ build рдЕрдирд┐рд╢реНрдЪрд┐рдд рдХрд╛рд│ рдЙрдШрдбрд╛ рдареЗрд╡рдгреНрдпрд╛рдРрд╡рдЬреА abort рдХрд░рддреЛ; input рд▓рд╛ submitter: рдпрд╛рджреАрд╣реА рджреЗрддрд╛ рдпреЗрддреЗ, рдЬреА рдХреЛрдг click рдХрд░реВ рд╢рдХрддреЗ рддреЗ рдард░рд╡рддреЗ (# illustrative).

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

рд╣реЗ AWS рд╢рд┐рд╡рд╛рдп public fork рд╡рд░ рдЪрд╛рд▓рддреЗ. рдзрдбрд╛ 06 рдЪреЗ variables рдЖрдгрд┐ EKS_CLUSTER рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдпреЗрдИрдкрд░реНрдпрдВрдд deploy jobs рд╕реНрд╡рддрдГ skip рд░рд╛рд╣рддрд╛рдд, рдореНрд╣рдгреВрди staging рдкреНрд░рддреНрдпрдХреНрд╖ рдЪрд╛рд▓рд▓реНрдпрд╛рд╡рд░рдЪ Review deployments рдмрдЯрдг рджрд┐рд╕рддреЗ.

# 1) the front door: make "test (node 22)" a required check on main, for admins too
gh api -X PUT "repos/{owner}/{repo}/branches/main/protection" --input - <<< '{ "required_status_checks":
  { "strict": true, "contexts": ["test (node 22)"] }, "enforce_admins": true, "required_pull_request_reviews": null, "restrictions": null }'

# 2) open a PR that breaks a test and watch the merge get blocked
git checkout -b break-a-test
sed -i.bak 's/writeHead(404/writeHead(500/' app/server.js && rm app/server.js.bak
git commit -am "break the 404 test on purpose" && git push -u origin break-a-test
gh pr create --fill && gh pr checks --watch     # test (node 22) тЭМ
gh pr merge --squash                            # refused: the base branch policy prohibits the merge
git checkout main && gh pr close break-a-test --delete-branch

# 3) the principal's office: create the production environment with yourself as reviewer
gh api -X PUT "repos/{owner}/{repo}/environments/production" --input - <<JSON
{ "wait_timer": 0, "reviewers": [ { "type": "User", "id": $(gh api user --jq .id) } ],
  "deployment_branch_policy": { "protected_branches": true, "custom_branch_policies": false } }
JSON
# then open Settings тЖТ Environments тЖТ production in the browser and read the three switches

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

тПня╕П рдкреБрдвреЗ

рдкреНрд░рд╛рдЪрд╛рд░реНрдпрд╛рдВрдиреА рд╕рд╣реА рдХреЗрд▓реА. рдЖрддрд╛, рдордзреЗ рдлрд▓рдХ рд░рд┐рдХрд╛рдорд╛ рди рд░рд╛рд╣рддрд╛ рдирд╡реА рд╕реВрдЪрдирд╛ рдЬреБрдиреА рдХрд╢реА рдмрджрд▓рддреЗ тАФ рдЖрдгрд┐ рдХрд╛рд▓рдЪреА рдкрд░рдд рдХрд╢реА рд▓рд╛рд╡рд╛рдпрдЪреА? ЁЯФБ

git checkout lesson-09-deploy-strategies

ЁЯЫС Lesson 08 тАФ Environments & approval gates: the principal's signature

ЁЯУН You are here: Lesson 08 of 12 ┬╖ Previous: lesson-07-build-push-image ┬╖ Next: lesson-09-deploy-strategies


ЁЯУж What's in this branch

Lessons 01тАУ07, plus the two doors between a green check and the main notice board: the front door (branch protection) and the principal's office (the approval gate). Real files:

ЁЯзТ Explain like I'm 5

The school has two notice boards. The practice board ЁЯУМ in the staff room is where the courier pins a new notice first, so a teacher can check it reads right. The main board in the hall is what the classes see. In CI those are staging and production тАФ in this course, two namespaces on one cluster.

Between the boards sits the principal's office ЁЯЫС. The van waits at the door with the notice until a named person signs, and the signature goes in a log book: who, when, which notice. That is an approval gate тАФ the run pauses on the production job until a required reviewer clicks Approve.

There is an earlier door too. Homework that failed the checking desk тЬЕ shouldn't reach the "to copy" tray at all. Branch protection makes the test (node 22) check a condition of the merge button: red check, no merge.

The route is trunk-based: small changes merge into main often (branches live hours, not weeks), and main is the one thing that gets built and shipped. The gate is not "which branch" тАФ it is "which check passed, and who signed".

ЁЯЧ║я╕П Diagram

flowchart LR
    pr["ЁЯФА pull request<br/>required check: test (node 22)"]
    main["ЁЯМ│ main<br/>protected branch"]
    stg["ЁЯУМ staging job<br/>environment: staging"]
    gate["ЁЯЫС production environment<br/>required reviewers, wait timer<br/>approval recorded"]
    prod["ЁЯУМ production job<br/>environment: production"]
    pr -->|"1 green check, merge"| main
    main -->|"2 ship builds and pushes, then"| stg
    stg -->|"3 pauses here"| gate
    gate -->|"4 a human clicks Approve"| prod

тЭУ What

ЁЯза Two doors, two questions

door where the question who answers
front door pull request тЖТ main did the checking desk stamp тЬЕ? the robot тАФ a required status check
principal's office staging тЖТ production should this copy go on the main board? a named human тАФ a required reviewer

ЁЯдФ Why

A green test says the code behaves; it says nothing about the config, the cluster, or the image that was actually built. Staging catches that class of surprise cheaply. The approval buys a human a moment and buys the school a record: when the main board breaks, "who approved what, when" is a lookup, not an argument. Branch protection makes the rest hold тАФ a gate on production is worth little if untested code can walk into main. The ArgoCD school's lesson 03 starts from this exact push pipeline before it replaces the van.

ЁЯФз How (in this repo)

Two jobs, two environments. deploy.yml runs staging, then production; needs: orders them, environment: attaches the rules:

  staging:
    runs-on: ubuntu-latest
    environment: staging                          # the practice notice board

  production:
    needs: staging
    runs-on: ubuntu-latest
    environment: production                       # ЁЯЫС required reviewers = the principal's signature

Nothing in the YAML says "wait for approval". As the file's header puts it, the production environment "must be created once in Settings тЖТ Environments with Required reviewers тАФ THAT checkbox is the approval gate".

How the pause works. When the run reaches the production job, GitHub evaluates the environment's protection rules. With required reviewers set, the job shows Waiting and the run summary grows a Review deployments button. A listed reviewer approves or rejects, with an optional comment; approve тЖТ the job starts with that environment's secrets and variables, reject тЖТ the job fails. The decision is kept with the run (the summary shows who approved) and in the environment's deployment history тАФ also readable with gh api repos/{owner}/{repo}/actions/runs/<id>/approvals.

Setting it up (once, in the fork). Settings тЖТ Environments тЖТ New environment тЖТ production тЖТ tick Required reviewers and add yourself тЖТ optionally a Wait timer тЖТ Deployment branches and tags тЖТ selected branches тЖТ main. Create staging too, with no reviewers. Public repositories get protection rules on the current plans; private ones may need a paid plan тАФ check GitHub's docs.

The front door. Settings тЖТ Branches (classic) or Settings тЖТ Rules (rulesets) тЖТ protect main тЖТ Require status checks to pass before merging тЖТ add test (node 22). A pull request with a red check then shows Merging is blocked.

Where the van is called. In ship.yml the ЁЯЪЪ deliver job (uses: ./.github/workflows/deploy.yml) only exists once vars.EKS_CLUSTER is set тАФ on a fork without AWS it is skipped, by design.

ЁЯФБ The same thing in CircleCI
      - hold-for-approval:               # ЁЯЫС the principal's signature: click Approve in the UI
          type: approval
          requires: [deploy-staging]

An approval job is a placeholder with no steps; the workflow stops there until someone with write access to the project clicks it, deploy-production lists it in requires:, and the workflow page shows who clicked.

ЁЯжК The same thing in GitLab CI
deploy-production:
  <<: *deploy
  needs: [deploy-staging]
  environment: { name: production }
  when: manual                                # ЁЯЫС the principal's signature: a human presses тЦ╢ in the UI

when: manual draws the play button; environment: files the deployment under Operate тЖТ Environments with its history. Restricting who may press play is a protected environment, a paid-tier feature at the time of writing.

ЁЯОй The same thing in Jenkins
    stage('ЁЯЫС approve') {
      options { timeout(time: 2, unit: 'DAYS') }
      steps { input message: 'Deploy to production?', ok: 'Approve' }   // the principal's signature
    }

input pauses the build and the approver's name lands in the build log. The two-day timeout aborts a forgotten build instead of holding it open indefinitely; input also takes a submitter: list naming who may click (# illustrative).

ЁЯзк Try it

This works on a public fork without AWS. The deploy jobs themselves stay skipped until the lesson 06 variables and EKS_CLUSTER exist, so the Review deployments button appears only once staging has actually run.

# 1) the front door: make "test (node 22)" a required check on main, for admins too
gh api -X PUT "repos/{owner}/{repo}/branches/main/protection" --input - <<< '{ "required_status_checks":
  { "strict": true, "contexts": ["test (node 22)"] }, "enforce_admins": true, "required_pull_request_reviews": null, "restrictions": null }'

# 2) open a PR that breaks a test and watch the merge get blocked
git checkout -b break-a-test
sed -i.bak 's/writeHead(404/writeHead(500/' app/server.js && rm app/server.js.bak
git commit -am "break the 404 test on purpose" && git push -u origin break-a-test
gh pr create --fill && gh pr checks --watch     # test (node 22) тЭМ
gh pr merge --squash                            # refused: the base branch policy prohibits the merge
git checkout main && gh pr close break-a-test --delete-branch

# 3) the principal's office: create the production environment with yourself as reviewer
gh api -X PUT "repos/{owner}/{repo}/environments/production" --input - <<JSON
{ "wait_timer": 0, "reviewers": [ { "type": "User", "id": $(gh api user --jq .id) } ],
  "deployment_branch_policy": { "protected_branches": true, "custom_branch_policies": false } }
JSON
# then open Settings тЖТ Environments тЖТ production in the browser and read the three switches

тЪая╕П Common mistakes

тПня╕П Next

The principal signed. Now, how does the new notice replace the old one without a blank board in between тАФ and how do you put yesterday's back? ЁЯФБ

git checkout lesson-09-deploy-strategies
тЖР Previousbuild push imageNext тЖТdeploy strategies

This page is the lesson's README from the lesson-08-environments-gates branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.