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

176 lines
8.4 KiB
Markdown

> **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-<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.