From fd6282c5a6ccef931d4df105b0e4cbac10d5b211 Mon Sep 17 00:00:00 2001 From: iunanua Date: Fri, 27 Mar 2026 17:23:51 +0100 Subject: [PATCH 01/10] workflow refactor to take into account major bumps in dependencies --- .../workflows/release-proposal-dispatch.yml | 167 +++++++++++++----- scripts/major-bumps-level.sh | 129 ++++++++++++++ 2 files changed, 253 insertions(+), 43 deletions(-) create mode 100755 scripts/major-bumps-level.sh diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index 4fa6104301..f89d6622d9 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -407,10 +407,104 @@ jobs: fi fi - # Update the CHANGELOG.md + # Commit the changes + cargo release commit --no-confirm -x + NEXT_VERSION=$(cargo metadata --format-version=1 --no-deps | jq -r --arg name "$NAME" '.packages[] | select(.name == $name) | .version') NEXT_TAG="$TAG_PREFIX$NEXT_VERSION" + # Add to results array + jq --arg name "$NAME" \ + --arg level "$LEVEL" \ + --arg tag "$NEXT_TAG" \ + --arg prev_tag "$TAG" \ + --arg version "$NEXT_VERSION" \ + --arg range "$RANGE" \ + --argjson commits "$COMMITS" \ + --arg path "$CRATE_PATH" \ + --arg initial_release "$INITIAL_RELEASE" \ + '. += [{"name": $name, "level": $level, "tag": $tag, "prev_tag": $prev_tag, "version": $version, "range": $range, "commits": $commits, "path": $path, "initial_release": $initial_release}]' \ + /tmp/api-changes.json > /tmp/api-changes.tmp && mv /tmp/api-changes.tmp /tmp/api-changes.json + done + + # Check if there are commits to push + if git diff --quiet "${{ steps.ephemeral-branch.outputs.ephemeral_branch }}"; then + echo "No changes to push. Cancelling the workflow." + exit 1 + fi + + # Output the results + echo "API changes summary:" + jq . /tmp/api-changes.json + + - name: Update version for crates with libdd-* direct dependency major bumps since last release + run: | + set -euo pipefail + BRANCH_NAME="${{ steps.proposal-branch.outputs.branch_name }}" + + ./scripts/major-bumps-level.sh /tmp/api-changes.json > /tmp/api-changes-with-major-bumps-pre-commit.json + + # Full same crate list as api-changes.json; rows updated in place when a major bump is applied. + cp /tmp/api-changes-with-major-bumps-pre-commit.json /tmp/api-changes-with-major-bumps.json + + # iterate over the major bumps and check the major bumps and update the version + jq -c '.[]' /tmp/api-changes-with-major-bumps-pre-commit.json | while read -r bump; do + NAME=$(echo "$bump" | jq -r '.name') + PREV_TAG=$(echo "$bump" | jq -r '.prev_tag') + TAG=$(echo "$bump" | jq -r '.tag') + VERSION=$(echo "$bump" | jq -r '.version') + MAJOR_BUMPS=$(echo "$bump" | jq -r '.major_bumps') + CRATE_PATH=$(echo "$bump" | jq -r '.path') + + if [ "$MAJOR_BUMPS" != "[]" ]; then + echo "Updating version for $NAME with major bumps: $MAJOR_BUMPS" + cargo release version -p "$NAME" --prev-tag-name "$PREV_TAG" --allow-branch "$BRANCH_NAME" -x major --no-confirm + + git commit -am "chore(release): update version for $NAME with major bumps" + + NEXT_VERSION=$(cargo metadata --format-version=1 --no-deps | jq -r --arg name "$NAME" '.packages[] | select(.name == $name) | .version') + NEXT_TAG="$NAME-v$NEXT_VERSION" + + echo "Updating tag $TAG to $NEXT_TAG and version $VERSION to $NEXT_VERSION for $NAME" + + jq --arg name "$NAME" \ + --arg nl "major" \ + --arg version "$NEXT_VERSION" \ + --arg tag "$NEXT_TAG" \ + 'map(if .name == $name then . + {level: $nl, version: $version, tag: $tag} else . end)' \ + /tmp/api-changes-with-major-bumps.json > /tmp/api-changes-with-major-bumps.tmp \ + && mv /tmp/api-changes-with-major-bumps.tmp /tmp/api-changes-with-major-bumps.json + fi + done + + # Output the results + echo "API changes with major bumps summary:" + jq . /tmp/api-changes-with-major-bumps.json + + - name: Generate CHANGELOGS + id: generate-changelogs + run: | + set -euo pipefail + BRANCH_NAME="${{ steps.proposal-branch.outputs.branch_name }}" + ORIGINAL_HEAD=$(cat /tmp/release_head_sha) + + echo "Generating CHANGELOGS" + + jq -c '.[]' /tmp/api-changes-with-major-bumps.json | while read -r bump; do + COMMITS=$(echo "$bump" | jq -r '.commits') + RANGE=$(echo "$bump" | jq -r '.range') + NAME=$(echo "$bump" | jq -r '.name') + TAG=$(echo "$bump" | jq -r '.prev_tag') + NEXT_TAG=$(echo "$bump" | jq -r '.tag') + NEXT_VERSION=$(echo "$bump" | jq -r '.version') + MAJOR_BUMPS=$(echo "$bump" | jq -r '.major_bumps') + + # FIXME: $COMMITS could be empty if there are no commits since last release + if [ "$COMMITS" = "[]" ]; then + echo "No commits since last release for $NAME, skipping CHANGELOG generation" + continue + fi + # Build a tight range from commits already found by commits-since-release.sh. # This will save some time analising unnecessary commits and prevent unrelated commits # go through git-cliff filtering process. @@ -444,43 +538,23 @@ jobs: "$CLIFF_CONTEXT_FILE" > "$CLIFF_FILTERED_FILE" git cliff --from-context "$CLIFF_FILTERED_FILE" -u -v --prepend "$CRATE_PATH/CHANGELOG.md" rm -f "$CLIFF_CONTEXT_FILE" "$CLIFF_HASHES_FILE" "$CLIFF_FILTERED_FILE" - git add "$CRATE_PATH/CHANGELOG.md" - # Commit the changes - cargo release commit --no-confirm -x - - # Add to results array - jq --arg name "$NAME" \ - --arg level "$LEVEL" \ - --arg tag "$NEXT_TAG" \ - --arg version "$NEXT_VERSION" \ - --arg initial_release "$INITIAL_RELEASE" \ - '. += [{"name": $name, "level": $level, "tag": $tag, "version": $version, "initial_release": $initial_release}]' \ - /tmp/api-changes.json > /tmp/api-changes.tmp && mv /tmp/api-changes.tmp /tmp/api-changes.json + git add "$CRATE_PATH/CHANGELOG.md" + git commit -m "chore(release): update CHANGELOG.md for $NAME" done - # Check if there are commits to push - if git diff --quiet "${{ steps.ephemeral-branch.outputs.ephemeral_branch }}"; then - echo "No changes to push. Cancelling the workflow." - exit 1 - fi - # Oldest → newest (chronological). Plain `git log` is newest-first; commit-headless should receive # parent→child order so replays/signing match git history. Space-separated SHAs for the action. COMMITS=$(git log --reverse "$ORIGINAL_HEAD".. --format='%H' | tr '\n' ' ' | xargs) echo "commits=$COMMITS" >> $GITHUB_OUTPUT - # Output the results - echo "API changes summary:" - jq . /tmp/api-changes.json - - name: Push commits uses: DataDog/commit-headless@action/v2.0.3 with: branch: ${{ steps.proposal-branch.outputs.branch_name }} head-sha: ${{ steps.commits-since-release.outputs.release_head_sha }} command: push - commits: "${{ steps.release-version-bumps.outputs.commits }}" + commits: "${{ steps.generate-changelogs.outputs.commits }}" - name: Upload release data uses: actions/upload-artifact@v4 @@ -489,6 +563,7 @@ jobs: path: | /tmp/commits-by-crate.json /tmp/api-changes.json + /tmp/api-changes-with-major-bumps.json retention-days: 1 - name: Cleanup on failure @@ -508,7 +583,7 @@ jobs: outputs: branch_name: ${{ steps.proposal-branch.outputs.branch_name }} ephemeral_branch: ${{ steps.ephemeral-branch.outputs.ephemeral_branch }} - + create-pr: needs: cargo-release runs-on: ubuntu-latest @@ -555,28 +630,34 @@ jobs: NON_DEFAULT="${NON_DEFAULT}"$'\n\n' fi - # Generate the PR body by merging commits and API changes + # PR body from api-changes-with-major-bumps.json (same crates as api-changes.json; tags/versions updated after libdd major bumps). # Note: read returns 1 when it reaches EOF, which is expected for heredocs read -r -d '' JQ_FILTER << 'EOF' || true - .[] | select((.commits | length) > 0) | - . as $crate | - ($api[0] | map(select(.name == $crate.name)) | first // { - level: "skipped because there were no changes to the public API" - }) as $api_info | - [ - "## \($crate.name)", - "", - (if $api_info.version then "**Next version:** `\($api_info.version)`" else null end), - "**Semver bump:** `\($api_info.level)`", - (if $api_info.tag then "**Tag:** `\($api_info.tag)`\n" else null end), - (if $api_info.initial_release == "true" then - "**Warning:** this is an initial release. Please verify that the version and commits included are correct.\n" - else null end), - (if ($crate.commits | length) > 0 then "### Commits\n\n" + ($crate.commits | map("- \(.subject)") | join("\n")) else null end) - ] | map(select(. != null and . != "")) | join("\n") + [ $api[0][] + | select((.commits | length) > 0) + | [ + "## \(.name)", + "", + (if .version then "**Next version:** `\(.version)`" else null end), + "**Semver bump:** `\(.level)`", + (if .tag then "**Tag:** `\(.tag)`\n" else null end), + (if (.major_bumps // [] | length) > 0 then + "### libdd dependency major bumps\n\n" + + ((.major_bumps // []) | map("- `\(.dependency)`: \(.previous_req) → \(.current_req)") | join("\n")) + + "\n" + else null end), + (if .initial_release == "true" then + "**Warning:** this is an initial release. Please verify that the version and commits included are correct.\n" + else null end), + (if (.commits | length) > 0 then "### Commits\n\n" + (.commits | map("- \(.subject)") | join("\n")) else null end) + ] + | map(select(. != null and . != "")) + | join("\n") + ] + | join("\n\n") EOF - COMMITS_AND_API_BODY=$(jq -r --slurpfile api /tmp/api-changes.json "$JQ_FILTER" /tmp/commits-by-crate.json) + COMMITS_AND_API_BODY=$(jq -nr --slurpfile api /tmp/api-changes-with-major-bumps.json "$JQ_FILTER") PR_BODY="# Release proposal for ${{ inputs.crate }} and its dependencies diff --git a/scripts/major-bumps-level.sh b/scripts/major-bumps-level.sh new file mode 100755 index 0000000000..5aff057c13 --- /dev/null +++ b/scripts/major-bumps-level.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# For each crate in an api-changes.json array, compare direct libdd-* dependency +# version requirements between the current tree and prev_tag. If any dependency's +# major version (first digit in the req string) increased, record details in `major_bumps`. +# +# Usage: major-bumps-level.sh API_CHANGES_JSON +# Writes enriched JSON (same shape + major_bumps) to stdout. + +set -euo pipefail + +usage() { + echo "Usage: $0 API_CHANGES_JSON" >&2 + exit 2 +} + +[[ ${1:-} ]] || usage +API_JSON=$1 +[[ -f "$API_JSON" ]] || { echo "Not a file: $API_JSON" >&2; exit 2; } + +level_rank() { + case "${1:-}" in + patch) echo 0 ;; + minor) echo 1 ;; + major) echo 2 ;; + *) echo -1 ;; + esac +} + +libdd_deps_for_crate() { + local manifest crate + manifest=$1 + crate=$2 + jq --arg crate "$crate" ' + .packages[] + | select(.name == $crate) + | [.dependencies[] + | select(.name | startswith("libdd-")) + | {key: .name, value: .req}] + | from_entries + ' <(cargo metadata --manifest-path "$manifest" --format-version=1 --no-deps) +} + +compare_libdd_bumps() { + local path_prev path_curr + path_prev=$1 + path_curr=$2 + jq -n --slurpfile prev "$path_prev" --slurpfile curr "$path_curr" ' + ($prev[0]) as $p + | ($curr[0]) as $c + | def first_num(s): + try ( + (s | tostring | match("[0-9]+") | .string | tonumber) + ) catch null + ; + [ + ($c | keys_unsorted[]) as $k + | select($p[$k] != null) + | first_num($p[$k]) as $pm + | first_num($c[$k]) as $cm + | select($pm != null and $cm != null and $cm > $pm) + | { + dependency: $k, + previous_req: $p[$k], + current_req: $c[$k] + } + ] + ' +} + +WORKTREES=() +cleanup() { + local d + for d in "${WORKTREES[@]:-}"; do + git worktree remove --force "$d" >/dev/null 2>&1 || true + done +} +trap cleanup EXIT + +n=$(jq length "$API_JSON") +OUT=$(mktemp) +echo '[]' >"$OUT" +FAIL=0 + +for ((i = 0; i < n; i++)); do + row=$(jq -c ".[$i]" "$API_JSON") + name=$(echo "$row" | jq -r .name) + prev_tag=$(echo "$row" | jq -r .prev_tag) + initial=$(echo "$row" | jq -r .initial_release) + lev=$(echo "$row" | jq -r .level) + + bumps_json='[]' + new_lev=$lev + + if [[ "$initial" == "true" ]] || [[ -z "$prev_tag" ]] || [[ "$prev_tag" == "null" ]]; then + : + else + cur_mf="${name}/Cargo.toml" + if [[ ! -f "$cur_mf" ]]; then + echo "ERROR: missing manifest $cur_mf" >&2 + exit 2 + fi + wt=$(mktemp -d) + WORKTREES+=("$wt") + git worktree add --detach "$wt" "$prev_tag" >/dev/null + prev_mf="${wt}/${name}/Cargo.toml" + if [[ ! -f "$prev_mf" ]]; then + echo "ERROR: missing manifest $prev_mf at $prev_tag" >&2 + exit 2 + fi + prev_json=$(mktemp) + curr_json=$(mktemp) + libdd_deps_for_crate "$prev_mf" "$name" >"$prev_json" + libdd_deps_for_crate "$cur_mf" "$name" >"$curr_json" + bumps_json=$(compare_libdd_bumps "$prev_json" "$curr_json") + rm -f "$prev_json" "$curr_json" + if [[ $(echo "$bumps_json" | jq 'length') -gt 0 ]]; then + echo "libdd-* direct dependency major bump for crate ${name} (prev_tag=${prev_tag}):" >&2 + echo "$bumps_json" | jq -r '.[] | " - \(.dependency): \(.previous_req) -> \(.current_req)"' >&2 + fi + fi + + enriched=$(echo "$row" | jq -c --argjson bumps "$bumps_json" --arg nl "$new_lev" \ + '. + {level: $nl, major_bumps: $bumps}') + jq --argjson enriched "$enriched" '. + [$enriched]' "$OUT" >"${OUT}.new" + mv "${OUT}.new" "$OUT" +done + +jq . "$OUT" +rm -f "$OUT" From 4664b021fa3f697ed4e4f233c34bbbb2b5265a87 Mon Sep 17 00:00:00 2001 From: iunanua Date: Mon, 30 Mar 2026 10:51:33 +0200 Subject: [PATCH 02/10] Get major-bumps-level script from workflow SHA --- .../workflows/release-proposal-dispatch.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index f89d6622d9..947aca780c 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -442,8 +442,23 @@ jobs: set -euo pipefail BRANCH_NAME="${{ steps.proposal-branch.outputs.branch_name }}" - ./scripts/major-bumps-level.sh /tmp/api-changes.json > /tmp/api-changes-with-major-bumps-pre-commit.json - + # Run the audit in a throwaway worktree so extra worktrees / cargo metadata do not touch the job checkout. + MAJOR_BUMPS_WT=$(mktemp -d "${RUNNER_TEMP:-/tmp}/major-bumps-wt.XXXXXX") + WF_SHA="${{ github.sha }}" + git worktree add --detach "$MAJOR_BUMPS_WT" "$WF_SHA" + set +e + ( cd "$MAJOR_BUMPS_WT" && ./scripts/major-bumps-level.sh /tmp/api-changes.json ) \ + > /tmp/api-changes-with-major-bumps-pre-commit.json + MB_RC=$? + git worktree remove --force "$MAJOR_BUMPS_WT" || true + set -e + if [[ "$MB_RC" -ne 0 ]]; then + echo "Major bumps level script failed with code $MB_RC" + echo "Major bumps level script output:" + cat /tmp/api-changes-with-major-bumps-pre-commit.json + exit "$MB_RC" + fi + # Full same crate list as api-changes.json; rows updated in place when a major bump is applied. cp /tmp/api-changes-with-major-bumps-pre-commit.json /tmp/api-changes-with-major-bumps.json From 7892b4c0dd1d1c524ccf3df340f4e4c339b55c8c Mon Sep 17 00:00:00 2001 From: iunanua Date: Mon, 30 Mar 2026 11:13:13 +0200 Subject: [PATCH 03/10] Add CRATE_PATH --- .github/workflows/release-proposal-dispatch.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index 947aca780c..3a6a39f530 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -513,6 +513,7 @@ jobs: NEXT_TAG=$(echo "$bump" | jq -r '.tag') NEXT_VERSION=$(echo "$bump" | jq -r '.version') MAJOR_BUMPS=$(echo "$bump" | jq -r '.major_bumps') + CRATE_PATH=$(echo "$bump" | jq -r '.path') # FIXME: $COMMITS could be empty if there are no commits since last release if [ "$COMMITS" = "[]" ]; then From e9c559c200b686bdc99b7229ef5a099cfad6076d Mon Sep 17 00:00:00 2001 From: iunanua Date: Mon, 30 Mar 2026 11:52:54 +0200 Subject: [PATCH 04/10] messages and cleanup --- .github/workflows/release-proposal-dispatch.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index 3a6a39f530..314d53575f 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -560,7 +560,7 @@ jobs: done # Oldest → newest (chronological). Plain `git log` is newest-first; commit-headless should receive - # parent→child order so replays/signing match git history. Space-separated SHAs for the action. + # parent → child order so replays/signing match git history. Space-separated SHAs for the action. COMMITS=$(git log --reverse "$ORIGINAL_HEAD".. --format='%H' | tr '\n' ' ' | xargs) echo "commits=$COMMITS" >> $GITHUB_OUTPUT @@ -637,10 +637,10 @@ jobs: NON_DEFAULT="" if [ -n "$MAIN_START_REF" ]; then - NON_DEFAULT="${NON_DEFAULT}"$'\n### Cut from non-default ref\n\n'"This proposal was generated from \`$MAIN_START_REF\` instead of the default latest \`origin/$MAIN_BRANCH\`."$'\n' + NON_DEFAULT="${NON_DEFAULT}"$'\n### :exclamation: Cut from non-default ref\n\n'"This proposal was generated from \`$MAIN_START_REF\` instead of the default latest \`origin/$MAIN_BRANCH\`."$'\n' fi if [ "$BYPASS_STANDARD_CHECKS" = "true" ]; then - NON_DEFAULT="${NON_DEFAULT}"$'\n### Non-default workflow options\n\n'"**bypass_standard_checks** was enabled: the ongoing-proposal branch guard was skipped; branches use proposal prefix \`$PROPOSAL_BRANCH_PREFIX\` and release prefix \`$RELEASE_BRANCH_PREFIX\`. Crates whose resolved git tag is not the latest SemVer tag for that crate are still included (normally skipped)."$'\n' + NON_DEFAULT="${NON_DEFAULT}"$'\n### :test_tube: Non-default workflow options\n\n'"**bypass_standard_checks** was enabled: the ongoing-proposal branch guard was skipped; branches use proposal prefix \`$PROPOSAL_BRANCH_PREFIX\` and release prefix \`$RELEASE_BRANCH_PREFIX\`. Crates whose resolved git tag is not the latest SemVer tag for that crate are still included (normally skipped)."$'\n' fi if [ -n "$NON_DEFAULT" ]; then NON_DEFAULT="${NON_DEFAULT}"$'\n\n' @@ -658,7 +658,7 @@ jobs: "**Semver bump:** `\(.level)`", (if .tag then "**Tag:** `\(.tag)`\n" else null end), (if (.major_bumps // [] | length) > 0 then - "### libdd dependency major bumps\n\n" + "### :warning: major bump forced due to:\n\n" + ((.major_bumps // []) | map("- `\(.dependency)`: \(.previous_req) → \(.current_req)") | join("\n")) + "\n" else null end), From 07799383d1a92bf471ec356d7346a3b70462dcf4 Mon Sep 17 00:00:00 2001 From: iunanua Date: Mon, 30 Mar 2026 12:23:46 +0200 Subject: [PATCH 05/10] Add skip-pr-title-semver-check label --- .github/workflows/release-proposal-dispatch.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index 314d53575f..a5e16b02ee 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -692,6 +692,7 @@ jobs: --label "release-proposal" \ --label "skip-metadata-check" \ --label "skip-changelog-check" \ + --label "skip-pr-title-semver-check" \ --base "${{ needs.cargo-release.outputs.ephemeral_branch }}" \ --draft From 1222b780fadc57c9685d6b150806da9207fd0b70 Mon Sep 17 00:00:00 2001 From: iunanua Date: Wed, 8 Apr 2026 11:41:57 +0200 Subject: [PATCH 06/10] remove unused vars --- .github/workflows/release-proposal-dispatch.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index a5e16b02ee..18cfd98e80 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -469,7 +469,6 @@ jobs: TAG=$(echo "$bump" | jq -r '.tag') VERSION=$(echo "$bump" | jq -r '.version') MAJOR_BUMPS=$(echo "$bump" | jq -r '.major_bumps') - CRATE_PATH=$(echo "$bump" | jq -r '.path') if [ "$MAJOR_BUMPS" != "[]" ]; then echo "Updating version for $NAME with major bumps: $MAJOR_BUMPS" @@ -500,7 +499,6 @@ jobs: id: generate-changelogs run: | set -euo pipefail - BRANCH_NAME="${{ steps.proposal-branch.outputs.branch_name }}" ORIGINAL_HEAD=$(cat /tmp/release_head_sha) echo "Generating CHANGELOGS" @@ -511,8 +509,6 @@ jobs: NAME=$(echo "$bump" | jq -r '.name') TAG=$(echo "$bump" | jq -r '.prev_tag') NEXT_TAG=$(echo "$bump" | jq -r '.tag') - NEXT_VERSION=$(echo "$bump" | jq -r '.version') - MAJOR_BUMPS=$(echo "$bump" | jq -r '.major_bumps') CRATE_PATH=$(echo "$bump" | jq -r '.path') # FIXME: $COMMITS could be empty if there are no commits since last release From 045068e085f0345d2ad89aa6b6f51ac8fa76657c Mon Sep 17 00:00:00 2001 From: iunanua Date: Wed, 8 Apr 2026 11:46:33 +0200 Subject: [PATCH 07/10] fetch WF_SHA --- .github/workflows/release-proposal-dispatch.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index 18cfd98e80..edfd1cd528 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -445,6 +445,10 @@ jobs: # Run the audit in a throwaway worktree so extra worktrees / cargo metadata do not touch the job checkout. MAJOR_BUMPS_WT=$(mktemp -d "${RUNNER_TEMP:-/tmp}/major-bumps-wt.XXXXXX") WF_SHA="${{ github.sha }}" + + echo "Fetching workflow SHA: $WF_SHA" + git fetch origin "$WF_SHA" + git worktree add --detach "$MAJOR_BUMPS_WT" "$WF_SHA" set +e ( cd "$MAJOR_BUMPS_WT" && ./scripts/major-bumps-level.sh /tmp/api-changes.json ) \ From 248c6005b89ca734a846b3249c136a24873bac41 Mon Sep 17 00:00:00 2001 From: iunanua Date: Wed, 8 Apr 2026 11:59:02 +0200 Subject: [PATCH 08/10] license header and CODEOWNERS --- .github/CODEOWNERS | 1 + scripts/major-bumps-level.sh | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9f60bbfe5d..d7dafb4f0b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -63,6 +63,7 @@ repository.datadog.yml @DataDog/apm-common-components-core rustfmt.toml @DataDog/libdatadog-core scripts/check_cargo_metadata.sh @DataDog/libdatadog-core scripts/commits-since-release.sh @DataDog/libdatadog-core +scripts/major-bumps-level.sh @DataDog/libdatadog-core scripts/publication-order.sh @DataDog/libdatadog-core scripts/reformat_copyright.sh @DataDog/libdatadog-core scripts/semver-level.sh @DataDog/libdatadog-core diff --git a/scripts/major-bumps-level.sh b/scripts/major-bumps-level.sh index 5aff057c13..aedf15d946 100755 --- a/scripts/major-bumps-level.sh +++ b/scripts/major-bumps-level.sh @@ -1,4 +1,8 @@ #!/usr/bin/env bash + +# Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/ +# SPDX-License-Identifier: Apache-2.0 + # For each crate in an api-changes.json array, compare direct libdd-* dependency # version requirements between the current tree and prev_tag. If any dependency's # major version (first digit in the req string) increased, record details in `major_bumps`. From 6b4c41b83ddf1ab3d3d3c3454d2423e50b8a6f2a Mon Sep 17 00:00:00 2001 From: iunanua Date: Wed, 8 Apr 2026 14:51:18 +0200 Subject: [PATCH 09/10] Remove fetch before worktree --- .github/workflows/release-proposal-dispatch.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release-proposal-dispatch.yml b/.github/workflows/release-proposal-dispatch.yml index edfd1cd528..89180e50bd 100644 --- a/.github/workflows/release-proposal-dispatch.yml +++ b/.github/workflows/release-proposal-dispatch.yml @@ -445,9 +445,6 @@ jobs: # Run the audit in a throwaway worktree so extra worktrees / cargo metadata do not touch the job checkout. MAJOR_BUMPS_WT=$(mktemp -d "${RUNNER_TEMP:-/tmp}/major-bumps-wt.XXXXXX") WF_SHA="${{ github.sha }}" - - echo "Fetching workflow SHA: $WF_SHA" - git fetch origin "$WF_SHA" git worktree add --detach "$MAJOR_BUMPS_WT" "$WF_SHA" set +e From fdcd0f54ed1320aea957021edf104bc385d01cc3 Mon Sep 17 00:00:00 2001 From: iunanua Date: Wed, 8 Apr 2026 15:21:57 +0200 Subject: [PATCH 10/10] Fix major-bumps-level multiple dep rows --- scripts/major-bumps-level.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/major-bumps-level.sh b/scripts/major-bumps-level.sh index aedf15d946..6bc333a0f4 100755 --- a/scripts/major-bumps-level.sh +++ b/scripts/major-bumps-level.sh @@ -34,12 +34,19 @@ libdd_deps_for_crate() { local manifest crate manifest=$1 crate=$2 + # Cargo metadata `.req` is the version *requirement* from the manifest, not the resolved version. + # Path / workspace deps often show req "*". Exclude dev-dependencies and build-dependencies + # (`kind` "dev" / "build"); only normal [dependencies] use kind null. When duplicates remain + # (e.g. target-specific), prefer non-"*" req so "*" from path-only edges does not win. jq --arg crate "$crate" ' .packages[] | select(.name == $crate) | [.dependencies[] | select(.name | startswith("libdd-")) + | select(.kind != "dev" and .kind != "build") | {key: .name, value: .req}] + | group_by(.key) + | map(sort_by(if .value == "*" then 1 else 0 end) | .[0]) | from_entries ' <(cargo metadata --manifest-path "$manifest" --format-version=1 --no-deps) }