> **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 - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — Scenario#92.01 #### 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 - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — Scenario#92.02 #### 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 - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — 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 `if` on `github.ref_name`; the glob and the missing predecessor are named ##### Verified by - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — 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.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 - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — 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](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — 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:forgejo` runs - **Then** the workflow holds the committed build command only - **And** `--stdout` prints the workflows instead of writing them ##### Verified by - [ExportForgejoCommandUnitTest](../../src/test/kotlin/de/hoennig/werkator/commands/ExportForgejoCommandUnitTest.kt) — 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 --strict` runs - **Then** it writes nothing and exits 1 - **And** without a committed `.werkator.yml` it exits 2 ##### Verified by - [ExportForgejoCommandUnitTest](../../src/test/kotlin/de/hoennig/werkator/commands/ExportForgejoCommandUnitTest.kt) — 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.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 - [ForgejoWorkflowExporterUnitTest](../../src/test/kotlin/de/hoennig/werkator/forgejo/ForgejoWorkflowExporterUnitTest.kt) — 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: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 - [ExportForgejoCommandUnitTest](../../src/test/kotlin/de/hoennig/werkator/commands/ExportForgejoCommandUnitTest.kt) — Scenario#92.10 ## The Solution - [ADR 0010](../adrs/0010-2026-10-03.forgejo-actions-compatible-config.md) 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](../rfcs/0006-one-workflow-for-forgejo-and-werkator.md) 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-.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.