PRD 0002: Test pipeline on Gitea Actions #3

Merged
didericis merged 6 commits from prd-0002-test-pipeline-on-gitea-actions into main 2026-05-09 02:48:04 -04:00
2 changed files with 109 additions and 0 deletions
Showing only changes of commit d0c2642943 - Show all commits
+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.