| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| .gitignore | ||
| README.md | ||
CI
Reusable Forgejo Actions workflows for the repositories on git.thinkerium.nl.
Pinning
Reference them at @v1. That tag moves to each release on the major line, so
fixes arrive without a commit in every repository, and a breaking change to an
input announces itself by landing on @v2 instead. Pin an exact @v1.4.2 when
a repository needs to stay put; @main is for this repository only, which uses
its own workflows to test them before anything else consumes them.
Branch model
The workflows assume, but do not require, two protected branches:
fix/expired-certs ──────────────────────────┐
▼
feat/tokens ──► develop ──────────────────► main ──► release
fix/parser ──►
Work reaches develop through pull requests, develop reaches main through
one more, and merging that one cuts a release. Urgent work goes straight to
main through its own pull request and is released on its own.
Both branch names are inputs, so master and development work as well. A
repository with no development branch is fine too: nothing matches, and each
pull request into the main branch becomes its own release entry.
One constraint is not negotiable. The merge from the development branch into the main branch must be a merge commit — squash it and every pull request boundary inside it is destroyed, with no way to recover the list afterwards. Restrict the merge style on the main branch accordingly. Pull requests into the development branch can be squashed or merged freely.
Conventional commits
Checks that the commits a pull request adds follow the conventional commit specification, using Cocogitto. Merge commits are ignored; fixup commits are not.
Add to a repository as .forgejo/workflows/pull-request.yml:
name: pull request
on:
pull_request:
jobs:
commits:
uses: thinkerium/ci/.forgejo/workflows/conventional-commits.yml@v1
Optional input: cocogitto-version, default 7.0.0.
Conventional pull request
Writes the title and the description of a pull request from the commits it adds. Open the pull request yourself; this keeps it current from then on.
The title is a conventional commit, derived in order of preference from:
- the branch name, when it reads as
type(scope)/slug—fix(auth)/expired-tokensbecomesfix(auth): expired tokens; - the commit subject, when the branch adds exactly one commit;
- the most significant type on the branch plus the branch name, as a fallback that always yields something valid.
It is written once, when the pull request is opened. Later pushes leave it alone, so a title you rewrite by hand stays rewritten — at the cost of going stale if a later commit changes the type.
The description is a Cocogitto changelog of the commits between the merge base and the branch tip, written between markers:
Anything you write above is kept.
<!-- cog:changelog:start -->
### Features
- **api**: add the /tokens endpoint (deadeb9)
<!-- cog:changelog:end -->
So is anything you write below.
Only the text between the markers is replaced. Write notes either side of the block and they survive; write them inside and the next push overwrites them. If one marker is deleted without the other the job fails rather than guess, since appending a second block would leave a changelog that never updates again.
Add to the same .forgejo/workflows/pull-request.yml:
jobs:
pull-request:
uses: thinkerium/ci/.forgejo/workflows/conventional-pull-request.yml@v1
The release pull request
A pull request from the development branch into the main branch is the release
itself. Its branch name says nothing about what is in it, and what is in it is
every pull request merged since the last release — so it is titled
chore(release): develop into main and described by that list rather than by
its commits:
### Pull requests in this release
- feat(api): add search (#32)
Back merges from the main branch are left out, since they carry what the main branch released already.
Optional inputs: main-branch, default main; development-branch, default
develop; cocogitto-version, default 7.0.0; template, a path to a Tera
changelog template in your own repository, default a template written for pull
request descriptions.
Commits that are not conventional are skipped rather than failing the job —
conventional-commits is what reports those. A branch with none of them gets a
placeholder description.
Two limitations worth knowing. Pull requests from forks are skipped, because
they run with a read-only token. And the built-in FORGEJO_TOKEN only reaches
its own repository, which is all this needs.
Release
Tags and publishes a release on every push to the main branch, with notes listing the pull requests it contains. Nothing is ever pushed to the branch: the version is derived from the commits, and the tag and the release are both created over the API.
Add to a repository as .forgejo/workflows/release.yml:
name: release
on:
push:
branches:
- main
jobs:
release:
uses: thinkerium/ci/.forgejo/workflows/release.yml@v1
The notes are one entry per pull request, grouped by type. A merge from the development branch is expanded into the requests it carries, so the release reads as the work it ships rather than as the one merge that delivered it:
### Features
- **api**: add search (#32)
### Bug Fixes
- **tls**: renew the certificates (#30)
The version comes from Cocogitto, derived from
the conventional commit types of those entries — a feat takes a minor, a
fix a patch, and a breaking change a major once past 1.0.0.
Tag prefixes
Nothing is asked of the calling repository. Cocogitto only recognises a tag as
a version when it knows the prefix, and answers with a brand new 0.1.0 rather
than an error when it does not, so the workflow works the prefix out and hands
cocogitto a configuration of its own:
- a
tag_prefixin yourcog.toml, if you keep one, always wins; - otherwise the prefix comes from the version tags already in the repository,
so
v1.4.2keeps taggingv, and1.4.2keeps tagging bare; - only a repository with no release yet falls back to the
tag-prefixinput, which defaults tov.
Your own cog.toml is read either way, so custom commit types and changelog
settings still apply — the prefix is prepended to a copy, never written into
your repository.
Note that if you do keep a cog.toml, tag_prefix belongs at the top
level; under [bump] cocogitto fails to parse the file.
Nothing is released when the push brings no new commits, or when everything in
it is a chore or a docs — the job succeeds and does nothing.
By default a major tag (v1) is moved to each release as well, so that
consuming repositories can track a major line instead of pinning an exact
version. Below 1.0.0 a breaking change is only a minor bump, so v0 moves
across breaking changes; pin exactly if that matters. Turn it off with
major-tag: false.
A repository with no releases yet starts at 0.1.0, cocogitto's default. Set
initial-version: "1.0.0" to declare the interface stable from the outset —
worth doing when other repositories build on it, since past 1.0.0 a breaking
change costs a major rather than a minor. It is read only when there are no
tags to count from, and never decides whether to release.
Optional inputs: main-branch, default main; development-branch, default
develop; major-tag, default true; initial-version, default unset;
tag-prefix, default v; cocogitto-version, default 7.0.0; template, a
path to a Tera changelog template in your own repository.
How the notes are built
Cocogitto reads commits, and has no way to walk only the first parent, so a release of twelve commits across three pull requests would read as twelve entries. The workflow therefore walks the first parent itself, recovers each pull request title from the merge commit Forgejo wrote, replays the result onto a scratch branch as one commit per request, and points cocogitto at that. The scratch branch is never pushed — which is also why the entries carry no commit hash, since those hashes would not exist anywhere after the job ends.