From cc2000c460dd8ee3e73f1bd5579d08728bea9333 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 08:58:49 +0000 Subject: [PATCH 1/7] ci(release): Release clients and server as Craft workspaces Co-Authored-By: Lorenzo Cian <17258265+lcian@users.noreply.github.com> --- .craft.yml | 67 ++++++++++ .../python/.github => .github}/release.yml | 8 +- .../workflows/changelog-preview-python.yml | 21 --- .github/workflows/changelog-preview-rust.yml | 21 --- .github/workflows/changelog-preview.yml | 124 ++++++++++++++++++ .../{release-clients.yml => release.yml} | 27 ++-- AGENTS.md | 7 +- clients/python/.craft.yml | 17 --- clients/rust/.craft.yml | 14 -- clients/rust/.github/release.yml | 29 ---- scripts/bump-version-server.sh | 17 +++ 11 files changed, 233 insertions(+), 119 deletions(-) create mode 100644 .craft.yml rename {clients/python/.github => .github}/release.yml (72%) delete mode 100644 .github/workflows/changelog-preview-python.yml delete mode 100644 .github/workflows/changelog-preview-rust.yml create mode 100644 .github/workflows/changelog-preview.yml rename .github/workflows/{release-clients.yml => release.yml} (62%) delete mode 100644 clients/python/.craft.yml delete mode 100644 clients/rust/.craft.yml delete mode 100644 clients/rust/.github/release.yml create mode 100755 scripts/bump-version-server.sh diff --git a/.craft.yml b/.craft.yml new file mode 100644 index 00000000..d436ad1b --- /dev/null +++ b/.craft.yml @@ -0,0 +1,67 @@ +minVersion: 3.3.0 + +# Each workspace is released on its own, with its own version, tags, release +# branch and changelog. Pick one with `--workspace ` or `CRAFT_WORKSPACE`. +# +# A workspace's changelog lists every PR that changes its directory or one of +# its `includePaths`. Files outside all workspaces (root config, CI, docs, ...) +# are excluded everywhere. Entries are grouped by the categories in +# `.github/release.yml`, which all workspaces share. +workspaces: + clients/python: + releaseBranchPrefix: release-py + preReleaseCommand: scripts/bump-version-py-client.sh + changelog: + policy: auto + scopeGrouping: false + unownedPaths: exclude + targets: + - name: github + tagPrefix: py-client@ + - name: pypi + - name: sentry-pypi + internalPypiRepo: getsentry/pypi + requireNames: + - /.*objectstore_client.*whl$/ + - /.*objectstore_client.*tar\.gz$/ + + clients/rust: + releaseBranchPrefix: release-rust + preReleaseCommand: scripts/bump-version-rs-client.sh + changelog: + policy: auto + scopeGrouping: false + unownedPaths: exclude + # The crates target publishes from source, so there are no build artifacts. + artifactProvider: + name: none + targets: + - name: github + tagPrefix: rs-client@ + - name: crates + + objectstore-server: + releaseBranchPrefix: release + preReleaseCommand: scripts/bump-version-server.sh + versioning: + policy: calver + changelog: + policy: auto + scopeGrouping: false + unownedPaths: exclude + # The crates the server binary is built from, plus its runtime options + # schema. Keep in sync with the server's path dependencies. + includePaths: + - objectstore-inventory-tracker + - objectstore-log + - objectstore-metrics + - objectstore-options + - objectstore-service + - objectstore-typed-options + - objectstore-typed-options-derive + - objectstore-types + - sentry-options + artifactProvider: + name: none + targets: + - name: github diff --git a/clients/python/.github/release.yml b/.github/release.yml similarity index 72% rename from clients/python/.github/release.yml rename to .github/release.yml index 23b3d0bd..1b35c37b 100644 --- a/clients/python/.github/release.yml +++ b/.github/release.yml @@ -1,5 +1,4 @@ -# Changelog categories for this client. Keep in sync with the other client's -# `.github/release.yml`. +# Changelog categories for every Craft workspace in `.craft.yml`. # # A custom file replaces Craft's defaults entirely. It groups entries by change # type, strips the `type(scope):` prefix, and catches every remaining change so @@ -8,6 +7,11 @@ changelog: exclude: labels: - skip-changelog + # Release commits. Releasing one workspace can touch files of another + # (e.g. the Rust client bumps `objectstore-types`, which the server + # includes), so they would otherwise show up in that changelog. + authors: + - getsentry-bot categories: - title: Breaking Changes commit_patterns: diff --git a/.github/workflows/changelog-preview-python.yml b/.github/workflows/changelog-preview-python.yml deleted file mode 100644 index 5798a85e..00000000 --- a/.github/workflows/changelog-preview-python.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Changelog Preview (Python client) - -on: - pull_request_target: - types: [opened, synchronize, reopened, edited, labeled, unlabeled] - paths: - - clients/python/** - -permissions: - contents: read - pull-requests: write - statuses: write - -jobs: - changelog-preview: - uses: getsentry/craft/.github/workflows/changelog-preview.yml@924b450ede4af30e30de4821a4b357d676c8df92 # v3 - with: - working-directory: clients/python - # Report as a check with a job summary. In comment mode, both client - # previews would overwrite the same PR comment. - comment: false diff --git a/.github/workflows/changelog-preview-rust.yml b/.github/workflows/changelog-preview-rust.yml deleted file mode 100644 index c71b94bc..00000000 --- a/.github/workflows/changelog-preview-rust.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Changelog Preview (Rust client) - -on: - pull_request_target: - types: [opened, synchronize, reopened, edited, labeled, unlabeled] - paths: - - clients/rust/** - -permissions: - contents: read - pull-requests: write - statuses: write - -jobs: - changelog-preview: - uses: getsentry/craft/.github/workflows/changelog-preview.yml@924b450ede4af30e30de4821a4b357d676c8df92 # v3 - with: - working-directory: clients/rust - # Report as a check with a job summary. In comment mode, both client - # previews would overwrite the same PR comment. - comment: false diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml new file mode 100644 index 00000000..34630060 --- /dev/null +++ b/.github/workflows/changelog-preview.yml @@ -0,0 +1,124 @@ +name: Changelog Preview + +# Comments on the PR with the changelogs it will be listed in, one per Craft +# workspace in `.craft.yml`, and the entry it adds to them. +# +# SECURITY: `pull_request_target` runs with a write token. The PR checkout is +# only used to read git history and Craft configuration; never run code from it. + +on: + pull_request_target: + types: [opened, synchronize, reopened, edited, labeled, unlabeled] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: changelog-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + preview: + runs-on: ubuntu-latest + steps: + # Prefer the merge ref; fall back to the head if the PR has conflicts. + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + id: checkout-merge + continue-on-error: true + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + fetch-depth: 0 + persist-credentials: false + + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + if: steps.checkout-merge.outcome == 'failure' + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Install Craft + env: + GH_TOKEN: ${{ github.token }} + CRAFT_VERSION: 3.3.0 + run: | + gh release download "$CRAFT_VERSION" --repo getsentry/craft --pattern craft --dir "$RUNNER_TEMP" + sudo install "$RUNNER_TEMP/craft" /usr/local/bin/craft + + - name: Comment changelog preview + env: + GH_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ github.token }} + CRAFT_LOG_LEVEL: Warn + PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + GIT_AUTHOR_NAME: changelog-preview + GIT_AUTHOR_EMAIL: changelog-preview@localhost + GIT_COMMITTER_NAME: changelog-preview + GIT_COMMITTER_EMAIL: changelog-preview@localhost + run: | + set -euo pipefail + + # Craft decides which workspaces a commit belongs to from the files it + # changes. Squash the PR into one commit on top of its base, like the + # merge will, and ask each workspace whether that commit is in it. + git fetch --quiet --no-tags origin "$BASE_REF" + base=$(git merge-base HEAD "origin/$BASE_REF") + git checkout --quiet --detach "$(git commit-tree "HEAD^{tree}" -p "$base" -m "#$PR_NUMBER")" + + touched=() + impacts=() + entry="" + skipped=false + for workspace in $(craft workspace list | jq -r '.[]'); do + commits=$(CRAFT_WORKSPACE="$workspace" craft changelog --since "$base" --format json | jq '.totalCommits') + if [[ "$commits" == 0 ]]; then + continue + fi + + result=$(CRAFT_WORKSPACE="$workspace" craft changelog --pr "$PR_NUMBER" --format json) + skipped=$(jq -r '.prSkipped' <<< "$result") + if [[ "$(jq -r '.versioningPolicy' <<< "$result")" == calver ]]; then + impact="CalVer" + else + case "$(jq -r '.bumpType' <<< "$result")" in + major) impact="🔴 Major" ;; + minor) impact="🟡 Minor" ;; + patch) impact="🟢 Patch" ;; + *) impact="⚪ None" ;; + esac + fi + touched+=("\`$workspace\`") + impacts+=("- \`$workspace\`: $impact") + + # The entry is the same in every changelog. Craft quotes this PR's + # lines in the full preview; keep them and their section heading. + if [[ -z "$entry" ]]; then + entry=$(jq -r '.changelog' <<< "$result" | awk ' + /^#+ / { heading = $0; printed = 0; next } + /^>/ { + if (!printed && heading != "") { print heading "\n"; printed = 1 } + sub(/^> ?/, ""); print + }') + fi + done + + marker="" + if [[ ${#touched[@]} -eq 0 ]]; then + summary="This PR doesn't change any released workspace, so it won't be listed in any changelog." + elif [[ "$skipped" == true ]]; then + summary="This PR is excluded from the changelogs of the workspaces it changes (\`skip-changelog\`): ${touched[*]}" + else + summary="This PR will be listed in the changelogs of:"$'\n\n'"$(printf '%s\n' "${impacts[@]}")"$'\n\n'"$entry" + fi + body="$marker"$'\n'"## Changelog Preview"$'\n\n'"$summary" + + comment_id=$(gh api --paginate "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ + --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1) + if [[ -n "$comment_id" ]]; then + gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" -f body="$body" > /dev/null + elif [[ ${#touched[@]} -gt 0 ]]; then + gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" -f body="$body" > /dev/null + fi + echo "$body" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release-clients.yml b/.github/workflows/release.yml similarity index 62% rename from .github/workflows/release-clients.yml rename to .github/workflows/release.yml index b985e0cf..0f2406fe 100644 --- a/.github/workflows/release-clients.yml +++ b/.github/workflows/release.yml @@ -1,18 +1,19 @@ -name: Release client library +name: Release on: workflow_dispatch: inputs: - client: - description: Client to release + workspace: + description: Craft workspace to release required: true type: choice options: - - python - - rust + - clients/python + - clients/rust + - objectstore-server version: - description: Version to release - required: true + description: Exact version, bump type (major, minor, patch), or auto. Leave blank for auto (clients) or CalVer (server) + required: false force: description: Force a release even when there are release-blockers (optional) required: false @@ -41,11 +42,13 @@ jobs: - uses: astral-sh/setup-uv@5a7eac68fb9809dea845d802897dc5c723910fa3 # v7.1.3 - name: Prepare release - uses: getsentry/action-prepare-release@3cea80dc3938c0baf5ec4ce752ecb311f8780cdc # v1.6.4 + uses: getsentry/craft@4b763f5419255bb520e3e5a49f5b91426dcd374d # 3.3.0 env: GITHUB_TOKEN: ${{ steps.token.outputs.token }} with: - version: ${{ github.event.inputs.version }} - force: ${{ github.event.inputs.force }} - merge_target: ${{ github.event.inputs.merge_target }} - path: clients/${{ github.event.inputs.client }} + workspace: ${{ inputs.workspace }} + version: ${{ inputs.version }} + force: ${{ inputs.force }} + merge_target: ${{ inputs.merge_target }} + git_user_name: getsentry-bot + git_user_email: bot@sentry.io diff --git a/AGENTS.md b/AGENTS.md index 8a3b873e..72ba8297 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,8 @@ cargo doc --workspace --all-features --no-deps --document-private-items ## Pull Requests -The Python and Rust clients are released separately, each with its own changelog (`clients/python/CHANGELOG.md`, `clients/rust/CHANGELOG.md`). When making a change that touches one of the clients, consider the user-facing changelog entry, which is generated from PR titles: +The Python client, the Rust client and the server are released separately as Craft workspaces (see `.craft.yml`), each with its own changelog (`clients/python/CHANGELOG.md`, `clients/rust/CHANGELOG.md`, `objectstore-server/CHANGELOG.md`). Changelog entries are generated from PR titles: -- A PR is listed in the changelog of every client whose directory it changes, grouped by the conventional commit type of its title (`feat`, `fix`, ...). The scope doesn't matter. -- If the title doesn't describe the change well for client users, add a `### Changelog Entry` section to the PR description with the text to use instead. It's used for every client the PR touches. +- A PR is listed in the changelog of every workspace whose files it changes, grouped by the conventional commit type of its title (`feat`, `fix`, ...). The scope doesn't matter. The server's changelog also covers the crates it is built from (`includePaths` in `.craft.yml`); files outside all workspaces aren't listed anywhere. +- A preview comment on the PR shows which changelogs it will be listed in. +- If the title doesn't describe the change well for users, add a `### Changelog Entry` section to the PR description with the text to use instead. It's used for every changelog the PR is listed in. diff --git a/clients/python/.craft.yml b/clients/python/.craft.yml deleted file mode 100644 index 82b19ceb..00000000 --- a/clients/python/.craft.yml +++ /dev/null @@ -1,17 +0,0 @@ -changelog: - policy: auto - # Group entries only by the categories in `.github/release.yml`. - scopeGrouping: false -preReleaseCommand: ../../scripts/bump-version-py-client.sh -releaseBranchPrefix: release-py - -targets: - - name: github - tagPrefix: py-client@ - - name: pypi - - name: sentry-pypi - internalPypiRepo: getsentry/pypi - -requireNames: - - /.*objectstore_client.*whl$/ - - /.*objectstore_client.*tar\.gz$/ diff --git a/clients/rust/.craft.yml b/clients/rust/.craft.yml deleted file mode 100644 index f1f55988..00000000 --- a/clients/rust/.craft.yml +++ /dev/null @@ -1,14 +0,0 @@ -changelog: - policy: auto - # Group entries only by the categories in `.github/release.yml`. - scopeGrouping: false -preReleaseCommand: ../../scripts/bump-version-rs-client.sh -releaseBranchPrefix: release-rust -# The crates target publishes from source, so there are no build artifacts. -artifactProvider: - name: none - -targets: - - name: github - tagPrefix: rs-client@ - - name: crates diff --git a/clients/rust/.github/release.yml b/clients/rust/.github/release.yml deleted file mode 100644 index 23b3d0bd..00000000 --- a/clients/rust/.github/release.yml +++ /dev/null @@ -1,29 +0,0 @@ -# Changelog categories for this client. Keep in sync with the other client's -# `.github/release.yml`. -# -# A custom file replaces Craft's defaults entirely. It groups entries by change -# type, strips the `type(scope):` prefix, and catches every remaining change so -# it still counts toward the version bump. -changelog: - exclude: - labels: - - skip-changelog - categories: - - title: Breaking Changes - commit_patterns: - - "^(?\\w+(?:\\([^)]+\\))?!:\\s*)" - semver: major - - title: New Features - commit_patterns: - - "^(?feat(?:\\([^)]+\\))?:\\s*)" - semver: minor - - title: Bug Fixes - commit_patterns: - - "^(?fix(?:\\([^)]+\\))?:\\s*)" - semver: patch - # Catch-all: any other conventional type, then any other title. - - title: Other Changes - commit_patterns: - - "^(?\\w+(?:\\([^)]+\\))?:\\s*)" - - ".*" - semver: patch diff --git a/scripts/bump-version-server.sh b/scripts/bump-version-server.sh new file mode 100755 index 00000000..7825a811 --- /dev/null +++ b/scripts/bump-version-server.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euxo pipefail + +ROOT_DIR="$(realpath $(dirname "$0")/..)" + +OLD_VERSION="${1}" +NEW_VERSION="${2}" + +echo "Current version: $OLD_VERSION" +echo "Bumping to version: $NEW_VERSION" + +cd $ROOT_DIR +cd objectstore-server +perl -pi -e "s/^version = \".*?\"/version = \"$NEW_VERSION\"/" Cargo.toml + +cd $ROOT_DIR +cargo metadata --format-version 1 >/dev/null # update `Cargo.lock` From 5d3a648ae8c88fe2e23745fb1cf6d4b1dd0a489c Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:02:48 +0000 Subject: [PATCH 2/7] ci(release): Never check out PR code in the changelog preview --- .github/workflows/changelog-preview.yml | 28 +++++++++---------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml index 34630060..31126384 100644 --- a/.github/workflows/changelog-preview.yml +++ b/.github/workflows/changelog-preview.yml @@ -3,8 +3,9 @@ name: Changelog Preview # Comments on the PR with the changelogs it will be listed in, one per Craft # workspace in `.craft.yml`, and the entry it adds to them. # -# SECURITY: `pull_request_target` runs with a write token. The PR checkout is -# only used to read git history and Craft configuration; never run code from it. +# SECURITY: `pull_request_target` runs with a write token. Only the base +# branch is checked out. The PR's commits are fetched as git objects to read +# which files they change, but never checked out, so no PR file is read or run. on: pull_request_target: @@ -22,19 +23,8 @@ jobs: preview: runs-on: ubuntu-latest steps: - # Prefer the merge ref; fall back to the head if the PR has conflicts. - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - id: checkout-merge - continue-on-error: true with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge - fetch-depth: 0 - persist-credentials: false - - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - if: steps.checkout-merge.outcome == 'failure' - with: - ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 persist-credentials: false @@ -61,11 +51,13 @@ jobs: set -euo pipefail # Craft decides which workspaces a commit belongs to from the files it - # changes. Squash the PR into one commit on top of its base, like the - # merge will, and ask each workspace whether that commit is in it. - git fetch --quiet --no-tags origin "$BASE_REF" - base=$(git merge-base HEAD "origin/$BASE_REF") - git checkout --quiet --detach "$(git commit-tree "HEAD^{tree}" -p "$base" -m "#$PR_NUMBER")" + # changes. Squash the PR into one commit on top of its merge base, like + # the merge will, and ask each workspace whether that commit is in it. + # `reset --soft` moves HEAD to that commit but keeps the base branch's + # files, so Craft reads its configuration from the base branch. + git fetch --quiet --no-tags origin "+refs/pull/$PR_NUMBER/head:refs/remotes/pr/head" + base=$(git merge-base pr/head "origin/$BASE_REF") + git reset --quiet --soft "$(git commit-tree "pr/head^{tree}" -p "$base" -m "#$PR_NUMBER")" touched=() impacts=() From 0cc369ce5178102aebba3ac3a411d4cdc71997f3 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:05:51 +0000 Subject: [PATCH 3/7] ci(release): Read PR paths from the API in the changelog preview --- .github/workflows/changelog-preview.yml | 32 ++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml index 31126384..9bf14232 100644 --- a/.github/workflows/changelog-preview.yml +++ b/.github/workflows/changelog-preview.yml @@ -4,8 +4,8 @@ name: Changelog Preview # workspace in `.craft.yml`, and the entry it adds to them. # # SECURITY: `pull_request_target` runs with a write token. Only the base -# branch is checked out. The PR's commits are fetched as git objects to read -# which files they change, but never checked out, so no PR file is read or run. +# branch is checked out; the PR's code is never fetched. Its changed paths come +# from the GitHub API. on: pull_request_target: @@ -42,7 +42,6 @@ jobs: GITHUB_TOKEN: ${{ github.token }} CRAFT_LOG_LEVEL: Warn PR_NUMBER: ${{ github.event.pull_request.number }} - BASE_REF: ${{ github.event.pull_request.base.ref }} GIT_AUTHOR_NAME: changelog-preview GIT_AUTHOR_EMAIL: changelog-preview@localhost GIT_COMMITTER_NAME: changelog-preview @@ -50,14 +49,25 @@ jobs: run: | set -euo pipefail - # Craft decides which workspaces a commit belongs to from the files it - # changes. Squash the PR into one commit on top of its merge base, like - # the merge will, and ask each workspace whether that commit is in it. - # `reset --soft` moves HEAD to that commit but keeps the base branch's - # files, so Craft reads its configuration from the base branch. - git fetch --quiet --no-tags origin "+refs/pull/$PR_NUMBER/head:refs/remotes/pr/head" - base=$(git merge-base pr/head "origin/$BASE_REF") - git reset --quiet --soft "$(git commit-tree "pr/head^{tree}" -p "$base" -m "#$PR_NUMBER")" + # Craft decides which workspaces a commit belongs to from the paths it + # changes. Build one commit on top of the base branch that changes the + # same paths as the PR (with placeholder content), and ask each + # workspace whether that commit is in it. `reset --soft` moves HEAD to + # it but keeps the base branch's files, which Craft reads its + # configuration from. + base=$(git rev-parse HEAD) + placeholder=$(git hash-object -w --stdin <<< "changelog preview") + export GIT_INDEX_FILE="$RUNNER_TEMP/changelog-preview-index" + git read-tree "$base" + gh api --paginate "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files" \ + | jq -j --arg blob "$placeholder" '.[] + | (if .previous_filename then "0 \("0" * 40)\t\(.previous_filename)\u0000" else "" end) + + (if .status == "removed" then "0 \("0" * 40)" else "100644 \($blob)" end) + + "\t\(.filename)\u0000"' \ + | git update-index -z --index-info + tree=$(git write-tree) + unset GIT_INDEX_FILE + git reset --quiet --soft "$(git commit-tree "$tree" -p "$base" -m "#$PR_NUMBER")" touched=() impacts=() From 6002762db533e6c9df155013f64aadfc559e46d6 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:13:11 +0000 Subject: [PATCH 4/7] ci(release): Drop the custom changelog preview and narrow server paths Co-Authored-By: Lorenzo Cian <17258265+lcian@users.noreply.github.com> --- .craft.yml | 13 +-- .github/release.yml | 5 - .github/workflows/changelog-preview.yml | 126 ------------------------ .github/workflows/release.yml | 2 - AGENTS.md | 3 +- 5 files changed, 4 insertions(+), 145 deletions(-) delete mode 100644 .github/workflows/changelog-preview.yml diff --git a/.craft.yml b/.craft.yml index d436ad1b..46f39961 100644 --- a/.craft.yml +++ b/.craft.yml @@ -49,18 +49,11 @@ workspaces: policy: auto scopeGrouping: false unownedPaths: exclude - # The crates the server binary is built from, plus its runtime options - # schema. Keep in sync with the server's path dependencies. + # The server crate and the storage service, which hold nearly all + # server behavior. Changes to the other crates the server is built from + # aren't listed. includePaths: - - objectstore-inventory-tracker - - objectstore-log - - objectstore-metrics - - objectstore-options - objectstore-service - - objectstore-typed-options - - objectstore-typed-options-derive - - objectstore-types - - sentry-options artifactProvider: name: none targets: diff --git a/.github/release.yml b/.github/release.yml index 1b35c37b..b4c0f22c 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -7,11 +7,6 @@ changelog: exclude: labels: - skip-changelog - # Release commits. Releasing one workspace can touch files of another - # (e.g. the Rust client bumps `objectstore-types`, which the server - # includes), so they would otherwise show up in that changelog. - authors: - - getsentry-bot categories: - title: Breaking Changes commit_patterns: diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml deleted file mode 100644 index 9bf14232..00000000 --- a/.github/workflows/changelog-preview.yml +++ /dev/null @@ -1,126 +0,0 @@ -name: Changelog Preview - -# Comments on the PR with the changelogs it will be listed in, one per Craft -# workspace in `.craft.yml`, and the entry it adds to them. -# -# SECURITY: `pull_request_target` runs with a write token. Only the base -# branch is checked out; the PR's code is never fetched. Its changed paths come -# from the GitHub API. - -on: - pull_request_target: - types: [opened, synchronize, reopened, edited, labeled, unlabeled] - -permissions: - contents: read - pull-requests: write - -concurrency: - group: changelog-preview-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - preview: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Install Craft - env: - GH_TOKEN: ${{ github.token }} - CRAFT_VERSION: 3.3.0 - run: | - gh release download "$CRAFT_VERSION" --repo getsentry/craft --pattern craft --dir "$RUNNER_TEMP" - sudo install "$RUNNER_TEMP/craft" /usr/local/bin/craft - - - name: Comment changelog preview - env: - GH_TOKEN: ${{ github.token }} - GITHUB_TOKEN: ${{ github.token }} - CRAFT_LOG_LEVEL: Warn - PR_NUMBER: ${{ github.event.pull_request.number }} - GIT_AUTHOR_NAME: changelog-preview - GIT_AUTHOR_EMAIL: changelog-preview@localhost - GIT_COMMITTER_NAME: changelog-preview - GIT_COMMITTER_EMAIL: changelog-preview@localhost - run: | - set -euo pipefail - - # Craft decides which workspaces a commit belongs to from the paths it - # changes. Build one commit on top of the base branch that changes the - # same paths as the PR (with placeholder content), and ask each - # workspace whether that commit is in it. `reset --soft` moves HEAD to - # it but keeps the base branch's files, which Craft reads its - # configuration from. - base=$(git rev-parse HEAD) - placeholder=$(git hash-object -w --stdin <<< "changelog preview") - export GIT_INDEX_FILE="$RUNNER_TEMP/changelog-preview-index" - git read-tree "$base" - gh api --paginate "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files" \ - | jq -j --arg blob "$placeholder" '.[] - | (if .previous_filename then "0 \("0" * 40)\t\(.previous_filename)\u0000" else "" end) - + (if .status == "removed" then "0 \("0" * 40)" else "100644 \($blob)" end) - + "\t\(.filename)\u0000"' \ - | git update-index -z --index-info - tree=$(git write-tree) - unset GIT_INDEX_FILE - git reset --quiet --soft "$(git commit-tree "$tree" -p "$base" -m "#$PR_NUMBER")" - - touched=() - impacts=() - entry="" - skipped=false - for workspace in $(craft workspace list | jq -r '.[]'); do - commits=$(CRAFT_WORKSPACE="$workspace" craft changelog --since "$base" --format json | jq '.totalCommits') - if [[ "$commits" == 0 ]]; then - continue - fi - - result=$(CRAFT_WORKSPACE="$workspace" craft changelog --pr "$PR_NUMBER" --format json) - skipped=$(jq -r '.prSkipped' <<< "$result") - if [[ "$(jq -r '.versioningPolicy' <<< "$result")" == calver ]]; then - impact="CalVer" - else - case "$(jq -r '.bumpType' <<< "$result")" in - major) impact="🔴 Major" ;; - minor) impact="🟡 Minor" ;; - patch) impact="🟢 Patch" ;; - *) impact="⚪ None" ;; - esac - fi - touched+=("\`$workspace\`") - impacts+=("- \`$workspace\`: $impact") - - # The entry is the same in every changelog. Craft quotes this PR's - # lines in the full preview; keep them and their section heading. - if [[ -z "$entry" ]]; then - entry=$(jq -r '.changelog' <<< "$result" | awk ' - /^#+ / { heading = $0; printed = 0; next } - /^>/ { - if (!printed && heading != "") { print heading "\n"; printed = 1 } - sub(/^> ?/, ""); print - }') - fi - done - - marker="" - if [[ ${#touched[@]} -eq 0 ]]; then - summary="This PR doesn't change any released workspace, so it won't be listed in any changelog." - elif [[ "$skipped" == true ]]; then - summary="This PR is excluded from the changelogs of the workspaces it changes (\`skip-changelog\`): ${touched[*]}" - else - summary="This PR will be listed in the changelogs of:"$'\n\n'"$(printf '%s\n' "${impacts[@]}")"$'\n\n'"$entry" - fi - body="$marker"$'\n'"## Changelog Preview"$'\n\n'"$summary" - - comment_id=$(gh api --paginate "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ - --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1) - if [[ -n "$comment_id" ]]; then - gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" -f body="$body" > /dev/null - elif [[ ${#touched[@]} -gt 0 ]]; then - gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" -f body="$body" > /dev/null - fi - echo "$body" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f2406fe..3382355d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,5 +50,3 @@ jobs: version: ${{ inputs.version }} force: ${{ inputs.force }} merge_target: ${{ inputs.merge_target }} - git_user_name: getsentry-bot - git_user_email: bot@sentry.io diff --git a/AGENTS.md b/AGENTS.md index 72ba8297..6738bc0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,6 +75,5 @@ cargo doc --workspace --all-features --no-deps --document-private-items The Python client, the Rust client and the server are released separately as Craft workspaces (see `.craft.yml`), each with its own changelog (`clients/python/CHANGELOG.md`, `clients/rust/CHANGELOG.md`, `objectstore-server/CHANGELOG.md`). Changelog entries are generated from PR titles: -- A PR is listed in the changelog of every workspace whose files it changes, grouped by the conventional commit type of its title (`feat`, `fix`, ...). The scope doesn't matter. The server's changelog also covers the crates it is built from (`includePaths` in `.craft.yml`); files outside all workspaces aren't listed anywhere. -- A preview comment on the PR shows which changelogs it will be listed in. +- A PR is listed in the changelog of every workspace whose files it changes, grouped by the conventional commit type of its title (`feat`, `fix`, ...). The scope doesn't matter. The server's changelog also covers `objectstore-service` (`includePaths` in `.craft.yml`); files outside all workspaces aren't listed anywhere. - If the title doesn't describe the change well for users, add a `### Changelog Entry` section to the PR description with the text to use instead. It's used for every changelog the PR is listed in. From b7679d9e243b324c9b52f1edb2920e481c695318 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:48:20 +0000 Subject: [PATCH 5/7] ci(release): Tag server releases with a server@ prefix Without a prefix, Craft finds the previous server release with an unfiltered git describe, so a client tag released after the last server release becomes the changelog base and drops the server PRs merged in between. With server@, Craft only considers server tags, for both the changelog base and the CalVer patch scan. Co-Authored-By: Lorenzo Cian <17258265+lcian@users.noreply.github.com> --- .craft.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.craft.yml b/.craft.yml index 46f39961..2948d9c9 100644 --- a/.craft.yml +++ b/.craft.yml @@ -58,3 +58,6 @@ workspaces: name: none targets: - name: github + # Without a prefix, Craft would take the nearest client tag as the + # previous server release and start the changelog there. + tagPrefix: server@ From e7513ce0b25984c912cde1d813d408016f65df03 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:31:27 +0000 Subject: [PATCH 6/7] ci(release): Add an empty server changelog Craft writes the server changelog on prepare but only commits tracked files, so without a checked-in file the first release commit would leave it out. Both clients already have one. --- objectstore-server/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 objectstore-server/CHANGELOG.md diff --git a/objectstore-server/CHANGELOG.md b/objectstore-server/CHANGELOG.md new file mode 100644 index 00000000..825c32f0 --- /dev/null +++ b/objectstore-server/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog From 3524bd1c8e23398dc2bf8ce3e100c5ba0e532400 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:53:01 +0000 Subject: [PATCH 7/7] ci(release): Preview changelogs per release workspace Craft 3.4.0's changelog preview supports release workspaces: one PR comment lists each workspace whose changelog lists the PR, with its bump, using the same path rules as the release changelog. Co-Authored-By: Lorenzo Cian <17258265+lcian@users.noreply.github.com> --- .github/workflows/changelog-preview.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/changelog-preview.yml diff --git a/.github/workflows/changelog-preview.yml b/.github/workflows/changelog-preview.yml new file mode 100644 index 00000000..314173ae --- /dev/null +++ b/.github/workflows/changelog-preview.yml @@ -0,0 +1,17 @@ +name: Changelog Preview + +on: + pull_request_target: + types: [opened, synchronize, reopened, edited, labeled, unlabeled] + +permissions: + contents: read + pull-requests: write + +jobs: + changelog-preview: + # Posts one comment listing every workspace in `.craft.yml` whose + # changelog lists this PR, with its bump. + uses: getsentry/craft/.github/workflows/changelog-preview.yml@4a8dba2a45b09f4e3fcfc86a79c94d0dd93295bb # 3.4.0 + with: + craft-version: 3.4.0