Imported from shakenfist/kerbside-patches (
AGENTS.md). Install upstream withnpx skills add shakenfist/kerbside-patches. Copyright stays with the author.
Agents Guide
This document provides guidance for AI agents working on the kerbside-patches codebase.
Project Overview
Kerbside-patches maintains patches against OpenStack components (Nova, Kolla, Kolla-Ansible) to enable native SPICE console functionality. It also contains container image build infrastructure for Kolla-Ansible deployments.
Where the documentation lives
| Question | Document |
|---|---|
| How is it put together? | ARCHITECTURE.md |
| How do I build patched container images? | docs/building.md |
| What does each helper script do? | docs/script-reference.md |
| How does CI data collection work? | docs/ci-data.md |
| How do the Zuul and Tempest jobs run? | docs/kolla-ansible-tempest-jobs.md |
| How do I iterate quickly on Kerbside? | docs/kolla-devmode.md |
| How does security scanning work? | docs/security-scanning.md |
| How do I query review.opendev.org? | docs/gerrit-api.md |
| How do I get changes landed upstream? | docs/tactics.md |
docs/index.md is the full index. New user-visible
documentation belongs in docs/; this file and ARCHITECTURE.md are a
summary and an index into it.
Key Patterns
Adding a New Patch
- Create
_patches/patchNNN-description.patch - Add it to the appropriate project's
ORDERfile .patch-messageis generated from the patch, not authored: edit the message inside the patch and regenerate withtools/extract-commit-message- Validate any
Depends-Onfooter withtools/check-depends-on.py - Use
_build/get-next-patch-number.pyto find the next available number (checks both files and open PRs)
Modifying Build Pipeline
Build scripts live in _build/. Key files:
common.sh-- shared functions and CLI argument parsingbuild-containers.sh-- orchestrates image builds and pushes via occystrap (proxy or sequential mode)imagebuild.sh-- runs kolla-build (supports--pushand--registryfor proxy mode)imagearchive.sh-- archives images with SBOMsdebsecan-report.sh-- scans built images for known CVEs using debsecan (non-destructive, images unchanged)
In CI, --use-proxy starts an occystrap filtering proxy
on localhost:5050 before building. kolla-build pushes
images directly to the proxy, which filters and forwards
to the CI registry. If the proxy fails to start, the
build falls back to the sequential occystrap process
push loop.
The Assembled Source Tree
_build/assemble-source.sh clones the upstream OpenStack
repos into src/ and applies our patch stack to them. That
tree is generated output, not source: it is regenerated from
scratch on every build and is listed in .gitignore.
- Never edit
src/to change patched behaviour, and never commit from it. Edits belong in_patches/; regenerate with_build/test-apply.shto see them applied. - Each subdirectory (
src/kolla,src/kolla-ansible, ...) is a separate git repository with its own.git. Thegit add -A .and commit calls in_build/apply-patches-and-test.shrun inside those repos, so this repository's.gitignoredoes not affect them. - Because
src/is ignored, a finished build worktree can be torn down with a plaingit worktree remove, with no--forceneeded.
Working with Layer Data
Layer metadata is collected during CI builds via occystrap inspect filters at three pipeline stages (as-built, post-normalize, post-exclude). In proxy mode, all images append to combined per-stage JSONL files. In sequential mode, per-image per-stage JSONL files are produced. Data flows:
- JSONL files written during push
- Files packaged into
layers.tar.gztarball - CI uploads the tarball as a build artifact
tools/collect-layer-data.pymerges the stages into one record per image and appends it to the time series filedata/layers/<build-name>/<image>.jsonlvia automated PRtools/summarize_layers.pyanalyzes the time series (growth, reuse and stage-comparison reports)
Modifying GitHub Actions Workflows
The main workflow is .github/workflows/functional-tests.yml.
It is large and has several jobs:
build_images-- builds container images (matrix strategy)test_installs-- deploys with kolla-ansible and runs testscollect_layer_data-- aggregates layer data from builds
The deploy steps (bootstrap, prechecks, pull, deploy,
install-clients, post-install) are shared with kerbside's
CI via the shakenfist/actions/deploy-kolla-ansible composite
action. Changes to the deploy flow should be made in that
action, not inlined in the workflow.
Every workflow needs a top-level permissions: block. Most of the
automation here authenticates as DAILY_REBASE_TOKEN rather than
GITHUB_TOKEN, so contents: read is usually the whole of it -- but
the block has to be there, and a new workflow without one fails the
fleet consistency audit.
If a job pushes a branch, its actions/checkout must take
token: ${{ secrets.DAILY_REBASE_TOKEN }}. Checkout persists whichever
token it used into .git/config, and that persisted credential is what
git push authenticates with -- setting GITHUB_TOKEN in the pushing
step's env only reaches gh, so with contents: read the push fails
as github-actions[bot] with a 403.
Never suppress a gitleaks finding for a credential that still
authorises something; rotate it first. For a false positive, add a
content regex to .gitleaks.toml rather than a path entry, because
patch files are rewritten wholesale on every upstream rebase. See
docs/security-scanning.md.
Other workflows:
rebase-tests.yml-- applies the patch stack and runs the test suites (_build/test-apply.sh) for kolla and kolla-ansible on both debian-13 and rocky-9 VM runnerstrigger-downstream.yml-- triggers kerbside CI on push to develop (e.g. after daily rebase PR merges)local-container-builds.yml-- tests local builds workdaily-rebase-checks.yml-- daily upstream rebase with Claude-assisted patch fixingci-reporting.yml-- on-demand refresh of the OpenDev CI reliability data indata/ci-reporting/. A workflow_dispatch dropdown picks the report; the report catalogue is intools/ci-report.sh(described indocs/ci-data.md) and the shared scan/chart engine istools/count_ci_log_errors.py. Incremental via committed per-report checkpoints so OpenDev is never re-scraped; uploads the refreshed data as a workflow artifact and proposes a data-update PR. The scan runs on a vm runner but the PR step runs on a static runner, becauseghis only installed on static runners (same split as the layer data flow)heal-data-prs.yml-- runs on every push to develop and union-merges develop into any automated data PR that GitHub reports as conflicted. Data PRs append records to shared time-series files underdata/, so concurrent PRs always conflict once one merges; the union of both sides' appended lines is always the correct resolution. The merge is verified append-only and well formed (tools/verify-data-merge.py) before being pushed (tools/heal-data-prs.sh)
Runner types and constraints:
vmrunners -- ephemeral VMs with full sudo access, provisioned on demand by the conductor (shakenfist/private-ci) perruns-onlabels[self-hosted, vm, <os>, <size>]. OS labels aredebian-13androcky-9(debian-12images still exist but kolla-ansible master no longer supports bookworm, so new jobs should usedebian-13); sizes ares,m,xl.staticrunners -- persistent machines without passwordless sudo for the CI userclaude-code-- persistent runner with the Claude Code CLI, used by the automated fixers
All test jobs run directly on a vm runner of the OS under test
(there are no longer any bare-metal runners or nested Shakenfist
VMs). The runners have passwordless sudo, so privileged steps are
fine; tools/ci-install-test-deps.sh installs the apt-vs-dnf
dependencies and, on rocky-9, upgrades Python to 3.12 via
tools/upgrade-python.
Python CLI tooling (tox, yq, occystrap, clingwrap, ...) is never
installed into the system Python: pip cannot upgrade distro-owned
packages, so system installs break on every distro upgrade. Instead
_build/setup-tools-venv.sh maintains a shared venv at
/srv/shakenfist/kerbside-patches-tools and symlinks its console
scripts into /usr/local/bin. _build/common.sh (and the lint/test
entry points) activate that venv when it exists; the symlinks cover
everything else. Add new Python CLI tools via that helper, not pip.
Documentation
- Do not edit README.md directly. Edit
README.md.tmplinstead. The daily rebase workflow regeneratesREADME.mdfrom the template, replacing%%date%%with the current date. Direct edits toREADME.mdwill be lost during regeneration. - Keep both files in sync when making manual changes.
Testing
- Patch application:
./_build/test-apply.sh --skip-tests <project> - Full test suite:
./_build/test-apply.sh <project> - Pre-commit:
pre-commit run --all-files(runs actionlint and shellcheck) - CI patch testing:
./_build/test-patches-for-ci.sh
Common Tasks
- Rebase patches on new upstream: Use
_build/rebase-with-claude.sh --bump-shas - Test a specific patch:
_build/test-apply.sh --test-patch patchNNN <project> - Check shared patch usage:
_build/find-patch-usage.py _patches/patchNNN.patch - Analyze layer optimization:
tools/summarize_layers.py(add--report growth,--report reuseor--report stagesto narrow the output)