Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions .cursor/rules/project-core.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ request writes them. Check Gate CI rejects other edits unless the description
contains a line `changelog: correction: <reason>`. See
`skills/CONTRIBUTING.md` and `changelog.d/README.md`.

## Change Logs

Each user-visible change of a released component adds one change-log fragment
per component instead of editing its change log. Run
`python3 script/release/changelog_fragments.py new <target> --section <section>`
and write the bullets in the new `changelog.d/<target>/` file. The target is
`go`, `java`, `python`, `rust`, `typescript`, `web`, `cli`, `server`, `agents`
(the docs change log), or `agents-packages` (`agents/CHANGELOG.md`). The
section is Breaking, Added, Changed, Deprecated, or Fixed; `agents-packages`
may also name a topic. Never edit the newest entry of a change log, add a
version heading, or change a Released line by hand. Check Gate CI rejects such
an edit unless the description contains a line
`changelog: correction: <reason>`.

Before each release, the release cutter runs
`python3 script/release/changelog_fragments.py prepare <components> --version <version> --date <YYYY-MM-DD>`
on a fresh branch from `origin/main`, opens the pull request with the
`release-prep` label, and merges it before creating the tag. The entry is then
on main when the release snapshots the reference. **Release changed
components** fails its plan when a selected component's change log does not
record the release yet. See `changelog.d/README.md`.

## Release Versions

Choose the version of each SDK, cli, and web release by what it ships:
Expand All @@ -81,8 +103,9 @@ Choose the version of each SDK, cli, and web release by what it ships:
- When the choice is unclear, ask the owner before releasing.

A patch release folds into its minor's change log entry. List it on the
Released line and mark its items "Since 1.NN.P". Developer reference pages
mark its changes "Changed in 1.NN.P".
Released line and mark its items "Since 1.NN.P"; the release-prep pull
request does both. Developer reference pages mark its changes
"Changed in 1.NN.P".

## Docs Information Placement

Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@

<!-- What ran locally, and what CI covers. -->

<!-- A skills/ change adds a fragment: python3 script/release/changelog_fragments.py new skills --bump <level> -->
<!-- A skills/ change adds a fragment: python3 script/release/changelog_fragments.py new skills --bump <level>. A user-visible component change adds one per component: new <target> --section <section>. -->

<!-- Fixes #123 -->
2 changes: 2 additions & 0 deletions .github/workflows/check-gate-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ jobs:
release_tooling:
- 'script/release/**'
- 'skills/script/check-package.py'
- 'docs/reference/*/changelog.mdx'
- 'agents/CHANGELOG.md'
- '.github/workflows/release-changed-components.yml'
- '.github/workflows/release-tooling-ci.yml'
- 'CONTRIBUTING.md'
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/release-changed-components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,15 @@ jobs:
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git commit -m "docs: snapshot component references ${RELEASE_VERSION}"
git push origin "${branch}"
compare_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/main...${branch}?expand=1"
# Check Gate CI accepts the change-log link rewrites of a snapshot only with this description line.
compare_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/main...${branch}?expand=1&body=changelog%3A%20correction%3A%20reference%20snapshot"
{
echo "## Reference snapshots for ${RELEASE_VERSION}"
echo
echo "Pushed \`${branch}\`. Open its pull request: ${compare_url}"
echo
echo "Keep the description line \`changelog: correction: reference snapshot\`."
echo
echo "Merge reference snapshot branches in release order."
echo
echo '```text'
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release-tooling-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
paths:
- "script/release/**"
- "skills/script/check-package.py"
- "docs/reference/*/changelog.mdx"
- "agents/CHANGELOG.md"
- ".github/workflows/release-changed-components.yml"
- ".github/workflows/cli-release.yml"
- ".github/workflows/docker-image-web-release.yml"
Expand All @@ -15,6 +17,8 @@ on:
paths:
- "script/release/**"
- "skills/script/check-package.py"
- "docs/reference/*/changelog.mdx"
- "agents/CHANGELOG.md"
- ".github/workflows/release-changed-components.yml"
- ".github/workflows/cli-release.yml"
- ".github/workflows/docker-image-web-release.yml"
Expand Down
23 changes: 22 additions & 1 deletion .github/workflows/server-zd-binary-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ jobs:
fi
echo "version=${version}" >> "${GITHUB_OUTPUT}"
echo "publish=${publish}" >> "${GITHUB_OUTPUT}"
# Every later job needs metadata, so this stops the assets, the release upload, and the snapshot.
- name: Require the change-log entry on the tagged commit
if: steps.version.outputs.publish == 'true'
shell: bash
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
if ! python3 script/release/changelog_fragments.py check-ready server "${VERSION}"; then
message="The change-log entry for server-zd/v${VERSION} must be merged to main before the tag. Delete the release and its tag, merge the release-prep pull request, then tag again."
echo "::error::${message}"
{
echo "## Change log not ready for server-zd ${VERSION}"
echo
echo "${message}"
} >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi

build:
name: Build and smoke ${{ matrix.os }} ${{ matrix.arch }}
Expand Down Expand Up @@ -462,12 +480,15 @@ jobs:
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git commit -m "docs: snapshot server reference ${VERSION}"
git push origin "${branch}"
compare_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/main...${branch}?expand=1"
# Check Gate CI accepts the change-log link rewrites of a snapshot only with this description line.
compare_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/main...${branch}?expand=1&body=changelog%3A%20correction%3A%20reference%20snapshot"
{
echo "## Server reference snapshot for ${VERSION}"
echo
echo "Pushed \`${branch}\`. Open its pull request: ${compare_url}"
echo
echo "Keep the description line \`changelog: correction: reference snapshot\`."
echo
echo "Merge reference snapshot branches in release order."
echo
echo '```text'
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/server-zd-image-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ jobs:
fi
echo "version=${version}" >> "${GITHUB_OUTPUT}"
echo "publish=${publish}" >> "${GITHUB_OUTPUT}"
# Every later job needs metadata, so this stops the image build and push.
- name: Require the change-log entry on the tagged commit
if: steps.version.outputs.publish == 'true'
shell: bash
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
if ! python3 script/release/changelog_fragments.py check-ready server "${VERSION}"; then
message="The change-log entry for server-zd/v${VERSION} must be merged to main before the tag. Delete the release and its tag, merge the release-prep pull request, then tag again."
echo "::error::${message}"
{
echo "## Change log not ready for server-zd ${VERSION}"
echo
echo "${message}"
} >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi

smoke:
name: Smoke ${{ matrix.arch }}
Expand Down
27 changes: 25 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,28 @@ request writes them. Check Gate CI rejects other edits unless the description
contains a line `changelog: correction: <reason>`. See
`skills/CONTRIBUTING.md` and `changelog.d/README.md`.

### Change Logs

Each user-visible change of a released component adds one change-log fragment
per component instead of editing its change log. Run
`python3 script/release/changelog_fragments.py new <target> --section <section>`
and write the bullets in the new `changelog.d/<target>/` file. The target is
`go`, `java`, `python`, `rust`, `typescript`, `web`, `cli`, `server`, `agents`
(the docs change log), or `agents-packages` (`agents/CHANGELOG.md`). The
section is Breaking, Added, Changed, Deprecated, or Fixed; `agents-packages`
may also name a topic. Never edit the newest entry of a change log, add a
version heading, or change a Released line by hand. Check Gate CI rejects such
an edit unless the description contains a line
`changelog: correction: <reason>`.

Before each release, the release cutter runs
`python3 script/release/changelog_fragments.py prepare <components> --version <version> --date <YYYY-MM-DD>`
on a fresh branch from `origin/main`, opens the pull request with the
`release-prep` label, and merges it before creating the tag. The entry is then
on main when the release snapshots the reference. **Release changed
components** fails its plan when a selected component's change log does not
record the release yet. See `changelog.d/README.md`.

### Release Versions

Choose the version of each SDK, cli, and web release by what it ships:
Expand All @@ -196,8 +218,9 @@ Choose the version of each SDK, cli, and web release by what it ships:
- When the choice is unclear, ask the owner before releasing.

A patch release folds into its minor's change log entry. List it on the
Released line and mark its items "Since 1.NN.P". Developer reference pages
mark its changes "Changed in 1.NN.P".
Released line and mark its items "Since 1.NN.P"; the release-prep pull
request does both. Developer reference pages mark its changes
"Changed in 1.NN.P".

### Docs Information Placement

Expand Down
27 changes: 25 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,28 @@ request writes them. Check Gate CI rejects other edits unless the description
contains a line `changelog: correction: <reason>`. See
`skills/CONTRIBUTING.md` and `changelog.d/README.md`.

## Change Logs

Each user-visible change of a released component adds one change-log fragment
per component instead of editing its change log. Run
`python3 script/release/changelog_fragments.py new <target> --section <section>`
and write the bullets in the new `changelog.d/<target>/` file. The target is
`go`, `java`, `python`, `rust`, `typescript`, `web`, `cli`, `server`, `agents`
(the docs change log), or `agents-packages` (`agents/CHANGELOG.md`). The
section is Breaking, Added, Changed, Deprecated, or Fixed; `agents-packages`
may also name a topic. Never edit the newest entry of a change log, add a
version heading, or change a Released line by hand. Check Gate CI rejects such
an edit unless the description contains a line
`changelog: correction: <reason>`.

Before each release, the release cutter runs
`python3 script/release/changelog_fragments.py prepare <components> --version <version> --date <YYYY-MM-DD>`
on a fresh branch from `origin/main`, opens the pull request with the
`release-prep` label, and merges it before creating the tag. The entry is then
on main when the release snapshots the reference. **Release changed
components** fails its plan when a selected component's change log does not
record the release yet. See `changelog.d/README.md`.

## Release Versions

Choose the version of each SDK, cli, and web release by what it ships:
Expand All @@ -119,8 +141,9 @@ Choose the version of each SDK, cli, and web release by what it ships:
- When the choice is unclear, ask the owner before releasing.

A patch release folds into its minor's change log entry. List it on the
Released line and mark its items "Since 1.NN.P". Developer reference pages
mark its changes "Changed in 1.NN.P".
Released line and mark its items "Since 1.NN.P"; the release-prep pull
request does both. Developer reference pages mark its changes
"Changed in 1.NN.P".

## Docs Information Placement

Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ Check Gate CI is the only required check. It waits for every workflow whose path

Each component has its own version and tag prefix. Create a GitHub Release for that tag only — workflows filter on the prefix so one release does not publish another component.

Write the change log before the tag. Pull requests add fragments under [`changelog.d/`](changelog.d/README.md) instead of editing a change log. Before a release, run `python3 script/release/changelog_fragments.py prepare <components> --version <version> --date <YYYY-MM-DD>` on a fresh branch from `origin/main`, for example `prepare go,java,python,rust,typescript,web,cli --version 1.45.0`, `prepare server --version 0.1.26`, or `prepare agents --version 0.5.0`. It writes each change log's release entry and deletes the fragments. Pass `--allow-empty` for a component with no user-visible changes. Open the pull request with the `release-prep` label and merge it; Check Gate CI requires it to equal `prepare` on its base. The release snapshot then copies the entry from main. **Release changed components** runs `check-ready` for every selected component and fails its plan when a change log does not record the release. Before creating a `server-zd/v` Release, run `python3 script/release/changelog_fragments.py check-ready server <version>`. The server binary and image releases run the same check on the tagged commit and fail before building, uploading, pushing an image, or snapshotting anything. Then delete the release and its tag, merge the release-prep pull request, and tag again.

For coordinated releases, run **Release changed components** from the **main** branch and enter one semantic version. The workflow applies that version to every selected component. It compares each component with its own latest reachable release tag, uses that tag as the start of the component's generated release notes, preflights every selected target tag, creates the GitHub Releases, and directly invokes each publisher. Every generated release starts with a **Breaking Changes** section containing `None.`. The CLI release workflow adds that section to manually created CLI Releases when it is absent. Release owners may replace `None.` with the actual breaking changes. The run succeeds only after every selected registry, Docker, CLI asset, and Homebrew publication succeeds. Go SDK publication is complete when its module tag and GitHub Release exist.

The agents packages under `agents/` keep their own version line, independent of the shared release version. Enter it in the optional **agents_version** input to release them as `agents/v<agents_version>` when `agents/**` changed. Leave it empty to skip them; the plan then reports that they changed. The workflow dispatches the two agents publishers instead of calling them, because PyPI and npm trusted publishing match the top-level workflow file. An agents Release differs from the generated ones: it is titled `Dex agents X.Y.Z`, its notes are the `## X.Y.Z` section of [`agents/CHANGELOG.md`](agents/CHANGELOG.md) built by [`script/release/agents_release_notes.py`](script/release/agents_release_notes.py) with a compare link to the previous `agents/v` tag, and it never takes the repository's Latest badge. The plan fails when that section is missing.
Expand Down
57 changes: 56 additions & 1 deletion changelog.d/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ name, so concurrent pull requests do not conflict.
| Directory | Release files |
| --- | --- |
| `skills/` | `skills/CHANGELOG.md`, `skills/VERSION`, `skills/dex-sdk/VERSION`, and the plugin and marketplace manifests |
| `go/`, `java/`, `python/`, `rust/`, `typescript/`, `web/`, `cli/`, `server/`, `agents/` | `docs/reference/<target>/changelog.mdx` |
| `agents-packages/` | `agents/CHANGELOG.md` |

A release component names the targets it releases. Each is its target, except
`agents`, which releases both `agents/` and `agents-packages/`.

## Add a fragment

Expand All @@ -33,6 +38,25 @@ bump: patch
headings; the release adds them.
- A name is `<slug>-<6 lowercase hex digits>.md`.

A component change takes a section instead of a bump:

```bash
python3 script/release/changelog_fragments.py new go --section Fixed
```

```markdown
---
section: Fixed
---
- The **Join** documentation no longer ... [API reference](pathname:///api/go/latest/dex/#type-join).
```

- `section` is `Breaking`, `Added`, `Changed`, `Deprecated`, or `Fixed`.
`agents-packages` may also name a topic, such as `Dex MCP conventions`.
- Fragments for the docs change logs follow the docs prose rules: **bold**
for identifiers, no inline backticks, and no braces or `<`.
- A change that ships in several components adds one fragment per target.

Check a fragment with `python3 script/release/changelog_fragments.py lint`.

## Release
Expand All @@ -46,11 +70,38 @@ python3 script/release/changelog_fragments.py prepare skills --date <YYYY-MM-DD>

The **Prepare skills release** workflow does the same and pushes a new
`release-prep/skills-v<version>-<run ID>-<attempt>` branch on every run.

Open the pull request with the `release-prep` label. Check Gate CI reads the
version and date from the new entry, runs the same assembly on the base, and
requires identical files. When a fragment lands on main first, the check fails
and prints the command to prepare again.

Components take the release version and may share one pull request:

```bash
python3 script/release/changelog_fragments.py prepare go,java,python,rust,typescript,web,cli --version 1.45.0 --date <YYYY-MM-DD>
```

- A new minor, or each server-zd `0.1.N`, gets a new entry with its Released
line. Each agents package release gets its own `agents/CHANGELOG.md` entry.
- A patch folds into its minor's entry: the Released line lists it, and its
items end with "Since 1.NN.P".
- Preparing a version again, after a fragment landed late, folds the new
fragments into the existing entry.
- `--allow-empty` releases a component with no fragments as
"No user-visible changes."
- A newest entry from before fragments, marked "Not released yet" or
"Unreleased", becomes the release entry, with its items kept.
- Older entries never change.

Merge the release-prep pull request before the tag.
`python3 script/release/changelog_fragments.py check-ready <component> <version>`
fails, and prints the prepare command, while the change log does not record
the release. **Release changed components** runs it for every selected
component before it creates any tag. The server binary and image releases
run it on the tagged commit and fail before they build, upload, or push
anything.

## Check Gate rules

- Outside a release-prep pull request, any change to a release file fails
Expand All @@ -63,4 +114,8 @@ and prints the command to prepare again.
- Pull requests opened before fragments existed, up to
`LEGACY_CHANGELOG_PR_MAX` in `script/release/changelog_fragments.py`, may
still bump skills the old way. Such a bump must raise `skills/VERSION` above
the base and add the matching new `skills/CHANGELOG.md` entry.
the base and add the matching new `skills/CHANGELOG.md` entry. In a component change log they may edit only the
newest entry that is still unreleased, or open one above the released
entries, and only while that target has no fragments.
- Reference snapshot pull requests carry the line
`changelog: correction: reference snapshot`.
Empty file.
Empty file added changelog.d/agents/.gitkeep
Empty file.
Empty file added changelog.d/cli/.gitkeep
Empty file.
Empty file added changelog.d/go/.gitkeep
Empty file.
Empty file added changelog.d/java/.gitkeep
Empty file.
Empty file added changelog.d/python/.gitkeep
Empty file.
Empty file added changelog.d/rust/.gitkeep
Empty file.
Empty file added changelog.d/server/.gitkeep
Empty file.
Empty file added changelog.d/typescript/.gitkeep
Empty file.
Empty file added changelog.d/web/.gitkeep
Empty file.
Loading
Loading