1
+ #! /bin/bash
2
+
3
+ # Version Bump Script
4
+ # Extracts version bump logic from GitHub Actions workflow
5
+ # Usage: ./version-bump.sh <bump_type> [base_ref]
6
+ # bump_type: patch, minor, or major
7
+ # base_ref: base reference for diff (default: origin/main)
8
+
9
+ set -euo pipefail
10
+
11
+ # Function to print usage
12
+ usage () {
13
+ echo " Usage: $0 <bump_type> [base_ref]"
14
+ echo " bump_type: patch, minor, or major"
15
+ echo " base_ref: base reference for diff (default: origin/main)"
16
+ exit 1
17
+ }
18
+
19
+ # Function to validate version format
20
+ validate_version () {
21
+ local version=" $1 "
22
+ if ! [[ " $version " =~ ^[0-9]+\. [0-9]+\. [0-9]+$ ]]; then
23
+ echo " ❌ Invalid version format: '$version '. Expected X.Y.Z format." >&2
24
+ return 1
25
+ fi
26
+ return 0
27
+ }
28
+
29
+ # Function to bump version
30
+ bump_version () {
31
+ local current_version=" $1 "
32
+ local bump_type=" $2 "
33
+
34
+ IFS=' .' read -r major minor patch <<< " $current_version"
35
+
36
+ # Validate that components are numeric
37
+ if ! [[ " $major " =~ ^[0-9]+$ ]] || ! [[ " $minor " =~ ^[0-9]+$ ]] || ! [[ " $patch " =~ ^[0-9]+$ ]]; then
38
+ echo " ❌ Version components must be numeric: major='$major ' minor='$minor ' patch='$patch '" >&2
39
+ return 1
40
+ fi
41
+
42
+ case " $bump_type " in
43
+ " patch" )
44
+ echo " $major .$minor .$(( patch + 1 )) "
45
+ ;;
46
+ " minor" )
47
+ echo " $major .$(( minor + 1 )) .0"
48
+ ;;
49
+ " major" )
50
+ echo " $(( major + 1 )) .0.0"
51
+ ;;
52
+ * )
53
+ echo " ❌ Invalid bump type: '$bump_type '. Expected patch, minor, or major." >&2
54
+ return 1
55
+ ;;
56
+ esac
57
+ }
58
+
59
+ # Function to update README version
60
+ update_readme_version () {
61
+ local readme_path=" $1 "
62
+ local namespace=" $2 "
63
+ local module_name=" $3 "
64
+ local new_version=" $4 "
65
+
66
+ if [ ! -f " $readme_path " ]; then
67
+ return 1
68
+ fi
69
+
70
+ # Check if README contains version references for this specific module
71
+ local module_source=" registry.coder.com/${namespace} /${module_name} /coder"
72
+ if grep -q " source.*${module_source} " " $readme_path " ; then
73
+ echo " Updating version references for $namespace /$module_name in $readme_path "
74
+ # Use awk to only update versions that follow the specific module source
75
+ awk -v module_source=" $module_source " -v new_version=" $new_version " '
76
+ /source.*=.*/ {
77
+ if ($0 ~ module_source) {
78
+ in_target_module = 1
79
+ } else {
80
+ in_target_module = 0
81
+ }
82
+ }
83
+ /version.*=.*"/ {
84
+ if (in_target_module) {
85
+ gsub(/version[[:space:]]*=[[:space:]]*"[^"]*"/, "version = \"" new_version "\"")
86
+ in_target_module = 0
87
+ }
88
+ }
89
+ { print }
90
+ ' " $readme_path " > " ${readme_path} .tmp" && mv " ${readme_path} .tmp" " $readme_path "
91
+ return 0
92
+ elif grep -q ' version\s*=\s*"' " $readme_path " ; then
93
+ echo " ⚠️ Found version references but no module source match for $namespace /$module_name "
94
+ return 1
95
+ fi
96
+
97
+ return 1
98
+ }
99
+
100
+ # Main function
101
+ main () {
102
+ # Parse arguments
103
+ if [ $# -lt 1 ] || [ $# -gt 2 ]; then
104
+ usage
105
+ fi
106
+
107
+ local bump_type=" $1 "
108
+ local base_ref=" ${2:- origin/ main} "
109
+
110
+ # Validate bump type
111
+ case " $bump_type " in
112
+ " patch" |" minor" |" major" )
113
+ ;;
114
+ * )
115
+ echo " ❌ Invalid bump type: '$bump_type '. Expected patch, minor, or major." >&2
116
+ exit 1
117
+ ;;
118
+ esac
119
+
120
+ echo " 🔍 Detecting modified modules..."
121
+
122
+ # Detect modified modules
123
+ local changed_files
124
+ changed_files=$( git diff --name-only " ${base_ref} " ...HEAD)
125
+ local modules
126
+ modules=$( echo " $changed_files " | grep -E ' ^registry/[^/]+/modules/[^/]+/' | cut -d' /' -f1-4 | sort -u)
127
+
128
+ if [ -z " $modules " ]; then
129
+ echo " ❌ No modules detected in changes"
130
+ exit 1
131
+ fi
132
+
133
+ echo " Found modules:"
134
+ echo " $modules "
135
+ echo " "
136
+
137
+ # Initialize tracking variables
138
+ local bumped_modules=" "
139
+ local updated_readmes=" "
140
+ local untagged_modules=" "
141
+ local has_changes=false
142
+
143
+ # Process each module
144
+ while IFS= read -r module_path; do
145
+ if [ -z " $module_path " ]; then continue ; fi
146
+
147
+ local namespace
148
+ namespace=$( echo " $module_path " | cut -d' /' -f2)
149
+ local module_name
150
+ module_name=$( echo " $module_path " | cut -d' /' -f4)
151
+
152
+ echo " 📦 Processing: $namespace /$module_name "
153
+
154
+ # Find latest tag
155
+ local latest_tag
156
+ latest_tag=$( git tag -l " release/${namespace} /${module_name} /v*" | sort -V | tail -1)
157
+ local readme_path=" $module_path /README.md"
158
+ local current_version
159
+
160
+ if [ -z " $latest_tag " ]; then
161
+ # No tag found, check if README has version references
162
+ if [ -f " $readme_path " ] && grep -q ' version\s*=\s*"' " $readme_path " ; then
163
+ # Extract version from README
164
+ local readme_version
165
+ readme_version=$( grep ' version\s*=\s*"' " $readme_path " | head -1 | sed ' s/.*version\s*=\s*"\([^"]*\)".*/\1/' )
166
+ echo " No git tag found, but README shows version: $readme_version "
167
+
168
+ # Validate extracted version format
169
+ if ! validate_version " $readme_version " ; then
170
+ echo " Starting from v1.0.0 instead"
171
+ current_version=" 1.0.0"
172
+ else
173
+ current_version=" $readme_version "
174
+ untagged_modules=" $untagged_modules \n- $namespace /$module_name (README: v$readme_version )"
175
+ fi
176
+ else
177
+ echo " No existing tags or version references found for $namespace /$module_name , starting from v1.0.0"
178
+ current_version=" 1.0.0"
179
+ fi
180
+ else
181
+ current_version=$( echo " $latest_tag " | sed ' s/.*\/v//' )
182
+ echo " Found git tag: $latest_tag (v$current_version )"
183
+ fi
184
+
185
+ echo " Current version: $current_version "
186
+
187
+ # Validate current version format
188
+ if ! validate_version " $current_version " ; then
189
+ exit 1
190
+ fi
191
+
192
+ # Calculate new version
193
+ local new_version
194
+ new_version=$( bump_version " $current_version " " $bump_type " )
195
+
196
+ echo " New version: $new_version "
197
+
198
+ # Update README if applicable
199
+ if update_readme_version " $readme_path " " $namespace " " $module_name " " $new_version " ; then
200
+ updated_readmes=" $updated_readmes \n- $namespace /$module_name "
201
+ has_changes=true
202
+ fi
203
+
204
+ bumped_modules=" $bumped_modules \n- $namespace /$module_name : v$current_version → v$new_version "
205
+ echo " "
206
+
207
+ done <<< " $modules"
208
+
209
+ # Output results
210
+ echo " 📋 Summary:"
211
+ echo " Bump Type: $bump_type "
212
+ echo " "
213
+ echo " Modules Updated:"
214
+ echo -e " $bumped_modules "
215
+ echo " "
216
+
217
+ if [ -n " $updated_readmes " ]; then
218
+ echo " READMEs Updated:"
219
+ echo -e " $updated_readmes "
220
+ echo " "
221
+ fi
222
+
223
+ if [ -n " $untagged_modules " ]; then
224
+ echo " ⚠️ Modules Without Git Tags:"
225
+ echo -e " $untagged_modules "
226
+ echo " These modules were versioned based on README content. Consider creating proper release tags after merging."
227
+ echo " "
228
+ fi
229
+
230
+ # Check for changes and provide guidance
231
+ if [ " $has_changes " = true ]; then
232
+ echo " ✅ Version bump completed successfully!"
233
+ echo " 📝 README files have been updated with new versions."
234
+ echo " "
235
+ echo " Next steps:"
236
+ echo " 1. Review the changes: git diff"
237
+ echo " 2. Commit the changes: git add . && git commit -m 'chore: bump module versions ($bump_type )'"
238
+ echo " 3. Push the changes: git push"
239
+ exit 0
240
+ else
241
+ echo " ℹ️ No README files were updated (no version references found matching module sources)."
242
+ echo " Version calculations completed, but no files were modified."
243
+ exit 0
244
+ fi
245
+ }
246
+
247
+ # Run main function with all arguments
248
+ main " $@ "
0 commit comments