This document is the maintainer guide for GitHub Actions, changelog
synchronization, notifications, and deployment automation in tenzir/news.
| Path | Purpose |
|---|---|
actions/sync/action.yaml |
Triggers this repository's synchronization workflow from a source repository. |
workflows/sync.yaml |
Serializes changelog synchronization and sends Discord notifications. |
workflows/changelog-x-relay.yaml |
Relays newly added stable releases to the workflows Worker for X drafting. |
workflows/changelog-check.yaml |
Tests the shared changelog helpers and campaign identities on pull requests. |
workflows/rebuild-content.yaml |
Requests a tenzir/content rebuild after a push to main. |
scripts/ |
Contains deterministic parsing and notification helpers. |
Source repositories trigger workflows/sync.yaml. The workflow clones the
source repository and synchronizes its changelog into a top-level project
directory.
The workflow runs in tenzir/news and uses concurrency: group: "sync" to
serialize updates. This avoids concurrent source repositories racing while
they pull and push the same target repository.
Each run performs these steps:
- Validate that the source exists in the
tenzirorganization. - Clone the source repository.
- Synchronize the primary changelog and configured module changelogs.
- Validate campaign identities used by the X publisher.
- Commit and push the result to
main. - Notify configured destinations about new entries and releases.
- Dispatch a rebuild request to
tenzir/content.
Top-level changelog config IDs and unreleased entry filename stems must use lowercase kebab-case. Synchronization fails before committing an invalid identity, so publication cannot be the first place an invalid campaign is detected.
The workflow uses these organization or repository settings:
| Setting | Purpose |
|---|---|
vars.TENZIR_GITHUB_APP_ID |
Identifies the Tenzir GitHub App. |
secrets.TENZIR_GITHUB_APP_PRIVATE_KEY |
Creates installation tokens. |
Add a workflow like this to the source repository:
name: Sync to News
on:
push:
branches: [main]
paths:
- "changelog/**"
workflow_dispatch:
jobs:
sync:
name: Trigger news sync
runs-on: ubuntu-latest
steps:
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.TENZIR_GITHUB_APP_ID }}
private-key: ${{ secrets.TENZIR_GITHUB_APP_PRIVATE_KEY }}
repositories: news
- name: Trigger sync workflow
uses: tenzir/news/.github/actions/sync@main
with:
token: ${{ steps.app-token.outputs.token }}The action derives the following defaults from the source repository:
project: The repository name, such asmcpfortenzir/mcp.target: The project name and target directory intenzir/news.path:changelog.
Override the target when the synchronized directory and source repository have different names:
- uses: tenzir/news/.github/actions/sync@main
with:
token: ${{ steps.app-token.outputs.token }}
target: platformFor an initial import, set skip_notifications: true. Remove it after the
first synchronization.
For a repository with module changelogs, add each module path to the source
workflow trigger and pass the paths through extra_paths:
on:
push:
branches: [main]
paths:
- "changelog/**"
- "plugins/*/changelog/**"
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.TENZIR_GITHUB_APP_ID }}
private-key: ${{ secrets.TENZIR_GITHUB_APP_PRIVATE_KEY }}
repositories: news
- uses: tenzir/news/.github/actions/sync@main
with:
token: ${{ steps.app-token.outputs.token }}
extra_paths: "plugins/*/changelog"The extra_paths input accepts space-separated glob patterns. Each matching
directory remains under the target project. For example,
plugins/foo/changelog synchronizes to
PROJECT/plugins/foo/changelog in this repository.
You can also dispatch the workflow without the composite action:
gh workflow run sync.yaml \
--repo tenzir/news \
--field project=PROJECT \
--field path=changelogThe synchronization workflow can notify Discord and trigger the X drafting
workflow. Setting skip_notifications: true adds [skip notifications] to the
sync commit, which suppresses both channels.
Configure these optional repository secrets:
| Secret | Purpose |
|---|---|
DISCORD_CHANGELOG_WEBHOOK |
Notifies about new unreleased entries. |
DISCORD_RELEASE_WEBHOOK |
Notifies about new top-level releases. |
When a secret is absent, the workflow skips the corresponding notification. Notification failures don't fail the synchronization run.
Every push to main runs workflows/changelog-x-relay.yaml. The workflow
collects every added manifest matching
PROJECT/changelog/releases/VERSION/manifest.yaml. Automatic posts are
limited to the tenzir (Tenzir Node) and platform projects. The relay skips
prereleases and refuses pushes containing more than one eligible release.
This prevents repository onboarding and historical backfills from publishing
stale release bursts; dispatch the intended URL manually after inspecting such
a push.
For every eligible release, the relay sends the permanent
https://tenzir.com/changelog/ID/VERSION/ URL to the workflows Cloudflare
Worker in
tenzir/infra (website/workflows/). The
Worker fetches the page's public Markdown representation, drafts one
Premium-length post when the release contains at least one feature, validates
the copy and URL placement, and publishes to @tenzir_company. A Durable
Object ledger keyed by the permanent URL prevents duplicate posts. See the
Worker's README for the complete publication and recovery design.
This repository needs one Actions secret:
| Secret | Purpose |
|---|---|
WORKFLOWS_NEWS_TOKEN |
Authenticates the relay against the Worker. Mirrors the workflows-news-token value in the Cloudflare Secrets Store. |
Manual dispatches take one canonical stable release URL. They default to a dry
run, which drafts and validates the post without accessing the ledger or X. To
recover an ambiguous write, inspect @tenzir_company and delete the uncertain
post when present. Then manually dispatch the same URL with
Draft and validate without publishing cleared and Retry after deleting
the uncertain X post enabled.
Every push to main runs workflows/rebuild-content.yaml. It sends a
news-updated repository dispatch to tenzir/content, which rebuilds
tenzir.com/changelog.
The workflow uses the same Tenzir GitHub App settings as synchronization and
requests a token scoped to the content repository.
Run these checks before committing changes to the notification scripts:
uv run --with-requirements .github/scripts/requirements.txt \
python .github/scripts/test_changelog.py
uv run --with-requirements .github/scripts/requirements.txt \
python .github/scripts/changelog.py
uvx ruff check .github/scripts
uvx ruff format --check .github/scripts
git diff --check