Homework
Setup
Prerequisites: An internet-connected workstation running a Linux-based OS.
Install Bash
Most of ci/scripts/ is plain POSIX sh - it only dispatches into a
pinned container, so it does not need Bash. apps/appctl, every
app's own apps/<name>/libs/appctl-* lifecycle scripts, and most
test/cases/*/test files are Bash, though, and that is what you will
be reading and writing in
Now Build Something below.
Check what you have:
bash --version
Target: Bash 5.x - whatever the latest 5.x release is. The patch version doesn't matter.
If it is missing, install the bash package from your distro.
Install Git
Check what you have:
git --version
Target: Git 2.x (for example, git version 2.44.0). Only v2 has been
tested.
If it is missing, install the git package from your distro.
Install Docker
Every check in ci/ - and the pre-commit hook that runs them - needs
a real Docker engine: each one dispatches into its own pinned
container image rather than trusting whatever version happens to be
on your host.
Podman is not a substitute here, unlike most "I just need a
container engine" tasks: this repo's own CI tooling (appctl-tester,
run later in Now Build Something) mounts the
host's /var/run/docker.sock into a container so it can drive the
host's real Docker engine from inside one - docker-outside-of-docker,
not a nested engine. Podman has no equivalent system-wide socket to
mount by default, so that mechanism, and everything built on it,
needs Docker specifically.
Check what you have:
docker compose version
If it is missing, install Docker Engine (or Docker Desktop) from your distro or from docker.com.
Your user also needs permission to talk to the Docker daemon's own
socket (/var/run/docker.sock) - on Linux this usually means being a
member of the local docker group:
groups | tr ' ' '\n' | grep -qx docker && echo "already in the docker group"
If that prints nothing, add yourself and start a new shell session (group membership only takes effect in a new login):
sudo usermod -aG docker "$(whoami)"
appctl-tester specifically mounts that same socket into its own
container, so this is not just "can I run docker ps" - confirm it
with:
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
docker:cli docker ps
If that fails with a permission error even after joining the docker
group, log out and back in (or reboot) before asking for help - a
stale shell session is the usual cause.
Clone Git Repo
cd "$HOME" && \
git clone https://gitlab.com/codrcodz/agentic-infra-lifecycle-mgmt.git && \
cd agentic-infra-lifecycle-mgmt && \
ls AGENTS.md
This creates an agentic-infra-lifecycle-mgmt directory in $HOME. You
should see a series of "Cloning into..." messages, and the final line
should print AGENTS.md - that is your confirmation it worked.
Agents
Prerequisites: Everything in Setup above. An agent is a separate program you install on top of your workstation, not a dependency of any check this repo runs.
An agent and a model are different things, easily confused
because the largest agents ship paired with their own model by
default. The model is what actually reasons about your code and
decides what to do next (Claude, GPT, Gemini, a local Llama build,
...). The agent is the surrounding program - the terminal interface,
the tool-calling loop that lets a model read/edit files and run
commands, and, specifically for this repo, the thing that reads
.agents/skills/ and decides when a skill is relevant. Swapping the
model under an agent does not change what the agent can do; it
changes how well it does it.
This repo already works with four agents, out of the box:
.agents/skills/ is the actual storage location (the open Agent
Skills standard), and .claude/skills/ / .opencode/skills/ are
symlinks into that same directory, so Claude Code and OpenCode see
the exact same skills without a separate copy to keep in sync.
- Claude Code - Anthropic's own agent, closed-source, ships paired with Claude. Install.
- Codex CLI - OpenAI's own agent, closed-source, ships paired with GPT/Codex models. Install.
- OpenCode - open-source, bring-your-own-model. Install.
- pi.dev (Pi) - open-source, bring-your-own-model. Install.
An open-source agent needs you to point it at a model of your own - a paid API key from a provider, or a model you run yourself. Running one yourself usually means a model server such as Ollama, optionally fronted by a chat interface like Open WebUI - a real option, but setting either of those up is out of scope for this homework. Pick whichever agent you already have a model for, and move on to Now Build Something below.
Now Build Something
Everything in the Talk section exists because apps/demo-app is small
enough to read in one sitting. The real test of whether any of it sank
in is whether you can point an agent at a second app and get the same
guarantees - lifecycle-aware, skill-wrapped, linted, tested - without
hand-holding it through each piece again.
Paste this to your agent of choice, from the repository root:
Create a new app called `lab-app` under `apps/`, following the exact
structure and conventions `apps/demo-app` already establishes:
- `apps/lab-app/compose.yml` deploying `ntfy` (`binwiederhier/ntfy`),
a simple open-source pub/sub notification server with a REST API -
one service, no separate database container or volume, deployed
via `docker compose` the same way `apps/demo-app` is.
- One or more `apps/lab-app/env/<name>.env` files, following
`apps/demo-app/env/`'s own shape.
- `apps/lab-app/libs/appctl-build`, `appctl-update`, `appctl-test`,
`appctl-deploy`, `appctl-destroy`, matching `apps/demo-app/libs/`'s
shape. `appctl-test` should check more than container health:
confirm ntfy's own HTTP health endpoint responds, and that a
message published to a topic can be read back from it.
- `apps/lab-app/test/cases/00-full-static-qa`,
`01-full-functional-eval`, `02-bad-tag-fails`, mirroring
`apps/demo-app/test/cases/`. `01-full-functional-eval` should drive
that same publish-then-poll round trip through the real lifecycle,
not just check that a container exists.
Once it exists, prove it works: run
`./ci/scripts/10-appctl-tester lab-app`, and confirm the full
pre-commit hook (`.githooks/pre-commit`) still passes.
Extra Credit
Prerequisites: Everything above, plus a gitlab.com account. This is
a separate, optional path - the rest of this page only ever needs
git clone against the upstream URL; nothing above requires a fork
or a runner of your own.
Everything above is verified locally, through
.githooks/pre-commit - the same checks .gitlab-ci.yml runs, never
a real pipeline itself. To watch your own change go through the real
thing, fork this project instead of cloning it, and register your own
runner against your fork, rather than waiting on someone else's
shared runner minutes.
Fork Instead of Clone
On gitlab.com, open this project and select Fork. Clone your fork instead of the upstream URL:
cd "$HOME" && \
git clone \
https://gitlab.com/<your-namespace>/agentic-infra-lifecycle-mgmt.git && \
cd agentic-infra-lifecycle-mgmt
Register Your Workstation as a Runner
Install gitlab-runner
(docs.gitlab.com/runner/install).
In your fork's Settings > CI/CD > Runners, select Create new
runner, give it a tag (e.g. local), and copy the authentication
token it shows you (glrt-...):
sudo gitlab-runner register \
--non-interactive \
--url "https://gitlab.com" \
--token "<your-glrt-token>" \
--executor "docker" \
--docker-image "alpine:3.20"
If you later add a job of your own that needs a real Docker engine -
the same docker-outside-of-docker pattern appctl-tester uses
locally, mounting /var/run/docker.sock - add that same mount under
[runners.docker] in config.toml:
[runners.docker]
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
Start it, then push any change to your fork and watch CI/CD > Pipelines run against your own runner:
sudo gitlab-runner run