@@ -48,14 +48,15 @@ jobs:
4848 fi
4949
5050 # ---------------------------------------------------------------------------
51- # Release: bump, commit to main, tag. Triggers release.yml via the tag push.
51+ # Release: bump Cargo.toml/Cargo.lock on a release/<tag> branch and open a PR.
52+ # Merging the PR triggers release-tag.yml, which creates the tag and triggers
53+ # release.yml.
5254 # ---------------------------------------------------------------------------
5355 release :
54- name : Bump, Commit, Tag
56+ name : Open Release PR
5557 needs : check
5658 if : needs.check.outputs.proceed == 'true'
5759 runs-on : ubuntu-latest
58- environment : release
5960 steps :
6061 - uses : DataDog/dd-octo-sts-action@96a25462dbcb10ebf0bfd6e2ccc917d2ab235b9a # v1.0.4
6162 id : octo-sts
6667 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
6768 with :
6869 fetch-depth : 0
69- token : ${{ steps.octo-sts.outputs.token }}
7070
7171 - name : Compute next version
7272 id : version
@@ -82,19 +82,28 @@ jobs:
8282 esac
8383 NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
8484 NEW_TAG="v${NEW_VERSION}"
85+ BRANCH="release/${NEW_TAG}"
8586 echo "current-tag=${CURRENT_TAG}" >> "$GITHUB_OUTPUT"
8687 echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
8788 echo "new-tag=${NEW_TAG}" >> "$GITHUB_OUTPUT"
89+ echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
8890 echo "Current: ${CURRENT_TAG}"
8991 echo "Next: ${NEW_TAG} (${BUMP} bump)"
9092
9193 - name : Preflight check
94+ env :
95+ GH_TOKEN : ${{ steps.octo-sts.outputs.token }}
96+ NEW_TAG : ${{ steps.version.outputs.new-tag }}
97+ BRANCH : ${{ steps.version.outputs.branch }}
9298 run : |
93- NEW_TAG="${{ steps.version.outputs.new-tag }}"
9499 if git tag -l "$NEW_TAG" | grep -q .; then
95100 echo "::error::Tag '${NEW_TAG}' already exists."
96101 exit 1
97102 fi
103+ if gh api "repos/${GITHUB_REPOSITORY}/git/refs/heads/${BRANCH}" >/dev/null 2>&1; then
104+ echo "::error::Branch '${BRANCH}' already exists."
105+ exit 1
106+ fi
98107
99108 - name : Install Rust
100109 run : |
@@ -126,26 +135,66 @@ jobs:
126135 - name : Refresh Cargo.lock
127136 run : cargo check --quiet 2>&1 | grep -v "^$" || true
128137
129- - name : Commit to main and tag
138+ - name : Create release branch via API (signed commit)
139+ id : commit
140+ env :
141+ GH_TOKEN : ${{ steps.octo-sts.outputs.token }}
142+ NEW_TAG : ${{ steps.version.outputs.new-tag }}
143+ CURRENT_TAG : ${{ steps.version.outputs.current-tag }}
144+ NEW_VERSION : ${{ steps.version.outputs.new-version }}
145+ BRANCH : ${{ steps.version.outputs.branch }}
130146 run : |
131- NEW_TAG="${{ steps.version.outputs.new-tag }}"
132- CURRENT_TAG="${{ steps.version.outputs.current-tag }}"
133- NEW_VERSION="${{ steps.version.outputs.new-version }}"
134-
135- git config user.name "dd-octo-sts[bot]"
136- git config user.email "dd-octo-sts[bot]@users.noreply.github.com"
137-
138- git add Cargo.toml Cargo.lock
139- git commit -m "$(cat <<EOF
140- chore(release): bump version to ${NEW_TAG}
141-
142- - Update Cargo.toml package version ${CURRENT_TAG#v} → ${NEW_VERSION}
143- - Refresh Cargo.lock
144- EOF
145- )"
146-
147- git push origin main
148-
149- git tag -a "$NEW_TAG" -m "$NEW_TAG"
150- git push origin "$NEW_TAG"
151- echo "Tagged and pushed ${NEW_TAG}"
147+ set -euo pipefail
148+ REPO="${GITHUB_REPOSITORY}"
149+
150+ BASE_SHA=$(gh api "repos/${REPO}/git/refs/heads/main" --jq .object.sha)
151+ BASE_TREE=$(gh api "repos/${REPO}/git/commits/${BASE_SHA}" --jq .tree.sha)
152+
153+ CARGO_TOML_BLOB=$(jq -n --rawfile c Cargo.toml '{content: $c, encoding: "utf-8"}' \
154+ | gh api "repos/${REPO}/git/blobs" --input - --jq .sha)
155+ CARGO_LOCK_BLOB=$(jq -n --rawfile c Cargo.lock '{content: $c, encoding: "utf-8"}' \
156+ | gh api "repos/${REPO}/git/blobs" --input - --jq .sha)
157+
158+ TREE_SHA=$(jq -n \
159+ --arg base "${BASE_TREE}" \
160+ --arg toml "${CARGO_TOML_BLOB}" \
161+ --arg lock "${CARGO_LOCK_BLOB}" \
162+ '{
163+ base_tree: $base,
164+ tree: [
165+ {path: "Cargo.toml", mode: "100644", type: "blob", sha: $toml},
166+ {path: "Cargo.lock", mode: "100644", type: "blob", sha: $lock}
167+ ]
168+ }' | gh api "repos/${REPO}/git/trees" --input - --jq .sha)
169+
170+ MESSAGE=$(printf 'chore(release): bump version to %s\n\n- Update Cargo.toml package version %s → %s\n- Refresh Cargo.lock' \
171+ "${NEW_TAG}" "${CURRENT_TAG#v}" "${NEW_VERSION}")
172+
173+ COMMIT_SHA=$(jq -n \
174+ --arg msg "${MESSAGE}" \
175+ --arg tree "${TREE_SHA}" \
176+ --arg parent "${BASE_SHA}" \
177+ '{message: $msg, tree: $tree, parents: [$parent]}' \
178+ | gh api "repos/${REPO}/git/commits" --input - --jq .sha)
179+
180+ gh api "repos/${REPO}/git/refs" \
181+ -f ref="refs/heads/${BRANCH}" \
182+ -f sha="${COMMIT_SHA}"
183+
184+ echo "commit-sha=${COMMIT_SHA}" >> "$GITHUB_OUTPUT"
185+ echo "Created signed commit ${COMMIT_SHA} on ${BRANCH}"
186+
187+ - name : Open Release PR
188+ env :
189+ GH_TOKEN : ${{ steps.octo-sts.outputs.token }}
190+ NEW_TAG : ${{ steps.version.outputs.new-tag }}
191+ CURRENT_TAG : ${{ steps.version.outputs.current-tag }}
192+ BRANCH : ${{ steps.version.outputs.branch }}
193+ run : |
194+ BODY=$(printf 'Automated version bump from %s to %s.\n\nMerging this PR triggers `release-tag.yml`, which creates the `%s` tag and in turn triggers `release.yml` (goreleaser).' \
195+ "${CURRENT_TAG}" "${NEW_TAG}" "${NEW_TAG}")
196+ gh pr create \
197+ --base main \
198+ --head "${BRANCH}" \
199+ --title "chore(release): bump version to ${NEW_TAG}" \
200+ --body "${BODY}"
0 commit comments