|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## What is this repository |
| 4 | + |
| 5 | +CI-Framework is an Ansible collection (`cifmw.general`) that bootstraps |
| 6 | +development and CI environments for RHOSO (Red Hat OpenStack Services on |
| 7 | +OpenShift). It is **not** intended for production or long-lived deployments. |
| 8 | + |
| 9 | +The upstream repository lives at |
| 10 | +`https://github.com/openstack-k8s-operators/ci-framework`. |
| 11 | + |
| 12 | +## Tech stack |
| 13 | + |
| 14 | +- **Ansible** collection (requires `ansible-core >= 2.15`). |
| 15 | +- **Python 3** modules and plugins under `plugins/`. |
| 16 | +- **Molecule** + Podman for per-role testing. |
| 17 | +- **ansible-test** for unit, sanity, and integration tests on plugins. |
| 18 | +- **Sphinx** for documentation (hosted on ReadTheDocs). |
| 19 | +- **Zuul**, **GitHub Actions**, and **Prow** for CI. |
| 20 | + |
| 21 | +## Repository layout |
| 22 | + |
| 23 | +| Path | Description | |
| 24 | +|---|---| |
| 25 | +| `roles/` | Ansible roles. Each has `defaults/`, `tasks/`, `molecule/`, `README.md`. | |
| 26 | +| `playbooks/` | End-to-end flows. Numbered stages (`01-bootstrap.yml` ... `99-logs.yml`), plus subdirectories for adoption, ceph, etc. | |
| 27 | +| `plugins/` | Collection plugins: `action/`, `filter/`, `modules/`, `module_utils/`. Test with `ansible-test`, not Molecule. | |
| 28 | +| `tests/` | `ansible-test` suites: `unit/` (pytest), `integration/targets/`, `sanity/ignore.txt`. | |
| 29 | +| `ci/` | CI-only playbooks: content provider, EDPM, kuttl, architecture validation, doc build, log collection. | |
| 30 | +| `scenarios/` | Scenario-oriented variable packs used by framework flows. | |
| 31 | +| `scripts/` | Environment setup, Molecule runner, ansible-test runner, Zuul/Molecule generation, snippet checks. | |
| 32 | +| `docs/` | Sphinx sources under `docs/source/`. | |
| 33 | +| `hooks/` | Hook playbooks consumed by the framework. Some hooks have their own `roles/` subdirectory. | |
| 34 | +| `custom/` | Local overrides (gitignored except `README.md`). Safe for local dev experiments, never committed. | |
| 35 | +| `containerfiles/` | Podman images for CI (`Containerfile.ci`, `Containerfile.tests`). | |
| 36 | +| `group_vars/` | Shared group variables (e.g., `all.yml`). Changes here affect every playbook run. | |
| 37 | +| `zuul.d/` | Zuul job and project definitions. **Some files are generated -- see below.** | |
| 38 | +| `_skeleton_role_/` | Template used by `ansible-galaxy role init` when creating new roles. | |
| 39 | + |
| 40 | +## Critical rules |
| 41 | + |
| 42 | +### Variable naming |
| 43 | + |
| 44 | +All Ansible role variables **must** match the pattern `^cifmw_[a-z_][a-z0-9_]*$`. |
| 45 | +This is enforced by `ansible-lint` with `strict: true` and `profile: production`. |
| 46 | + |
| 47 | +### FQCN required |
| 48 | + |
| 49 | +All module calls must use fully-qualified collection names. |
| 50 | +The following FQCN rules are enabled in `.ansible-lint`: |
| 51 | +`fqcn-builtins`, `fqcn[action]`, `fqcn[action-core]`, `fqcn[canonical]`, `fqcn[deep]`. |
| 52 | + |
| 53 | +### Generated files -- do not hand-edit |
| 54 | + |
| 55 | +The following files are **generated** by `scripts/create_role_molecule.py`: |
| 56 | + |
| 57 | +- `zuul.d/molecule.yaml` |
| 58 | +- `zuul.d/projects.yaml` (molecule section) |
| 59 | + |
| 60 | +To regenerate: `make role_molecule`. To verify consistency: `make check_zuul_files`. |
| 61 | +If you hand-edit these files, CI will reject the change. |
| 62 | + |
| 63 | +### Read-only / generated paths |
| 64 | + |
| 65 | +Do **not** modify these paths directly: |
| 66 | + |
| 67 | +| Path | Reason | |
| 68 | +|---|---| |
| 69 | +| `zuul.d/molecule.yaml` | Generated by `scripts/create_role_molecule.py`. | |
| 70 | +| `zuul.d/projects.yaml` | Generated (molecule section). | |
| 71 | +| `custom/` | Gitignored. Local-only overrides, never committed. | |
| 72 | +| `hooks/playbooks/roles/` | Excluded from ansible-lint. Owned by hook authors. | |
| 73 | + |
| 74 | +All other paths (`roles/`, `playbooks/`, `plugins/`, `group_vars/`, `scenarios/`, |
| 75 | +`hooks/playbooks/`, `ci/`, `scripts/`, `docs/`) are safe to edit following the |
| 76 | +conventions in this file. |
| 77 | + |
| 78 | +### Debugging patterns |
| 79 | + |
| 80 | +Use `block`/`rescue` for complex task sequences. Dump relevant variables in |
| 81 | +the `rescue` block, then `ansible.builtin.fail` to stop execution. This |
| 82 | +makes CI failures much easier to diagnose. |
| 83 | + |
| 84 | +## Playbook numbering |
| 85 | + |
| 86 | +Top-level playbooks in `playbooks/` follow a numbered-stage convention: |
| 87 | + |
| 88 | +| Prefix | Stage | |
| 89 | +|---|---| |
| 90 | +| `01-` | Bootstrap | |
| 91 | +| `02-` | Infrastructure | |
| 92 | +| `03-` | Build packages | |
| 93 | +| `04-` | Build containers | |
| 94 | +| `05-` | Build operators | |
| 95 | +| `06-` | Deploy | |
| 96 | +| `07-` | Admin setup | |
| 97 | +| `08-` | Run tests | |
| 98 | +| `09-` | Compliance | |
| 99 | +| `99-` | Log collection (always runs last) | |
| 100 | + |
| 101 | +Do **not** reuse or reorder existing numbers. Non-stage playbooks (e.g., |
| 102 | +`hooks.yml`, `update.yml`) live alongside without a number prefix. |
| 103 | + |
| 104 | +## Creating a new role |
| 105 | + |
| 106 | +Always use the Makefile: |
| 107 | + |
| 108 | +``` |
| 109 | +make new_role ROLE_NAME=my_role |
| 110 | +``` |
| 111 | + |
| 112 | +This generates the skeleton, Molecule config, and updates Zuul jobs. |
| 113 | +Every new role must have: |
| 114 | + |
| 115 | +1. A `README.md` documenting its parameters. |
| 116 | +2. Molecule test scenarios. |
| 117 | +3. Documentation that builds cleanly (checked in CI). |
| 118 | + |
| 119 | +If the role cannot be tested via Molecule, remove the `molecule/` directory |
| 120 | +and run `make role_molecule` to regenerate Zuul jobs. Add a note in the |
| 121 | +role's `README.md` explaining why. |
| 122 | + |
| 123 | +## Testing |
| 124 | + |
| 125 | +### Commands |
| 126 | + |
| 127 | +| Command | What it does | |
| 128 | +|---|---| |
| 129 | +| `make pre_commit` | Runs pre-commit hooks (shellcheck, black, ansible-lint) with dependency install. | |
| 130 | +| `make molecule` | Runs Molecule tests for all roles with dependency install. | |
| 131 | +| `make ansible_test` | Runs ansible-test (units + sanity + integration) with dependency install. | |
| 132 | +| `make tests` | Runs pre-commit + Molecule. | |
| 133 | +| `make check_zuul_files` | Regenerates Zuul YAML and fails if it differs from committed files. | |
| 134 | +| `make docs` | Builds Sphinx documentation under `docs/_build/html/`. | |
| 135 | +| `make spelling` | Runs `pyspelling` on docs. | |
| 136 | +| `make plugin-development-enable` | Rewrites import paths and sets `PYTHONPATH` for local plugin dev. | |
| 137 | +| `make plugin-development-disable` | Reverts the changes made by `plugin-development-enable`. | |
| 138 | + |
| 139 | +### Container-based testing (requires Podman) |
| 140 | + |
| 141 | +| Command | What it does | |
| 142 | +|---|---| |
| 143 | +| `make run_ctx_pre_commit` | Pre-commit in a container. | |
| 144 | +| `make run_ctx_molecule` | Molecule in a container. | |
| 145 | +| `make run_ctx_ansible_test` | ansible-test in a container. | |
| 146 | +| `make run_ctx_all_tests` | All of the above. | |
| 147 | + |
| 148 | +### Molecule specifics |
| 149 | + |
| 150 | +- Config: `.config/molecule/config_podman.yml` (host) or `config_local.yml` (container). |
| 151 | +- Test a single role: `TEST_SINGLE_ROLE=my_role make molecule` or `make run_ctx_molecule`. |
| 152 | +- Molecule scenarios live under `roles/<name>/molecule/`. |
| 153 | + |
| 154 | +## Linting and code style |
| 155 | + |
| 156 | +- **ansible-lint**: `production` profile, `strict: true`. Config in `.ansible-lint`. |
| 157 | +- **Python**: Formatted with `black`. |
| 158 | +- **Shell**: Checked with `shellcheck` (severity=error, excludes SC2071). |
| 159 | +- **Pre-commit**: Config in `.pre-commit-config.yaml`. Run with `make pre_commit` or `make run_ctx_pre_commit`. |
| 160 | +- **Spelling**: `pyspelling` on docs. Run with `make spelling`. |
| 161 | + |
| 162 | +### Excluded from linting |
| 163 | + |
| 164 | +ansible-lint skips: `.github/`, `scripts/`, `docs/`, `containerfiles/`, `ci/`, |
| 165 | +and the generated Zuul files (`zuul.d/projects.yaml`, `zuul.d/molecule.yaml`). |
| 166 | + |
| 167 | +## Commit conventions |
| 168 | + |
| 169 | +- **Title**: Must begin with the role name in brackets or parentheses: |
| 170 | + `[my_role] Add feature X` or `(my_role) Fix bug Y`. |
| 171 | + For cross-cutting changes use a category: `[ci]`, `[docs]`, `[Feature]`. |
| 172 | +- **Body**: Must be longer than 10 characters and describe **why** the change |
| 173 | + was made. |
| 174 | +- **Sign-off**: Required (`git commit --signoff`). The sign-off certifies a |
| 175 | + [DCO](https://developercertificate.org/). AI agents cannot sign off on behalf |
| 176 | + of a human -- the committer must add it themselves or amend the commit. |
| 177 | +- **AI attribution**: Use `Co-Authored-By:` for substantial AI-generated code, |
| 178 | + `Assisted-By:` for minor AI help. Disclose the scope in the PR description. |
| 179 | + |
| 180 | +## Branch workflow |
| 181 | + |
| 182 | +- The default branch is `main`. |
| 183 | +- Feature work happens on topic branches. |
| 184 | +- PRs target `main` unless otherwise specified. |
| 185 | +- Branch names should be descriptive (e.g., `fix-reproducer-pull-secret`, |
| 186 | + `feature/OSPRH-12345-new-role`). |
| 187 | + |
| 188 | +## PR process |
| 189 | + |
| 190 | +- PRs are auto-set to draft on open. To undraft, push a non-`nit:` change. |
| 191 | +- Minimum **2 approvals** required (excluding the author). |
| 192 | +- Security-sensitive code requires additional maintainer review. |
| 193 | +- Ownership is defined in `OWNERS` and `OWNERS_ALIASES`. |
| 194 | + |
| 195 | +## Relationship to ci-framework-jobs |
| 196 | + |
| 197 | +The `ci-framework-jobs` repository holds downstream Zuul job definitions that |
| 198 | +consume this repository. Jobs in that repo declare |
| 199 | +`required-projects: openstack-k8s-operators/ci-framework` and |
| 200 | +`roles: zuul: openstack-k8s-operators/ci-framework` so Zuul checks out this |
| 201 | +repo and exposes its roles during job execution. Uni jobs orchestrate this |
| 202 | +repo's `reproducer.yml` playbook as their main entry point. |
| 203 | + |
| 204 | +When making changes here that affect CI behavior, coordinate with the |
| 205 | +corresponding job definitions in `ci-framework-jobs`. |
| 206 | + |
| 207 | +## Plugin development |
| 208 | + |
| 209 | +To develop collection plugins locally without installing the collection: |
| 210 | + |
| 211 | +``` |
| 212 | +make plugin-development-enable |
| 213 | +``` |
| 214 | + |
| 215 | +This rewrites import paths and sets `PYTHONPATH`. Revert with: |
| 216 | + |
| 217 | +``` |
| 218 | +make plugin-development-disable |
| 219 | +``` |
| 220 | + |
| 221 | +Plugins are tested with `ansible-test`, not Molecule. |
0 commit comments