From 89bf03eb0eaf888d04890c320aeb4679790928b8 Mon Sep 17 00:00:00 2001 From: pbio <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:27:48 +0000 Subject: [PATCH 1/3] fix(ci): include commits in all relevant workspace changelogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, when a commit touched files in multiple workspaces (e.g., both agents/relayer and agents/validator), it would only appear in whichever workspace was checked first due to `break 2` exiting both loops. This fix: 1. Tracks ALL matched workspaces per commit using an associative array 2. Adds each commit to every workspace changelog it touches 3. Supports explicit scope override via conventional commit format (e.g., `feat(relayer):` will only add to relayer changelog) Example: A commit touching both `agents/relayer/` and `agents/validator/` will now correctly appear in both changelogs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ci/generate-workspace-changelog.sh | 77 ++++++++++++++----- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/rust/scripts/ci/generate-workspace-changelog.sh b/rust/scripts/ci/generate-workspace-changelog.sh index bb7c3b9bd10..b9b14b97dc8 100755 --- a/rust/scripts/ci/generate-workspace-changelog.sh +++ b/rust/scripts/ci/generate-workspace-changelog.sh @@ -104,39 +104,78 @@ should_include_workspace() { return 1 } +# Map from conventional commit scope to workspace path +declare -A SCOPE_TO_WORKSPACE=( + ["relayer"]="agents/relayer" + ["validator"]="agents/validator" + ["scraper"]="agents/scraper" + ["lander"]="lander" + ["cosmos"]="chains/hyperlane-cosmos" + ["ethereum"]="chains/hyperlane-ethereum" + ["sealevel"]="chains/hyperlane-sealevel" + ["fuel"]="chains/hyperlane-fuel" + ["starknet"]="chains/hyperlane-starknet" + ["aleo"]="chains/hyperlane-aleo" +) + +# Declare associative array for workspace matching (outside loop to avoid redeclaration) +declare -A matched_workspaces + # Get all commits in the range (filter to rust/main directory) git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r commit_hash; do # Get commit message commit_msg=$(git log -1 --format="%s" "$commit_hash") + # Check if commit message has an explicit scope like feat(relayer): or fix(validator): + # This takes priority over file-based detection + explicit_scope="" + if [[ "$commit_msg" =~ ^[a-z]+\(([a-z]+)\): ]]; then + scope="${BASH_REMATCH[1]}" + if [ -n "${SCOPE_TO_WORKSPACE[$scope]:-}" ]; then + explicit_scope="${SCOPE_TO_WORKSPACE[$scope]}" + fi + fi + # Get files changed in this commit (within rust/main) files=$(git diff-tree --no-commit-id --name-only -r "$commit_hash" -- rust/main) # Categorize based on workspace membership - workspace="" - for file in $files; do - # Strip rust/main/ prefix if present - file=$(echo "$file" | sed 's|^rust/main/||') - - # Check which workspace this file belongs to - for member in $WORKSPACE_MEMBERS; do - if [[ "$file" =~ ^"$member"(/|$) ]]; then - workspace="$member" - break 2 # Break both loops - fi + # A commit can belong to multiple workspaces if it touches files in each + # Clear the array from any previous iteration (declare -A doesn't reset existing arrays) + matched_workspaces=() + + if [ -n "$explicit_scope" ]; then + # If explicit scope in commit message, use only that workspace + matched_workspaces["$explicit_scope"]=1 + else + # Otherwise, detect workspaces from changed files + for file in $files; do + # Strip rust/main/ prefix if present + file=$(echo "$file" | sed 's|^rust/main/||') + + # Check which workspace this file belongs to + for member in $WORKSPACE_MEMBERS; do + if [[ "$file" =~ ^"$member"(/|$) ]]; then + matched_workspaces["$member"]=1 + break # Break inner loop only - file matched, check next file + fi + done done - done + fi - # Default to "other" if no workspace found - if [ -z "$workspace" ]; then - workspace="other" + # If no workspaces matched, categorize as "other" + if [ ${#matched_workspaces[@]} -eq 0 ]; then + matched_workspaces["other"]=1 fi - # Sanitize workspace name for file system (replace / with __) - workspace_file=$(echo "$workspace" | tr '/' '_') + # Store commit in ALL matched workspace category files + for workspace in "${!matched_workspaces[@]}"; do + # Sanitize workspace name for file system (replace / with __) + workspace_file=$(echo "$workspace" | tr '/' '_') - # Store commit in workspace category file (just message, PR# already in message) - echo "$commit_msg" >> "$TEMP_DIR/$workspace_file" + # Store commit in workspace category file (just message, PR# already in message) + echo "$commit_msg" >> "$TEMP_DIR/$workspace_file" + done done # Function to generate changelog for a specific workspace From 8fb6f677e7e71fe3fdba5c8072b0daf70d2255fa Mon Sep 17 00:00:00 2001 From: pbio <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:42:21 +0000 Subject: [PATCH 2/3] nit: fix comment --- rust/scripts/ci/generate-workspace-changelog.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/scripts/ci/generate-workspace-changelog.sh b/rust/scripts/ci/generate-workspace-changelog.sh index b9b14b97dc8..59b26e8ef7b 100755 --- a/rust/scripts/ci/generate-workspace-changelog.sh +++ b/rust/scripts/ci/generate-workspace-changelog.sh @@ -118,7 +118,7 @@ declare -A SCOPE_TO_WORKSPACE=( ["aleo"]="chains/hyperlane-aleo" ) -# Declare associative array for workspace matching (outside loop to avoid redeclaration) +# Note: matched_workspaces is reset each iteration; declared here for clarity declare -A matched_workspaces # Get all commits in the range (filter to rust/main directory) From 9ec884cabfc0252365117d8a87e2734322933376 Mon Sep 17 00:00:00 2001 From: pbio <10051819+paulbalaji@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:07:51 +0000 Subject: [PATCH 3/3] CR: coderabbit --- rust/scripts/ci/generate-workspace-changelog.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/rust/scripts/ci/generate-workspace-changelog.sh b/rust/scripts/ci/generate-workspace-changelog.sh index 59b26e8ef7b..affc2e8e00f 100755 --- a/rust/scripts/ci/generate-workspace-changelog.sh +++ b/rust/scripts/ci/generate-workspace-changelog.sh @@ -118,11 +118,12 @@ declare -A SCOPE_TO_WORKSPACE=( ["aleo"]="chains/hyperlane-aleo" ) -# Note: matched_workspaces is reset each iteration; declared here for clarity -declare -A matched_workspaces - # Get all commits in the range (filter to rust/main directory) -git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r commit_hash; do +# Note: We use process substitution (< <(...)) instead of a pipe to avoid running +# the while loop in a subshell, which would break associative array behavior. +while read -r commit_hash; do + # Associative array to track which workspaces this commit belongs to + declare -A matched_workspaces=() # Get commit message commit_msg=$(git log -1 --format="%s" "$commit_hash") @@ -141,8 +142,6 @@ git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r com # Categorize based on workspace membership # A commit can belong to multiple workspaces if it touches files in each - # Clear the array from any previous iteration (declare -A doesn't reset existing arrays) - matched_workspaces=() if [ -n "$explicit_scope" ]; then # If explicit scope in commit message, use only that workspace @@ -176,7 +175,7 @@ git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r com # Store commit in workspace category file (just message, PR# already in message) echo "$commit_msg" >> "$TEMP_DIR/$workspace_file" done -done +done < <(git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main) # Function to generate changelog for a specific workspace generate_workspace_changelog() {