Files
claude-code-action/docs/forgejo.md
T
Vlad MocanuandClaude Opus 4.6 b03ccb518c feat(prd-5): add dedicated claude-bot user for Forgejo
- Add bot_email action input for custom git commit email on Forgejo
- Update claude.yml to use CLAUDE_BOT_TOKEN with bot_id/bot_name/bot_email
- Update git-config.ts to use provided email over noreply format
- Add "Dedicated Bot User" setup guide to docs/forgejo.md
- Update CLAUDE.md with bot identity documentation

Infrastructure (done outside code):
- claude-bot user created (ID 48, non-admin, with avatar)
- Claude team with write access in all 12 orgs
- CLAUDE_BOT_TOKEN org-level secret in all 12 orgs

Progress: ~85% complete - pending E2E validation after merge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 20:56:55 +02:00

349 lines
16 KiB
Markdown

# Forgejo Platform Support
This action supports [Forgejo](https://forgejo.org/) (and Forgejo Actions) as an alternative to GitHub. The same action code runs on both platforms — platform detection is automatic or explicitly configured.
## Prerequisites
- **Forgejo instance** with [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/) enabled
- **Forgejo runner** registered and operational (e.g., a `grunt` or `ubuntu-latest` runner)
- **Anthropic API key** or **Claude Code OAuth token** for Claude API access
- **Forgejo token** with repo access (automatically available as `github.token` in workflows, or use a [dedicated bot user](#dedicated-bot-user-recommended))
## Quick Start
### 1. Add Secrets
Add secrets at the **organization level** (Settings > Actions > Secrets) so all repos can share them:
- `ANTHROPIC_API_KEY` — Your Anthropic API key (starting with `sk-ant-`)
- `CLAUDE_BOT_TOKEN` — API token for the dedicated bot user (see [Dedicated Bot User](#dedicated-bot-user-recommended) below)
### 2. Create Workflow
Create `.forgejo/workflows/claude.yml`:
```yaml
name: Claude Assistant
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned, labeled]
pull_request_review:
types: [submitted]
jobs:
claude-response:
runs-on: grunt # Your Forgejo runner label
steps:
- uses: https://git.wxs.ro/actions/claude-code-action@v1 # Or your mirror URL
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
platform: "forgejo"
github_token: ${{ secrets.CLAUDE_BOT_TOKEN }}
bot_id: "48" # Your bot user's Forgejo ID
bot_name: "claude-bot"
bot_email: "claude@wxs.ro"
```
### 3. Agent Mode (Automation)
For automation workflows triggered by `workflow_dispatch`, push events, or schedules:
```yaml
name: Claude Automation
on:
workflow_dispatch:
inputs:
task:
description: "Task for Claude"
required: true
jobs:
run-claude:
runs-on: grunt
steps:
- uses: https://git.wxs.ro/actions/claude-code-action@v1
with:
prompt: ${{ github.event.inputs.task }}
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
platform: "forgejo"
github_token: ${{ secrets.CLAUDE_BOT_TOKEN }}
bot_id: "48"
bot_name: "claude-bot"
bot_email: "claude@wxs.ro"
```
## Platform Detection
The action detects the platform using a three-tier strategy:
| Priority | Method | Details |
| -------- | --------------------------------- | -------------------------------------------------------- |
| 1 | **Explicit input** | `platform: "forgejo"` in workflow — most reliable |
| 2 | **`FORGEJO` env var** | Set to `"true"` by Forgejo runner v7.0+ |
| 3 | **`GITHUB_SERVER_URL` heuristic** | If the URL doesn't contain `github.com`, assumes Forgejo |
**Recommendation**: Always set `platform: "forgejo"` explicitly. Auto-detection works but the explicit input eliminates ambiguity.
## Differences from GitHub
### Authentication
| Aspect | GitHub | Forgejo |
| ------------------------ | ------------------------------- | ------------------------------------------------------------- |
| **Default auth** | GitHub App OIDC token exchange | Direct `GITHUB_TOKEN` / `FORGEJO_TOKEN` |
| **Token setup** | Automatic via Claude GitHub App | Manual — provide `github_token` input |
| **App token revocation** | Automatic cleanup step | Skipped (no app token) |
| **Bot identity** | `claude[bot]` (ID: 41898282) | Dedicated bot user via `bot_id`/`bot_name`/`bot_email` inputs |
On Forgejo, there is no OIDC token exchange with Anthropic. The action uses the workflow token (`github.token`) or a user-provided `github_token` directly.
### API Differences
| Feature | GitHub | Forgejo |
| --------------------- | ------------------------------------------- | ------------------------------------------------------------------- |
| **GraphQL API** | Used for PR/issue data fetching | Not available — REST API only |
| **Data fetching** | Single GraphQL query per entity | 5-8 parallel REST API calls |
| **Actor validation** | Checks for bot/app accounts | Skipped — no bot accounts on Forgejo |
| **GitHub MCP server** | `ghcr.io/github/github-mcp-server` included | Skipped — GitHub-specific |
| **CI/Actions MCP** | Full GitHub Actions API | Works via Octokit REST + `GITHUB_API_URL` |
| **Permission check** | `getCollaboratorPermissionLevel()` | `repos.get()` fallback (workflow tokens lack collaborator endpoint) |
| **Git ref lookup** | `git.getRef()` (`/git/ref/heads/...`) | `repos.getBranch()` (`/git/refs` is plural on Forgejo) |
### Bun Installation
The upstream action uses `oven-sh/setup-bun`. On Forgejo, this is replaced with a universal `curl` install because Forgejo runners pre-fetch all `uses:` action references before evaluating `if:` conditions, causing failures for GitHub-hosted actions that aren't mirrored.
### Comment URLs
Comment tracking URLs (job links, branch links, PR creation links) use Forgejo URL patterns instead of GitHub patterns. This is handled automatically.
## Action Inputs
The `platform` input is the only Forgejo-specific addition:
```yaml
platform:
description: "Platform to run on: 'auto' (default auto-detects), 'github', or 'forgejo'"
required: false
default: "auto"
```
All other inputs work identically on both platforms. See the [Usage Guide](./usage.md) for the full list.
## Dedicated Bot User (Recommended)
On GitHub, Claude gets its own identity via the GitHub App (`claude[bot]`). On Forgejo, there's no such mechanism — by default the action runs under whatever token you provide, meaning commits and comments appear as the human user.
Creating a dedicated bot user solves this:
- **Clear attribution**: Commits and comments show as `claude-bot`, not your personal account
- **Least privilege**: The bot token has only the permissions Claude needs
- **Audit trail**: Easy to distinguish human actions from bot actions in repo history
- **Security**: If the action is compromised, only the bot's scoped token is exposed
### Setup Steps
#### 1. Create the bot user
Go to **Site Administration > User Accounts > Create User Account** or use the admin API:
- **Username**: `claude-bot`
- **Email**: e.g., `claude@yourdomain.com`
- **Password**: any strong password (you'll use API tokens, not password login)
- **Uncheck** "Require user to change password"
- Leave "Admin" **unchecked**
- Optionally set a display name and avatar to distinguish from human users
#### 2. Add to organizations via teams
For each organization where Claude should operate:
1. Go to **Org Settings > Teams > New Team**
2. Create a team named `Claude` with:
- **Permission**: `Write`
- **Access**: All Repositories (so new repos are automatically covered)
3. Add `claude-bot` to the team's **Members**
#### 3. Generate a scoped API token
Log in as `claude-bot`, go to **Settings > Applications**, and create a token:
- **Token Name**: `claude-code-action`
- **Scopes**:
- `repo` — read/write (issues, PRs, comments, branches, commits)
- `user:read` — needed for API identity
- Leave `admin`, `organization`, `package` **unchecked**
#### 4. Store the token as an org-level secret
Add the token as `CLAUDE_BOT_TOKEN` at the **organization level** (Settings > Actions > Secrets) with visibility set to "All repositories". This way all repos in the org share the same secret.
#### 5. Get the bot user's ID
You'll need the Forgejo user ID for the `bot_id` input:
```bash
curl -s "https://your-forgejo-instance/api/v1/users/claude-bot" | jq '.id'
```
#### 6. Configure the workflow
```yaml
- uses: https://git.wxs.ro/actions/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
platform: "forgejo"
github_token: ${{ secrets.CLAUDE_BOT_TOKEN }}
bot_id: "48" # From step 5
bot_name: "claude-bot"
bot_email: "claude@yourdomain.com" # Must match the bot user's registered email
```
The `bot_email` input overrides the default noreply email format. When it matches the bot user's registered email in Forgejo, commits will be linked to the bot's profile in the UI.
### Token Rotation
Forgejo API tokens can expire. Plan for rotation:
1. **Set a calendar reminder** for before the token expires
2. **Generate a new token** by logging in as `claude-bot` (same scopes as above)
3. **Update the org secret** `CLAUDE_BOT_TOKEN` with the new token value
4. **No code changes needed** — the workflow reads the secret at runtime
Alternatively, generate a token without an expiration date (if your Forgejo instance allows it).
## Included CI Workflows
This fork includes four Forgejo-native workflows in `.forgejo/workflows/`:
### `claude.yml` — Tag Mode (@claude Mentions)
Responds to `@claude` mentions on issues, PR review comments, and PR reviews. Also triggers on issue assignment and label events. This is the main interactive workflow for using Claude on Forgejo.
### `ci.yml` — Tests, Formatting, Type Checking
Runs on push to `main` and pull requests. Three parallel jobs:
- `test` — `bun test` (with `GITHUB_SERVER_URL` override so GitHub-context unit tests pass)
- `prettier` — `bun run format:check`
- `typecheck` — `bun run typecheck`
### `test-action.yml` — Action Self-Test
Tests the action itself on Forgejo in agent mode. Runs weekly and on PRs that modify `src/`, `action.yml`, or `.forgejo/`. Validates:
- Platform detection returns "forgejo"
- Token authentication works
- Claude Code executes successfully
- Output file is produced
### `upstream-sync.yml` — Upstream Sync Check
Runs weekly. Compares `main` with `upstream/main` (anthropics/claude-code-action). If upstream has new commits:
- Creates or updates an issue with the `upstream-sync` label
- Includes commit log, diff stats, and merge instructions
- Lists conflict-prone files
## Merging Upstream Changes
This fork is designed for easy upstream merges. Only 4-5 files have Forgejo-specific modifications, and changes are placed at function entry points using an early-return pattern.
### Merge Process
```bash
# Fetch latest upstream
git fetch upstream main
# Merge into your main branch
git merge upstream/main
# If conflicts occur, they'll be in these files:
# - src/entrypoints/run.ts (platform branch at top)
# - src/modes/tag/index.ts (data fetcher swap)
# - src/mcp/install-mcp-server.ts (platform routing)
# - action.yml (platform input, bun install)
# Resolve conflicts, keeping both the upstream changes and
# the Forgejo platform branches at function entry points
# Run tests to verify
bun test
# Push
git push origin main
```
### Conflict Mitigation
All Forgejo changes follow the **early-return pattern** — platform-specific logic is placed at the top of functions, before the existing GitHub code. This means:
- Upstream changes to the GitHub code path rarely conflict with Forgejo additions
- When conflicts do occur, they're localized to the few lines where the platform branch starts
- Resolution is straightforward: keep both the platform check and the upstream changes
### What Happens When Upstream Adds New Features
New upstream features that use GraphQL or GitHub-specific APIs won't automatically work on Forgejo. The Forgejo data fetcher (`src/forgejo/data/fetcher.ts`) would need REST equivalents added. The action will still run — features that depend on missing data will simply lack that context.
## E2E Test Results
Both execution modes have been validated end-to-end on a Forgejo instance (git.wxs.ro):
| Mode | Trigger | Status | Details |
| -------------- | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Agent mode** | `workflow_dispatch` | Validated | Platform detection, token auth, Claude execution, output file — all green |
| **Tag mode** | `issue_comment` (`@claude`) | Validated | Issue context (title + body) read correctly, platform detected as `forgejo`, tracking comment created with spinner then updated with response (31s), job link URL correct |
### Tag Mode Verified Capabilities
- `@claude` trigger phrase detected in issue comments
- Issue body and title fully visible to Claude
- Tracking comment created with progress spinner, then updated with final response
- Forgejo job run URL correctly formatted in tracking comment
- Branch creation works via `repos.getBranch()` API (Forgejo uses `/git/refs` not `/git/ref`)
- Permission check passes via `repos.get()` fallback
### Not Yet E2E Tested
- PR review comment triggers (`pull_request_review_comment`)
- PR review triggers (`pull_request_review`)
- Label triggers (`issues` with `labeled` action)
- Assignee triggers (`issues` with `assigned` action)
- Branch creation + code changes from tag mode (issue asked a question, no code changes needed)
## Known Limitations
- **Git ref API**: Forgejo uses `/git/refs` (plural) instead of GitHub's `/git/ref` (singular). The action handles this by using `repos.getBranch()` on Forgejo for branch SHA lookups.
- **User display name fetch**: `GET /users/{username}` returns 403 without `read:user` scope. Non-fatal — falls back to login name.
- **`pull_request_target` event**: May not exist in Forgejo. The action degrades gracefully (maps to `pull_request`).
- **GitHub MCP server**: The `ghcr.io/github/github-mcp-server` Docker image is GitHub-specific and is skipped on Forgejo.
- **Commit signing**: SSH signing has not been tested on Forgejo. The `use_commit_signing` option (GitHub API signing) won't work since it uses GitHub's commit API.
- **`downloadJobLogForWorkflowRun`**: Forgejo's Actions API may lack this endpoint. The CI MCP server attempts it via Octokit REST but may return limited data.
- **Bot filtering**: `allowed_bots` checks won't match `claude[bot]` since Forgejo has no bot accounts. Actor validation is skipped entirely on Forgejo.
## Troubleshooting
### "Resource not accessible by integration" errors
This usually means the token lacks permissions. On Forgejo:
- Use a personal access token with `repo` scope via the `github_token` input
- The default `github.token` in Forgejo workflows may have limited permissions depending on your runner configuration
### Tests fail with platform detection errors
Unit tests assume GitHub context by default. When running on a Forgejo runner, set `GITHUB_SERVER_URL=https://github.com` before running tests (the CI workflow handles this automatically).
### Bun installation fails
If `curl -fsSL https://bun.sh/install | bash` fails on your runner, pre-install Bun on the runner image or use the `path_to_bun_executable` input to point to a custom Bun binary.
### Upstream workflows run and fail
If `.github/workflows/` from upstream are executing on your Forgejo runner, configure the runner to only read from `.forgejo/workflows/`. This is a runner-level setting — see your Forgejo instance's runner configuration.