From 148f26b28ed05d1236e6f36e6e16126d154a9f95 Mon Sep 17 00:00:00 2001 From: jack-edmonds-dd Date: Wed, 20 May 2026 10:09:39 -0400 Subject: [PATCH] =?UTF-8?q?The=20previous=20`release-prepare.yml`=20pushed?= =?UTF-8?q?=20a=20version-bump=20commit=20directly=20to=20`main`=20and=20t?= =?UTF-8?q?hen=20pushed=20an=20annotated=20tag=20=E2=80=94=20both=20blocke?= =?UTF-8?q?d=20under=20DataDog=20org=20rulesets=20(PR-required,=20signed-c?= =?UTF-8?q?ommits,=20tag-creation=20restricted=20to=20allowlisted=20apps).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New flow: - `release-prepare.yml` (schedule/dispatch): bumps Cargo.toml & Cargo.lock, creates a `release/v` branch via Git Data API (signed by GitHub App), opens a PR. PR approval becomes the release gate, replacing the `release` GitHub Environment. - `release-tag.yml` (pull_request: closed): creates a lightweight tag pointing at the merge SHA via `gh api .../git/refs`. dd-octo-sts is on the org-wide tag protection bypass list (SECENG wiki 6360924697), and the tag inherits the signed merge commit. - Trust policies updated: `release` reverted to ref-based subject (no more `environment: release`), `pull_requests: write` added. New `release-tag` policy scoped to the PR-close workflow. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/chainguard/release-tag.sts.yaml | 12 +++ .github/chainguard/release.sts.yaml | 3 +- .github/workflows/release-prepare.yml | 103 +++++++++++++++++------- .github/workflows/release-tag.yml | 45 +++++++++++ 4 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 .github/chainguard/release-tag.sts.yaml create mode 100644 .github/workflows/release-tag.yml diff --git a/.github/chainguard/release-tag.sts.yaml b/.github/chainguard/release-tag.sts.yaml new file mode 100644 index 00000000..3cfc7b5f --- /dev/null +++ b/.github/chainguard/release-tag.sts.yaml @@ -0,0 +1,12 @@ +# Policy for: .github/workflows/release-tag.yml in DataDog/pup +issuer: https://token.actions.githubusercontent.com +subject: repo:DataDog/pup:pull_request + +claim_pattern: + event_name: pull_request + job_workflow_ref: DataDog/pup/\.github/workflows/release-tag\.yml@refs/pull/[0-9]+/merge + ref: refs/pull/[0-9]+/merge + repository: DataDog/pup + +permissions: + contents: write diff --git a/.github/chainguard/release.sts.yaml b/.github/chainguard/release.sts.yaml index ce07ec6f..10e8efbe 100644 --- a/.github/chainguard/release.sts.yaml +++ b/.github/chainguard/release.sts.yaml @@ -1,6 +1,6 @@ # Policy for: .github/workflows/release-prepare.yml in DataDog/pup issuer: https://token.actions.githubusercontent.com -subject: repo:DataDog/pup:environment:release +subject: repo:DataDog/pup:ref:refs/heads/main claim_pattern: event_name: schedule|workflow_dispatch @@ -10,3 +10,4 @@ claim_pattern: permissions: contents: write + pull_requests: write diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml index aab61283..dc6db341 100644 --- a/.github/workflows/release-prepare.yml +++ b/.github/workflows/release-prepare.yml @@ -48,14 +48,15 @@ jobs: fi # --------------------------------------------------------------------------- - # Release: bump, commit to main, tag. Triggers release.yml via the tag push. + # Release: bump Cargo.toml/Cargo.lock on a release/ branch and open a PR. + # Merging the PR triggers release-tag.yml, which creates the tag and triggers + # release.yml. # --------------------------------------------------------------------------- release: - name: Bump, Commit, Tag + name: Open Release PR needs: check if: needs.check.outputs.proceed == 'true' runs-on: ubuntu-latest - environment: release steps: - uses: DataDog/dd-octo-sts-action@96a25462dbcb10ebf0bfd6e2ccc917d2ab235b9a # v1.0.4 id: octo-sts @@ -66,7 +67,6 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - token: ${{ steps.octo-sts.outputs.token }} - name: Compute next version id: version @@ -82,19 +82,28 @@ jobs: esac NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" NEW_TAG="v${NEW_VERSION}" + BRANCH="release/${NEW_TAG}" echo "current-tag=${CURRENT_TAG}" >> "$GITHUB_OUTPUT" echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" echo "new-tag=${NEW_TAG}" >> "$GITHUB_OUTPUT" + echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT" echo "Current: ${CURRENT_TAG}" echo "Next: ${NEW_TAG} (${BUMP} bump)" - name: Preflight check + env: + GH_TOKEN: ${{ steps.octo-sts.outputs.token }} + NEW_TAG: ${{ steps.version.outputs.new-tag }} + BRANCH: ${{ steps.version.outputs.branch }} run: | - NEW_TAG="${{ steps.version.outputs.new-tag }}" if git tag -l "$NEW_TAG" | grep -q .; then echo "::error::Tag '${NEW_TAG}' already exists." exit 1 fi + if gh api "repos/${GITHUB_REPOSITORY}/git/refs/heads/${BRANCH}" >/dev/null 2>&1; then + echo "::error::Branch '${BRANCH}' already exists." + exit 1 + fi - name: Install Rust run: | @@ -126,26 +135,66 @@ jobs: - name: Refresh Cargo.lock run: cargo check --quiet 2>&1 | grep -v "^$" || true - - name: Commit to main and tag + - name: Create release branch via API (signed commit) + id: commit + env: + GH_TOKEN: ${{ steps.octo-sts.outputs.token }} + NEW_TAG: ${{ steps.version.outputs.new-tag }} + CURRENT_TAG: ${{ steps.version.outputs.current-tag }} + NEW_VERSION: ${{ steps.version.outputs.new-version }} + BRANCH: ${{ steps.version.outputs.branch }} run: | - NEW_TAG="${{ steps.version.outputs.new-tag }}" - CURRENT_TAG="${{ steps.version.outputs.current-tag }}" - NEW_VERSION="${{ steps.version.outputs.new-version }}" - - git config user.name "dd-octo-sts[bot]" - git config user.email "dd-octo-sts[bot]@users.noreply.github.com" - - git add Cargo.toml Cargo.lock - git commit -m "$(cat <> "$GITHUB_OUTPUT" + echo "Created signed commit ${COMMIT_SHA} on ${BRANCH}" + + - name: Open Release PR + env: + GH_TOKEN: ${{ steps.octo-sts.outputs.token }} + NEW_TAG: ${{ steps.version.outputs.new-tag }} + CURRENT_TAG: ${{ steps.version.outputs.current-tag }} + BRANCH: ${{ steps.version.outputs.branch }} + run: | + 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).' \ + "${CURRENT_TAG}" "${NEW_TAG}" "${NEW_TAG}") + gh pr create \ + --base main \ + --head "${BRANCH}" \ + --title "chore(release): bump version to ${NEW_TAG}" \ + --body "${BODY}" diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml new file mode 100644 index 00000000..af997f7d --- /dev/null +++ b/.github/workflows/release-tag.yml @@ -0,0 +1,45 @@ +name: Tag Release + +on: + pull_request: + types: [closed] + +permissions: + id-token: write + contents: read + +jobs: + tag: + name: Create release tag + if: > + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.head.ref, 'release/') && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: DataDog/dd-octo-sts-action@96a25462dbcb10ebf0bfd6e2ccc917d2ab235b9a # v1.0.4 + id: octo-sts + with: + scope: datadog/pup + policy: release-tag + + - name: Create tag + env: + GH_TOKEN: ${{ steps.octo-sts.outputs.token }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }} + run: | + set -euo pipefail + TAG_NAME="${HEAD_REF#release/}" + if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Invalid tag name '${TAG_NAME}' derived from head_ref '${HEAD_REF}'" + exit 1 + fi + if gh api "repos/${GITHUB_REPOSITORY}/git/refs/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "::error::Tag '${TAG_NAME}' already exists." + exit 1 + fi + gh api "repos/${GITHUB_REPOSITORY}/git/refs" \ + -f ref="refs/tags/${TAG_NAME}" \ + -f sha="${MERGE_SHA}" + echo "Created tag ${TAG_NAME} → ${MERGE_SHA}"