Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,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
Expand Down
187 changes: 141 additions & 46 deletions .github/workflows/release-proposal-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,117 @@ 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 }}"

# 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

# 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')

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
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')
CRATE_PATH=$(echo "$bump" | jq -r '.path')

# 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.
Expand Down Expand Up @@ -444,43 +551,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
# parentchild order so replays/signing match git history. Space-separated SHAs for the action.
# parentchild 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
Expand All @@ -489,6 +576,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
Expand All @@ -508,7 +596,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
Expand Down Expand Up @@ -546,37 +634,43 @@ 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'
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
"### :warning: major bump forced due to:\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

Expand All @@ -595,6 +689,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

Expand Down
Loading
Loading