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

ЁЯУо рдзрдбрд╛ 11 тАФ CI рдЖрдгрд┐ containers рдордзрд▓реНрдпрд╛ scripts: рдореЗрд▓рд░реВрдо

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдкреБрдвреЗ: lesson-12-debugging-testing


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

рдзрдбреЗ 01тАУ11. рддреНрдпрд╛рдЪ scripts, CI runner рдиреЗ рдЖрдгрд┐ container рдЪреНрдпрд╛ рдЖрдд рдЪрд╛рд▓рд╡рд▓реЗрд▓реНрдпрд╛ тАФ рдХрд╛рдп рдмрджрд▓рддреЗ, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ entrypoint рд▓рд╛ рд▓рд╛рдЧрдгрд╛рд▒реНрдпрд╛ рддреАрди рдУрд│реА.

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

рдореЗрд▓рд░реВрдо (CI) рдордзреНрдпреЗ рдЖрдгрд┐ shipping container (Docker) рдЪреНрдпрд╛ рдЖрдд рдХреЗрдЕрд░рдЯреЗрдХрд░рдЪреЗ рдСрдлрд┐рд╕ рдирд╕рддреЗ: screen рдирд╛рд╣реА, keyboard рдирд╛рд╣реА, рдкреНрд░рд╢реНрдирд╛рдЪреЗ рдЙрддреНрддрд░ рджреНрдпрд╛рдпрд▓рд╛ рдХреЛрдгреА рдирд╛рд╣реА. script рд▓рд╛ рдЬреЗ environment рджрд┐рд▓реЗ рдЬрд╛рддреЗ рддреНрдпрд╛рддреВрдирдЪ рддрд┐рд▓рд╛ рд╕рдЧрд│реЗ рдХрд│рд╛рдпрд▓рд╛ рд╣рд╡реЗ, рддрд┐рдиреЗ рдХрд╛рдп рдХреЗрд▓реЗ рддреЗ log рдордзреНрдпреЗ рд╕рд╛рдВрдЧрд╛рдпрд▓рд╛ рд╣рд╡реЗ, рдЖрдгрд┐ рдПрдХ exit code рдкрд░рдд рджреНрдпрд╛рдпрд▓рд╛ рд╣рд╡рд╛. container рдЪреНрдпрд╛ рдЖрдд script рд╣реА рдмрд▒реНрдпрд╛рдЪрджрд╛ рдЗрдорд╛рд░рддреАрддрд▓реА рдЕрдЧрджреА рдкрд╣рд┐рд▓реА рд╡реНрдпрдХреНрддреА рдЕрд╕рддреЗ тАФ PID 1 тАФ рдореНрд╣рдгреВрди рддрд┐рдЪреА рд╢реЗрд╡рдЯрдЪреА рдХреГрддреА рдореНрд╣рдгрдЬреЗ рдмрд╛рдЬреВрд▓рд╛ рд╣реЛрдКрди рдЦрд▒реНрдпрд╛ program рд▓рд╛ badge рдШреЗрдК рджреЗрдгреЗ: exec "$@".

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

flowchart LR
  G["ЁЯУо GitHub Actions<br/>run: bash tests/run_tests.sh"] --> X["exit 0 тЖТ green tick<br/>exit 1 тЖТ red cross"]
  D["ЁЯР│ docker run img"] --> E["entrypoint.sh<br/>set -euo pipefail ┬╖ read $ENV ┬╖ exec \"$@\""] --> A["the app as PID 1"]

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

ЁЯдФ рдХрд╛

"рдорд╛рдЭреНрдпрд╛ machine рд╡рд░ рдЪрд╛рд▓рддреЗ" рдпрд╛рдЪреЗ рдХрд╛рд░рдг рдЬрд╡рд│рдЬрд╡рд│ рдиреЗрд╣рдореА environment рдЕрд╕рддреЗ: PATH, HOME, shell, locale, рддреБрдордЪреНрдпрд╛ laptop рд╡рд░ рдЕрд╕рд▓реЗрд▓рд╛ рдкрдг image рдордзреНрдпреЗ рдирд╕рд▓реЗрд▓рд╛ рдПрдЦрд╛рджрд╛ tool. рд╕реБрд░реБрд╡рд╛рддреАрдкрд╛рд╕реВрдирдЪ рдореЗрд▓рд░реВрдорд╕рд╛рдареА рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ scripts рдирд╛ рд╣рд╛ рддреНрд░рд╛рд╕ рдХрдзреАрдЪ рд╣реЛрдд рдирд╛рд╣реА.

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

рдЗрдерд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ script рдордзреНрдпреЗ #!/usr/bin/env bash, strict mode рдЖрд╣реЗ, prompts рдирд╛рд╣реАрдд, рдЖрдгрд┐ рддреА рдЖрдкрд▓реЗ inputs flags рдордзреВрди рдХрд┐рдВрд╡рд╛ SCHOOL_REGISTER рдордзреВрди рдШреЗрддреЗ. CI/CD рд╢рд╛рд│рд╛ рдпрд╛ repo рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ push рд╡рд░ tests/run_tests.sh рдЪрд╛рд▓рд╡рддреЗ.

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

bash -n bin/*.sh && echo "syntax ok"                              # what a CI lint step does first
env -i PATH=/usr/bin:/bin HOME=/nonexistent bash tests/run_tests.sh | tail -1   # a bare environment, like cron or CI
docker run --rm -v "$PWD:/s" -w /s bash:5 bash tests/run_tests.sh | tail -1    # (if Docker) inside a container
docker run --rm -v "$PWD:/s" -w /s alpine sh -c 'sh bin/school.sh list'        # alpine has no bash: watch it fail on [[ ]]
cat > /tmp/entrypoint.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "preparing with REGISTER=${SCHOOL_REGISTER:-default}" >&2
exec "$@"
EOF
chmod +x /tmp/entrypoint.sh; SCHOOL_REGISTER=x /tmp/entrypoint.sh echo "now I am the app"

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

syntax ok; рдореЛрдХрд│реНрдпрд╛ environment рдордзреНрдпреЗ рдЖрдгрд┐ bash container рдордзреНрдпреЗ 14 passed, 0 failed; alpine рдЪрд╛ run рдкрд╣рд┐рд▓реНрдпрд╛рдЪ bashism рд╡рд░ fail рд╣реЛрддреЛ; entrypoint рдЖрдкрд▓реА рддрдпрд╛рд░реА stderr рд╡рд░ log рдХрд░рддреЗ рдЖрдгрд┐ echo рдмрджрд▓рд▓реЗрд▓рд╛ process рдореНрд╣рдгреВрди рдЪрд╛рд▓рддреЛ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдордЪреНрдпрд╛ scripts рдореЛрдХрд│реНрдпрд╛ environment рдордзреНрдпреЗ рдЖрдгрд┐ container рдордзреНрдпреЗ рдЯрд┐рдХрддрд╛рдд, рдЖрдгрд┐ PID 1 рдЦрд▒реНрдпрд╛ program рдХрдбреЗ рд╕реЛрдкрд╡рдгрд╛рд░реА entrypoint рддреБрдореНрд╣реА рд▓рд┐рд╣реВ рд╢рдХрддрд╛.

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

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдкреНрд░рддреНрдпреЗрдХ Dockerfile рдЪрд╛ ENTRYPOINT, рдкреНрд░рддреНрдпреЗрдХ CI run: block, рдкреНрд░рддреНрдпреЗрдХ kubectl exec тАж sh рдореНрд╣рдгрдЬреЗ рд╣рд╛рдЪ рдзрдбрд╛. Docker рдЖрдгрд┐ CI/CD рд╢рд╛рд│рд╛ рдореНрд╣рдгрдЬреЗ рдЬрд┐рдереЗ рдпрд╛ scripts рд░рд╛рд╣рддрд╛рдд.

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

рдзрдбрд╛ 12 тАФ debugging рдЖрдгрд┐ testing: script рдХрд╛рдп рдХрд░рддреЗ рддреЗ рдкрд╛рд╣рд╛, рдЖрдгрд┐ bash рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ test runner рдиреЗ рддреЗ рд╕рд┐рджреНрдз рдХрд░рд╛.

ЁЯУо Lesson 11 тАФ Scripts in CI & containers: the mailroom

ЁЯУН You are here: Lesson 11 of 12 ┬╖ Next: lesson-12-debugging-testing


ЁЯУж What's in this branch

Lessons 01тАУ11. The same scripts, run by a CI runner and inside a container тАФ what changes, and the three lines every entrypoint needs.

ЁЯзТ Explain like I'm 5

In the mailroom (CI) and inside a shipping container (Docker) there is no caretaker's office: no screen, no keyboard, nobody to answer a question. The script must know everything from the environment it is handed, say what it did in the log, and hand back one exit code. Inside the container the script is often the very first person in the building тАФ PID 1 тАФ so its last act should be to step aside and let the real program take the badge: exec "$@".

ЁЯЧ║я╕П Diagram

flowchart LR
  G["ЁЯУо GitHub Actions<br/>run: bash tests/run_tests.sh"] --> X["exit 0 тЖТ green tick<br/>exit 1 тЖТ red cross"]
  D["ЁЯР│ docker run img"] --> E["entrypoint.sh<br/>set -euo pipefail ┬╖ read $ENV ┬╖ exec \"$@\""] --> A["the app as PID 1"]

тЭУ What

ЁЯдФ Why

"Works on my machine" is almost always the environment: PATH, HOME, the shell, the locale, a tool that exists on your laptop and not in the image. Scripts written for the mailroom from the start never hit it.

ЁЯФз How (in this repo)

Every script here has #!/usr/bin/env bash, strict mode, no prompts, and takes its inputs from flags or SCHOOL_REGISTER. The CI/CD school runs tests/run_tests.sh on every push of this repo.

ЁЯзк Try it

bash -n bin/*.sh && echo "syntax ok"                              # what a CI lint step does first
env -i PATH=/usr/bin:/bin HOME=/nonexistent bash tests/run_tests.sh | tail -1   # a bare environment, like cron or CI
docker run --rm -v "$PWD:/s" -w /s bash:5 bash tests/run_tests.sh | tail -1    # (if Docker) inside a container
docker run --rm -v "$PWD:/s" -w /s alpine sh -c 'sh bin/school.sh list'        # alpine has no bash: watch it fail on [[ ]]
cat > /tmp/entrypoint.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "preparing with REGISTER=${SCHOOL_REGISTER:-default}" >&2
exec "$@"
EOF
chmod +x /tmp/entrypoint.sh; SCHOOL_REGISTER=x /tmp/entrypoint.sh echo "now I am the app"

тЬЕ Verify тАФ what you should see

syntax ok; 14 passed, 0 failed in the bare environment and in the bash container; the alpine run fails at the first bashism; the entrypoint logs its preparation to stderr and the echo runs as the replaced process.

ЁЯПБ What you just proved

Your scripts survive a bare environment and a container, and you can write the entrypoint that hands PID 1 to the real program.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: every Dockerfile's ENTRYPOINT, every CI run: block, every kubectl exec тАж sh is this lesson. The Docker and CI/CD schools are where these scripts live.

тПня╕П Next

Lesson 12 тАФ debugging & testing: see what a script does, and prove it with a test runner written in bash.

тЖР PreviousschedulingNext тЖТdebugging testing

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