From 526ae823719725a3bb0efa148e488cdeaaf5e399 Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Thu, 23 Apr 2026 13:03:26 +0200 Subject: [PATCH 1/8] Pin default datadog-ci version --- README.md | 6 ++++-- action.yaml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e6b05c6..ba785c0 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,10 @@ The action has the following options: | `service` | Service name to use with the uploaded test results. | False | | | `env` | Optional environment to add to the tests | False | | | `logs` | When set to "true" enables forwarding content from the XML reports as Logs. The content inside ``, ``, and `` is collected as logs. Logs from elements inside a `` are automatically connected to the test. | False | | -| `datadog-ci-version` | Version of datadog-ci to install. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (`^`, `~`, `>=`, `latest`) is still supported but deprecated. | False | `v5` | +| `datadog-ci-version` | Version of datadog-ci to install. Defaults to the pinned datadog-ci release shipped with this action version. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (`^`, `~`, `>=`, `latest`) is still supported but deprecated. | False | `v5.13.1` | | `github-token` | GitHub token to use for authenticated datadog-ci release resolution. Defaults to the workflow `github.token` when omitted. | False | `github.token` | | `extra-args` | Extra args to be passed to the datadog-ci junit upload command. | False | | -This action passes the workflow `github.token` to the install step by default. That is primarily useful when `datadog-ci-version` uses a floating release selector such as `v5`, because GitHub release resolution can then be authenticated. To avoid depending on latest-within-major resolution, pin an exact `datadog-ci` version such as `v5.6.0` or `5.6.0`. +By default, this action installs the exact `datadog-ci` release pinned by the action version you use. To receive `datadog-ci` updates, update `datadog/junit-upload-github-action` to a newer release. + +This action passes the workflow `github.token` to the install step by default. That is primarily useful when `datadog-ci-version` uses a floating release selector such as `v5`, because GitHub release resolution can then be authenticated. diff --git a/action.yaml b/action.yaml index 1e11fae..60b923f 100644 --- a/action.yaml +++ b/action.yaml @@ -38,8 +38,8 @@ inputs: description: Set to "true" to enable forwarding content from XML reports as logs. datadog-ci-version: required: false - description: Version of datadog-ci to install. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (^, ~, >=, latest) is still supported but deprecated. - default: "v5" + description: Version of datadog-ci to install. Defaults to the pinned datadog-ci release shipped with this action version. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (^, ~, >=, latest) is still supported but deprecated. + default: "v5.13.1" github-token: required: false default: "" From 74caffdf8857ac4dab8f2c97b37f442e5448fe67 Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Thu, 23 Apr 2026 13:25:03 +0200 Subject: [PATCH 2/8] Address default datadog-ci feedback --- .github/workflows/test.yaml | 13 +++++++++++++ README.md | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 535dac0..9467a04 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,6 +9,19 @@ on: - cron: '0 0 * * *' # Runs at midnight UTC every day jobs: + test-default-datadog-ci-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check that the default datadog-ci version is pinned + run: | + set -euo pipefail + default_version=$(ruby -ryaml -e 'puts YAML.load_file("action.yaml").fetch("inputs").fetch("datadog-ci-version").fetch("default")') + if [[ ! "$default_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Expected datadog-ci-version default to be an exact release tag, got '$default_version'" + exit 1 + fi + test: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index ba785c0..666d05f 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The action has the following options: | `github-token` | GitHub token to use for authenticated datadog-ci release resolution. Defaults to the workflow `github.token` when omitted. | False | `github.token` | | `extra-args` | Extra args to be passed to the datadog-ci junit upload command. | False | | -By default, this action installs the exact `datadog-ci` release pinned by the action version you use. To receive `datadog-ci` updates, update `datadog/junit-upload-github-action` to a newer release. +By default, this action installs the exact `datadog-ci` release pinned by the action version you use. To receive `datadog-ci` updates, update `datadog/junit-upload-github-action` to a newer release or use +the `datadog-ci-version` configuration to specify the version or range. This action passes the workflow `github.token` to the install step by default. That is primarily useful when `datadog-ci-version` uses a floating release selector such as `v5`, because GitHub release resolution can then be authenticated. From d0bc45d0a4d54378a79758ab407889935418e437 Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Thu, 23 Apr 2026 17:04:37 +0200 Subject: [PATCH 3/8] Add datadog-ci release automation --- .github/workflows/bump-datadog-ci.yml | 128 ++++++++++++++++++ .../workflows/release-on-datadog-ci-bump.yml | 74 ++++++++++ .github/workflows/test.yaml | 12 ++ scripts/bump-datadog-ci-version.sh | 30 ++++ 4 files changed, 244 insertions(+) create mode 100644 .github/workflows/bump-datadog-ci.yml create mode 100644 .github/workflows/release-on-datadog-ci-bump.yml create mode 100755 scripts/bump-datadog-ci-version.sh diff --git a/.github/workflows/bump-datadog-ci.yml b/.github/workflows/bump-datadog-ci.yml new file mode 100644 index 0000000..4bf2b30 --- /dev/null +++ b/.github/workflows/bump-datadog-ci.yml @@ -0,0 +1,128 @@ +name: Bump datadog-ci default + +on: + workflow_dispatch: + inputs: + datadog-ci-version: + description: Optional exact datadog-ci release tag. Defaults to the latest datadog-ci release. + required: false + type: string + +permissions: + contents: write + issues: write + pull-requests: write + +env: + BUMP_LABEL: datadog-ci-version-bump + +jobs: + bump: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Prefer a PAT or GitHub App token so PRs created by this workflow can trigger CI. + # Falls back to GITHUB_TOKEN when the secret is not configured. + token: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} + + - name: Compute bump + id: versions + env: + GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} + REQUESTED_DATADOG_CI_VERSION: ${{ inputs['datadog-ci-version'] }} + run: | + set -euo pipefail + + current_version=$(ruby -ryaml -e 'puts YAML.load_file("action.yaml").fetch("inputs").fetch("datadog-ci-version").fetch("default")') + if [[ -n "$REQUESTED_DATADOG_CI_VERSION" ]]; then + latest_version="$REQUESTED_DATADOG_CI_VERSION" + else + latest_version=$(gh api repos/DataDog/datadog-ci/releases/latest --jq '.tag_name') + fi + + if [[ ! "$latest_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Expected an exact datadog-ci release tag like v5.13.1, got '$latest_version'" + exit 1 + fi + + echo "current_version=$current_version" >> "$GITHUB_OUTPUT" + echo "latest_version=$latest_version" >> "$GITHUB_OUTPUT" + echo "branch_name=automation/datadog-ci-${latest_version#v}" >> "$GITHUB_OUTPUT" + if [[ "$current_version" == "$latest_version" ]]; then + echo "needs_bump=false" >> "$GITHUB_OUTPUT" + else + echo "needs_bump=true" >> "$GITHUB_OUTPUT" + fi + + - name: Report no bump + if: steps.versions.outputs.needs_bump == 'false' + run: | + echo "datadog-ci-version already defaults to ${{ steps.versions.outputs.current_version }}." + + - name: Check for an existing bump PR + if: steps.versions.outputs.needs_bump == 'true' + id: existing_pr + env: + GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} + BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} + run: | + set -euo pipefail + pr_number=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // ""') + echo "number=$pr_number" >> "$GITHUB_OUTPUT" + + - name: Report existing bump PR + if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number != '' + run: | + echo "An open bump PR already exists: #${{ steps.existing_pr.outputs.number }}." + + - name: Create bump commit + if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number == '' + env: + BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} + LATEST_VERSION: ${{ steps.versions.outputs.latest_version }} + run: | + set -euo pipefail + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$BRANCH_NAME" + + scripts/bump-datadog-ci-version.sh "$LATEST_VERSION" + + git add action.yaml README.md + git commit -m "Bump datadog-ci to $LATEST_VERSION" + git push --force --set-upstream origin "$BRANCH_NAME" + + - name: Open bump PR + if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number == '' + env: + GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} + BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} + CURRENT_VERSION: ${{ steps.versions.outputs.current_version }} + LATEST_VERSION: ${{ steps.versions.outputs.latest_version }} + run: | + set -euo pipefail + + gh label create "$BUMP_LABEL" \ + --description "Triggers a junit-upload-github-action release after merge" \ + --color "1D76DB" \ + --force + + body_file=$(mktemp) + cat > "$body_file" < "$notes_file" <&2 + exit 1 +fi + +version="$1" + +if [[ ! "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Expected an exact datadog-ci release tag like v5.13.1, got '$version'" >&2 + exit 1 +fi + +ruby - "$version" <<'RUBY' +version = ARGV.fetch(0) + +action_path = 'action.yaml' +action = File.read(action_path) +action_pattern = /(^ datadog-ci-version:\n(?:(?!^ [A-Za-z0-9_-]+:).*\n)*?^ default: )"v\d+\.\d+\.\d+"/ +abort "Unable to find datadog-ci-version default in #{action_path}" unless action.match?(action_pattern) +File.write(action_path, action.sub(action_pattern) { "#{Regexp.last_match(1)}\"#{version}\"" }) + +readme_path = 'README.md' +readme = File.read(readme_path) +readme_pattern = /^(\| `datadog-ci-version` \|.*\| False\s+\| `)v\d+\.\d+\.\d+(`\s+\|)$/ +abort "Unable to find datadog-ci-version default in #{readme_path}" unless readme.match?(readme_pattern) +File.write(readme_path, readme.sub(readme_pattern) { "#{Regexp.last_match(1)}#{version}#{Regexp.last_match(2)}" }) +RUBY From 7d022d06a412eaa29dc9b25beda6a0b05591995f Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Thu, 23 Apr 2026 17:15:10 +0200 Subject: [PATCH 4/8] Use local datadog-ci bump script --- .github/workflows/bump-datadog-ci.yml | 128 ------------------------ .github/workflows/test.yaml | 3 + README.md | 6 ++ scripts/create-datadog-ci-bump-pr.sh | 136 ++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 128 deletions(-) delete mode 100644 .github/workflows/bump-datadog-ci.yml create mode 100755 scripts/create-datadog-ci-bump-pr.sh diff --git a/.github/workflows/bump-datadog-ci.yml b/.github/workflows/bump-datadog-ci.yml deleted file mode 100644 index 4bf2b30..0000000 --- a/.github/workflows/bump-datadog-ci.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: Bump datadog-ci default - -on: - workflow_dispatch: - inputs: - datadog-ci-version: - description: Optional exact datadog-ci release tag. Defaults to the latest datadog-ci release. - required: false - type: string - -permissions: - contents: write - issues: write - pull-requests: write - -env: - BUMP_LABEL: datadog-ci-version-bump - -jobs: - bump: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - # Prefer a PAT or GitHub App token so PRs created by this workflow can trigger CI. - # Falls back to GITHUB_TOKEN when the secret is not configured. - token: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} - - - name: Compute bump - id: versions - env: - GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} - REQUESTED_DATADOG_CI_VERSION: ${{ inputs['datadog-ci-version'] }} - run: | - set -euo pipefail - - current_version=$(ruby -ryaml -e 'puts YAML.load_file("action.yaml").fetch("inputs").fetch("datadog-ci-version").fetch("default")') - if [[ -n "$REQUESTED_DATADOG_CI_VERSION" ]]; then - latest_version="$REQUESTED_DATADOG_CI_VERSION" - else - latest_version=$(gh api repos/DataDog/datadog-ci/releases/latest --jq '.tag_name') - fi - - if [[ ! "$latest_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Expected an exact datadog-ci release tag like v5.13.1, got '$latest_version'" - exit 1 - fi - - echo "current_version=$current_version" >> "$GITHUB_OUTPUT" - echo "latest_version=$latest_version" >> "$GITHUB_OUTPUT" - echo "branch_name=automation/datadog-ci-${latest_version#v}" >> "$GITHUB_OUTPUT" - if [[ "$current_version" == "$latest_version" ]]; then - echo "needs_bump=false" >> "$GITHUB_OUTPUT" - else - echo "needs_bump=true" >> "$GITHUB_OUTPUT" - fi - - - name: Report no bump - if: steps.versions.outputs.needs_bump == 'false' - run: | - echo "datadog-ci-version already defaults to ${{ steps.versions.outputs.current_version }}." - - - name: Check for an existing bump PR - if: steps.versions.outputs.needs_bump == 'true' - id: existing_pr - env: - GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} - BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} - run: | - set -euo pipefail - pr_number=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // ""') - echo "number=$pr_number" >> "$GITHUB_OUTPUT" - - - name: Report existing bump PR - if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number != '' - run: | - echo "An open bump PR already exists: #${{ steps.existing_pr.outputs.number }}." - - - name: Create bump commit - if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number == '' - env: - BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} - LATEST_VERSION: ${{ steps.versions.outputs.latest_version }} - run: | - set -euo pipefail - - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git checkout -b "$BRANCH_NAME" - - scripts/bump-datadog-ci-version.sh "$LATEST_VERSION" - - git add action.yaml README.md - git commit -m "Bump datadog-ci to $LATEST_VERSION" - git push --force --set-upstream origin "$BRANCH_NAME" - - - name: Open bump PR - if: steps.versions.outputs.needs_bump == 'true' && steps.existing_pr.outputs.number == '' - env: - GH_TOKEN: ${{ secrets.GH_RELEASE_AUTOMATION_TOKEN || github.token }} - BRANCH_NAME: ${{ steps.versions.outputs.branch_name }} - CURRENT_VERSION: ${{ steps.versions.outputs.current_version }} - LATEST_VERSION: ${{ steps.versions.outputs.latest_version }} - run: | - set -euo pipefail - - gh label create "$BUMP_LABEL" \ - --description "Triggers a junit-upload-github-action release after merge" \ - --color "1D76DB" \ - --force - - body_file=$(mktemp) - cat > "$body_file" <. + BUMP_LABEL Label applied to the PR. Defaults to datadog-ci-version-bump. + REMOTE Git remote to push to. Defaults to origin. + REPO GitHub repo for gh commands. Defaults to gh repo view's repo. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +if [[ $# -gt 1 ]]; then + usage >&2 + exit 1 +fi + +for command in gh git ruby; do + if ! command -v "$command" >/dev/null 2>&1; then + echo "Missing required command: $command" >&2 + exit 1 + fi +done + +if [[ -n "$(git status --porcelain)" ]]; then + echo "Working tree must be clean before creating a bump PR." >&2 + exit 1 +fi + +gh auth status >/dev/null + +base_branch="${BASE_BRANCH:-main}" +bump_label="${BUMP_LABEL:-datadog-ci-version-bump}" +remote="${REMOTE:-origin}" +repo="${REPO:-$(gh repo view --json nameWithOwner --jq '.nameWithOwner')}" +requested_version="${1:-}" + +current_version=$(ruby -ryaml -e 'puts YAML.load_file("action.yaml").fetch("inputs").fetch("datadog-ci-version").fetch("default")') +if [[ -n "$requested_version" ]]; then + latest_version="$requested_version" +else + latest_version=$(gh api repos/DataDog/datadog-ci/releases/latest --jq '.tag_name') +fi + +if [[ ! "$latest_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Expected an exact datadog-ci release tag like v5.13.1, got '$latest_version'" >&2 + exit 1 +fi + +version_gt() { + local left="${1#v}" + local right="${2#v}" + local left_major left_minor left_patch right_major right_minor right_patch + + IFS=. read -r left_major left_minor left_patch <<< "$left" + IFS=. read -r right_major right_minor right_patch <<< "$right" + + (( left_major > right_major )) && return 0 + (( left_major < right_major )) && return 1 + (( left_minor > right_minor )) && return 0 + (( left_minor < right_minor )) && return 1 + (( left_patch > right_patch )) +} + +if ! version_gt "$latest_version" "$current_version"; then + echo "No datadog-ci bump needed. Current default is $current_version; latest is $latest_version." + exit 0 +fi + +branch_name="${BRANCH_NAME:-datadog-ci-bump/${latest_version#v}}" + +existing_pr_url=$(gh pr list \ + --repo "$repo" \ + --head "$branch_name" \ + --state open \ + --json url \ + --jq '.[0].url // ""') +if [[ -n "$existing_pr_url" ]]; then + echo "An open bump PR already exists: $existing_pr_url" + exit 0 +fi + +if git show-ref --verify --quiet "refs/heads/$branch_name"; then + echo "Local branch '$branch_name' already exists. Delete it or set BRANCH_NAME to another value." >&2 + exit 1 +fi + +git fetch "$remote" "$base_branch" +git checkout -b "$branch_name" FETCH_HEAD + +scripts/bump-datadog-ci-version.sh "$latest_version" + +git add action.yaml README.md +git commit -m "Bump datadog-ci to $latest_version" +git push -u "$remote" "$branch_name" + +gh label create "$bump_label" \ + --repo "$repo" \ + --description "Triggers a junit-upload-github-action release after merge" \ + --color "1D76DB" \ + --force + +body_file=$(mktemp) +trap 'rm -f "$body_file"' EXIT +cat > "$body_file" < Date: Thu, 23 Apr 2026 17:21:54 +0200 Subject: [PATCH 5/8] Use local datadog-ci release script --- .../workflows/release-on-datadog-ci-bump.yml | 74 -------- .github/workflows/test.yaml | 1 + README.md | 4 +- RELEASE.md | 39 +++++ scripts/release-datadog-ci-bump.sh | 161 ++++++++++++++++++ 5 files changed, 202 insertions(+), 77 deletions(-) delete mode 100644 .github/workflows/release-on-datadog-ci-bump.yml create mode 100644 RELEASE.md create mode 100755 scripts/release-datadog-ci-bump.sh diff --git a/.github/workflows/release-on-datadog-ci-bump.yml b/.github/workflows/release-on-datadog-ci-bump.yml deleted file mode 100644 index 74c7da7..0000000 --- a/.github/workflows/release-on-datadog-ci-bump.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Release on datadog-ci bump - -on: - pull_request: - types: - - closed - -permissions: - contents: write - pull-requests: read - -env: - BUMP_LABEL: datadog-ci-version-bump - -jobs: - release: - if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'datadog-ci-version-bump') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }} - - - name: Create action release - env: - GH_TOKEN: ${{ github.token }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_URL: ${{ github.event.pull_request.html_url }} - run: | - set -euo pipefail - - datadog_ci_version=$(ruby -ryaml -e 'puts YAML.load_file("action.yaml").fetch("inputs").fetch("datadog-ci-version").fetch("default")') - if [[ ! "$datadog_ci_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Expected datadog-ci-version default to be an exact release tag, got '$datadog_ci_version'" - exit 1 - fi - - existing_release_tag=$(git tag --points-at "$GITHUB_SHA" --list 'v[0-9]*.[0-9]*.[0-9]*' | head -n 1) - if [[ -n "$existing_release_tag" ]]; then - echo "Commit $GITHUB_SHA is already released as $existing_release_tag." - exit 0 - fi - - latest_tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1) - if [[ -z "$latest_tag" ]]; then - echo "No existing semver action tag found." - exit 1 - fi - - version="${latest_tag#v}" - IFS=. read -r major minor patch <<< "$version" - next_tag="v${major}.${minor}.$((patch + 1))" - major_tag="v${major}" - - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - git tag "$next_tag" "$GITHUB_SHA" - git tag -f "$major_tag" "$GITHUB_SHA" - git push origin "refs/tags/$next_tag" - git push --force origin "refs/tags/$major_tag" - - notes_file=$(mktemp) - cat > "$notes_file" <&2 + exit 1 +fi + +for command in gh git ruby; do + if ! command -v "$command" >/dev/null 2>&1; then + echo "Missing required command: $command" >&2 + exit 1 + fi +done + +if [[ -n "$(git status --porcelain)" ]]; then + echo "Working tree must be clean before creating a release." >&2 + exit 1 +fi + +gh auth status >/dev/null + +base_branch="${BASE_BRANCH:-main}" +bump_label="${BUMP_LABEL:-datadog-ci-version-bump}" +remote="${REMOTE:-origin}" +repo="${REPO:-$(gh repo view --json nameWithOwner --jq '.nameWithOwner')}" +pr_limit="${PR_LIMIT:-200}" + +git fetch --tags "$remote" +git fetch "$remote" "$base_branch" +base_ref="refs/remotes/$remote/$base_branch" + +latest_tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1) +if [[ -z "$latest_tag" ]]; then + echo "No existing immutable action release tag found." >&2 + exit 1 +fi + +if ! git merge-base --is-ancestor "$latest_tag" "$base_ref"; then + echo "Latest immutable action tag $latest_tag is not in $remote/$base_branch history." >&2 + exit 1 +fi + +release_pr_number="" +release_pr_url="" +release_pr_merged_at="" +release_sha="" + +while IFS=$'\t' read -r pr_number pr_url merged_at merge_sha; do + [[ -z "$pr_number" ]] && continue + [[ -z "$merge_sha" ]] && continue + + if ! git merge-base --is-ancestor "$merge_sha" "$base_ref"; then + continue + fi + + if git merge-base --is-ancestor "$merge_sha" "$latest_tag"; then + continue + fi + + if ! git merge-base --is-ancestor "$latest_tag" "$merge_sha"; then + echo "Skipping PR #$pr_number because its merge commit is not after $latest_tag." >&2 + continue + fi + + release_pr_number="$pr_number" + release_pr_url="$pr_url" + release_pr_merged_at="$merged_at" + release_sha="$merge_sha" + break +done < <( + gh pr list \ + --repo "$repo" \ + --state merged \ + --base "$base_branch" \ + --label "$bump_label" \ + --limit "$pr_limit" \ + --json number,url,mergedAt,mergeCommit \ + --jq 'sort_by(.mergedAt) | .[] | [.number, .url, .mergedAt, .mergeCommit.oid] | @tsv' +) + +if [[ -z "$release_sha" ]]; then + echo "No unreleased merged PR with label '$bump_label' found on $remote/$base_branch." + exit 0 +fi + +version="${latest_tag#v}" +IFS=. read -r major minor patch <<< "$version" +next_tag="v${major}.${minor}.$((patch + 1))" +major_tag="v${major}" + +if git rev-parse --verify --quiet "refs/tags/$next_tag" >/dev/null; then + echo "Next release tag $next_tag already exists locally." >&2 + exit 1 +fi + +if gh release view "$next_tag" --repo "$repo" >/dev/null 2>&1; then + echo "GitHub Release $next_tag already exists." >&2 + exit 1 +fi + +datadog_ci_version=$(git show "$release_sha:action.yaml" | ruby -ryaml -e 'puts YAML.load($stdin.read).fetch("inputs").fetch("datadog-ci-version").fetch("default")') +if [[ ! "$datadog_ci_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Expected datadog-ci-version default to be an exact release tag at $release_sha, got '$datadog_ci_version'" >&2 + exit 1 +fi + +notes_file=$(mktemp) +trap 'rm -f "$notes_file"' EXIT +cat > "$notes_file" < Date: Thu, 23 Apr 2026 17:36:51 +0200 Subject: [PATCH 6/8] Release only latest datadog-ci bump by default --- RELEASE.md | 9 +- scripts/release-datadog-ci-bump.sh | 164 ++++++++++++++++++++++------- 2 files changed, 134 insertions(+), 39 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 3cd01cd..de423ec 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -28,7 +28,7 @@ After a `datadog-ci-version-bump` PR is merged, run: scripts/release-datadog-ci-bump.sh ``` -The script fetches `main` and tags, finds the oldest merged PR with the `datadog-ci-version-bump` label that is on `main` but not included in the latest immutable action tag, and releases that merge commit. A release creates the next patch tag, updates the moving major tag, and creates a GitHub Release. +The script fetches `main` and tags, finds the latest merged PR with the `datadog-ci-version-bump` label that is on `main` but not included in the latest immutable action tag, and releases that merge commit. A release creates the next patch tag, updates the moving major tag, and creates a GitHub Release. Preview the release without creating tags or a GitHub Release: @@ -36,4 +36,9 @@ Preview the release without creating tags or a GitHub Release: scripts/release-datadog-ci-bump.sh --dry-run ``` -If multiple bump PRs were merged without releases, run the release script again after each successful release until it reports that no unreleased merged PRs remain. +If multiple bump PRs were merged without releases, the script warns and releases only the latest one. To intentionally release an older merge commit separately, stop and pass a specific PR number or commit SHA before releasing the latest one: + +```bash +scripts/release-datadog-ci-bump.sh --pr 123 +scripts/release-datadog-ci-bump.sh --sha abc1234 +``` diff --git a/scripts/release-datadog-ci-bump.sh b/scripts/release-datadog-ci-bump.sh index 3efc997..e384431 100755 --- a/scripts/release-datadog-ci-bump.sh +++ b/scripts/release-datadog-ci-bump.sh @@ -3,11 +3,17 @@ set -euo pipefail usage() { cat <<'EOF' -Usage: scripts/release-datadog-ci-bump.sh [--dry-run] +Usage: scripts/release-datadog-ci-bump.sh [--dry-run] [--pr NUMBER | --sha COMMIT] -Finds the oldest merged datadog-ci bump PR on main that is not included in the +Finds the latest merged datadog-ci bump PR on main that is not included in the latest immutable action release tag. When one exists, creates the next patch -action tag, updates the moving major tag, and creates a GitHub Release. +action tag, updates the moving major tag, and creates a GitHub Release. If older +unreleased bump PRs exist, the script warns and releases only the latest change. + +Options: + --dry-run Print the release that would be created. + --pr NUMBER Release a specific merged bump PR instead of the latest one. + --sha COMMIT Release a specific commit on main instead of finding a PR. Environment: BASE_BRANCH Branch to inspect. Defaults to main. @@ -19,13 +25,43 @@ EOF } dry_run=false -if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then - usage - exit 0 -elif [[ "${1:-}" == "--dry-run" ]]; then - dry_run=true -elif [[ $# -gt 0 ]]; then - usage >&2 +requested_pr="" +requested_sha="" +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + --dry-run) + dry_run=true + shift + ;; + --pr) + if [[ -n "$requested_sha" || -n "$requested_pr" || -z "${2:-}" ]]; then + usage >&2 + exit 1 + fi + requested_pr="$2" + shift 2 + ;; + --sha) + if [[ -n "$requested_pr" || -n "$requested_sha" || -z "${2:-}" ]]; then + usage >&2 + exit 1 + fi + requested_sha="$2" + shift 2 + ;; + *) + usage >&2 + exit 1 + ;; + esac +done + +if [[ -n "$requested_pr" && ! "$requested_pr" =~ ^[0-9]+$ ]]; then + echo "--pr expects a numeric pull request number, got '$requested_pr'" >&2 exit 1 fi @@ -68,45 +104,95 @@ release_pr_number="" release_pr_url="" release_pr_merged_at="" release_sha="" +unreleased_count=0 -while IFS=$'\t' read -r pr_number pr_url merged_at merge_sha; do - [[ -z "$pr_number" ]] && continue - [[ -z "$merge_sha" ]] && continue +validate_release_sha() { + local sha="$1" - if ! git merge-base --is-ancestor "$merge_sha" "$base_ref"; then - continue + if ! git cat-file -e "${sha}^{commit}" 2>/dev/null; then + echo "Commit '$sha' was not found locally." >&2 + return 1 fi - if git merge-base --is-ancestor "$merge_sha" "$latest_tag"; then - continue + if ! git merge-base --is-ancestor "$sha" "$base_ref"; then + echo "Commit '$sha' is not in $remote/$base_branch history." >&2 + return 1 fi - if ! git merge-base --is-ancestor "$latest_tag" "$merge_sha"; then - echo "Skipping PR #$pr_number because its merge commit is not after $latest_tag." >&2 - continue + if git merge-base --is-ancestor "$sha" "$latest_tag"; then + echo "Commit '$sha' is already included in latest action release $latest_tag." >&2 + return 1 fi - release_pr_number="$pr_number" - release_pr_url="$pr_url" - release_pr_merged_at="$merged_at" - release_sha="$merge_sha" - break -done < <( - gh pr list \ + if ! git merge-base --is-ancestor "$latest_tag" "$sha"; then + echo "Commit '$sha' is not after latest action release $latest_tag." >&2 + return 1 + fi +} + +if [[ -n "$requested_sha" ]]; then + release_sha=$(git rev-parse "$requested_sha") + validate_release_sha "$release_sha" +elif [[ -n "$requested_pr" ]]; then + pr_data=$(gh pr view "$requested_pr" \ --repo "$repo" \ - --state merged \ - --base "$base_branch" \ - --label "$bump_label" \ - --limit "$pr_limit" \ - --json number,url,mergedAt,mergeCommit \ - --jq 'sort_by(.mergedAt) | .[] | [.number, .url, .mergedAt, .mergeCommit.oid] | @tsv' -) + --json number,url,mergedAt,mergeCommit,baseRefName,labels,state \ + --jq ' + if .state != "MERGED" then + error("PR #\(.number) is not merged") + elif .baseRefName != "'"$base_branch"'" then + error("PR #\(.number) targets \(.baseRefName), not '"$base_branch"'") + elif ([.labels[].name] | index("'"$bump_label"'") | not) then + error("PR #\(.number) does not have label '"$bump_label"'") + else + [.number, .url, .mergedAt, .mergeCommit.oid] | @tsv + end + ') + IFS=$'\t' read -r release_pr_number release_pr_url release_pr_merged_at release_sha <<< "$pr_data" + validate_release_sha "$release_sha" +else + while IFS=$'\t' read -r pr_number pr_url merged_at merge_sha; do + [[ -z "$pr_number" ]] && continue + [[ -z "$merge_sha" ]] && continue + + if ! validate_release_sha "$merge_sha" 2>/dev/null; then + continue + fi + + unreleased_count=$((unreleased_count + 1)) + release_pr_number="$pr_number" + release_pr_url="$pr_url" + release_pr_merged_at="$merged_at" + release_sha="$merge_sha" + done < <( + gh pr list \ + --repo "$repo" \ + --state merged \ + --base "$base_branch" \ + --label "$bump_label" \ + --limit "$pr_limit" \ + --json number,url,mergedAt,mergeCommit \ + --jq 'sort_by(.mergedAt) | .[] | [.number, .url, .mergedAt, .mergeCommit.oid] | @tsv' + ) + + if (( unreleased_count > 1 )); then + echo "Warning: found $unreleased_count unreleased merged PRs with label '$bump_label'." >&2 + echo "This script will release only the latest one, #$release_pr_number." >&2 + echo "To release an older merge commit separately, stop and rerun with --pr NUMBER or --sha COMMIT before releasing the latest one." >&2 + fi +fi if [[ -z "$release_sha" ]]; then echo "No unreleased merged PR with label '$bump_label' found on $remote/$base_branch." exit 0 fi +if [[ -n "$release_pr_number" ]]; then + release_source="#$release_pr_number: $release_pr_url" +else + release_source="$release_sha" +fi + version="${latest_tag#v}" IFS=. read -r major minor patch <<< "$version" next_tag="v${major}.${minor}.$((patch + 1))" @@ -133,15 +219,19 @@ trap 'rm -f "$notes_file"' EXIT cat > "$notes_file" <> "$notes_file" +fi echo "Latest action release tag: $latest_tag" echo "Next action release tag: $next_tag" echo "Moving major tag: $major_tag" echo "Release commit: $release_sha" -echo "Release PR: #$release_pr_number" +if [[ -n "$release_pr_number" ]]; then + echo "Release PR: #$release_pr_number" +fi echo "datadog-ci version: $datadog_ci_version" if [[ "$dry_run" == "true" ]]; then From 615518dbe8543a5908c6eab637dc251020efe19a Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Thu, 23 Apr 2026 21:02:04 +0200 Subject: [PATCH 7/8] Clarify helper test step name --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9ddfe92..cd834ab 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,7 +21,7 @@ jobs: echo "Expected datadog-ci-version default to be an exact release tag, got '$default_version'" exit 1 fi - - name: Check datadog-ci version bump script + - name: Check local release helper scripts run: | set -euo pipefail bash -n scripts/bump-datadog-ci-version.sh From 268afb0f4b7d01cc928bc55e95eaf8da5608087e Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Mon, 27 Apr 2026 15:45:53 +0200 Subject: [PATCH 8/8] Bump datadog-ci to v5.14.0 --- README.md | 2 +- RELEASE.md | 2 +- action.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93f8566..60d2604 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The action has the following options: | `service` | Service name to use with the uploaded test results. | False | | | `env` | Optional environment to add to the tests | False | | | `logs` | When set to "true" enables forwarding content from the XML reports as Logs. The content inside ``, ``, and `` is collected as logs. Logs from elements inside a `` are automatically connected to the test. | False | | -| `datadog-ci-version` | Version of datadog-ci to install. Defaults to the pinned datadog-ci release shipped with this action version. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (`^`, `~`, `>=`, `latest`) is still supported but deprecated. | False | `v5.13.1` | +| `datadog-ci-version` | Version of datadog-ci to install. Defaults to the pinned datadog-ci release shipped with this action version. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (`^`, `~`, `>=`, `latest`) is still supported but deprecated. | False | `v5.14.0` | | `github-token` | GitHub token to use for authenticated datadog-ci release resolution. Defaults to the workflow `github.token` when omitted. | False | `github.token` | | `extra-args` | Extra args to be passed to the datadog-ci junit upload command. | False | | diff --git a/RELEASE.md b/RELEASE.md index de423ec..ac922e5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -15,7 +15,7 @@ The script uses `gh` to check the latest `DataDog/datadog-ci` release. If that r To test a specific version instead of the latest release: ```bash -scripts/create-datadog-ci-bump-pr.sh v5.13.1 +scripts/create-datadog-ci-bump-pr.sh v5.14.0 ``` Review and merge the PR normally. diff --git a/action.yaml b/action.yaml index 60b923f..c02fe82 100644 --- a/action.yaml +++ b/action.yaml @@ -39,7 +39,7 @@ inputs: datadog-ci-version: required: false description: Version of datadog-ci to install. Defaults to the pinned datadog-ci release shipped with this action version. Use a major version like `v5` to get the latest release within that major version, or a specific tag like `v5.6.0` to pin. Legacy npm semver syntax (^, ~, >=, latest) is still supported but deprecated. - default: "v5.13.1" + default: "v5.14.0" github-token: required: false default: ""