Skip to content

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