Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract:change-request:
- any-glob-to-any-file:
- 'tools/bitbucket/**'
- 'tools/change-request/**'
- 'tools/forgejo/**'
- 'tools/github/**'
- 'tools/gitlab/**'
- 'tools/jira-patch/**'
Expand Down Expand Up @@ -101,6 +102,7 @@ contract:source-control:
- changed-files:
- any-glob-to-any-file:
- 'tools/asf-svn/**'
- 'tools/forgejo/**'
- 'tools/fossil/**'
- 'tools/github/**'
- 'tools/gitlab/**'
Expand All @@ -112,6 +114,7 @@ contract:tracker:
- changed-files:
- any-glob-to-any-file:
- 'tools/bitbucket/**'
- 'tools/forgejo/**'
- 'tools/fossil/**'
- 'tools/github/**'
- 'tools/github-body-field/**'
Expand Down
2 changes: 1 addition & 1 deletion docs/adapters/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ extension point = a documented, labelled slot with a tracking issue.
| [`tools/forwarder-relay`](../../tools/forwarder-relay/) | ASF-security ([`tools/gmail/asf-relay.md`](../../tools/gmail/asf-relay.md)) | huntr.com, HackerOne, GHSA relay |
| [`tools/scan-format`](../../tools/scan-format/) | ASVS | other scanner formats |
| [`tools/vcs`](../../tools/vcs/) | Git, Mercurial, Fossil | Subversion [\#602](https://github.com/apache/magpie/issues/602), Jujutsu [\#603](https://github.com/apache/magpie/issues/603), Perforce [\#605](https://github.com/apache/magpie/issues/605) |
| Forge / tracker | [`github`](../../tools/github/), [`jira`](../../tools/jira/), [`bitbucket`](../../tools/bitbucket/) `partial-read-only` foundation, [`sourcehut`](../../tools/sourcehut/), [`fossil`](../../tools/fossil/), [`gitlab`](../../tools/gitlab/) `partial-read-only` foundation | Forgejo/Gitea [\#310](https://github.com/apache/magpie/issues/310), Pagure [\#312](https://github.com/apache/magpie/issues/312), deeper Bitbucket/Jira coverage [\#606](https://github.com/apache/magpie/issues/606), GitLab [\#305](https://github.com/apache/magpie/issues/305), Bugzilla [\#302](https://github.com/apache/magpie/issues/302) |
| Forge / tracker | [`github`](../../tools/github/), [`forgejo`](../../tools/forgejo/) `partial` foundation, [`jira`](../../tools/jira/), [`bitbucket`](../../tools/bitbucket/) `partial-read-only` foundation, [`sourcehut`](../../tools/sourcehut/), [`fossil`](../../tools/fossil/), [`gitlab`](../../tools/gitlab/) `partial-read-only` foundation | Forgejo/Gitea [\#310](https://github.com/apache/magpie/issues/310), Pagure [\#312](https://github.com/apache/magpie/issues/312), deeper Bitbucket/Jira coverage [\#606](https://github.com/apache/magpie/issues/606), GitLab [\#305](https://github.com/apache/magpie/issues/305), Bugzilla [\#302](https://github.com/apache/magpie/issues/302) |
Comment thread
potiuk marked this conversation as resolved.
| [`tools/chat`](../../tools/chat/) | [`chat-slack`](../../tools/chat-slack/) | Discord [#1421](https://github.com/apache/magpie/issues/1421) |
| Agent harness | Claude Code, [Codex](codex.md) `experimental` ([#313](https://github.com/apache/magpie/issues/313)), [Gemini CLI](gemini.md) `experimental` ([#314](https://github.com/apache/magpie/issues/314)), [Local LLM (Ollama / llama.cpp / vLLM)](local-llm.md) ([#315](https://github.com/apache/magpie/issues/315)), [Cursor](cursor.md) ([#316](https://github.com/apache/magpie/issues/316)), [Goose](goose.md) `guide only` ([#319](https://github.com/apache/magpie/issues/319)), [Aider](aider.md) `guide only` ([#317](https://github.com/apache/magpie/issues/317)), [GitHub Copilot](copilot.md) `guide only` ([#318](https://github.com/apache/magpie/issues/318)), Grok `reviewer backend only` ([#1416](https://github.com/apache/magpie/issues/1416)) | Amazon Q [#320](https://github.com/apache/magpie/issues/320)–OpenHands [#322](https://github.com/apache/magpie/issues/322) |
| Security cross-ref | [`tools/osv`](../../tools/osv/) | — |
Expand Down
1 change: 1 addition & 0 deletions docs/labels-and-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ or a contract-free mix of substrates (e.g. `tools/spec-inventory` is
| [`tools/container-gateway`](../tools/container-gateway/) | `substrate:sandbox` | Per-project policy proxy for the podman / docker API; label-scoped, mount- and privilege-checked container access from inside the sandbox |
| [`tools/forwarder-relay`](../tools/forwarder-relay/) | `contract:report-relay` | Adapter contract for inbound-relay backends (ASF Security relay, huntr.com, HackerOne triagers). Pure interface spec; adapters declare detection + credit-extraction + reporter-addressing rules. |
| [`tools/bitbucket`](../tools/bitbucket/) | `contract:change-request` + `contract:tracker` | Coverage: `partial`. Bitbucket Cloud and Bitbucket Data Center bridge foundation for repository metadata context, branch restriction context for PR-management decisions, pull-request discovery/fetching, read-only commit fetching, read-only diff fetching, comments-only discussion fetching, read-only review-state fetching, Cloud-only pull-request task listing/fetching, read-only merge-check context fetching, and read-only status fetching, plus narrowly scoped Cloud pull-request comment creation and approve/unapprove actions. Tracker coverage includes Cloud-only issue listing/fetching, issue comment fetching, issue attachment metadata fetching, and confirmed issue-comment creation. The `partial` qualifier means this tool implements named contract operations but does not satisfy the complete contract and must not be counted as a complete/selectable backend. Broader pull-request review/mutation, broader issue writes, and linked Jira handoff coverage remain incomplete. |
| [`tools/forgejo`](../tools/forgejo/) | `contract:tracker` + `contract:source-control` + `contract:change-request` | Coverage: `partial`. Forgejo / Gitea REST API and `tea` CLI forge bridge foundation for issue listing/fetching, confirmed issue creation, body edits, comments, labels, and milestones under `contract:tracker`, git-backed branch/commit/push operations under `contract:source-control`, and pull-request creation via REST API / compare URL and label edits under `contract:change-request`. Project boards are unsupported (`no-op`) due to absence of REST card/column endpoints. The `partial` qualifier means this tool implements named contract operations but does not satisfy the complete contract and must not be counted as a complete/selectable backend. |
| [`tools/fossil`](../tools/fossil/) | `contract:tracker` + `contract:source-control` | Fossil SCM forge bridge: integrates local SQLite-backed ticket tracking, wiki, and forum reads with the version-control shim |
| [`tools/github`](../tools/github/) | `contract:tracker` + `contract:source-control` + `contract:change-request` | GitHub REST / GraphQL tracker substrate (called by every lifecycle phase) plus the Git source-control binding documented in [`source-control.md`](../tools/github/source-control.md) (runnable backend in [`tools/vcs`](../tools/vcs/)) and the pull-request review/merge gate (`change-request`; the ASF default backend, alongside `tools/jira-patch/` and `tools/mail-patch/` for SVN-first projects) |
| [`tools/gitlab`](../tools/gitlab/) | `contract:tracker` + `contract:source-control` + `contract:change-request` | Coverage: `partial`. GitLab REST API v4 forge bridge foundation for repository metadata context under `contract:source-control`, issue listing/fetching under `contract:tracker`, and merge request discovery, diffs, commits, and CI pipeline status under `contract:change-request`. The `partial` qualifier means this tool implements named contract operations but does not satisfy the complete contract and must not be counted as a complete/selectable backend. Write operations, issue mutation, and merge request mutations remain out of scope for this foundation. |
Expand Down
6 changes: 3 additions & 3 deletions docs/vendor-neutrality.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,9 +576,9 @@ generated block below.

| Capability contract | Neutral? | Class | Backends today | Basis |
|---|---|---|---|---|
| `contract:tracker` | ✅ | vendor-backed | Atlassian, Fossil, GitHub, SourceHut | 4 backend vendors: Atlassian, Fossil, GitHub, SourceHut; partial foundation, not counted: bitbucket, gitlab |
| `contract:source-control` | ✅ | vendor-backed | Fossil, Git, GitHub, SourceHut, Subversion | 5 backend vendors: Fossil, Git, GitHub, SourceHut, Subversion; partial foundation, not counted: gitlab |
| `contract:change-request` | ✅ | vendor-backed | Atlassian, GitHub, email | 3 backend vendors: Atlassian, GitHub, email; partial foundation, not counted: bitbucket, gitlab |
| `contract:tracker` | ✅ | vendor-backed | Atlassian, Fossil, GitHub, SourceHut | 4 backend vendors: Atlassian, Fossil, GitHub, SourceHut; partial foundation, not counted: bitbucket, forgejo, gitlab |
| `contract:source-control` | ✅ | vendor-backed | Fossil, Git, GitHub, SourceHut, Subversion | 5 backend vendors: Fossil, Git, GitHub, SourceHut, Subversion; partial foundation, not counted: forgejo, gitlab |
| `contract:change-request` | ✅ | vendor-backed | Atlassian, GitHub, email | 3 backend vendors: Atlassian, GitHub, email; partial foundation, not counted: bitbucket, forgejo, gitlab |
| `contract:mail-archive` | ✅ | vendor-backed | ASF, Google, SourceHut | 3 backend vendors: ASF, Google, SourceHut |
| `contract:chat` | ❌ | vendor-backed | Slack | only 1 backend vendor (Slack); needs 1 more |
| `contract:mail-source` | ✅ | vendor-backed | ASF, Google, Maildir | 3 backend vendors: ASF, Google, Maildir |
Expand Down
37 changes: 37 additions & 0 deletions tools/forgejo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [`tools/forgejo/`](#toolsforgejo)
- [Prerequisites](#prerequisites)
- [Configuration](#configuration)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->

# `tools/forgejo/`

**Capability:** contract:tracker + contract:source-control + contract:change-request
Comment thread
potiuk marked this conversation as resolved.

**Coverage:** partial

**Kind:** implementation

**Vendor:** Forgejo / Gitea

Forgejo / Gitea REST substrate. Pure read/write wrapper used by every lifecycle phase (triage / intake / fix / resolve / stats). See [`tool.md`](tool.md) for the operation catalogue and the per-area files ([`issue-template.md`](issue-template.md), [`labels.md`](labels.md), [`operations.md`](operations.md), [`project-board.md`](project-board.md), [`status-rollup.md`](status-rollup.md)) for specifics.

This tool implements three capability contracts: `contract:tracker` (issues / labels), `contract:source-control` (Git branch / commit / diff / push, documented in [`source-control.md`](source-control.md)), and `contract:change-request` — partial pull-request recipes driven by `tea pr` and REST endpoints. On Forgejo/Gitea the `change-request` `land` verb resolves to `tea pr merge` (the forge lands and closes atomically).

## Prerequisites

- **Runtime:** Bash — this is a doc-only adapter; skills invoke the `tea` CLI (`tea`) and `git`, no local package.
- **CLIs:** `tea` (authenticated), `git` (source-control capability), `curl`, `jq`.
- **Credentials / auth:** Every REST recipe in [`operations.md`](operations.md) (collaborator lookup, issue create, issue body edit, comments, PR create) sends `Authorization: token $TEA_TOKEN` to `$FORGEJO_HOST`, so both variables are required whenever those recipes run. The token value is stored in a private configuration file in the user's home directory (e.g. `~/.config/tea/token` or `~/.config/apache-magpie/user.md` per [`AGENTS.md` § Local setup](../../AGENTS.md#local-setup)) and exported to the agent's environment. `$FORGEJO_HOST` is the base URL including the scheme (`https://forge.example.org`), since the recipes build `$FORGEJO_HOST/api/v1/...` from it. For `tea` CLI operations, `tea login list` must show an authenticated login.
- **Network:** The Forgejo/Gitea instance host (`$FORGEJO_HOST`) must be added to the sandbox network allowlist, permitting access to the instance API (`/api/v1/`) and Git remote; source-control recipes are offline except explicit `fetch` / `push`.

## Configuration

Adopters select Forgejo-backed tracker, source-control, and change-request behavior through `<project-config>/project.md` repository keys such as `tracker_repo`, `upstream_repo`, and the source-control / change-request entries in the *Tools enabled* table. Forgejo issue body fields, labels, and PR-management knobs live in the matching `<project-config>/*-config.md` files documented from `projects/_template/README.md`.
116 changes: 116 additions & 0 deletions tools/forgejo/issue-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Forgejo / Gitea — issue-body field schema](#forgejo--gitea--issue-body-field-schema)
- [Where the schema is authoritative](#where-the-schema-is-authoritative)
- [Field roles the skills use](#field-roles-the-skills-use)
- [Body-field surgery](#body-field-surgery)
- [Empty-field convention](#empty-field-convention)
- [Issue-template to CVE 5.x mapping](#issue-template-to-cve-5x-mapping)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

# Forgejo / Gitea — issue-body field schema

The skills treat every tracker's **issue body** as a structured
document with named fields, rather than free-form prose. Each field is
a markdown `### <field name>` heading followed by a value block; the
set of field names is the schema the skills read from and write to.

The field names themselves are project-specific (they come from the
project's Forgejo issue-template configuration). Generic skill logic refers to
them by **role** — *"the CVE-tool-link field"*, *"the public-advisory
URL field"* — and the role → concrete-name mapping is declared in the
project manifest. For an adopting project, see
[`../../<project-config>/project.md`](../../<project-config>/project.md#issue-template-fields).

## Where the schema is authoritative

Three surfaces need to stay in lock-step whenever a field is added,
renamed, or removed:

1. **The Forgejo Template** — `.gitea/ISSUE_TEMPLATE/issue_report.yaml` (or equivalent `.forgejo/`)
in the tracker repo. This is what Forgejo renders as the "New
issue" form and is the machine-readable schema.
2. **The project manifest** — `<project-config>/project.md` declares
the concrete field name each skill role maps to. Renaming the
field in the YAML requires updating this mapping.
3. **The skills that write fresh issue bodies** — `security-issue-import`
emits a heredoc body with the full field set; when the schema
changes, the heredoc must change in lock-step.

No skill parses the YAML at runtime. The field list is hand-
maintained in the project manifest and in the `security-issue-import`
heredoc, and the three surfaces are kept aligned by convention.

## Field roles the skills use

The generic lifecycle refers to fields by these roles:

| Role | Read by | Written by | Purpose |
|---|---|---|---|
| `issue-description` | dedupe | import | The verbatim inbound report; private to the security team. |
| `public-summary` | CVE JSON generator | release manager (Step 13) | Sanitised one-paragraph public summary for the advisory. |
| `affected-versions` | CVE JSON generator, sync | sync proposes, user confirms | The `>= X, < Y` range that populates CVE 5.x `affected[]`. |
| `security-thread` | dedupe, sync (reporter-notification lookup) | import | Private pointer to the inbound mail thread — the Gmail `threadId`, any PonyMail archive URL, and the root `Message-ID` (archive-independent message handle; backtick-wrapped). **Never** exported to the public CVE record. |
| `public-advisory-url` | CVE JSON generator, sync (gates close) | sync (Step 14) | Public archive URL; tagged `vendor-advisory` in `references[]`. |
| `reporter-credit` | CVE JSON generator | import (placeholder), sync (after reporter confirms) | Credit line as the reporter wants to appear in the public advisory. |
| `pr-with-fix` | sync, CVE JSON generator | fix, sync | URL of the merged `<upstream>` PR. |
| `remediation-developer` | CVE JSON generator | sync (auto-populated from `pr-with-fix` author when set; manual edits preserved) | Person(s) who authored the fix; one credit per line. |
| `cwe` | CVE JSON generator | sync proposes, user confirms | CWE number for the CVE 5.x `problemTypes[]`. |
| `severity` | CVE JSON generator | sync proposes, user confirms | CVSS severity; never copy the reporter's self-assigned value. |
| `cve-tool-link` | sync, security-cve-allocate (blocker check) | security-cve-allocate | Canonical link to the CVE record in the project's CVE tool. |

The concrete field names each role maps to for the adopting project
live in the project manifest.

## Body-field surgery

Skills that update one field without touching the rest use the
following pattern:

1. **Read** the full body (`curl -fsS -H "Authorization: token $TEA_TOKEN" "$FORGEJO_HOST/api/v1/repos/<tracker>/issues/<N>" | jq -r .body` or `tea issues <N> --repo <tracker> --output json | jq -r .body`).
2. **Split** on `\n### ` to get per-field sections.
3. **Replace** the target section's value (between its `### <name>\n\n`
header and the next `### ` or end-of-body).
4. **Join** the sections back together.
5. **Write** the new body via the REST API PATCH endpoint (`curl -fsS -X PATCH -H "Authorization: token $TEA_TOKEN" -H "Content-Type: application/json" --data-binary @<scratch>/issue-body.json "$FORGEJO_HOST/api/v1/repos/<tracker>/issues/<N>"` with the payload formatted via the Write tool per [`operations.md`](operations.md#edit--body)).

Never construct the body by string concatenation in a shell command —
literal backticks, `$(…)`, and newlines in the body are silently
corrupted by shell quoting. Always materialise the edited body to a
temp file first.

## Empty-field convention

Forgejo's issue-form renderer behaves similarly to GitHub's. The skills honour this convention:

- On **read**, treat `_No response_` as "field unset".
- On **write**, preserve `_No response_` in fields the triager has
not yet filled (do not collapse them to empty strings or delete
the heading).
- The CVE JSON generator treats `_No response_` as absence and simply
omits the corresponding CVE-record element.

## Issue-template to CVE 5.x mapping

The `generate-cve-json` tool maps body-field roles to CVE 5.x record
elements as follows (generic — applies to any project using this
schema):

| Body field role | CVE 5.x element |
|---|---|
| `public-summary` | `descriptions[].value` |
| `affected-versions` | `affected[].versions[].version` / `.lessThan` |
| `cwe` | `problemTypes[].descriptions[].cweId` |
| `severity` | `metrics[].other.content` (textual severity) |
| `public-advisory-url` | `references[]` with `tags: ["vendor-advisory"]` |
| `pr-with-fix` | `references[]` with `tags: ["patch"]` |
| `reporter-credit` | `credits[]` with `type: "finder"` |
| `remediation-developer` | `credits[]` with `type: "remediation developer"` |
| `security-thread` | **not exported** — private-only |
| `cve-tool-link` | **not exported** — points at the tool itself, not at a public URL |
Loading