Skip to content
Back to skills

Forgejo Cli

BSecurity

CLI for Forgejo API — issues, PRs, repos, labels, webhooks, Actions runners. Dual auth (AGENT/USER).

  • 36 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 10, 2026
ai-agentspythongoshellbashnodedockerdebuggingcode-reviewgitapi

Works with

  • cli
  • api

Security analysis

B88/100
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 2 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add theheavenlyd3mon/hermes-profiles --skill forgejo-cli --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Forgejo Cli?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Forgejo Cli
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/theheavenlyd3mon-forgejo-cli/badge)](https://www.skillsdirectory.com/skills/theheavenlyd3mon-forgejo-cli)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: forgejo-cli
description: "CLI for Forgejo API — issues, PRs, repos, labels, webhooks, Actions runners. Dual auth (AGENT/USER)."
version: 1.1.1
tags: [forgejo, git, api, code-review]
---

# forgejo-cli

Python CLI at `~/.hermes/scripts/forgejo-cli` wrapping the Forgejo API v1.

## Auth

Two tokens stored in `~/.hermes/.env`:
- `FORGEJO_AGENT_TOKEN` — jasper bot (default)
- `FORGEJO_USER_TOKEN` — magnus user (use `--user` flag)

## Usage

```
forgejo-cli <command> [<subcommand>] [OPTIONS]

Commands:
  issue       Manage issues (list, show, create, comment, label, assign)
  pr          Manage pull requests (list, show, create, diff, review, comment, merge)
  repo        Manage repositories (list, show, create, search)
  label       Manage labels (list, create)
  hook        Manage webhooks (list, create, delete)
  user        User info and settings
  comment     Manage comments (list, create, delete)

Global flags:
  --json      Machine-readable JSON output
  --dry-run   Preview without making changes
  --agent     Use AGENT token (default)
  --user      Use USER token
  --force     Skip confirmations
  --quiet     Suppress non-essential output
```

## Common Operations

```bash
# List issues
forgejo-cli issue list --owner magnus --repo test

# Show issue
forgejo-cli issue show --owner magnus --repo test --index 3

# Add comment
forgejo-cli issue comment --owner magnus --repo test --index 3 --body "Fixed"

# Get PR diff for review
forgejo-cli pr diff --owner magnus --repo myrepo --index 1

# Submit PR review (as jasper)
forgejo-cli pr review --owner magnus --repo myrepo --index 1 --body "LGTM" --event approve

# Merge a PR
forgejo-cli pr merge --owner magnus --repo myrepo --index 3 --dry-run   # Preview first
forgejo-cli pr merge --owner magnus --repo myrepo --index 3 --force      # Execute merge

# Merge via API (when CLI returns 405 or PR has conflicts to resolve first)
# See references/pr-merge-via-api.md for full workflow

# Create a PR
forgejo-cli pr create --owner magnus --repo myrepo --title "feat: add auth" --head feat/add-auth --base main --body "Closes #42"
forgejo-cli pr create --owner magnus --repo myrepo --title "draft: WIP" --head feat/wip --base main --draft

# Create an issue with labels (label IDs are NUMERIC)
forgejo-cli issue create --owner magnus --repo myrepo --title "Bug: login fails" --body "Details here" --labels 8,9

# List repos
forgejo-cli repo list --json

# Create a repo (NOT YET IMPLEMENTED in CLI — use API directly, see references/repo-creation-via-api.md)
# Documentation says `repo create` but the method isn't coded yet

# List labels
forgejo-cli label list --owner magnus --repo test

# Get current user info
forgejo-cli user show
forgejo-cli --user user show
```

## Server Setup

The Forgejo instance runs via Docker on `phatalbert`. See `references/server-setup.md` for the docker-compose.yml, SSH port mapping details (rootless gotcha), volume strategy, and admin accounts.

## Test Suite

Test script at `~/.hermes/scripts/forgejo-cli-test.sh`. Run with:
```bash
bash ~/.hermes/scripts/forgejo-cli-test.sh
```

## Forgejo Docker Deployment

See `references/fj-deployment.md` for Forgejo-specific Docker patterns: rootless image quirks, SSH port config, entrypoint config generation, `INSTALL_LOCK` requirements, database setup, and the `***` secrets masking pitfall.

## Forgejo Actions (CI/CD)

Forgejo Actions is a CI/CD system similar to GitHub Actions. Requires both server-side config and a runner. The `forgejo-actions` skill covers runner lifecycle, step container behavior, workflow patterns, and debugging in detail.

### Enabling Actions on Forgejo

Add to `/data/gitea/conf/app.ini` inside the forgejo container:
```bash
docker exec forgejo sh -c 'printf "\n[actions]\nENABLED=true\n" >> /data/gitea/conf/app.ini'
docker restart forgejo
```

### Registering a runner

```bash
# Get registration token
curl -s "https://git.brandyapple.com/api/v1/admin/runners/registration-token" \
  -H "Authorization: token $FORGEJO_USER_TOKEN"

# Register and start on the target host
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v runner-data:/data \
  data.forgejo.org/forgejo/runner:4.0.0 \
  forgejo-runner register \
  --instance https://git.brandyapple.com \
  --token <token> --name <host>-runner \
  --labels docker:docker://node:20-bookworm --no-interactive

# Run daemon
docker run -d --name forgejo-runner --user root \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v runner-data:/data --restart unless-stopped \
  data.forgejo.org/forgejo/runner:4.0.0 \
  forgejo-runner daemon
```

### Critical runner config

After registration, edit `/data/config.yaml` in the runner volume. See the `forgejo-actions` skill for full config reference — key settings:
- `container.docker_host: automount` — required to mount host Docker socket (runner > 5.0.3). Default is `"-"` which skips mounting.
- `container.valid_volumes: ['**']` — allows volume mounts from host

### Debugging

Step output is only visible in the Forgejo web UI, not in `docker logs forgejo-runner`. See `forgejo-actions` skill for Docker events debugging patterns.

Full runner/deploy workflow details in the `forgejo-gitea` skill's "Forgejo Actions (CI/CD)" section.

## Release Workflow

The forgejo-cli does not implement `release create`. Use the Forgejo API directly for the full release lifecycle:

```bash
# 1. Tag and push
git tag -a vX.Y.Z -m "vX.Y.Z — Title"
git push origin vX.Y.Z

# 2. Write release notes and POST data to a JSON file
# (Use the JSON-file approach to avoid shell escaping issues)
cat > /tmp/release-data.json << 'ENDJSON'
{
  "tag_name": "vX.Y.Z",
  "name": "vX.Y.Z — Release Title",
  "body": "## What's New\n\nRelease notes here.\n",
  "draft": false,
  "prerelease": false
}
ENDJSON

# 3. Create the release
curl -s -X POST "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/releases" \
  -H "Authorization: token $FORGEJO_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d @/tmp/release-data.json

# 4. Get the release ID for any subsequent edits
curl -s "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/releases" \
  -H "Authorization: token $FORGEJO_AGENT_TOKEN" | \
  python3 -c "import sys,json; [print(f'ID: {r[\"id\"]}  Tag: {r[\"tag_name\"]}') for r in json.load(sys.stdin)]"
```

### Gotcha: `name` not `title`

Forgejo's release API uses **`name`** as the release display title, **not** `title`. If you send `"title": "vX.Y.Z — Release"`, the field is silently ignored and the tag name is used as a fallback. The correct field:

```json
{"tag_name": "vX.Y.Z", "name": "vX.Y.Z — Release Title", "body": "..."}
```

To fix a release that was created with the wrong name, PATCH by release ID:

```bash
curl -s -X PATCH "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/releases/{id}" \
  -H "Authorization: token $FORGEJO_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "vX.Y.Z — Corrected Title"}'
```

PATCH by tag (`/releases/tag/{tag}`) returns 404 — you must use the numeric release ID.

📄 **`references/release-workflow.md`** — Full worked example with rollback instructions, the complete API sequence, and recovery steps for release mistakes.

## PR Review Workflow

The `references/pr-review-workflow.md` file covers the end-to-end automated code review workflow triggered by forgejo-prs webhooks: fetching diffs, composing review bodies with complex JSON, submitting reviews via API, and handling inline comments vs summary reviews.

## Pitfalls

### `--labels` requires numeric IDs (not strings)

The `--labels` flag accepts comma-separated label IDs. These must be **integers**. Non-numeric values are silently dropped.

```bash
# ✅ Correct: numeric IDs
forgejo-cli issue create --owner magnus --repo test --title "Bug" --labels 8,9

# ❌ Wrong: string values cause 422 API error
forgejo-cli issue create --owner magnus --repo test --title "Bug" --labels "8,9"
```

To look up label IDs by name:

```bash
forgejo-cli label list --owner magnus --repo test --json
```

This returns labels with their numeric `id` field. See issue #48 for the v1.1.1 fix history.

### Shell metacharacters in `--body` break `issue create`

The `--body` value is passed through the shell, so text containing `$`, backticks, parentheses, `&`, `|`, `;`, or unbalanced quotes causes parsing errors or silent truncation.

**Fix:** Use the Forgejo API directly with a JSON file for complex bodies:

```bash
# Write body to file
cat > /tmp/body.json << 'ENDOFBODY'
{"title": "Issue title", "body": "Complex body with (parens) and $dollar signs"}
ENDOFBODY

# POST via API
. ~/.hermes/.env 2>/dev/null
curl -s -X POST "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/issues" \
  -H "Authorization: Bearer $FORGEJO_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d @/tmp/body.json
```

Or pipe from Python to avoid any shell escaping:
```bash
. ~/.hermes/.env 2>/dev/null
python3 -c "import json; body = open('/tmp/body.md').read(); print(json.dumps({'title': '...', 'body': body}))" \
  | curl -s -X POST "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/issues" \
    -H "Authorization: Bearer $FORGE...EN" \
    -H "Content-Type: application/json" \
    -d @-
```

**Best option for complex bodies: Use `execute_code` with `urllib.request`.**

This eliminates ALL shell interaction — no quoting, no temp files, no token expansion:
```python
import json, urllib.request, os

env_path = os.path.expanduser("~/.hermes/.env")
token = None
with open(env_path) as f:
    for line in f:
        line = line.strip()
        if "FORGEJO_AGENT_TOKEN" in line and "=" in line:
            token = line.split("=", 1)[1].strip().strip('"').strip("'")

body = open("/tmp/body.md").read()
payload = json.dumps({"title": "Issue title", "body": body})

req = urllib.request.Request(
    "https://git.brandyapple.com/api/v1/repos/{owner}/{repo}/issues",
    data=payload.encode(),
    headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
    method="POST"
)
with urllib.request.urlopen(req) as resp:
    r = json.loads(resp.read())
    print(f"Created #{r['number']}: {r['title']}")
```

The same pattern works for PR creation — POST to `/pulls` instead of `/issues` with `head` and `base` fields. See `references/pr-creation-via-api.md`.

## Known Gaps ⚠️

| Claimed Feature | Actual Status | Workaround |
|---|---|---|
| `repo create` | Not implemented (only `list`, `show`, `search` exist) | Use raw API — see `references/repo-creation-via-api.md` |
| `repo show` | Accepts `--owner --repo` | `repo get` in code; `repo show` alias may not exist — try `--json` on `repo list` filtered by name |
| `pr merge` | Requires `--force` or `--dry-run` flag (not obvious from help output). Returns 405 when PR isn't mergeable (branch divergence, conflicts) | API-based merge — see `references/pr-merge-via-api.md` |
| `release create` | Not implemented (no release commands exist at all) | Use raw API — see `references/release-workflow.md` |
| Standalone PR comment (merged PR) | No subcommand for commenting on already-merged PRs | Use `POST /issues/{id}/comments` — see `references/pr-review-workflow.md` |
When a CLI subcommand is missing, the Forgejo REST API at `git.brandyapple.com/api/v1` is the backup. The `references/repo-creation-via-api.md` file has the exact curl incantation for repo creation, and `references/pr-creation-via-api.md` covers PR creation.

Files in this skill

  • SKILL.md11.2 KB
  • scripts/forgejo-cli32.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…