docs: add README covering install, bot reviews, migration, restore

This commit is contained in:
Randa 2026-05-10 20:42:17 +04:00
parent af50266471
commit 06e11f4220

255
README.md Normal file
View file

@ -0,0 +1,255 @@
# forgejo-stack
A self-hosted Forgejo + Claude Code integration in a portable Docker Compose
stack. Tar it up, move it to any Linux box with Docker, untar, run
`./setup.sh` — done.
## What's in the box
- **Forgejo** at `http://localhost:3000`, SSH at `ssh://git@localhost:2222`
- **Postgres 16** as Forgejo's database
- **forgejo-runner** for Forgejo Actions (GitHub Actions compatible)
- **forgejo-mcp** sidecar so Claude Code can drive PR/issue/repo operations
natively via MCP
- **Claude + Gemini review bots** that auto-comment on PRs in any repo you
opt in via `bootstrap-repo.sh`
- **GitHub migration** via `scripts/migrate-from-github.sh`
## Requirements
- Linux (or WSL2)
- Docker 24+ with the compose plugin
- `curl`, `jq`, `awk`, `git`, `ssh` (usually preinstalled)
- ~2 GB free disk (data grows with your repos)
## Quick start
```bash
git clone <stack-repo-url> forgejo-stack
cd forgejo-stack
# Optional: edit .env.example to set your hostname/ports/API keys before
# first run. .env is auto-created from .env.example with random secrets
# filled in.
./setup.sh
```
The script is idempotent — re-running it is safe and a no-op for things
that already exist.
When done:
- Web UI: `http://localhost:3000`
- Admin user: see `FORGEJO_ADMIN_USER` in `.env` (defaults to `forgejo_admin`
— Forgejo reserves `admin` as a username)
- Admin password: see `FORGEJO_ADMIN_PASSWORD` in `.env`
Run `./scripts/smoke-test.sh` to verify the install end-to-end.
## Configuration
Everything lives in `.env`. Notable knobs:
| Var | Default | Purpose |
|---------------------------|------------------|----------------------------------------------------------|
| `FORGEJO_DOMAIN` | `localhost` | Used in every URL Forgejo emits |
| `FORGEJO_SSH_DOMAIN` | `localhost` | Used for SSH clone URLs |
| `FORGEJO_HTTP_PORT` | `3000` | Host port for the web UI |
| `FORGEJO_SSH_PORT` | `2222` | Host port for git-over-SSH |
| `FORGEJO_ADMIN_USER` | `forgejo_admin` | Admin username (cannot be `admin` — Forgejo reserves it) |
| `ANTHROPIC_API_KEY` | (empty) | Required for Claude review bot. If empty, bot skips. |
| `GEMINI_API_KEY` | (empty) | Required for Gemini review bot. |
| `CLAUDE_BOT_ENABLED` | `true` | Set `false` to disable Claude reviews globally |
| `GEMINI_BOT_ENABLED` | `true` | Set `false` to disable Gemini reviews globally |
| `GITHUB_TOKEN` | (empty) | Required for `migrate-from-github.sh`. Scopes: `repo`, `read:org` |
| `DOCKER_GID` | `984` | Host docker group GID — runner needs it to talk to the docker socket |
After editing `.env`, re-run `./setup.sh` to apply (it's idempotent).
## Bot reviews
Both review bots run as Forgejo Actions workflows. Forgejo 10 does **not**
propagate workflows from a `<owner>/.forgejo` repo to other repos under the
same owner (verified empirically), so each repo has to opt in explicitly:
```bash
./scripts/bootstrap-repo.sh <owner>/<repo>
# or, with the default admin owner:
./scripts/bootstrap-repo.sh <repo>
```
That copies `.forgejo/workflows/{claude,gemini}-review.yml` into the target
repo. The org-level repo `<owner>/.forgejo` (created by `setup.sh`) is the
canonical source — `bootstrap-repo.sh` reads from it and falls back to the
`templates/` directory in this stack if the org repo file is missing.
After opt-in, both bots auto-review on PR open and on every push. To skip
a single PR, add the `skip-bot-review` label.
To disable a bot for **all** repos: edit `.env`, set
`CLAUDE_BOT_ENABLED=false` (or `GEMINI_BOT_ENABLED=false`), re-run
`./setup.sh`.
To customize the prompt for a single repo, commit
`.forgejo/prompts/claude-review.md` (or `gemini-review.md`) into the repo —
the workflow uses repo-local prompts when present and falls back to the
defaults from the `<owner>/.forgejo` repo.
## Migrating from GitHub
```bash
# Single repo (default forgejo owner = FORGEJO_ADMIN_USER)
./scripts/migrate-from-github.sh octocat/Hello-World
# All public repos owned by an account
./scripts/migrate-from-github.sh --all <gh-username>
# Include private repos (token must have repo scope)
./scripts/migrate-from-github.sh --all <gh-username> --include-private
# Replace an already-migrated target
./scripts/migrate-from-github.sh octocat/Hello-World --force
```
Known limitations of GitHub→Forgejo migration:
- `@username` mentions in issues/PRs become orphaned (Forgejo can't resolve
them to local users)
- PR review threads can flatten into top-level comments
- Reactions on comments may drop
- GitHub Actions workflow files migrate as-is. Most work under Forgejo
Actions; some `actions/...` references may need adjustment
After migration, run `./scripts/bootstrap-repo.sh <owner>/<repo>` if you
want bot reviews on the migrated repo.
## Moving the stack to a new machine
```bash
# On the source box
docker compose down
tar czf forgejo-stack.tgz -C "$(dirname "$PWD")" "$(basename "$PWD")"
# Transfer however you like
scp forgejo-stack.tgz target:~/
# On the target
tar xzf forgejo-stack.tgz
cd forgejo-stack
./setup.sh --restore
```
`--restore` keeps existing Forgejo data (admin user, tokens, runner
registration, repos all survive), and only re-creates the per-host
artifacts that don't travel inside the data dirs:
- `~/space/.mcp.json` — registers the forgejo MCP server with Claude Code
- `~/.claude/projects/-home-luffy-space/memory/forgejo-local.md` — reference
memory entry pointing Claude Code at the stack
## Upgrading to LAN or public hostname
The stack ships localhost-first. To expose on your LAN or the internet:
1. Edit `.env`:
```
FORGEJO_DOMAIN=forge.example.com
FORGEJO_SSH_DOMAIN=forge.example.com
FORGEJO_ROOT_URL=https://forge.example.com/
```
2. (Public TLS only) Copy the Caddy override and bring the stack up with it:
```
cp docker-compose.caddy.yml.example docker-compose.caddy.yml
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d
```
Caddy provisions Let's Encrypt automatically. Ports 80 and 443 must be
reachable from the public internet.
3. Re-run `./setup.sh` so the bootstrap reflects the new URLs in workflows
and memory.
## Security notes
- The `forgejo-runner` container mounts `/var/run/docker.sock`. That is
effectively root on the host. If this matters to you, switch the runner to
host-mode execution at the cost of slower job startup.
- The runner container joins the host's docker group via `group_add`. The
default GID is `984`; override with `DOCKER_GID` in `.env` if your host
uses a different group.
- Bot tokens (`CLAUDE_BOT_TOKEN`, `GEMINI_BOT_TOKEN`) are stored as
Forgejo Actions secrets. They are not visible in finished workflow logs by
default, but anyone with admin access to your Forgejo can read them.
- API keys (`ANTHROPIC_API_KEY`, `GEMINI_API_KEY`) live both in `.env` and
as Forgejo Actions secrets. Keep `.env` out of git (already gitignored).
- `bootstrap-forgejo.sh` deletes Forgejo Actions secrets when their value
is empty in `.env` (Forgejo rejects empty-string PUTs with 422). So
leaving an API key blank disables the corresponding bot — it does not
leave a stale secret behind.
## Troubleshooting
- **Setup fails on `wait_for_url`**: Forgejo is slow to start the first
time on a fresh box. Re-run `./setup.sh`; the container will already be
up by then.
- **`forgejo admin user create` rejects the username**: Forgejo reserves
certain usernames (`admin`, `api`, `assets`, …). Pick another value for
`FORGEJO_ADMIN_USER` in `.env`. The default is `forgejo_admin`.
- **SSH on port 22 is wedged or "address already in use"**: do not enable
`START_SSH_SERVER=true` in the Forgejo container — the upstream image
already bundles its own SSH stack and the two collide. The compose file
ships with `START_SSH_SERVER=false`; leave it that way.
- **Runner stuck "waiting for /data/.runner-token"**: bootstrap writes the
token then restarts the runner. If the file never appears, check that
the admin token in `.env` works
(`curl -H "Authorization: token $FORGEJO_ADMIN_TOKEN" http://localhost:3000/api/v1/version`).
- **Runner exits with `permission denied … /var/run/docker.sock`**: the
in-container UID isn't in the host's docker group. Find the GID with
`getent group docker | cut -d: -f3` and set `DOCKER_GID` in `.env`,
then `docker compose up -d --force-recreate runner`.
- **No runner-list endpoint on Forgejo 10**: Forgejo's API exposes only
`/admin/runners/registration-token`, not a list endpoint. The smoke test
checks `data/runner/.runner` instead — that file is written by
`forgejo-runner` on successful registration.
- **`docker exec` into `forgejo-mcp` returns "exec: \"/bin/sh\": not found"**:
the `ronmi/forgejo-mcp` image is `FROM scratch` — there is no shell, no
`ls`, just the binary. The long-running container exposes an HTTP MCP
endpoint (port `8181` by default); Claude Code itself launches a fresh
`docker run --rm -i … stdio` container per session via the entry in
`~/space/.mcp.json`.
- **MCP not visible to Claude Code**: `cat ~/space/.mcp.json` should show
`mcpServers.forgejo`. Restart Claude Code so it re-reads the config.
- **Bot review didn't fire**: the most common cause is forgetting to opt
the repo in. Run `./scripts/bootstrap-repo.sh <owner>/<repo>` once. Then
check that the org-level secrets are set
(`Site Administration → Actions → Secrets`), the runner is online, and
the PR doesn't have the `skip-bot-review` label.
- **Bot review still skips after I added an API key**: re-run `./setup.sh`.
API keys are pushed into Forgejo Actions secrets only when bootstrap
runs. Without re-running, the workflow expands `${{ secrets.X }}` to an
empty string and short-circuits.
- **`PUT /user/actions/secrets` returns 422 [Data]: Required**: Forgejo 10
rejects empty string secrets. The bootstrap detects this and `DELETE`s
the secret instead, which lets workflows short-circuit cleanly. If you
see this 422 directly, it's because something else is calling the API
with an empty value.
## Repository layout
```
forgejo-stack/
├── docker-compose.yml # 4-service stack: db, forgejo, runner, forgejo-mcp
├── docker-compose.caddy.yml.example # opt-in TLS reverse proxy override
├── .env.example # all stack config
├── setup.sh # entry point — idempotent
├── scripts/
│ ├── lib.sh # shared bash helpers
│ ├── bootstrap-forgejo.sh # provisions admin/tokens/bots/runner/secrets
│ ├── bootstrap-repo.sh # opts a single repo into bot reviews
│ ├── migrate-from-github.sh # GitHub → Forgejo migration
│ └── smoke-test.sh # end-to-end verification
├── templates/
│ ├── workflows/ # claude-review.yml, gemini-review.yml
│ └── prompts/ # default review prompts (markdown)
└── data/ # all persistent state — gitignored
├── db/ # postgres
├── forgejo/ # forgejo (repos, users, lfs, …)
└── runner/ # forgejo-runner state
```