No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-10 03:57:20 +00:00
.forgejo/workflows fix(ci): run the tag job on this repo's own pool, host-mode 2026-08-09 06:03:41 -07:00
hack docs(lib): explain the ambiguous-unicode warning this file triggers 2026-08-09 20:56:45 -07:00
action.yaml feat: record what wrote each entry, and backfill missing ones 2026-08-05 02:04:03 -07:00
README.md feat: record what wrote each entry, and backfill missing ones 2026-08-05 02:04:03 -07:00

changelog-action

A reusable Forgejo composite action that writes CHANGELOG.md entries from a release's commit range. It asks an OpenAI-compatible inference service to turn the commits since the previous v* tag into a short, plain-language summary, splices a dated ## [x.y.z] section into the changelog, refreshes the compare links, and commits the result back to the branch.

It is composite (shell only) — it needs git, curl, and jq in the job, but no Node runtime — so it runs on any of our Forgejo runner pools, including the ones without a DinD sidecar.

One release can produce several changelog files in different languages (for example CHANGELOG.md in English and CHANGELOG.es.md in Spanish) in a single commit.

Using it

Add a step after your tag is pushed:

- name: Update changelog for the new release
  uses: https://git.brooktrails.org/brooktrails/changelog-action@v1
  with:
    tag: v${{ steps.tags.outputs.version }}
    token: ${{ secrets.FORGEJO_TOKEN }}
    inference-url: ${{ vars.INFERENCE_URL }}   # optional; omit for the default

and commit a .changelog.env at your repo root stating what your software is:

PRODUCT_NAME="SLP (Simple LLM Proxy)"
PRODUCT_DESCRIPTION="an OpenAI-compatible HTTP router that forwards requests to upstream inference backends based on the model ID in the request body."
# optional per-repo defaults:
# INFERENCE_MODEL=mistral-small-4

Those two strings become the first sentence of the prompt: "You write release notes for SLP (Simple LLM Proxy), an OpenAI-compatible HTTP router that…". Everything else in the prompt (voice, what to leave out, no attribution, output-only) is fixed in the action so every project's entries read the same way.

Multiple languages

- uses: https://git.brooktrails.org/brooktrails/changelog-action@v1
  with:
    tag: v${{ steps.tags.outputs.version }}
    token: ${{ secrets.FORGEJO_TOKEN }}
    targets: |
      CHANGELOG.md:US English
      CHANGELOG.es.md:Spanish

Each path:language pair gets its own inference call and its own file; all changed files land in one docs(changelog): update for <tag> [skip ci] commit. The target files must already exist (create CHANGELOG.es.md with the same heading/link-block scaffold your CHANGELOG.md uses).

Why it never fails your release

The release has already happened by the time this step runs, so a changelog problem must not fail the job. Every failure path logs a ::warning:: and exits 0. A failed summary leaves that file untouched — recoverable by re-running update-changelog.sh against the tag later — rather than committing a wrong or placeholder entry that would silently become the historical record.

Inputs

Input Required Default Purpose
mode no update update writes the entry for tag; backfill repairs history.
backfill-limit no 1 In backfill mode, stubs converted per run (0 = all).
tag no* — Release tag to write an entry for, e.g. v1.2.3. *Required in update mode.
token yes — Forgejo token for clone + push (secrets.FORGEJO_TOKEN).
targets no (from changelog-path/language) path:language pairs, one per line.
changelog-path no CHANGELOG.md File for the single-target case.
language no US English Language for the single-target case (free text).
product-name no (from .changelog.env) Override the product name in the prompt.
product-description no (from .changelog.env) Override the product description in the prompt.
inference-url no script default OpenAI-compatible endpoint base URL.
inference-model no script default Model id to route to.
config-file no .changelog.env Per-repo config file the scripts source.
branch no main Branch to update.
server-url no workflow server Forgejo base URL.
repository no current repo owner/name.
git-user-name no forgejo-actions Commit author/committer name.
git-user-email no forgejo-actions@localhost Commit author/committer email.

Precedence for identity/inference values is: action input > .changelog.env > built-in default. For a fully custom prompt, set SYSTEM_PROMPT in .changelog.env; that bypasses product-name/product-description.

Backfilling history

A release whose changelog commit never landed leaves no trace in the file — it simply skips a version, which is different from a STUB: entry and invisible until something looks for it. mode: backfill closes that gap in two phases: stub every missing version (cheap, no inference, so it finishes even when the endpoint is down), then convert up to backfill-limit of the stubs into real entries.

Run it on a schedule rather than from a release. Backfill repairs releases that already happened, so it has no deadline, and a schedule retries by itself when the endpoint is unavailable — which a release-triggered run cannot.

on:
  schedule:
    - cron: "17 4 * * *"

jobs:
  backfill:
    runs-on: [dind, trusted]
    steps:
      - uses: https://git.brooktrails.org/brooktrails/changelog-action@v1
        with:
          mode: backfill
          token: ${{ secrets.CI_BOT_TOKEN }}
          targets: |
            CHANGELOG.md:US English
            CHANGELOG.es.md:Spanish

With the default backfill-limit: 1, a backlog of N entries clears over N runs. That keeps each run short and lets you read the first entries before the rest are written — worth having when the endpoint is a service you are still changing.

Local maintenance

The hack/ scripts also run by hand, against a repo that adopts the action, to fix up history. Run them from inside the target repo (so git ranges and .changelog.env resolve there), pointing at this checkout:

# rewrite one release's entry: excise to a stub, then regenerate
/path/to/changelog-action/hack/enstubbify.sh 0.6.0
/path/to/changelog-action/hack/backfill-changelog.sh 0.6.0
git diff CHANGELOG.md            # review; git checkout to discard

# a different language / file:
CHANGELOG=CHANGELOG.es.md LANGUAGE=Spanish \
  /path/to/changelog-action/hack/backfill-changelog.sh 0.6.0
  • hack/update-changelog.sh <tag> — write the entry for one release.
  • hack/backfill-changelog.sh [--limit N] [version…] — rewrite STUB: entries into real summaries (no-op on real prose, so safe to re-run).
  • hack/enstubbify.sh --missing — create STUB: sections for every released version the file has no section for at all.
  • hack/enstubbify.sh [--create] <version…> — replace an entry with a STUB: line to be regenerated; --create adds a missing section in sorted position.
  • hack/changelog-lib.sh — shared plumbing; not run directly.

Identity comes from the target repo's .changelog.env; override anything via environment (PRODUCT_NAME, PRODUCT_DESCRIPTION, LANGUAGE, CHANGELOG, INFERENCE_URL, INFERENCE_MODEL).

Requirements

The job needs git, curl, and jq on PATH, and network access to the inference endpoint. Install them in a prior step if your job image lacks them, e.g. apt-get install -y --no-install-recommends git curl jq ca-certificates.