- **ADR 0010** (accepted): the build configuration stays expressible as a Forgejo Actions workflow. A new build-definition key needs a workflow counterpart; host-side keys are exempt. - **RFC 0006** (proposed): one workflow for Forgejo and Werkator, and the steps towards it. Step 1 is implemented here. - **`werkator export:forgejo`**, wrapped by **`tools/werkator-to-forgejo`**: converts the committed `.werkator.yml` (never the `.git` layers or `~/.werkator.yml`) into `.forgejo/workflows/werkator-<build>.yml`, one per definition that triggers on its own, follow-ups as jobs with `needs`. Untranslatable constructs become comments and stderr lines; `--strict` refuses. - `AGENTS.md`: the ADR 0010 rule as a hard invariant; column-aligned Markdown tables. - v1.28.0 On Werkator's own `.werkator.yml` the converter names two constructs: `builds.deploy` has no committed trigger (the host adds it), and `werkdock.env` is sandbox environment. PR-doc: `docs/prs/2026-10-03-PR#92-forgejo-actions-compatible-config.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #92
8.4 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
Werkator's .werkator.yml and the Forgejo Actions workflow format describe the same builds in two ways.
Every new build-definition key could widen that gap, and nothing in the repository said that it should not.
A project that builds on Forgejo and on Werkator has to keep two CI descriptions in step.
Nobody could tell how far apart the two formats are for a given repository.
Non-Goals
- No full Forgejo Actions compatibility;
uses:, expressions and matrices stay out. - Werkator does not read workflows yet; that is step 5 of RFC 0006.
- The converter does not guess host policy: it reads the committed file only.
The Scenarios
Feature: .werkator.yml converts into Forgejo Actions workflows
Scenario#92.01: One workflow per definition that triggers on its own
So that a trigger, which belongs to a workflow in Forgejo, keeps its build.
- Given a
.werkator.ymlwith a pusheddefaultand adeploywithout a trigger - When it is converted
- Then each becomes a workflow with checkout, clean, build and the upload of its artifacts
- And each job carries its effective settings,
builds.defaultapplied - And the
deployworkflow hasworkflow_dispatchonly, and that is named
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.01
Scenario#92.02: A branch selector becomes a branches filter
So that the same branches build on both systems.
- Given
trigger.brancheswith*, an exclusion and a? - When it is converted
- Then
*becomes**, exclusions go last, a filter of exclusions alone starts with**, and?is escaped and named
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.02
Scenario#92.03: atTimes becomes a schedule
So that a nightly build stays nightly.
- Given
atTimeswith a daily and an hourly time - When it is converted
- Then each becomes a cron line
- And a schedule over more than one branch is named, because Forgejo schedules only the default branch
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.03
Scenario#92.04: A follow-up becomes a job with needs
So that a deployment still runs only after a green build.
- Given a chain of follow-ups, one with literal branches, one with a glob, and one whose predecessor is missing
- When it is converted
- Then the chain becomes jobs of the root's workflow with
needs - And literal branches become an
ifongithub.ref_name; the glob and the missing predecessor are named
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.04
Scenario#92.05: Image, environment and timeout
So that a Docker build runs in the same image.
- Given a build with
docker.image,docker.envandtimeout: 90s, and one with a Dockerfile only - When it is converted
- Then the first gets
container.image,envandtimeout-minutes: 2 - And the Dockerfile is named, because a workflow needs a prebuilt image
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.05
Scenario#92.06: Nothing is dropped silently
So that the list of untranslated constructs measures the gap ADR 0010 keeps from growing.
- Given
activeWithin,stdoutLog,requirePullRequest,statusContext, a multi-line command and$branch - When it is converted
- Then each is listed, and written as a comment above the place it belongs to
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.06
Scenario#92.07: Only the committed file is read
So that no secret and no host policy ends up in a committed workflow.
- Given a machine config with a token and a build command, and an instance file with defaults
- When
export:forgejoruns - Then the workflow holds the committed build command only
- And
--stdoutprints the workflows instead of writing them
Verified by
- ExportForgejoCommandUnitTest — Scenario#92.07
Scenario#92.08: --strict refuses
So that a CI check can demand a complete conversion.
- Given a construct without a workflow counterpart
- When
export:forgejo --strictruns - Then it writes nothing and exits 1
- And without a committed
.werkator.ymlit exits 2
Verified by
- ExportForgejoCommandUnitTest — Scenario#92.08
Scenario#92.09: What the existing repositories showed
So that the converter's output for the repositories Werkator builds today says what they need.
- Given a
builds.defaultwithout a trigger, an image built from a Dockerfile, and a follow-up with an emptycleanCommand - When it is converted
- Then the settings base gets no workflow
- And the Dockerfile is named although the image has a name, and so is the follow-up that builds on its predecessor's worktree
Verified by
- ForgejoWorkflowExporterUnitTest — Scenario#92.09
Scenario#92.10: The workflows go into the target repository
So that the converter can be pointed at any checkout, from anywhere.
- Given a directory inside a git repository whose root holds the
.werkator.yml - When
export:forgejoruns on it, with--output-dir .gitea/workflows - Then the root's
.werkator.ymlis read, and the workflows are written below the root - And a directory outside any git repository is a usage error
Verified by
- ExportForgejoCommandUnitTest — Scenario#92.10
The Solution
- ADR 0010 records the direction: the build configuration stays expressible as a Forgejo Actions workflow. A new build-definition key needs a workflow counterpart; host-side keys are exempt.
- RFC 0006 proposes one workflow for both systems and the steps towards it.
- Step 1 of the RFC is implemented here:
ConfigLoader.loadCommittedbinds the committed.werkator.ymlalone,builds.defaultapplied.ForgejoWorkflowExporter(new packageforgejo) writes one workflow per definition that triggers on its own, with a small YAML writer, so comments can sit where they belong.ExportForgejoCommand(export:forgejo [REPO]) finds the root of the target repository withgit rev-parse --show-toplevel, reads the.werkator.ymlthere, writes.forgejo/workflows/werkator-<build>.ymlinto it, prints the untranslated constructs on stderr, and refuses under--strict.tools/werkator-to-forgejoruns the command fromWERKATOR_BIN, thewerkatoronPATH, or the jar of the checkout.
- It is a Kotlin command, not a Python script: it binds the file exactly as Werkator does, and its tests run in the existing suite.
- A trial run over the repositories of mih09 and over hs.hsadmin.ng converted every file; RFC 0006 lists what it found.
Both forges run Gitea, which reads
.gitea/workflows, so--output-dirmatters until the RFC settles the default.werkdock.envis converted toenvnow, since Werkator has no environment outside a sandbox. AGENTS.mdcarries the rule as a hard invariant and lists ADR 0010 among the decisions in force.
Additional Changes
AGENTS.mdasks for column-aligned Markdown tables in new documents.docs/configuration.md,docs/llms.md, the architecture skill and the package list inAGENTS.mddescribe the converter.- Version 1.29.0 with its release note.
Follow-up PRs
- RFC 0006 step 2: the
GITHUB_*andFORGEJO_*variables in every build.