1+ #! /bin/bash
2+ # .github/hooks/version-hook.sh
3+
4+ set -e
5+
6+ # Colors for output
7+ GREEN=' \033[0;32m'
8+ YELLOW=' \033[1;33m'
9+ BLUE=' \033[0;34m'
10+ NC=' \033[0m' # No Color
11+
12+ # Path to your pyproject.toml file
13+ PYPROJECT_PATH=" pyproject.toml"
14+ CHANGELOG_PATH=" CHANGELOG.md"
15+
16+ # Extract current version from pyproject.toml
17+ get_current_version () {
18+ # Completely rewritten version extraction
19+ # Use awk for more precise extraction
20+ VERSION=$( awk -F' "' ' /^version = / {print $2}' " $PYPROJECT_PATH " )
21+ echo " $VERSION "
22+ }
23+
24+ # Check if version has changed compared to the remote branch
25+ version_has_changed () {
26+ CURRENT_VERSION=$( get_current_version)
27+ CURRENT_BRANCH=$( git branch --show-current)
28+
29+ echo " Detected current version: $CURRENT_VERSION "
30+
31+ # Try to get the version from the remote repo
32+ if git ls-remote --exit-code origin & > /dev/null; then
33+ # Check if the remote branch exists
34+ if git show-ref --verify --quiet refs/remotes/origin/$CURRENT_BRANCH ; then
35+ # Get the remote version from pyproject.toml
36+ REMOTE_VERSION=$( git show origin/$CURRENT_BRANCH :$PYPROJECT_PATH 2> /dev/null |
37+ awk -F' "' ' /^version = / {print $2}' || echo " not-found" )
38+
39+ if [ " $REMOTE_VERSION " != " $CURRENT_VERSION " ]; then
40+ echo -e " ${YELLOW} Version changed from $REMOTE_VERSION to $CURRENT_VERSION ${NC} "
41+ return 0 # True - version has changed
42+ else
43+ echo -e " ${BLUE} Version unchanged: $CURRENT_VERSION ${NC} "
44+ fi
45+ else
46+ # New branch
47+ echo -e " ${YELLOW} New branch or branch not yet pushed, using version: $CURRENT_VERSION ${NC} "
48+ return 0
49+ fi
50+ else
51+ # If remote doesn't exist yet, consider this a new project
52+ echo -e " ${YELLOW} Remote not found, assuming new project with version $CURRENT_VERSION ${NC} "
53+ return 0 # True - new project
54+ fi
55+
56+ return 1 # False - version hasn't changed
57+ }
58+
59+ # Extract GitHub repository name from git remote
60+ get_repo_info () {
61+ REMOTE_URL=$( git config --get remote.origin.url)
62+ if [[ $REMOTE_URL =~ github\. com[:/]([^/]+)/([^/.]+) ]]; then
63+ OWNER=" ${BASH_REMATCH[1]} "
64+ REPO=" ${BASH_REMATCH[2]} "
65+ echo " $OWNER /$REPO "
66+ else
67+ echo " "
68+ fi
69+ }
70+
71+ # Format commit message with PR and issue links
72+ format_commit_message () {
73+ local MSG=" $1 "
74+ local REPO_INFO=" $2 "
75+
76+ # Skip if no repo info
77+ if [ -z " $REPO_INFO " ]; then
78+ echo " $MSG "
79+ return
80+ fi
81+
82+ # Extract PR number if present in commit message (PR #123)
83+ local PR_FORMATTED=" $MSG "
84+ if [[ $MSG =~ Merge\ pull\ request\ # ([0-9]+) ]]; then
85+ PR_NUM= " ${BASH_REMATCH[1]} "
86+ # Get the PR author using GitHub API if possible (requires curl and GitHub token)
87+ if command -v curl > /dev/null && [ -n " $GITHUB_TOKEN " ]; then
88+ PR_AUTHOR=$( curl -s -H " Authorization: token $GITHUB_TOKEN " \
89+ " https://api.github.com/repos/$REPO_INFO /pulls/$PR_NUM " |
90+ grep -o ' "login": *"[^"]*"' | head -1 | sed ' s/"login": *"\(.*\)"/\1/' )
91+
92+ if [ -n " $PR_AUTHOR " ]; then
93+ PR_FORMATTED=" $MSG [PR #$PR_NUM by @$PR_AUTHOR ]"
94+ else
95+ PR_FORMATTED=" $MSG [PR #$PR_NUM ]"
96+ fi
97+ else
98+ PR_FORMATTED=" $MSG [PR #$PR_NUM ]"
99+ fi
100+ elif [[ $MSG =~ \( # ([0-9]+)\) ]]; then
101+ PR_NUM= " ${BASH_REMATCH[1]} "
102+ PR_FORMATTED= " $MSG (PR #$PR_NUM )"
103+ fi
104+
105+ # Extract issue numbers (fixes #123, closes #456, etc.)
106+ local ISSUES_FORMATTED= " $PR_FORMATTED "
107+ local ISSUE_KEYWORDS= " fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved"
108+
109+ if [[ $PR_FORMATTED =~ ($ISSUE_KEYWORDS )[:space:]* # ([0-9]+) ]]; then
110+ ISSUE_NUM= " ${BASH_REMATCH[2]} "
111+ ISSUES_FORMATTED= " ${PR_FORMATTED/ \# $ISSUE_NUM / [#$ISSUE_NUM](https:// github.com/ $REPO_INFO / issues/ $ISSUE_NUM )} "
112+ fi
113+
114+ echo " $ISSUES_FORMATTED "
115+ }
116+
117+ # Update the changelog file with the new version
118+ update_changelog () {
119+ VERSION=$1
120+ DATE=$( date +" %Y-%m-%d" )
121+ REPO_INFO=$( get_repo_info)
122+
123+ # Create changelog if it doesn't exist
124+ if [ ! -f " $CHANGELOG_PATH " ]; then
125+ echo " # Changelog" > " $CHANGELOG_PATH "
126+ echo " " >> " $CHANGELOG_PATH "
127+ echo -e " ${GREEN} Created new changelog file${NC} "
128+ fi
129+
130+ # Get commits since the last version tag
131+ LAST_TAG=$( git describe --tags --abbrev=0 2> /dev/null || echo " " )
132+
133+ if [ -z " $LAST_TAG " ]; then
134+ # If no tags exist, get all commits
135+ RAW_CHANGES=$( git log --pretty=format:" %s" | grep -v " Merge\|Update changelog\|Apply formatting" | head -15)
136+ else
137+ # Get commits since the last tag
138+ RAW_CHANGES=$( git log ${LAST_TAG} ..HEAD --pretty=format:" %s" | grep -v " Merge\|Update changelog\|Apply formatting" )
139+ fi
140+
141+ # Format changes with PR and issue links
142+ CHANGES=" "
143+ if [ -z " $RAW_CHANGES " ]; then
144+ CHANGES=" - Minor updates and fixes"
145+ else
146+ while IFS= read -r line; do
147+ # Format each commit message
148+ FORMATTED_LINE=$( format_commit_message " $line " " $REPO_INFO " )
149+ CHANGES=" ${CHANGES} - ${FORMATTED_LINE} \n"
150+ done <<< " $RAW_CHANGES"
151+ fi
152+
153+ # Add new version section at the top of the changelog
154+ TMP_FILE=$( mktemp)
155+ echo " # Changelog" > " $TMP_FILE "
156+ echo " " >> " $TMP_FILE "
157+ echo " ## [$VERSION ] - $DATE " >> " $TMP_FILE "
158+ echo " " >> " $TMP_FILE "
159+ echo " ### Changes" >> " $TMP_FILE "
160+ echo -e " $CHANGES " >> " $TMP_FILE "
161+ echo " " >> " $TMP_FILE "
162+ # If changelog exists and has content, append everything after the first line
163+ if [ -s " $CHANGELOG_PATH " ]; then
164+ tail -n +2 " $CHANGELOG_PATH " >> " $TMP_FILE "
165+ fi
166+ mv " $TMP_FILE " " $CHANGELOG_PATH "
167+
168+ # Stage the changelog
169+ git add " $CHANGELOG_PATH "
170+
171+ echo -e " ${GREEN} Updated $CHANGELOG_PATH with changes for version $VERSION ${NC} "
172+ }
173+
174+ # Main execution
175+ main () {
176+ if version_has_changed; then
177+ VERSION=$( get_current_version)
178+
179+ # Debug output to verify correct version extraction
180+ echo " Version detected for tagging: '$VERSION '"
181+
182+ # Update changelog and commit
183+ update_changelog " $VERSION "
184+ git commit -m " Update changelog for version $VERSION " || true
185+
186+ # Create tag name - Fix: ensure the version is correctly formatted for a git tag
187+ TAG_VERSION=" v$VERSION "
188+
189+ # Debug output
190+ echo " Creating tag: '$TAG_VERSION '"
191+
192+ # Tag the commit with the version
193+ git tag -a " $TAG_VERSION " -m " Version $VERSION "
194+ echo -e " ${GREEN} Tagged commit with $TAG_VERSION ${NC} "
195+
196+ echo -e " ${GREEN} All version update tasks completed successfully!${NC} "
197+ fi
198+ }
199+
200+ # Run the main function
201+ main
0 commit comments