Shared Forgejo API contract: pinned spec + operational policy for Fjord and fj
  • Shell 81.3%
  • Makefile 18.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stephen Way 438e946238
All checks were successful
CI / Contract drift (push) Successful in 4s
Re-vendor the contract at Forgejo 17.0.0 (#8)
**What changes.** rasterhub.com now runs `17.0.0+gitea-1.22.0`; the vendored spec was pinned at `16.0.3`. The three spec files are `make fetch` + `make regenerate` against the live swagger, nothing hand-written. README and GAPS.md now state 17.0.0 as the vendored version, and GAPS.md gains entry 10 for the job log format switch.

No path, operation or schema was removed, and no schema gained a required field. Six operations are new:

```
GET    /repos/{owner}/{repo}/actions/jobs/{job_id}
POST   /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun
POST   /repos/{owner}/{repo}/actions/runs/{run_id}/rerun
PUT    /repos/{owner}/{repo}/issues/{index}/lock
DELETE /repos/{owner}/{repo}/issues/{index}/lock
GET    /user/actions/secrets
```

Two new schemas, `ActionRunJobStep` and `IssueLockOption`. `ActionRunJob` gains `html_url` and `steps`.

Existing operations that moved:

- `GET actions/jobs/{job_id}/logs` gains `step`, `q`, `qi` and `format` query parameters and a second response media type, `application/x-ndjson`.
- Workflow dispatch gains a documented 400. Compare gains `verification` and `files` query parameters. Team member `PUT` gains a 409.
- `EditDeadlineOption.due_date` and `CreateUserOption.email` are no longer required. `MigrateRepoOptions.service` gains `bitbucketdc`.
- Some "Deprecated" prose in descriptions became `x-deprecated: true`.

`versionFloor` stays at `7.0.0`. 17.0.0 is a capability ceiling a consumer detects at runtime, not a new floor.

**Evidence.** Probed on rasterhub.com at 17.0.0 on 2026-10-16. `GET actions/jobs/104670` on rasterstate/fjord-ios returns 200 with `steps[]` (`number`, `name`, `status`, `started`, `stopped`), `html_url` and `attempt`. `GET /user/actions/secrets` returns 200. Run rerun answers 204 once a run has finished and 400 before that; job rerun reruns the job and every job that depends on it. Job logs return `text/plain` whatever the `Accept` order and `application/x-ndjson` only under `format=ndjson` (GAPS.md entry 10). None of these paths are in the 16.0.3 spec this replaces, which is the evidence a consumer's capability table needs for them. `make check` passes locally.

This is not a no-op for a consumer that generates exhaustive Swift enums. The second log media type and the new dispatch 400 each add a case to a generated response enum, so Fjord needs three small `switch` changes when it moves its submodule pointer. Those land with the pointer bump in fjord-ios, not here.

**Not verified.** `PUT` and `DELETE issues/{index}/lock` were not exercised for this PR, since both mutate a live issue. fj does not generate from this contract yet, so nothing was checked on its side.

Reviewed-on: #8
2026-10-16 22:37:35 +00:00
.forgejo/workflows Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
patches Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
scripts Mark upload request bodies required from policy (#6) 2026-09-11 19:14:25 +00:00
.gitattributes Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
.gitignore Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
CODEOWNERS Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
GAPS.md Re-vendor the contract at Forgejo 17.0.0 (#8) 2026-10-16 22:37:35 +00:00
LICENSE Add Apache 2.0 licence and NOTICE (#4) 2026-08-04 16:55:02 +00:00
Makefile Mark upload request bodies required from policy (#6) 2026-09-11 19:14:25 +00:00
NOTICE Add Apache 2.0 licence and NOTICE (#4) 2026-08-04 16:55:02 +00:00
openapi.v1.yaml Re-vendor the contract at Forgejo 17.0.0 (#8) 2026-10-16 22:37:35 +00:00
openapi.yaml Re-vendor the contract at Forgejo 17.0.0 (#8) 2026-10-16 22:37:35 +00:00
OPERATIONS.md Extract the Forgejo API contract into a standalone repo 2026-05-20 19:33:50 -07:00
policy.yaml Mark upload request bodies required from policy (#6) 2026-09-11 19:14:25 +00:00
README.md Re-vendor the contract at Forgejo 17.0.0 (#8) 2026-10-16 22:37:35 +00:00
swagger.v1.json Re-vendor the contract at Forgejo 17.0.0 (#8) 2026-10-16 22:37:35 +00:00

forgejo-api-contract

The shared API contract for Forgejo clients built at Raster & State. One pinned spec, one operational policy, consumed by both Fjord (Swift / iOS) and fj (Rust CLI).

This repo is the single source of truth for:

  • which Forgejo API version the clients target,
  • the OpenAPI 3.0 document they generate code from,
  • the operational policy (retry budget, body cap, redaction list, version floor) the clients are expected to honor in lockstep.

It contains no compiled code. Each consumer vendors this repo as a git submodule and runs its own language-specific code generation.

What's here

Path Role
swagger.v1.json Upstream Forgejo spec, vendored at a pinned version (Forgejo 17.0.0, Gitea 1.22-compatible).
openapi.v1.yaml Raw OpenAPI 3.0 conversion of the swagger via swagger2openapi. Committed so consumers don't need Node to build.
openapi.yaml The patched spec: openapi.v1.yaml plus policy.yaml's requiredOverrides: and requestBodyOverrides: applied. This is the document consumers generate from.
policy.yaml Operational policy shared by every consumer: version floor, retry table, body cap, redaction allowlist, optionality overrides, required-field overrides.
patches/ Reserved for future Redocly-style overlays. Empty today; the simple required: rewrites are driven from policy.yaml.
scripts/apply-required-overrides.sh Reads policy.yaml's requiredOverrides:, applies them to openapi.v1.yaml. Validates each schema and field exists before writing.
scripts/apply-requestbody-overrides.sh Reads policy.yaml's requestBodyOverrides:, marks each named operation's request body required: true. Runs after the required-field pass and writes openapi.yaml.
scripts/generate-policy-swift.sh Consumer-side convenience: renders policy.yaml into a Swift constants file. Invoked by Fjord with its own output path. A generate-policy-rs.sh will join it when fj adopts the contract.
Makefile Contract-level targets: fetch, convert, patch, check.
OPERATIONS.md Re-vendor cadence, patch hygiene, ownership, breaking-change protocol.
GAPS.md Live-verified divergences between the vendored spec and real deployed servers (version skew, undocumented semantics, missing webhook surfaces).

Contract-level commands

make help          # list targets
make fetch         # re-download swagger.v1.json from rasterhub.com
make convert       # rerun swagger2openapi (writes openapi.v1.yaml)
make patch         # apply policy.yaml overrides (writes openapi.yaml)
make regenerate    # convert + patch
make check         # fail if either committed spec drifts (CI gate)

make check is this repo's CI gate. Language-specific code generation (Swift types, Rust constants) is each consumer's responsibility and runs in the consumer's own CI.

How consumers use this

Each consumer adds this repo as a git submodule, points its code generator at openapi.yaml, and runs its own policy.yaml-to-constants step:

  • Fjord vendors the submodule at Fjord/Tools/forgejo-contract/, runs swift-openapi-generator against openapi.yaml, and runs scripts/generate-policy-swift.sh to produce ForgejoPolicy constants.
  • fj (planned) will vendor the submodule, generate Rust models, and run a generate-policy-rs.sh for the matching policy.rs constants.

Prerequisites

  • node 24.x (for swagger2openapi, invoked via npx --yes).
  • yq, Mike Farah's Go implementation (not the Python yq wrapper). The patching script refuses to run on the wrong flavor.
  • make, bash, curl.

Re-vendoring

See OPERATIONS.md for the cadence, the patch-hygiene rules, and the major-version bump protocol.

versionFloor vs spec version

policy.yaml's versionFloor.semver is the minimum Forgejo major version clients agree to talk to (7.0.0, set by fj's pre-existing version-warning baseline). It is independent of the spec version vendored in swagger.v1.json (Forgejo 17.0.0 today); the spec floats forward, the floor changes only when both clients agree to drop compatibility with older servers.