docs: document CI status check and main branch protection
test / run tests/run_tests.py (pull_request) Failing after 31s

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
This commit is contained in:
2026-05-08 20:21:54 -04:00
parent 6b140402dd
commit d0c2642943
2 changed files with 109 additions and 0 deletions
+26
View File
@@ -1,5 +1,7 @@
# claude-bottle # 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. Spins up an isolated container for running Claude Code with a curated set of skills and env vars.
## Why "claude-bottle"? ## 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 is killed with SIGKILL the exit trap won't fire and the container may be
left running; remove it with `docker rm -f <container-name>`. left running; remove it with `docker rm -f <container-name>`.
## 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 ## Egress
Agent containers route HTTP / HTTPS traffic through a per-agent Agent containers route HTTP / HTTPS traffic through a per-agent
+83
View File
@@ -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
`<workflow-name> / <job-name>` — here `test / run tests/run_tests.py`.
Confirm the actual rendered context string in **Repo → Actions →
<run> → 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.