-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit-ch
executable file
·210 lines (174 loc) · 6.5 KB
/
git-ch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/bin/bash
# ANSI color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo -e "${RED}ERROR: ANTHROPIC_API_KEY is not set${NC}" >&2
exit 1
fi
# Check if in a git repository
if ! REPO_DIR=$(git rev-parse --show-toplevel 2>/dev/null); then
echo -e "${RED}Not a git repository.${NC}"
exit 1
fi
cd "$REPO_DIR" || exit 1
# Get the last two tags sorted by version number (natural sort)
TAGS=($(git tag -l | sort -V | tail -n 2))
if [ ${#TAGS[@]} -lt 2 ]; then
exit 1
fi
OLD_TAG="${TAGS[0]}"
NEW_TAG="${TAGS[1]}"
# Get the commit hash for the new tag
NEW_TAG_COMMIT=$(git rev-list -n 1 "${NEW_TAG}")
# Get all commits between tags, excluding the old tag's commit and README changes
COMMITS_FOR_LOG=$(git log "${OLD_TAG}..${NEW_TAG_COMMIT}" --pretty=format:"%H %s" -- . ':!README.md')
COMMITS=$(git log "${OLD_TAG}..${NEW_TAG_COMMIT}" --pretty=format:"%s" -- . ':!README.md')
# Get the diff between tags, excluding README.md
DIFF=$(git diff "${OLD_TAG}..${NEW_TAG}" -- . ':!README.md')
generate_changelog() {
local include_diff=$1
# Create the system message with changelog guidelines
local system_message=$(cat << EOT
You are a technical changelog generator that produces clear, concise, and well-structured changelogs. Your task is to analyze conventional commit messages and their diffs to identify and summarize key changes.
1. Commit Message Mapping:
- feat: -> Features section
- fix: -> Bug Fixes section
- perf: -> Improvements section
- refactor: -> Improvements section
- BREAKING CHANGE: -> Breaking Changes section
- Any commits with breaking changes indicator (!) -> Breaking Changes section
- deps: -> relevant section based on impact
2. Structure:
- Use only these sections in order: Breaking Changes, Features, Improvements, Bug Fixes
- Only include sections that have content
- Focus on significant changes that affect users or the system
- Be concise and specific
- Exclude documentation changes (like README updates)
3. Dependency Updates Style:
- Only list specific versions for major dependencies (e.g., "Updated OpenTelemetry to v1.33.0")
- For important but minor updates, mention without version (e.g., "Updated Google API dependencies")
- Group minor security/compatibility updates into a single line
- Don't enumerate individual package versions unless they're critically important
- If there are multiple minor dependency updates, summarize them (e.g., "Updated several Go dependencies for security and compatibility")
4. Format:
- First line must be "# Version X.Y.Z" (using provided version)
- Use "### Section Name" for sections
- One blank line after section header
- Use "- " for bullet points
- One blank line between sections
- Remove trailing period from bullet points
- When a section has no relevant changes, omit it
5. Example:
# Version 2.1.0
### Breaking Changes
- Changed authentication flow to OAuth2-only, requiring API key migration
### Features
- Added metrics collection API with Prometheus integration
### Improvements
- Updated Redis client to v7.2.0 for improved stability
- Updated several dependencies for security and performance
- Optimized query performance with new indexing strategy
### Bug Fixes
- Fixed race condition in concurrent job processing
Respond with only the changelog content.
EOT
)
local escaped_system=$(echo "$system_message" | jq -R -s .)
# Create the user message
# First escape all our variables using jq and base64 for diff
local escaped_commits=$(echo "$COMMITS" | jq -R -s .)
local escaped_diff=$(echo "$DIFF" | base64 | jq -R -s .)
local escaped_tag=$(echo "$NEW_TAG" | jq -R -s .)
# Create the user message with proper XML structure
local user_message
if [ "$include_diff" = true ]; then
user_message=$(cat << EOT
<context>
<version>${escaped_tag}</version>
<commits>
${escaped_commits}
</commits>
<diff encoding="base64">
${escaped_diff}
</diff>
</context>
Based on the commits and diff above, generate a changelog for the specified version. The diff is base64 encoded - decode it first. Follow the guidelines to create a clear, technical changelog that highlights significant changes, improvements, and fixes.
EOT
)
else
user_message=$(cat << EOT
<context>
<version>${escaped_tag}</version>
<commits>
${escaped_commits}
</commits>
</context>
Based on the commits above, generate a changelog for the specified version. Follow the guidelines to create a clear, technical changelog that highlights significant changes, improvements, and fixes.
EOT
)
fi
local escaped_user=$(echo "$user_message" | jq -R -s .)
# Make the API call
local response=$(curl -s -X POST \
https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
-d @- << EOF
{
"model": "claude-3-5-sonnet-20240620",
"max_tokens": 2048,
"system": ${escaped_system},
"messages": [
{
"role": "user",
"content": ${escaped_user}
}
]
}
EOF
)
# Check if the response contains an error
if echo "$response" | jq -e 'has("error")' > /dev/null; then
local error_message=$(echo "$response" | jq -r '.error.message')
if [[ "$error_message" == *"context_length"* ]] && [ "$include_diff" = true ]; then
echo "retrying_without_diff"
return
fi
echo -e "${RED}API Error: $error_message${NC}" >&2
return 1
fi
# Extract and verify the changelog
local changelog=$(echo "$response" | jq -r '.content[0].text // empty')
if [ -z "$changelog" ]; then
echo -e "${RED}Error: Empty changelog in response${NC}" >&2
return 1
fi
echo "$changelog"
}
# First try with diff
CHANGELOG=$(generate_changelog true)
# If we got "retrying_without_diff", try again without diff
if [ "$CHANGELOG" = "retrying_without_diff" ]; then
CHANGELOG=$(generate_changelog false)
fi
# Print changelog and commits if we have content
if [ -n "$CHANGELOG" ]; then
{
echo "$CHANGELOG"
echo
echo "## Commits"
echo "$COMMITS_FOR_LOG" | while read -r line; do
commit_hash=$(echo "$line" | cut -d' ' -f1)
commit_msg=$(echo "$line" | cut -d' ' -f2-)
echo "* $commit_hash $commit_msg"
done
}
exit 0
else
exit 1
fi