The Forgejo Dialect
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-erroris silently ignored by the Forgejo runner, so it is removed from the output and reported as a build warning (emitting it is misleading).permissionsis dropped too, because Forgejo reads none of its scopes and lists the key in the run page’s “Workflow warnings” box. The one exception isid-token: a job that assumes a cloud role over OIDC keepsid-token: write, and nothing else from itspermissionsblock. - Maps runner labels. GitHub-hosted labels like
ubuntu-latesthave 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 bareuses: actions/checkout@v4is rewritten to a resolvable form. Commonactions/*are mapped under an actions root (https://code.forgejo.orgby default, overridable viaforgejo.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-artifactandactions/download-artifactv4 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, andoverwrite). 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.
Dependencies in chant graph
Section titled “Dependencies in chant graph”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.
Lint rules and editor support
Section titled “Lint rules and editor support”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.
Migrating from GitHub Actions
Section titled “Migrating from GitHub Actions”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.
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:
| Fate | Meaning |
|---|---|
translated | carried across as-is |
approximated | carried with a close equivalent |
needs-review | confirm/adjust on Forgejo (permissions, which the runner does not enforce, unresolved uses:, unmapped runner label) |
lost | the 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.