Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ uv run ruff check src/ tests/ # lint
uv run ruff format --check src/ tests/ # format check
uv run mypy src/ # type check
uv run pytest tests/unit/ # unit tests
uv run codespell src/ tests/ # spell check
```

Never use `pip install`. All dependency management goes through `uv` and `pyproject.toml`.
Expand Down Expand Up @@ -56,6 +57,12 @@ examples/ # Example project layout (artifacts.yaml, spread.yaml, concierge.
4. **Avoid `Any`.** Prefer specific types; `mypy --strict` must pass. Legacy `Any` in YAML-handling helpers is tolerated but should not spread.
5. **CLI consistency: `run` / `expand` pairs.** Commands that execute a subprocess (`run`) and commands that print the equivalent command (`expand`) must be aligned in arguments, flags, and semantics. If `opcli foo run --bar baz` executes something, then `opcli foo expand --bar baz` must print the equivalent command with the same flags accepted. This applies to `spread`, `pytest`, and any future command groups with this pattern.
6. **Stepdown rule.** Within each module, order functions so callers appear before callees. Public API at the top, then private helpers below in call-order. Read top-to-bottom like a narrative.
7. **License headers required.** Every new `.py` file must begin with:
```python
# Copyright <year> Canonical Ltd.
# See LICENSE file for licensing details.
```
CI enforces this via `skywalking-eyes` and `.licenserc.yaml`. YAML, TOML, Markdown, and other non-Python files are excluded — see `.licenserc.yaml` for the full exclusion list.

---

Expand All @@ -67,7 +74,7 @@ examples/ # Example project layout (artifacts.yaml, spread.yaml, concierge.
| Packaging | `uv` |
| CLI | `Typer` |
| Data models | `Pydantic V2` |
| Lint/format | `Ruff` (rules: `E F W I UP B SIM PL RUF`; ignores: `B008` globally, `E501` in `spread.py`) |
| Lint/format | `Ruff` (see `pyproject.toml [tool.ruff.lint]` for full config — selected rule groups include `A B C D E F I N PL RUF S SIM TC UP W`) |
| YAML (user files) | `ruamel.yaml` (preserves comments) |
| Templating | `Jinja2` (pytest invocation templates) |
| Testing | `pytest` + `pytest-mock` |
Expand Down Expand Up @@ -112,7 +119,7 @@ The virtual backend in `spread.yaml` accepts opcli-only keys that are stripped d

| Key | Values | Default | Effect |
|---|---|---|---|
| `type` | `integration-test`, `tutorial` | (required) | Selects the backend template |
| `type` | `integration-test` | (required) | Selects the backend template |
| `runner` | JSON array of labels | — | CI runner labels for GitHub Actions matrix |
| `cpu`, `memory`, `disk` | integer | 4, 8, 20 | Local LXD VM resource allocation |

Expand Down Expand Up @@ -229,6 +236,9 @@ The table below distinguishes *mechanically enforced* rules from *advisory* ones
| Type safety (`mypy --strict`) | ✅ CI blocks | `ci.yml` step |
| Unit tests pass | ✅ CI blocks | `ci.yml` step |
| Coverage ≥ 85% | ✅ CI blocks | `pytest --cov-fail-under=85` |
| Spell check (`codespell`) | ✅ CI blocks | `ci.yml` lint step |
| License headers | ✅ CI blocks | `.licenserc.yaml` via `skywalking-eyes` |
| Shell scripts (`shellcheck`) | ✅ CI blocks | `ci.yml` shellcheck step |
| No push to main | ⚠️ Advisory | Branch protection (enable on canonical/charm-ci) |
| CI green before merge | ⚠️ Advisory | Branch protection (enable on canonical/charm-ci) |
| Docs updated with code | ⚠️ Advisory | PR review discipline |
Expand Down
40 changes: 25 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ A **local-first CLI tool** for Canonical operator developers to build charms, ro

`opcli` replaces the monolithic [`operator-workflows`](https://github.com/canonical/operator-workflows) approach with a modular pipeline based on explicit build plans (`artifacts.yaml`), stable build output (`artifacts.build.yaml`), and [spread](https://github.com/canonical/spread)-based test execution.

## Contents

- [Documentation](#documentation)
- [Installation](#installation)
- [Quick start](#quick-start)
- [Commands](#commands)
- [`artifacts.yaml` schema](#artifactsyaml-schema)
- [`spread.yaml` virtual backends](#spreadyaml-virtual-backends)
- [`integration-suites` in `spread.yaml`](#integration-suites-in-spreadyaml)
- [CI vs local](#ci-vs-local)
- [GitHub Actions reusable workflows](#github-actions-reusable-workflows)
- [Secrets for integration tests](#secrets-for-integration-tests)
- [Development](#development)

## Documentation

| Document | Purpose |
Expand All @@ -14,23 +28,25 @@ A **local-first CLI tool** for Canonical operator developers to build charms, ro

## Installation

```bash
sudo snap install astral-uv --classic
uv tool install git+https://github.com/canonical/charm-ci.git
export PATH="$HOME/.local/bin:$PATH" # or: uv tool update-shell && exec $SHELL
opcli --help
```

### Prerequisites

- Python 3.12+
- [uv](https://docs.astral.sh/uv/) (`sudo snap install astral-uv --classic`)
- [charmcraft](https://charmcraft.io/) (`sudo snap install charmcraft --classic`)
- [rockcraft](https://rockcraft.io/) (`sudo snap install rockcraft --classic`) — if building rocks
- [LXD](https://canonical.com/lxd) (`sudo lxd init --auto && sudo usermod -aG lxd $USER`)
- [spread](https://github.com/canonical/spread) (installed via `opcli install spread`) — for spread workflow
- [spread](https://github.com/canonical/spread) installed via `opcli install spread` after opcli is set up
- [concierge](https://github.com/canonical/concierge/) (`sudo snap install concierge --classic`) — for env provisioning

### Install opcli

```bash
sudo snap install astral-uv --classic
uv tool install git+https://github.com/canonical/charm-ci.git
export PATH="$HOME/.local/bin:$PATH" # or: uv tool update-shell && exec $SHELL
opcli --help
```

## Quick start

### Local testing with spread
Expand Down Expand Up @@ -150,12 +166,6 @@ opcli pytest expand --suite machine-charm/tests/integration/

When a single `integration-suites` entry exists, `--suite` is auto-detected. With multiple suites, it's required.

### `opcli tutorial`

| Command | Description |
|---|---|
| `expand <file>` | Extract shell commands from a tutorial (`.md`/`.rst`) and print as a shell script for `eval`. |

## `artifacts.yaml` schema

```yaml
Expand Down Expand Up @@ -187,7 +197,7 @@ Key fields:

## `spread.yaml` virtual backends

opcli recognises virtual backend types (`integration-test`, `tutorial`) and expands them into concrete spread backends at runtime:
opcli recognises the `integration-test` virtual backend type and expands it into a concrete spread backend at runtime:

```yaml
backends:
Expand Down
Loading