11#! /usr/bin/env bash
22# Script to generate workspace-grouped changelog for rust agents
3+ #
4+ # Usage:
5+ # generate-workspace-changelog.sh [COMMIT_RANGE] [WORKSPACE_FILTER] [FLAGS]
6+ #
7+ # Arguments:
8+ # COMMIT_RANGE - Git commit range (e.g., "v1.4.0..HEAD"). Defaults to latest tag..HEAD
9+ # WORKSPACE_FILTER - Optional comma-separated list of workspaces to include (e.g., "agents/relayer,agents/validator")
10+ # If omitted, all workspaces are included
11+ #
12+ # Flags:
13+ # --no-header - Omit the "## What's Changed" header (useful for composing changelogs)
14+ # --write-to-workspace VERSION - Update CHANGELOG.md files in each workspace directory
15+ #
16+ # Examples:
17+ # ./generate-workspace-changelog.sh # All workspaces, latest tag..HEAD
18+ # ./generate-workspace-changelog.sh "v1.4.0..v1.5.0" # All workspaces, specific range
19+ # ./generate-workspace-changelog.sh "v1.4.0..v1.5.0" "agents/relayer" # Single workspace
20+ # ./generate-workspace-changelog.sh "v1.4.0..v1.5.0" "agents/relayer,agents/validator" # Multiple workspaces
21+ # ./generate-workspace-changelog.sh "v1.4.0..v1.5.0" "agents/relayer" --no-header # No header
22+ # ./generate-workspace-changelog.sh "v1.4.0..v1.5.0" "" --write-to-workspace "1.5.0" # Update workspace CHANGELOGs
23+ #
324set -euo pipefail
425
526# Determine script directory and repo structure
627SCRIPT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
728RUST_MAIN_DIR=" $SCRIPT_DIR /../main"
829
30+ # Parse arguments
31+ COMMIT_RANGE=" "
32+ WORKSPACE_FILTER=" "
33+ SHOW_HEADER=true
34+ UPDATE_CHANGELOGS=false
35+ VERSION=" "
36+
37+ # Parse all arguments
38+ i=1
39+ while [ $i -le $# ]; do
40+ arg=" ${! i} "
41+
42+ if [ " $arg " = " --no-header" ]; then
43+ SHOW_HEADER=false
44+ elif [ " $arg " = " --write-to-workspace" ]; then
45+ UPDATE_CHANGELOGS=true
46+ # Get next argument as version
47+ i=$(( i + 1 ))
48+ if [ $i -le $# ]; then
49+ VERSION=" ${! i} "
50+ else
51+ echo " Error: --write-to-workspace requires a VERSION argument" >&2
52+ exit 1
53+ fi
54+ elif [ -z " $COMMIT_RANGE " ]; then
55+ COMMIT_RANGE=" $arg "
56+ elif [ -z " $WORKSPACE_FILTER " ]; then
57+ WORKSPACE_FILTER=" $arg "
58+ fi
59+
60+ i=$(( i + 1 ))
61+ done
62+
963# Get the commit range
10- if [ $# -eq 0 ]; then
11- # No arguments - use unreleased commits
64+ if [ -z " $COMMIT_RANGE " ]; then
65+ # No commit range specified - use unreleased commits
1266 LATEST_TAG=$( git tag -l " agents-v*" --sort=-version:refname | grep -E " ^agents-v[0-9]+\.[0-9]+\.[0-9]+$" | head -1 || echo " " )
1367 if [ -z " $LATEST_TAG " ]; then
1468 COMMIT_RANGE=" HEAD"
1569 else
1670 COMMIT_RANGE=" ${LATEST_TAG} ..HEAD"
1771 fi
72+ fi
73+
74+ # Parse workspace filter into array
75+ if [ -n " $WORKSPACE_FILTER " ]; then
76+ IFS=' ,' read -ra FILTER_ARRAY <<< " $WORKSPACE_FILTER"
1877else
19- COMMIT_RANGE= " $1 "
78+ FILTER_ARRAY=()
2079fi
2180
2281# Temporary directory for categorization
@@ -26,6 +85,25 @@ trap "rm -rf $TEMP_DIR" EXIT
2685# Extract workspace members from rust/main/Cargo.toml
2786WORKSPACE_MEMBERS=$( grep -A 100 ' ^\[workspace\]' " $RUST_MAIN_DIR /Cargo.toml" | sed -n ' /^members = \[/,/^\]/p' | grep ' "' | sed ' s/[", ]//g' )
2887
88+ # Helper function to check if a workspace should be included
89+ should_include_workspace () {
90+ local workspace=" $1 "
91+
92+ # If no filter specified, include all
93+ if [ ${# FILTER_ARRAY[@]} -eq 0 ]; then
94+ return 0
95+ fi
96+
97+ # Check if workspace is in filter list
98+ for filter in " ${FILTER_ARRAY[@]} " ; do
99+ if [ " $workspace " = " $filter " ]; then
100+ return 0
101+ fi
102+ done
103+
104+ return 1
105+ }
106+
29107# Get all commits in the range (filter to rust/main directory)
30108git log --no-merges --format=" %H" $COMMIT_RANGE -- rust/main | while read -r commit_hash; do
31109 # Get commit message
@@ -50,9 +128,9 @@ git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r com
50128 done
51129 done
52130
53- # Default to "Other " if no workspace found
131+ # Default to "other " if no workspace found
54132 if [ -z " $workspace " ]; then
55- workspace=" Other "
133+ workspace=" other "
56134 fi
57135
58136 # Sanitize workspace name for file system (replace / with __)
@@ -62,20 +140,113 @@ git log --no-merges --format="%H" $COMMIT_RANGE -- rust/main | while read -r com
62140 echo " $workspace |$commit_msg |$short_hash " >> " $TEMP_DIR /$workspace_file "
63141done
64142
65- # Process workspace members in the order they appear in Cargo.toml, then "Other"
66- for workspace in $WORKSPACE_MEMBERS " Other" ; do
67- workspace_file=$( echo " $workspace " | tr ' /' ' _' )
143+ # Function to generate changelog for a specific workspace
144+ generate_workspace_changelog () {
145+ local workspace=" $1 "
146+ local include_header=" ${2:- true} " # Default to including header
147+ local workspace_file=$( echo " $workspace " | tr ' /' ' _' )
68148
69149 if [ -f " $TEMP_DIR /$workspace_file " ]; then
70- echo " ### $workspace "
71- echo " "
72-
73- # Sort and deduplicate commits (extract workspace name, msg, hash)
150+ if [ " $include_header " = " true " ] ; then
151+ echo " ### $workspace "
152+ echo " "
153+ fi
74154 sort -u " $TEMP_DIR /$workspace_file " | while IFS=' |' read -r ws msg hash ; do
75155 echo " * $msg (#$hash )"
76156 done
77- echo " "
157+ fi
158+ }
159+
160+ # If updating workspace changelogs, update files and exit
161+ if [ " $UPDATE_CHANGELOGS " = true ]; then
162+ if [ -z " $VERSION " ]; then
163+ echo " Error: VERSION is required for --write-to-workspace" >&2
164+ exit 1
165+ fi
166+
167+ echo " Updating workspace CHANGELOG.md files for version $VERSION ..."
168+ UPDATED_COUNT=0
169+
170+ for workspace in $WORKSPACE_MEMBERS ; do
171+ # Skip if workspace is filtered out
172+ if ! should_include_workspace " $workspace " ; then
173+ continue
174+ fi
175+
176+ workspace_file=$( echo " $workspace " | tr ' /' ' _' )
177+
178+ # Skip if no changes for this workspace
179+ if [ ! -f " $TEMP_DIR /$workspace_file " ]; then
180+ continue
181+ fi
182+
183+ # Generate changelog content for this workspace (no header)
184+ WORKSPACE_CHANGELOG=$( generate_workspace_changelog " $workspace " false)
185+
186+ if [ -z " $WORKSPACE_CHANGELOG " ]; then
187+ continue
188+ fi
189+
190+ WORKSPACE_CHANGELOG_FILE=" $RUST_MAIN_DIR /$workspace /CHANGELOG.md"
191+ WORKSPACE_DIR=$( dirname " $WORKSPACE_CHANGELOG_FILE " )
192+
193+ # Ensure directory exists
194+ mkdir -p " $WORKSPACE_DIR "
195+
196+ # Read existing changelog if it exists
197+ if [ -f " $WORKSPACE_CHANGELOG_FILE " ]; then
198+ CURRENT_WORKSPACE_CHANGELOG=$( cat " $WORKSPACE_CHANGELOG_FILE " )
199+ else
200+ CURRENT_WORKSPACE_CHANGELOG=" "
201+ fi
202+
203+ # Prepend new version to workspace changelog
204+ {
205+ echo " ## [$VERSION ] - $( date +%Y-%m-%d) "
206+ echo " "
207+ echo " $WORKSPACE_CHANGELOG "
208+ if [ -n " $CURRENT_WORKSPACE_CHANGELOG " ]; then
209+ echo " "
210+ echo " $CURRENT_WORKSPACE_CHANGELOG "
211+ fi
212+ } > " $WORKSPACE_CHANGELOG_FILE "
213+
214+ echo " Updated $workspace /CHANGELOG.md"
215+ UPDATED_COUNT=$(( UPDATED_COUNT + 1 ))
216+ done
217+
218+ echo " Updated $UPDATED_COUNT workspace changelog(s)"
219+ exit 0
220+ fi
221+
222+ # Generate output (for display/PR body)
223+ if [ " $SHOW_HEADER " = true ]; then
224+ echo " ## What's Changed"
225+ echo " "
226+ fi
227+
228+ # Process workspace members in the order they appear in Cargo.toml, then "other"
229+ FIRST_WORKSPACE=true
230+ for workspace in $WORKSPACE_MEMBERS " other" ; do
231+ # Skip if workspace is filtered out
232+ if ! should_include_workspace " $workspace " ; then
233+ continue
234+ fi
235+
236+ workspace_file=$( echo " $workspace " | tr ' /' ' _' )
237+
238+ if [ -f " $TEMP_DIR /$workspace_file " ]; then
239+ # Add separator between workspaces (except before first one)
240+ if [ " $FIRST_WORKSPACE " = false ]; then
241+ echo " "
242+ fi
243+ FIRST_WORKSPACE=false
244+
245+ generate_workspace_changelog " $workspace "
78246 fi
79247done
80248
81- echo " <!-- generated by workspace changelog script -->"
249+ if [ " $SHOW_HEADER " = true ]; then
250+ echo " "
251+ echo " <!-- generated by workspace changelog script -->"
252+ fi
0 commit comments