Files
werkator/docs/prs/2026-10-03-PR#92-forgejo-actions-compatible-config.md
miandmhoennig 923b605e57
werkator build succeeded after 01:44
werkator/deploy build succeeded after 00:07
feat(forgejo): ADR 0010, RFC 0006, and export:forgejo converting .werkator.yml into Forgejo Actions workflows (#92)
- **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
2026-10-03 13:31:19 +02:00

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.yml with a pushed default and a deploy without 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.default applied
  • And the deploy workflow has workflow_dispatch only, and that is named
Verified by

Scenario#92.02: A branch selector becomes a branches filter

So that the same branches build on both systems.

  • Given trigger.branches with *, 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

Scenario#92.03: atTimes becomes a schedule

So that a nightly build stays nightly.

  • Given atTimes with 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

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 if on github.ref_name; the glob and the missing predecessor are named
Verified by

Scenario#92.05: Image, environment and timeout

So that a Docker build runs in the same image.

  • Given a build with docker.image, docker.env and timeout: 90s, and one with a Dockerfile only
  • When it is converted
  • Then the first gets container.image, env and timeout-minutes: 2
  • And the Dockerfile is named, because a workflow needs a prebuilt image
Verified by

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

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:forgejo runs
  • Then the workflow holds the committed build command only
  • And --stdout prints the workflows instead of writing them
Verified by

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 --strict runs
  • Then it writes nothing and exits 1
  • And without a committed .werkator.yml it exits 2
Verified by

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.default without a trigger, an image built from a Dockerfile, and a follow-up with an empty cleanCommand
  • 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

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:forgejo runs on it, with --output-dir .gitea/workflows
  • Then the root's .werkator.yml is read, and the workflows are written below the root
  • And a directory outside any git repository is a usage error
Verified by

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.loadCommitted binds the committed .werkator.yml alone, builds.default applied.
    • ForgejoWorkflowExporter (new package forgejo) 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 with git rev-parse --show-toplevel, reads the .werkator.yml there, writes .forgejo/workflows/werkator-<build>.yml into it, prints the untranslated constructs on stderr, and refuses under --strict.
    • tools/werkator-to-forgejo runs the command from WERKATOR_BIN, the werkator on PATH, 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-dir matters until the RFC settles the default. werkdock.env is converted to env now, since Werkator has no environment outside a sandbox.
  • AGENTS.md carries the rule as a hard invariant and lists ADR 0010 among the decisions in force.

Additional Changes

  • AGENTS.md asks for column-aligned Markdown tables in new documents.
  • docs/configuration.md, docs/llms.md, the architecture skill and the package list in AGENTS.md describe the converter.
  • Version 1.29.0 with its release note.

Follow-up PRs

  • RFC 0006 step 2: the GITHUB_* and FORGEJO_* variables in every build.