ЁЯУж рдзрдбрд╛ 07 тАФ image build рдЖрдгрд┐ push рдХрд░рд╛: рдлреЛрдЯреЛрдХреЙрдкреА рдорд╢реАрди
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 07 ┬╖ рдорд╛рдЧреЗ: lesson-06-secrets-oidc ┬╖ рдкреБрдвреЗ: lesson-08-environments-gates
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ06, рдЖрдгрд┐ рдлреЛрдЯреЛрдХреЙрдкреА рдбреЗрд╕реНрдХ: рддрдкрд╛рд╕рд▓реЗрд▓рд╛ рдЧреГрд╣рдкрд╛рда рдбрдмрд╛ рдмрдирддреЛ, рддреНрдпрд╛рд▓рд╛ commit рдЪрд╛ рдард╕рд╛ рдорд┐рд│рддреЛ, рдЖрдгрд┐ рддреЛ рд▓реЙрдХрд░рдордзреНрдпреЗ рдареЗрд╡рд▓рд╛ рдЬрд╛рддреЛ. рдЦрд▒реНрдпрд╛ files:
- .github/workflows/ship.yml тАФ
buildjob (build, smoke-test, save) рдЖрдгрд┐pushjob (load, рдмреЕрдЬ, ECR login, tag, push) - app/Dockerfile рдЖрдгрд┐ app/server.js тАФ
ARG APP_VERSIONрдЖрдд рднрд╛рдЬреВрди рдмрд╕рд╡рд▓реЗ рдЬрд╛рддреЗ, рдЖрдгрд┐/version тАжрдЫрд╛рдкрддреЗ рдореНрд╣рдгрдЬреЗ рдЪрд╛рд▓реВ copy рдЖрдкрдг рдХреЛрдгрддрд╛ commit рдЖрд╣реЛрдд рддреЗ рд╕рд╛рдВрдЧреВ рд╢рдХрддреЗ - .circleci/config.yml, .gitlab-ci.yml, Jenkinsfile тАФ рддреЗрдЪ build, smoke, tag, push рдкреНрд░рддреНрдпреЗрдХреА рдПрдХрд╛рдЪ job рдордзреНрдпреЗ
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рддрдкрд╛рд╕рдгреА рдбреЗрд╕реНрдХрдиреЗ тЬЕ рдЧреГрд╣рдкрд╛рдард╛рд╡рд░ рд╢рд┐рдХреНрдХрд╛ рдорд╛рд░рд▓рд╛. рдЖрддрд╛ рдлреЛрдЯреЛрдХреЙрдкреА рдорд╢реАрди ЁЯН▒ рд╕реВрдЪрдирд╛ рдлрд▓рдХрд╛рдВрд╡рд░ рдЬрд╛рдгрд╛рд░реА рдкреНрд░рдд рдХрд╛рдврддреЗ, рдЖрдгрд┐ рдпрд╛ рдбреЗрд╕реНрдХрдЪреНрдпрд╛ рддреАрди рд╕рд╡рдпреА рдЖрд╣реЗрдд.
рдкреНрд░рдд рдХрд╛рдврддрд╛рдирд╛рдЪ рддреНрдпрд╛рд╡рд░ рдард╕рд╛ рд▓рд┐рд╣рд╛. Commit рд▓рд╛ 40 рдЕрдХреНрд╖рд░рд╛рдВрдЪрд╛ рдард╕рд╛ рдЕрд╕рддреЛ, рддреЛ рдореНрд╣рдгрдЬреЗ
SHA. рддреНрдпрд╛рдЪреА рдкрд╣рд┐рд▓реА 7 рдЕрдХреНрд╖рд░реЗ рдбрдмреНрдпрд╛рдЪреНрдпрд╛ рдЖрдд APP_VERSION рдореНрд╣рдгреВрди рдЬрд╛рддрд╛рдд, рдореНрд╣рдгрдЬреЗ рдЪрд╛рд▓реВ ре▓рдк
"рддреВ рдХреЛрдгрддреА рдкреНрд░рдд рдЖрд╣реЗрд╕?" рдпрд╛рдЪреЗ рдЙрддреНрддрд░ рджреЗрдК рд╢рдХрддреЛ. рдкреВрд░реНрдг SHA label рд╡рд░ рдЬрд╛рддреЛ тАФ image рдЪрд╛ tag тАФ рдореНрд╣рдгрдЬреЗ
рд▓реЙрдХрд░рдЪреА рдлрд│реА рддреНрдпрд╛рдЪ рдкреНрд░рд╢реНрдирд╛рдЪреЗ рдЙрддреНрддрд░ рдирд╛рд╡рд╛рдиреЗ рджреЗрддреЗ. latest рдирд╛рд╣реА, main рдирд╛рд╣реА: рддреЗ рдЖрдард╡рдбреНрдпрд╛-рдЖрдард╡рдбреНрдпрд╛рд▓рд╛ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ bytes рдХрдбреЗ рдирд┐рд░реНрджреЗрд╢ рдХрд░рддрд╛рдд.
рдареЗрд╡рдгреНрдпрд╛рдЖрдзреА рдкреНрд░рдд рдиреАрдЯ рдкрд╛рд╣рд╛. Tests runner рдЪреНрдпрд╛ Node рд╡рд░ рдЪрд╛рд▓рд▓реНрдпрд╛; рдбрдмреНрдпрд╛рдЪреЗ рд╕реНрд╡рддрдГрдЪреЗ Node,
рд╕реНрд╡рддрдГрдЪреНрдпрд╛ files, рд╕реНрд╡рддрдГрдЪрд╛ port рдЕрд╕рддреЛ. рдореНрд╣рдгреВрди рдбреЗрд╕реНрдХ рдбрдмрд╛ рд╕реБрд░реВ рдХрд░рддреЛ, /healthz рдЙрддреНрддрд░ рджреЗрдИрдкрд░реНрдпрдВрдд рд╡рд┐рдЪрд╛рд░рдд рд░рд╛рд╣рддреЛ, рдЖрдгрд┐
/ рдЕрдкреЗрдХреНрд╖рд┐рдд version рдЫрд╛рдкрддреЗ рдХрд╛ рддреЗ рддрдкрд╛рд╕рддреЛ. рддреЛ рдбрдмреНрдпрд╛рдЪрд╛ smoke test рдЖрд╣реЗ, рдХреЛрдбрдЪрд╛ рдирд╛рд╣реА.
рдиреАрдЯ рд╣рд╕реНрддрд╛рдВрддрд░рдг рдХрд░рд╛. рдлреЛрдЯреЛрдХреЙрдкреА рдорд╢реАрди рдЖрдгрд┐ рдлрд╛рдЗрд▓рд┐рдВрдЧ рдбреЗрд╕реНрдХ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдЦреЛрд▓реНрдпрд╛рдВрдордзреНрдпреЗ рдЖрд╣реЗрдд, рдореНрд╣рдгреВрди рдкреНрд░рдд рдзрдбрд╛ 05 рдЪреНрдпрд╛ рдкрд╛рдХрд┐рдЯрд╛рддреВрди рдкреНрд░рд╡рд╛рд╕ рдХрд░рддреЗ; рдлрд╛рдЗрд▓рд┐рдВрдЧ рдбреЗрд╕реНрдХ рдзрдбрд╛ 06 рдЪреНрдпрд╛ рдбреЗ рдкрд╛рд╕рдиреЗ рд▓реЙрдХрд░ ЁЯПж рдЙрдШрдбрддреЛ; рд▓реЙрдХрд░ рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ рдбрдмреНрдпрд╛рдЪрд╛ X-ray рдХрд╛рдврддреЛ (scan on push).
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
build["ЁЯН▒ docker build<br/>--build-arg APP_VERSION=abc1234"]
smoke["ЁЯзк smoke-test the BOX<br/>curl /healthz, grep version abc1234"]
env["ЁЯУО image.tar.gz<br/>docker save, then gzip"]
push["ЁЯЪЪ push job, another machine<br/>docker load, ЁЯкк badge, ЁЯОл ECR login"]
ecr["ЁЯПж ECR hello-courier:abc1234тАж<br/>tag = full SHA, scan on push"]
build -->|"1 run it"| smoke
smoke -->|"2 only if it answers"| env
env -->|"3 download by name"| push
push -->|"4 docker push"| ecr
тЭУ рдХрд╛рдп
- Build argument =
--build-arg APP_VERSION=тАжтЖТ Dockerfile рдордзреНрдпреЗARGтЖТENVтЖТserver.jsрдордзреНрдпреЗprocess.env.APP_VERSION. Version label рдореНрд╣рдгрдЬреЗ short SHA (7 рдЕрдХреНрд╖рд░реЗ). - Smoke test = container рд╕реБрд░реВ рдХрд░рд╛,
/healthz200 рджреЗрдИрдкрд░реНрдпрдВрдд рд╡рд┐рдЪрд╛рд░рдд рд░рд╛рд╣рд╛, рдордЧ/version <short SHA>рдЫрд╛рдкрддреЗ рдХрд╛ рддреЗ рддрдкрд╛рд╕рд╛. рдпрд╛рд╡рд░реВрди рд╕рд┐рджреНрдз рд╣реЛрддреЗ рдХреА рдбрдмрд╛ рд╕реБрд░реВ рд╣реЛрддреЛ, port 3000 рд╡рд░ рдРрдХрддреЛ, рдЖрдгрд┐ рдпрд╛рдЪ commit рдкрд╛рд╕реВрди build рдЭрд╛рд▓рд╛ рдЖрд╣реЗ. - Image tag = рдкреВрд░реНрдг commit SHA (40 рдЕрдХреНрд╖рд░реЗ). рддреЛрдЪ tag, рддреЗрдЪ bytes;
git show <sha>рдХреЛрдб рд╢реЛрдзрддреЛ; рдирдВрддрд░рдЪрд╛ deploy рдореНрд╣рдгрдЬреЗ "рд╣реА string рдмрджрд▓рд╛". рд╣рд▓рдгрд╛рд░рд╛ tag (main,latest) рдХрд╛рд▓рд╛рдВрддрд░рд╛рдиреЗ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ images рдХрдбреЗ рдирд┐рд░реНрджреЗрд╢ рдХрд░рддреЛ тАФ bookmark рдореНрд╣рдгреВрди рдареАрдХ, deploy target рдореНрд╣рдгреВрди рдЪреБрдХреАрдЪрд╛. - Scan on push = ECR рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ image рдЪреЗ layers рдУрд│рдЦреАрдЪреНрдпрд╛ CVEs рд╕рд╛рдареА рддрдкрд╛рд╕рддреЗ; рд╣реА repository рдЪреА setting рдЖрд╣реЗ, pipeline рдЪреА рдирд╛рд╣реА.
ЁЯза рдПрдХрдЪ рдард╕рд╛, рдЖрдд рдЖрдгрд┐ рдмрд╛рд╣реЗрд░
commit abc1234ef56тАж (40 chars тАФ git's fingerprint of the code)
тФЬтФА INSIDE the box: APP_VERSION=abc1234 (short SHA тАФ what the app prints)
тФФтФА ON the label: hello-courier:abc1234ef56тАж (full SHA тАФ what you deploy by)
Tag рд╡рд┐рд░реБрджреНрдз digest. Commit-SHA tag рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рд╕рд╛рдВрдЧрддреЛ рдХреА image рдХреЛрдгрддреНрдпрд╛ source revision рдкрд╛рд╕реВрди рдмрдирд▓реА;
image рдЪрд╛ digest (sha256:тАж, docker image inspect рдЖрдгрд┐ ECR console рдордзреНрдпреЗ рджрд┐рд╕рддреЛ)
рдиреЗрдордХреЗ, рди рдмрджрд▓рдгрд╛рд░реЗ image bytes рдУрд│рдЦрддреЛ. рддреЛрдЪ commit рджреЛрдирджрд╛ build рдХрд░рд╛ рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ tag
рдкрдг рдХрджрд╛рдЪрд┐рдд рджреЛрди digests рдорд┐рд│рддреАрд▓ тАФ tag рдореНрд╣рдгрдЬреЗ рдкрддреНрддрд╛, digest рдореНрд╣рдгрдЬреЗ рдУрд│рдЦ, рдЬрд╕реЗ
Docker рд╢рд╛рд│реЗрдЪрд╛ рдзрдбрд╛ 11
рд╕рд╛рдВрдЧрддреЛ. рдпрд╛рдЪ рдХрд╛рд░рдгрд╛рд╕рд╛рдареА рдзрдбрд╛ 12 base images digest рдиреЗ pin рдХрд░рддреЛ.
ЁЯдФ рдХрд╛
Version рдЖрдд рднрд╛рдЬреВрди рдмрд╕рд╡рд▓реЗрд▓реЗ рдирд╕реЗрд▓ рддрд░ "рдХреЛрдгрддреЗ version рдЪрд╛рд▓реВ рдЖрд╣реЗ?" рд╣рд╛ рдЕрдВрджрд╛рдЬрдЪ рдЕрд╕рддреЛ. Smoke test рд╢рд┐рд╡рд╛рдп, рдПрдЦрд╛рджреА file рд╡рд┐рд╕рд░рдгрд╛рд░реА
Dockerfile unit tests рдкрд╛рд╕ рдХрд░рддреЗ рдЖрдгрд┐ рдкрд╣рд┐рд▓реНрдпрд╛рдЪ readiness probe рд▓рд╛ рдорд░рддреЗ. SHA tags рд╢рд┐рд╡рд╛рдп, "рдХрд╛рд▓рдЪреЗ рдкрд░рдд
рд▓рд╛рд╡рд╛" рдЕрд╢рдХреНрдп рд╣реЛрддреЗ, latest рдЧрдкрдЪреВрдк рдЦреЛрдЯреЗ рдмреЛрд▓рддреЛ, рдЖрдгрд┐ рдирдВрддрд░рдЪреНрдпрд╛ GitOps commits рдХрдбреЗ рдмрджрд▓рдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдЕрд░реНрдердкреВрд░реНрдг рдХрд╛рд╣реАрдЪ рдирд╕рддреЗ.
Docker рд╢рд╛рд│реЗрдЪрд╛ рдзрдбрд╛ 12 рдиреЗрдордХреНрдпрд╛ рдпрд╛рдЪ рд╣рд╕реНрддрд╛рдВрддрд░рдгрд╛рд╡рд░ рд╕рдВрдкрддреЛ; рддреНрдпрд╛рдЪрд╛ рдзрдбрд╛ 08 tag рд╕реНрд╡рдЪреНрдЫрддреЗрдмрджреНрджрд▓ рд╕рд╛рдВрдЧрддреЛ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
рдард╕рд╛ рдЖрдд рднрд╛рдЬреВрди рдмрд╕рд╡рд╛. app/Dockerfile рддреЛ build argument рдореНрд╣рдгреВрди рдШреЗрддреЗ рдЖрдгрд┐ environment рдордзреНрдпреЗ рдЧреЛрдард╡рддреЗ:
ARG APP_VERSION=dev # CI passes the commit SHA here (lesson 07)
ENV APP_VERSION=$APP_VERSION
рдбрдмрд╛ build рдХрд░рд╛ рдЖрдгрд┐ рддреНрдпрд╛рдЪрд╛ smoke-test рдХрд░рд╛. ship.yml рдордзрд▓рд╛ build job тАФ
${GITHUB_SHA::7} рдореНрд╣рдгрдЬреЗ bash рдордзреНрдпреЗ "рдкрд╣рд┐рд▓реА 7 рдЕрдХреНрд╖рд░реЗ"; loop рдбрдмреНрдпрд╛рд▓рд╛ рд╕реБрд░реВ рд╡реНрд╣рд╛рдпрд▓рд╛ рд╕реБрдорд╛рд░реЗ рджрд╣рд╛ рд╕реЗрдХрдВрдж рджреЗрддреЛ:
- name: ЁЯН▒ docker build (version label = short SHA)
run: docker build --build-arg APP_VERSION=${GITHUB_SHA::7} -t hello-courier:${GITHUB_SHA} app
- name: ЁЯзк test the BOX, not just the code
run: |
docker run -d --rm -p 3000:3000 --name smoke hello-courier:${GITHUB_SHA}
for i in $(seq 1 20); do curl -fsS localhost:3000/healthz && break; sleep 0.5; done
curl -fsS localhost:3000/ | grep -q "version ${GITHUB_SHA::7}"
docker stop smoke
- name: ЁЯТ╛ keep the image for the push job (same run, different machine)
run: docker save hello-courier:${GITHUB_SHA} | gzip > image.tar.gz
grep -q рдЪреА рдУрд│ рдЬреБрдирд╛ рдХрд┐рдВрд╡рд╛ рдЪреБрдХреАрдЪреЗ label рдЕрд╕рд▓реЗрд▓рд╛ build рдкрдХрдбрддреЗ: рдбрдмреНрдпрд╛рдиреЗ version dev рдЫрд╛рдкрд▓реЗ, рддрд░ рдХрд╛рд╣реАрд╣реА push рд╣реЛрдгреНрдпрд╛рдЖрдзреАрдЪ job fail рд╣реЛрддреЛ.
Load, log in, tag, push. push job рдирд╡реНрдпрд╛ рдорд╢реАрдирд╡рд░ рдЙрддрд░рддреЛ, рдореНрд╣рдгреВрди рддреЛ рдкрд╛рдХрд┐рдЯрд╛рдкрд╛рд╕реВрди рд╕реБрд░реВ рдХрд░рддреЛ, рдзрдбрд╛ 06 рдЪрд╛ рдмреЕрдЬ рджрд╛рдЦрд╡рддреЛ, рдордЧ:
- run: docker load < image.tar.gz
- name: ЁЯОл log in to ECR
id: ecr
uses: aws-actions/amazon-ecr-login@v2
- name: ЁЯП╖я╕П tag with the commit SHA and push
id: meta
env:
IMAGE: ${{ steps.ecr.outputs.registry }}/${{ vars.ECR_REPOSITORY }}:${{ github.sha }}
run: |
docker tag hello-courier:${GITHUB_SHA} "$IMAGE"
docker push "$IMAGE"
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
amazon-ecr-login рдбреЗ рдкрд╛рд╕рдЪреЗ рд░реВрдкрд╛рдВрддрд░ docker login рдордзреНрдпреЗ рдХрд░рддреЗ рдЖрдгрд┐ registry hostname output рдХрд░рддреЗ, рдореНрд╣рдгреВрди IMAGE рд╣реЛрддреЗ
123456789012.dkr.ecr.ap-south-1.amazonaws.com/hello-courier:<full sha> тАФ job рдЪрд╛ image output рдореНрд╣рдгреВрди export рдХреЗрд▓реЗрд▓реА, рдзрдбрд╛ 10 рдордзреНрдпреЗ
delivery van рд▓рд╛ рдорд┐рд│рдгрд╛рд░реА рдиреЗрдордХреА string. Scan рдЪреЗ рдирд┐рдХрд╛рд▓ ECR console рдордзреНрдпреЗ image рд╢реЗрдЬрд╛рд░реА рджрд┐рд╕рддрд╛рдд (repository рдзрдбрд╛ 06 рдордзреНрдпреЗ scanOnPush=true рд╕рд╣ рдмрдирд╡рд▓реА рд╣реЛрддреА).
ЁЯФБ рд╣реЗрдЪ CircleCI рдордзреНрдпреЗ
IMAGE="${REGISTRY}/${ECR_REPOSITORY}:${CIRCLE_SHA1}"
docker build --build-arg APP_VERSION=${CIRCLE_SHA1:0:7} -t "$IMAGE" app
docker run -d --rm -p 3000:3000 --name smoke "$IMAGE"
sleep 2 && docker exec smoke wget -qO- localhost:3000/healthz && docker stop smoke
docker push "$IMAGE"
рдПрдХрдЪ job build рдХрд░рддреЛ, aws ecr get-login-password рдиреЗ log in рдХрд░рддреЛ рдЖрдгрд┐ push рдХрд░рддреЛ, рдореНрд╣рдгреВрди рдкрд╛рдХрд┐рдЯрд╛рдЪреА рдЧрд░рдЬ рдирд╛рд╣реА.
setup_remote_docker рд╕рд╣ container рд╡реЗрдЧрд│реНрдпрд╛ engine рд╡рд░ рдЪрд╛рд▓рддреЛ, рдореНрд╣рдгреВрдирдЪ smoke test curl localhost рдРрд╡рдЬреА docker exec smoke wget рд╡рд╛рдкрд░рддреЛ.
ЁЯжК рд╣реЗрдЪ GitLab CI рдордзреНрдпреЗ
- IMAGE="${REGISTRY}/${ECR_REPOSITORY}:${CI_COMMIT_SHA}"
- docker build --build-arg APP_VERSION=${CI_COMMIT_SHORT_SHA} -t "$IMAGE" app
- docker run -d --rm -p 3000:3000 --name smoke "$IMAGE" && sleep 2 && docker exec smoke wget -qO- localhost:3000/healthz && docker stop smoke
- docker push "$IMAGE"
CI_COMMIT_SHA рд╣рд╛ рдкреВрд░реНрдг SHA рдЖрд╣реЗ; GitLab рдЪрд╛ built-in CI_COMMIT_SHORT_SHA 8 рдЕрдХреНрд╖рд░рд╛рдВрдЪрд╛ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдпрд╛ рдмреЛрд▓реАрдд version
рдЪреА рдУрд│ рдПрдХ рдЕрдХреНрд╖рд░рд╛рдиреЗ рд▓рд╛рдВрдм рджрд┐рд╕рддреЗ. Docker рд╕реНрд╡рддрдГ docker:27-dind service рдордзреВрди рдпреЗрддреЛ.
ЁЯОй рд╣реЗрдЪ Jenkins рдордзреНрдпреЗ
IMAGE="${REGISTRY}/${ECR_REPOSITORY}:${GIT_COMMIT}"
docker build --build-arg APP_VERSION=$(echo "$GIT_COMMIT" | cut -c1-7) -t "$IMAGE" app # POSIX sh: no ${VAR:0:7}
docker run -d --rm -p 3000:3000 --name smoke "$IMAGE" && sleep 2 \
&& docker exec smoke wget -qO- localhost:3000/healthz && docker stop smoke
docker push "$IMAGE"
GIT_COMMIT рд╣рд╛ Jenkins рдЪрд╛ рдкреВрд░реНрдг SHA рдЖрд╣реЗ. рд╕рдВрдкреВрд░реНрдг sh block рдзрдбрд╛ 06 рдордзрд▓реНрдпрд╛ withAWS(...) рдЪреНрдпрд╛ рдЖрдд рдмрд╕рддреЛ,
рддреНрдпрд╛рдЪ рдкреНрд░рдХрд╛рд░реЗ log in рдХрд░рддреЛ, рдЖрдгрд┐ agent рдХрдбреЗ рд╕реНрд╡рддрдГрдЪрд╛ Docker CLI рдЕрд╕рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
SHA=$(git rev-parse HEAD) # 1) build exactly like CI, from your checkout тАФ no AWS needed
docker build --build-arg APP_VERSION=${SHA::7} -t hello-courier:${SHA} app
# 2) smoke-test the BOX тАФ the same lines as ship.yml
docker run -d --rm -p 3000:3000 --name smoke hello-courier:${SHA}
for i in $(seq 1 20); do curl -fsS localhost:3000/healthz && break; sleep 0.5; done
curl -fsS localhost:3000/ | grep -q "version ${SHA::7}" && echo "тЬЕ the box says ${SHA::7}"
docker stop smoke
# 3) the version is baked in, not guessed
docker image inspect hello-courier:${SHA} --format '{{json .Config.Env}}' # тАж"APP_VERSION=abc1234"тАж
# 4) the same build ran in your fork's mailroom тАФ the build job needs no AWS
RUN=$(gh run list --workflow ship.yml --limit 1 --json databaseId --jq '.[0].databaseId')
gh run view "$RUN" # "build & smoke-test the image" тЬЕ ┬╖ "push to ECR" skipped without the lesson 06 variables
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
latestрдХрд┐рдВрд╡рд╛ branch рдЪреНрдпрд╛ рдирд╛рд╡рд╛рдиреЗ deploy рдХрд░рдгреЗ тАФ tag рдЪрд╛ рдЕрд░реНрде рдПрдХрдЪ bytes рдЪрд╛ рд╕рдВрдЪ рдЕрд╕рд╛ рд░рд╛рд╣рдд рдирд╛рд╣реА, рдЖрдгрд┐ "рдХрд╛рд▓рдЪреЗ рдкрд░рдд рд▓рд╛рд╡рд╛" рдЕрдВрджрд╛рдЬрдкрдВрдЪреЗ рд╣реЛрддреЗ- рдХреЛрдб рддрдкрд╛рд╕рдгреЗ рдкрдг рдбрдмрд╛ рди рддрдкрд╛рд╕рдгреЗ тАФ file рдЪреБрдХрд╡рдгрд╛рд░рд╛
COPY, рдЪреБрдХреАрдЪрд╛USERрдХрд┐рдВрд╡рд╛ port unit tests рдкрд╛рд╕ рдХрд░рддреЛ рдЖрдгрд┐ рдкрд╣рд┐рд▓реНрдпрд╛рдЪ probe рд▓рд╛ рдорд░рддреЛ - "рдкрдЯрдХрди рд╣реЛрддреЗ рдореНрд╣рдгреВрди" push job рдордзреНрдпреЗ рдкреБрдиреНрд╣рд╛ build рдХрд░рдгреЗ тАФ push рдЭрд╛рд▓реЗрд▓реА image рддреБрдореНрд╣реА smoke-test рдХреЗрд▓реЗрд▓реА рд░рд╛рд╣рдд рдирд╛рд╣реА; рддреНрдпрд╛рдРрд╡рдЬреА рдкрд╛рдХреАрдЯ рд╕реЛрдмрдд рдиреНрдпрд╛
тПня╕П рдкреБрдвреЗ
рдкреНрд░рдд рддрд┐рдЪреНрдпрд╛ рдард╢рд╛рд╕рд╣ рд▓реЙрдХрд░рдордзреНрдпреЗ рдЖрд╣реЗ. рдореБрдЦреНрдп рдлрд▓рдХрд╛рд╡рд░ рдкреЛрд╣реЛрдЪрдгреНрдпрд╛рдЖрдзреА рддреА рдЖрдзреА рд╕рд░рд╛рд╡ рдлрд▓рдХрд╛рд╡рд░ рдЬрд╛рддреЗ тАФ рдЖрдгрд┐ рдкреНрд░рд╛рдЪрд╛рд░реНрдп рд╕рд╣реА рдХрд░рддрд╛рдд. ЁЯЫС
git checkout lesson-08-environments-gates