Skip to content

Commit fa1113f

Browse files
authored
Whats new generation (#10775)
* Whats new agent! * docs: enhance GitHub issue and PR context in commit analysis instructions * docs: enhance commit analysis process with prioritization and verification steps * docs: improve formatting and clarity in release notes generation instructions * docs: update analysis scripts to exclude cherry-picked commits in log outputs * Add comprehensive documentation for Aspire Release Notes generation - Introduced modular documentation structure including README, commit analysis, API documentation, writing guidelines, validation checklist, and overview. - Detailed the process for analyzing commits and extracting features for release notes. - Provided guidelines for writing accurate API documentation with examples and validation processes. - Established a clear validation checklist to ensure accuracy and completeness of release notes. - Included structured templates and writing style guidelines to maintain consistency across documentation. * docs: update release notes documentation structure and content for clarity * docs: enhance commit analysis documentation with detailed examples and user impact insights * docs: update example command in data collection guide to reflect correct branch version * docs: remove outdated component coverage section from API documentation * docs: restructure API documentation for clarity and accuracy in usage examples * docs: add release notes for .NET Aspire 9.5 with new features and improvements * docs: update markdownlint configuration to ignore 'tools/' directory
1 parent bc97b33 commit fa1113f

18 files changed

+5636
-1
lines changed

.github/workflows/markdownlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ jobs:
2424
run: |
2525
echo "::add-matcher::.github/workflows/markdownlint-problem-matcher.json"
2626
27-
markdownlint --ignore '.dotnet/' --ignore '**/AnalyzerReleases.*.md' '**/*.md'
27+
markdownlint --ignore '.dotnet/' --ignore 'tools/' --ignore '**/AnalyzerReleases.*.md' '**/*.md'

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,7 @@ playground/**/publish/
154154

155155
#Aspire CLI
156156
.aspire/
157+
158+
# Release notes automation output
159+
tools/ReleaseNotes/analysis-output/
160+
tools/ReleaseNotes/release-notes-*.md

tools/ReleaseNotes/APIDiff.proj

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<Project Sdk="Microsoft.Build.Traversal/3.0.23">
2+
3+
<PropertyGroup>
4+
<Configuration>Release</Configuration>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<NoIncremental>true</NoIncremental>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<!-- Projects to exclude from API generation -->
11+
<ExcludedProjects Include="Aspire.Cli" />
12+
<ExcludedProjects Include="Aspire.ProjectTemplates" />
13+
<ExcludedProjects Include="Aspire.Hosting.AppHost" />
14+
<ExcludedProjects Include="Aspire.Hosting.Testing" />
15+
<ExcludedProjects Include="Aspire.Hosting.Analyzers" />
16+
<ExcludedProjects Include="Aspire.RuntimeIdentifier.Tool" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<!-- Dynamically discover all .csproj files under src, excluding test projects and excluded projects -->
21+
<DiscoveredProjects Include="../../src/**/*.csproj" Exclude="../../src/**/*.Tests.csproj;@(ExcludedProjects->'../../src/**/%(Identity)/*.csproj');../../src/Aspire.AppHost.Sdk/Aspire.RuntimeIdentifier.Tool/*.csproj;../../src/Aspire.ProjectTemplates/**/*.csproj" />
22+
</ItemGroup>
23+
24+
<!-- Custom target to build and generate API files -->
25+
<Target Name="BuildAndGenerateAPI">
26+
<Message Text="Building all projects and generating API files..." Importance="high" />
27+
<Message Text="Found @(DiscoveredProjects -> Count()) projects:" Importance="high" />
28+
<Message Text=" %(DiscoveredProjects.Identity)" Importance="high" />
29+
30+
<!-- Build all projects and generate API files in parallel -->
31+
<MSBuild Projects="@(DiscoveredProjects)"
32+
BuildInParallel="true"
33+
Targets="Restore;Build;GenAPIGenerateReferenceAssemblySource"
34+
Properties="Configuration=$(Configuration);TargetFramework=$(TargetFramework);NoIncremental=$(NoIncremental)"
35+
ContinueOnError="true" />
36+
37+
<Message Text="API generation completed for all projects" Importance="high" />
38+
</Target>
39+
40+
<!-- Debug target to list discovered projects -->
41+
<Target Name="ListProjects">
42+
<Message Text="=== PROJECT DISCOVERY RESULTS ===" Importance="high" />
43+
<Message Text="Total projects discovered: @(DiscoveredProjects -> Count())" Importance="high" />
44+
<Message Text="Projects:" Importance="high" />
45+
<Message Text=" %(DiscoveredProjects.Identity)" Importance="high" />
46+
<Message Text="=== EXCLUDED PROJECTS ===" Importance="high" />
47+
<Message Text="Excluded project patterns:" Importance="high" />
48+
<Message Text=" @(ExcludedProjects->'../../src/**/%(Identity)/*.csproj')" Importance="high" />
49+
<Message Text="=============================" Importance="high" />
50+
</Target>
51+
52+
<!-- Export project list and API file paths for external tooling -->
53+
<Target Name="ExportProjectInfo">
54+
<ItemGroup>
55+
<!-- Find all existing API files by scanning all api directories -->
56+
<ExistingApiFiles Include="../../src/**/api/*.cs" />
57+
<!-- Convert to absolute paths -->
58+
<ExistingApiFilesFullPath Include="@(ExistingApiFiles -> '%(FullPath)')" />
59+
</ItemGroup>
60+
61+
<!-- Create output directory if it doesn't exist -->
62+
<MakeDir Directories="$(OutputPath)" Condition="'$(OutputPath)' != ''" />
63+
64+
<!-- Write project list to file -->
65+
<WriteLinesToFile File="$(OutputPath)projects.txt"
66+
Lines="@(DiscoveredProjects)"
67+
Overwrite="true"
68+
Condition="'$(OutputPath)' != ''" />
69+
70+
<!-- Write API file paths to file -->
71+
<WriteLinesToFile File="$(OutputPath)api-files.txt"
72+
Lines="@(ExistingApiFilesFullPath)"
73+
Overwrite="true"
74+
Condition="'$(OutputPath)' != ''" />
75+
76+
<!-- Also output to console for immediate use -->
77+
<Message Text="=== EXPORTED PROJECT INFO ===" Importance="high" />
78+
<Message Text="Projects file: $(OutputPath)projects.txt" Importance="high" Condition="'$(OutputPath)' != ''" />
79+
<Message Text="API files list: $(OutputPath)api-files.txt" Importance="high" Condition="'$(OutputPath)' != ''" />
80+
<Message Text="" Importance="high" />
81+
<Message Text="Total projects: @(DiscoveredProjects -> Count())" Importance="high" />
82+
<Message Text="Existing API files (@(ExistingApiFilesFullPath -> Count()) total):" Importance="high" />
83+
<Message Text=" %(ExistingApiFilesFullPath.Identity)" Importance="high" />
84+
<Message Text="=============================" Importance="high" />
85+
</Target>
86+
87+
<!-- Default target -->
88+
<Target Name="Build" DependsOnTargets="BuildAndGenerateAPI" />
89+
90+
</Project>
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#!/bin/bash
2+
3+
# Automated analysis of all components based on configuration
4+
# Usage: ./analyze-all-components.sh <base_branch> <target_branch>
5+
6+
set -e
7+
8+
BASE_BRANCH=${1:-origin/release/9.4}
9+
TARGET_BRANCH=${2:-origin/main}
10+
11+
TOOLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
CONFIG_FILE="$TOOLS_DIR/config/component-priority.json"
13+
ANALYSIS_DIR="$TOOLS_DIR/analysis-output"
14+
15+
echo "🔍 Starting automated component analysis"
16+
echo "📋 Using config: $CONFIG_FILE"
17+
echo "📊 Output directory: $ANALYSIS_DIR"
18+
echo "⏱️ This may take several minutes for large repositories..."
19+
20+
# Start total timing
21+
SCRIPT_START_TIME=$(date +%s)
22+
23+
# Ensure analysis directory exists
24+
mkdir -p "$ANALYSIS_DIR"
25+
26+
# Check if jq is available for JSON processing
27+
if ! command -v jq &> /dev/null; then
28+
echo "⚠️ jq not found, using fallback analysis"
29+
# Fallback to manual component list
30+
COMPONENTS=(
31+
"src"
32+
"extension/"
33+
"eng/scripts"
34+
)
35+
else
36+
echo "📊 Processing components from configuration..."
37+
# Extract component paths from JSON config - flat array structure
38+
RAW_COMPONENTS=($(jq -r '.analysis_priorities[]' "$CONFIG_FILE" 2>/dev/null || echo ""))
39+
40+
# Expand glob patterns to actual directories
41+
COMPONENTS=()
42+
for pattern in "${RAW_COMPONENTS[@]}"; do
43+
if [[ "$pattern" == *"*"* ]]; then
44+
# This is a glob pattern, expand it from the git root
45+
echo "🔍 Expanding glob pattern: $pattern"
46+
# Change to git root directory for proper glob expansion
47+
GIT_ROOT=$(git rev-parse --show-toplevel)
48+
cd "$GIT_ROOT"
49+
for expanded_path in $pattern; do
50+
if [ -d "$expanded_path" ]; then
51+
COMPONENTS+=("$expanded_path")
52+
echo " ✅ Found: $expanded_path"
53+
fi
54+
done
55+
# Return to tools directory
56+
cd "$TOOLS_DIR"
57+
else
58+
# Regular path, add as-is if it exists
59+
GIT_ROOT=$(git rev-parse --show-toplevel)
60+
if [ -d "$GIT_ROOT/$pattern" ]; then
61+
COMPONENTS+=("$pattern")
62+
echo " ✅ Found: $pattern"
63+
fi
64+
fi
65+
done
66+
67+
# If config reading failed or no components found, use fallback
68+
if [ ${#COMPONENTS[@]} -eq 0 ]; then
69+
echo "⚠️ Could not read config or no valid components found, using fallback list"
70+
COMPONENTS=(
71+
"src"
72+
"extension/"
73+
"eng/scripts"
74+
)
75+
fi
76+
fi
77+
78+
# Function to analyze a single component
79+
analyze_component() {
80+
local component_start=$(date +%s)
81+
local component_path="$1"
82+
local output_file="$2"
83+
84+
echo " 📁 Analyzing: $component_path"
85+
86+
# Change to git root for proper path resolution
87+
local original_dir=$(pwd)
88+
cd "$(git rev-parse --show-toplevel)"
89+
90+
# Check if there are any changes in this component first
91+
local change_count=$(git diff --name-status $BASE_BRANCH..$TARGET_BRANCH -- "$component_path/" 2>/dev/null | wc -l | tr -d ' ')
92+
local commit_count=$(git log --oneline --no-merges --cherry-pick --right-only $BASE_BRANCH...$TARGET_BRANCH -- "$component_path/" 2>/dev/null | wc -l | tr -d ' ')
93+
94+
# Return to original directory
95+
cd "$original_dir"
96+
97+
if [ "$change_count" -eq 0 ] && [ "$commit_count" -eq 0 ]; then
98+
echo " ⏭️ No changes found, skipping file creation"
99+
local component_end=$(date +%s)
100+
echo " ⏱️ Completed in $((component_end - component_start))s"
101+
return 1 # Return non-zero to indicate no file was created
102+
fi
103+
104+
echo " ✅ Found $change_count file changes and $commit_count commits, creating analysis file"
105+
106+
# Use existing analyze_folder.sh if available
107+
if [ -f "$TOOLS_DIR/analyze_folder.sh" ]; then
108+
"$TOOLS_DIR/analyze_folder.sh" "$component_path" > "$output_file"
109+
else
110+
# Fallback manual analysis - ensure we're in git root for commands
111+
echo "# Analysis for $component_path" > "$output_file"
112+
echo "" >> "$output_file"
113+
echo "## File Changes" >> "$output_file"
114+
115+
# Change to git root for git commands
116+
cd "$(git rev-parse --show-toplevel)"
117+
git diff --stat $BASE_BRANCH..$TARGET_BRANCH -- "$component_path/" >> "$output_file" 2>/dev/null || echo "No changes found" >> "$output_file"
118+
echo "" >> "$output_file"
119+
echo "## All Commits" >> "$output_file"
120+
git log --oneline --no-merges --cherry-pick --right-only $BASE_BRANCH...$TARGET_BRANCH -- "$component_path/" >> "$output_file" 2>/dev/null || echo "No commits found" >> "$output_file"
121+
122+
# Add playground/test examples if available
123+
if [[ "$component_path" == "playground/" ]] || [[ "$component_path" == "tests/" ]]; then
124+
echo "" >> "$output_file"
125+
echo "## Notable Changes" >> "$output_file"
126+
git diff --name-status $BASE_BRANCH..$TARGET_BRANCH -- "$component_path/" | grep "^A" | head -10 >> "$output_file" 2>/dev/null || echo "No new files added" >> "$output_file"
127+
fi
128+
129+
# Return to original directory
130+
cd "$original_dir"
131+
fi
132+
133+
local component_end=$(date +%s)
134+
echo " ⏱️ Completed in $((component_end - component_start))s"
135+
return 0 # Return zero to indicate file was created successfully
136+
}
137+
138+
# Function to generate safe filename from component path
139+
generate_filename() {
140+
local component="$1"
141+
echo "$component" | sed 's|/|-|g' | sed 's|^src-||' | sed 's|-$||'
142+
}
143+
144+
# Analyze all components
145+
echo "🎯 Analyzing components..."
146+
ANALYSIS_START=$(date +%s)
147+
148+
component_count=0
149+
files_created=0
150+
total_components=${#COMPONENTS[@]}
151+
echo "📊 Processing $total_components components..."
152+
153+
for component in "${COMPONENTS[@]}"; do
154+
((component_count++))
155+
echo "[$component_count/$total_components] Processing: $component"
156+
component_name=$(generate_filename "$component")
157+
output_file="$ANALYSIS_DIR/$component_name.md"
158+
159+
if analyze_component "$component" "$output_file"; then
160+
((files_created++))
161+
fi
162+
done
163+
164+
ANALYSIS_END=$(date +%s)
165+
echo "✅ Component analysis completed in $((ANALYSIS_END - ANALYSIS_START))s"
166+
echo "📊 Created $files_created analysis files out of $total_components components"
167+
168+
# Generate summary report
169+
echo "📊 Generating summary report..."
170+
SUMMARY_START=$(date +%s)
171+
summary_file="$ANALYSIS_DIR/analysis-summary.md"
172+
173+
cat > "$summary_file" << EOF
174+
# Component Analysis Summary
175+
176+
Generated on: $(date)
177+
Branch comparison: $BASE_BRANCH -> $TARGET_BRANCH
178+
179+
## Components Analyzed
180+
181+
EOF
182+
183+
for component in "${COMPONENTS[@]}"; do
184+
component_name=$(generate_filename "$component")
185+
if [ -f "$ANALYSIS_DIR/$component_name.md" ]; then
186+
# Extract the actual component path from the analysis file instead of using the original pattern
187+
actual_component_path=$(grep "📁 ANALYZING:" "$ANALYSIS_DIR/$component_name.md" 2>/dev/null | sed 's/📁 ANALYZING: //' | tr -d '[:space:]' || echo "$component")
188+
if [ -n "$actual_component_path" ]; then
189+
# Ensure we're in the git repository root for the file count
190+
cd "$(git rev-parse --show-toplevel)"
191+
file_count=$(git diff --name-status $BASE_BRANCH..$TARGET_BRANCH -- "$actual_component_path" 2>/dev/null | wc -l | tr -d ' ')
192+
cd "$TOOLS_DIR"
193+
echo "- **$actual_component_path** ($file_count files) - [Analysis]($component_name.md)" >> "$summary_file"
194+
else
195+
echo "- **$component** (unknown files) - [Analysis]($component_name.md)" >> "$summary_file"
196+
fi
197+
fi
198+
done
199+
200+
cat >> "$summary_file" << EOF
201+
202+
## Analysis Files Generated
203+
204+
EOF
205+
206+
ls -la "$ANALYSIS_DIR"/*.md | awk '{print "- " $9 " (" $5 " bytes)"}' >> "$summary_file"
207+
208+
SUMMARY_END=$(date +%s)
209+
echo "✅ Summary generation completed in $((SUMMARY_END - SUMMARY_START))s"
210+
211+
# Calculate total time
212+
SCRIPT_END_TIME=$(date +%s)
213+
TOTAL_TIME=$((SCRIPT_END_TIME - SCRIPT_START_TIME))
214+
215+
echo ""
216+
echo "✅ Component analysis complete!"
217+
echo "⏱️ Total execution time: ${TOTAL_TIME}s"
218+
echo ""
219+
echo "📊 Timing Summary:"
220+
echo " Component Analysis: $((ANALYSIS_END - ANALYSIS_START))s"
221+
echo " Summary Generation: $((SUMMARY_END - SUMMARY_START))s"
222+
echo ""
223+
echo "📄 Summary: $summary_file"
224+
echo "📁 Detailed analysis files in: $ANALYSIS_DIR/"
225+
echo ""
226+
echo "📋 Analysis files generated:"
227+
ls -1 "$ANALYSIS_DIR"/*.md | sed 's/^/ /'

0 commit comments

Comments
 (0)