From d0c2642943df20166bb18ab1648285be93700979 Mon Sep 17 00:00:00 2001 From: didericis Date: Fri, 8 May 2026 20:21:54 -0400 Subject: [PATCH] docs: document CI status check and main branch protection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Gitea Actions test-status badge plus a short README "CI" section covering how to read the check and what to do when it's red. Capture the (out-of-tree) branch-protection rule on `main` in docs/ci.md so the gate that requires the test check is reproducible from the repo alone — covers both the Gitea UI path and the equivalent API call. Refs: PRD 0002 Assisted-by: Claude Code --- README.md | 26 +++++++++++++++++ docs/ci.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 docs/ci.md diff --git a/README.md b/README.md index 51bf9ae..0a9eb37 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # claude-bottle +[![test](https://gitea.dideric.is/didericis/claude-bottle/actions/workflows/test.yml/badge.svg?branch=main)](https://gitea.dideric.is/didericis/claude-bottle/actions?workflow=test.yml) + Spins up an isolated container for running Claude Code with a curated set of skills and env vars. ## Why "claude-bottle"? @@ -37,6 +39,30 @@ The container is removed automatically when the session ends. If the script is killed with SIGKILL the exit trap won't fire and the container may be left running; remove it with `docker rm -f `. +## CI + +Every push to a PR (and every push to `main`) runs the full test suite +on Gitea Actions via `.gitea/workflows/test.yml`. The status badge at +the top of this README links to the workflow's run history; the same +check appears on each PR. + +Reading the check: + +- Green — `tests/run_tests.py` exited 0 on the runner. +- Red — at least one test failed (or the workflow itself errored). + Click through to the run, expand the failing step, and read the + unittest output. Reproduce locally with `tests/run_tests.py` (or + `tests/run_tests.py unit` if you don't have Docker handy); + integration tests skip cleanly when Docker isn't reachable. +- Skipped tests — expected when the runner has no Docker daemon. They + still leave the job green; if you actually want them executed, + ensure Docker is available on the runner host. + +Pushing a fix to a red PR re-triggers the workflow automatically — no +manual rerun needed. Branch protection on `main` requires this check +to be green before the merge button is enabled; see [`docs/ci.md`](docs/ci.md) +for how those rules are configured. + ## Egress Agent containers route HTTP / HTTPS traffic through a per-agent diff --git a/docs/ci.md b/docs/ci.md new file mode 100644 index 0000000..2ddcf4a --- /dev/null +++ b/docs/ci.md @@ -0,0 +1,83 @@ +# CI + +The test workflow lives at [`.gitea/workflows/test.yml`](../.gitea/workflows/test.yml). +It runs `tests/run_tests.py` (full suite — unit + integration) on: + +- every push to a branch with an open pull request, and +- every push to `main`. + +Integration tests need Docker on the runner; they skip cleanly via +`tests/_docker.skip_unless_docker` when no daemon is reachable. + +## Branch protection on `main` + +Branch protection is **not** captured in tree — Gitea applies it via +the repo settings UI / API, not a checked-in config file. Reproducing +the rule on a fresh clone or migration therefore means re-applying the +same setting through one of the two paths below. + +### Via the Gitea UI + +1. Go to the repo on `gitea.dideric.is`. +2. **Settings** → **Branches** → **Branch protection rules** → + **Add rule**. +3. **Branch name pattern:** `main`. +4. Enable **Enable Status Check** and select the check named + `test / run tests/run_tests.py` (the workflow's job display name). + The check has to have run at least once on the repo for Gitea to + list it; push a no-op commit on a feature branch first if needed. +5. (Recommended) Also enable **Require pull request before merging** + so changes to `main` always go through a PR — otherwise a direct + push to `main` bypasses the status check entirely. +6. Save. + +After saving, open a PR. The "Merge" button should be disabled until +the test check is green. + +### Via the Gitea API + +Equivalent call (requires an admin token in `$GITEA_TOKEN`): + +```sh +curl -X POST \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + https://gitea.dideric.is/api/v1/repos/didericis/claude-bottle/branch_protections \ + -d '{ + "rule_name": "main", + "enable_status_check": true, + "status_check_contexts": ["test / run tests/run_tests.py"], + "required_approvals": 0, + "block_on_outdated_branch": false + }' +``` + +The exact field for the check name is +`status_check_contexts` (an array of glob patterns). The Gitea +Actions check appears under +` / ` — here `test / run tests/run_tests.py`. +Confirm the actual rendered context string in **Repo → Actions → + → Job summary** before pasting into the API call; Gitea +versions occasionally tweak the formatting and a typo here silently +matches no checks (rule loads, but never blocks). + +To inspect the live rule: + +```sh +curl -H "Authorization: token $GITEA_TOKEN" \ + https://gitea.dideric.is/api/v1/repos/didericis/claude-bottle/branch_protections +``` + +## Verifying the gate + +Once the rule is in place, prove it works once with a deliberately-failing +test on a throwaway branch: + +1. Create a branch (`gate-test-DELETEME`), add a test that fails (e.g. + `self.assertTrue(False)`), push, open a PR. +2. Wait for the check to go red. Confirm the "Merge" button is + disabled / shows the unmet status check. +3. Close the PR and delete the branch. Do not merge. + +This is a one-time check after applying the rule, not a recurring +exercise.