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 changelog entries to the workflows Worker for X drafting. |
workflows/changelog-check.yaml |
Tests the shared changelog helpers 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.
- Commit and push the result to
main. - Notify configured destinations about new entries and releases.
- Dispatch a rebuild request to
tenzir/content.
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, which collects
newly added feature entries matching PROJECT/changelog/unreleased/SLUG.md
and relays the commit SHA and entry paths to the workflows Cloudflare Worker
in tenzir/infra (website/workflows/).
Everything else — fetching and parsing the entries, drafting posts with
GPT-5.6 Sol through an AI Gateway, deterministic validation (weighted
280-character limit, canonical changelog URL exactly once in the final post,
no mentions or em dashes, thread shape matching the entry content), and
OAuth 1.0a publication to @tenzir_company — happens in that Worker as a
durable Cloudflare Workflow. See the Worker's README for the full design,
including the Durable Object ledger that guarantees a crash can never publish
a duplicate post.
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. |
To recover an ambiguous write (the Worker refused to publish because a prior
write's outcome is unknown), delete the entire partial thread from
@tenzir_company, including any post whose creation was ambiguous. Then
manually dispatch the relay for the same entry with Retry after deleting the
entire partial X thread 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
uvx ruff check .github/scripts
uvx ruff format --check .github/scripts
git diff --check