Skip to content

The Forgejo Dialect

llms.txt lists every page for an agent

Forgejo (the forge behind Codeberg, self-hosted Forgejo, and Gitea) runs GitHub-Actions-compatible workflows. You write the same Workflow, Job, Step and composites as for GitHub Actions, imported from @intentius/chant-lexicon-forgejo instead of the github package:

import { Workflow, Job, Step, Checkout, SetupNode } from "@intentius/chant-lexicon-forgejo";
export const workflow = new Workflow({
name: "CI",
on: { push: { branches: ["main"] } },
});
export const build = new Job({
"runs-on": "ubuntu-latest",
steps: [
Checkout({}).step,
SetupNode({ nodeVersion: "22", cache: "npm" }).step,
new Step({ name: "Test", run: "npm test" }),
],
});

On build, the dialect:

  • Drops keys Forgejo ignores. continue-on-error is silently ignored by the Forgejo runner, so it is removed from the output and reported as a build warning (emitting it is misleading). permissions is dropped too, because Forgejo reads none of its scopes and lists the key in the run page’s “Workflow warnings” box. The one exception is id-token: a job that assumes a cloud role over OIDC keeps id-token: write, and nothing else from its permissions block.
  • Maps runner labels. GitHub-hosted labels like ubuntu-latest have no fixed meaning on Forgejo. They are mapped to a default Forgejo label (docker), overridable per project. Unmapped labels pass through with a warning.
  • Resolves uses: action refs. Forgejo has no GitHub Marketplace, so a bare uses: actions/checkout@v4 is rewritten to a resolvable form. Common actions/* are mapped under an actions root (https://code.forgejo.org by default, overridable via forgejo.actionsRoot); docker/* are pinned to their full GitHub URL. Local (./...), docker://, and full-URL refs pass through untouched. Anything else passes through and is reported as a warning so it’s never silently unresolvable.
  • Pins the artifact actions to v3. actions/upload-artifact and actions/download-artifact v4 and later refuse to run on GitHub Enterprise Server, and a Forgejo runner reads as one to an action, so the step fails. Those refs become @v3, and an upload step loses the inputs only v4 has (include-hidden-files, which v3 does by default, and overwrite). An earlier version, a SHA or a branch is left as written.

Everything else is emitted by the github serializer, which already produces the exact YAML shape Forgejo executes.

A forgejo project graphs the way a github one does: chant graph --format ir|mermaid|dot draws each workflow as a box of its jobs, with job needs, reusable-workflow calls (uses: ./.forgejo/workflows/<file> as well as ./.github/workflows/), workflow_run triggers and local composite actions as edges. The github lexicon resolves them; see workflow concepts. The graph reads the source before the dialect applies, so a rewritten action ref does not change it.

Forgejo has no lint rules or LSP completions/hover of its own. A forgejo workflow has the same TypeScript shape as the github one, so chant lint and editor tooling run github’s checks and catalog against forgejo source unmodified (completionProvider() and hoverProvider() forward straight to githubPlugin’s). Each GHA0xx rule is wrapped under a WFJ- prefix (e.g. WFJ-GHA001) with its check logic untouched, so a project (or chant audit) can load the github and forgejo plugins together without an id collision. See the github lexicon’s Lint Rules page for the full GHA0xx catalog.

GitHub and Forgejo YAML are near-identical, so the migration applies the same dialect as a build. The useful part is the compare, which shows what the move costs.

Terminal window
chant migrate .github/workflows/ci.yml --to forgejo -o .forgejo/workflows/ci.yml --validate

--validate prints a security posture report classifying each property’s fate across the edge:

FateMeaning
translatedcarried across as-is
approximatedcarried with a close equivalent
needs-reviewconfirm/adjust on Forgejo (permissions, which the runner does not enforce, unresolved uses:, unmapped runner label)
lostthe Forgejo runner ignores it (continue-on-error)

The same view is available to agents as the forgejo:compare MCP tool, which takes a workflow file and returns per-property fates plus summary counts. It is read-only.

chant