-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgit-version.sh
More file actions
executable file
·243 lines (209 loc) · 6.06 KB
/
Copy pathgit-version.sh
File metadata and controls
executable file
·243 lines (209 loc) · 6.06 KB
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/bin/bash
# git-version.sh - Generate version strings from Git repository information
# Similar to setuptools-scm for Python
#
# Usage:
# ./git-version.sh [OPTIONS]
#
# Options:
# --tag-regex PATTERN Regex pattern to match specific tags (e.g. "server-v")
# --lang LANGUAGE Generate language-specific output (e.g. "python")
#
# Example:
# ./git-version.sh --tag-regex "server-" # Extract version from server-v* tags
# ./git-version.sh --tag-regex "frontend-" --lang python # Generate Python version assignment
# ./git-version.sh > version.txt # Redirect output to a file
set -e
# Function to check if git is available and find repository root
check_git() {
if ! command -v git &> /dev/null; then
echo "ERROR: Git is not installed or not in PATH" >&2
exit 1
fi
# Try to find git repository root, looking upward if needed
local original_dir=$(pwd)
# Use git rev-parse to find .git directory or repository root
if ! git rev-parse --is-inside-work-tree &> /dev/null; then
# Not in a git repository at all
echo "ERROR: Not inside a git repository" >&2
exit 1
fi
# Change to the git root directory
local git_root=$(git rev-parse --show-toplevel)
if [[ -n "$git_root" ]]; then
if [[ "$(pwd)" != "$git_root" ]]; then
echo "Found git repository root at: $git_root" >&2
cd "$git_root"
fi
else
echo "ERROR: Could not determine git repository root" >&2
exit 1
fi
}
# Function to get the nearest tag
get_nearest_tag() {
local pattern="$1"
if [[ -n "$pattern" ]]; then
# Get all tags that match the pattern, sort by version, and take the newest
git tag -l "${pattern}*" | sort -V | tail -n 1 2>/dev/null || echo ""
else
# No pattern specified, just get the most recent tag
git describe --tags --abbrev=0 2>/dev/null || echo ""
fi
}
# Function to get local version parts
get_local_version() {
local tag="$1"
local distance=$(git rev-list "$tag"..HEAD --count 2>/dev/null || echo "0")
local commit_hash=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# Format: distance.gCOMMIT_HASH (note the 'g' prefix for git)
echo "${distance}.g${commit_hash}"
}
# Function to get dirty marker
is_dirty() {
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
echo ".dirty"
else
echo ""
fi
}
# Function to get current timestamp in ISO format
get_timestamp() {
# Format: YYYY-MM-DDTHH:MM:SSZ
date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u +"%Y%m%d%H%M%S"
}
# Language templates
get_language_template() {
local lang="$1"
local version="$2"
case "$lang" in
"python")
cat << EOF
# Auto-generated by git-version.sh
__version__="${version}"
EOF
;;
"json")
cat << EOF
{
"version": "${version}"
}
EOF
;;
*)
echo "ERROR: Unsupported language: $lang" >&2
exit 1
;;
esac
}
# Append dirty marker if needed
append_dirty_marker() {
local version="$1"
local dirty_marker="$2"
if [[ -n "$dirty_marker" ]]; then
echo "${version}${dirty_marker}"
else
echo "$version"
fi
}
# Function to get version from latest tag
get_tag_version() {
local tag="$1"
local pattern="$2"
if [[ -n "$pattern" ]]; then
# Extract version from tag using the pattern
# This handles formats like "server-v1.2.3" or "frontend-v2.0.0"
echo "$tag" | sed -E "s/^${pattern}v?//"
else
# No pattern specified, just remove 'v' prefix if present
echo "$tag" | sed 's/^v//'
fi
}
# Function to get the branch name
get_branch_name() {
local branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [[ "$branch" == "HEAD" ]]; then
# Detached HEAD state, just use the commit hash
echo "detached"
else
echo "$branch"
fi
}
# Function to get fallback version when no tags exist
get_fallback_version() {
local commit_count=$(git rev-list --count HEAD 2>/dev/null || echo "0")
local commit_hash=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
echo "0.0.0.dev${commit_count}+g${commit_hash}"
}
# Main function to generate the version string
generate_version() {
local pattern="$1"
check_git
local tag=$(get_nearest_tag "$pattern")
local branch=$(get_branch_name)
local dirty_marker=$(is_dirty)
local timestamp=$(get_timestamp)
# If we have a tag, use it as base version
if [[ -n "$tag" ]]; then
local version=$(get_tag_version "$tag" "$pattern")
local local_ver=$(get_local_version "$tag")
# If we're exactly on a tag and not dirty
if [[ "$local_ver" == "0.gunknown" || "$local_ver" == "0.g"* ]] && [[ -z "$dirty_marker" ]]; then
echo "${version}.${timestamp}"
else
# Distance from tag, include local version part
distance=$(echo "$local_ver" | cut -d. -f1)
commit=$(echo "$local_ver" | cut -d. -f2)
# If we're on a development/feature branch, add dev suffix
if [[ "$branch" != "main" && "$branch" != "master" ]]; then
version="${version}.dev${distance}+${commit}"
else
version="${version}.post${distance}+${commit}"
fi
# Append dirty marker if needed
version="$(append_dirty_marker "$version" "$dirty_marker")"
# Append timestamp
echo "${version}.${timestamp}"
fi
else
# No tags found, use fallback versioning
fallback="$(get_fallback_version)"
version="$(append_dirty_marker "$fallback" "$dirty_marker")"
echo "${version}.${timestamp}"
fi
}
# Parse arguments
pattern=""
language=""
while [[ $# -gt 0 ]]; do
case "$1" in
--tag-regex)
pattern="$2"
shift 2
;;
--lang)
language="$2"
shift 2
;;
--*)
echo "ERROR: Unknown option: $1" >&2
exit 1
;;
*)
echo "ERROR: Unexpected argument: $1" >&2
exit 1
;;
esac
done
# Store original directory to return to it later
original_dir=$(pwd)
# Output the version
version=$(generate_version "$pattern")
# Return to original directory
cd "$original_dir"
# Apply language template if specified
if [[ -n "$language" ]]; then
get_language_template "$language" "$version"
else
echo "$version"
fi