@@ -102,9 +102,15 @@ check_command uniq ;
102
102
check_command xargs ;
103
103
104
104
# rest of the script vars
105
- LOG_FILE=" chglog_generation_${PPID} .log"
106
- ERR_FILE=" chglog_generation_errors_${PPID} .log"
107
- LOCK_FILE=" ${TMPDIR:-/ tmp} /org.pak.multicast.chglog-generation-shell"
105
+ declare cache
106
+ CHGLOG_GIT_LOG_CACHE_FILE=" ${TMPDIR:-/ tmp} /.changelog_git_hist_buffer.txt"
107
+ # shellcheck disable=SC2086
108
+ # LOG_FILE="chglog_generation_${PPID}.log"
109
+ # shellcheck disable=SC2086
110
+ # ERR_FILE="chglog_generation_errors_${PPID}.log"
111
+ # shellcheck disable=SC2086
112
+ # LOCK_FILE="${TMPDIR:-/tmp}/org.pak.multicast.chglog-generation-shell"
113
+ # shellcheck disable=SC2086
108
114
EXIT_CODE=0
109
115
110
116
# USAGE:
@@ -114,16 +120,27 @@ EXIT_CODE=0
114
120
# Results:
115
121
# returns the most recent tag associated with the current commit.
116
122
function id_current_tag() {
117
- local INPUT_HINT_FOR_CURRENT=" ${1} "
118
- git describe --tags --abbrev=0 ${INPUT_HINT_FOR_CURRENT}
123
+ local INPUT_HINT_FOR_CURRENT=" $1 " ;
124
+ # Define regular expression pattern (copied from the improved regex in GHI #273)
125
+ local GIT_REF_PATTERN=' ^[a-zA-Z0-9][-a-zA-Z0-9_\+\./]*$' ;
126
+ # Validate the input to ensure it matches expected tag patterns
127
+ if printf " %s\n" " $INPUT_HINT_FOR_CURRENT " | grep -qE -e " $GIT_REF_PATTERN " 2> /dev/null; then
128
+ # assume input is safe enough to use as a git ref
129
+ git describe --tags --abbrev=0 " $INPUT_HINT_FOR_CURRENT " 2> /dev/null ;
130
+ else
131
+ # assume input is unsafe to use as a git ref an fallback to none,
132
+ # e.g., same as no input
133
+ git describe --tags --abbrev=0 2> /dev/null ;
134
+ fi ;
119
135
} # end id_current_tag()
120
136
121
137
# USAGE:
122
138
# ~$ run_enumerate_tag_history
123
139
# Results:
124
140
# returns a list of tags that are ancestors of the current HEAD, excluding the current tag.
125
141
function run_enumerate_tag_history() {
126
- local HEAD_TAG=$( id_current_tag HEAD)
142
+ local HEAD_TAG
143
+ HEAD_TAG=" $( id_current_tag HEAD) "
127
144
{ git rev-list --tags --ancestry-path " $( git tag --no-contains " ${HEAD_TAG} " | sort -V | head -n1) ^..HEAD" ; wait ; } | xargs -I{} git tag --points-at " {}" 2> /dev/null ;
128
145
wait ;
129
146
} # end run_enumerate_tag_history()
@@ -134,8 +151,12 @@ function run_enumerate_tag_history() {
134
151
# returns a cached list of tags that are ancestors of the current HEAD, excluding development tags.
135
152
# If the cache is empty, it populates the cache by calling run_enumerate_tag_history.
136
153
function enumerate_tag_history() {
154
+ if [[ -z " ${cache} " ]] && [[ -r " ${CHGLOG_GIT_LOG_CACHE_FILE} " ]]; then
155
+ cache=$( cat < " ${CHGLOG_GIT_LOG_CACHE_FILE} " ; wait ; )
156
+ fi ;
137
157
if [[ -z " ${cache} " ]] ; then
138
- cache=$( run_enumerate_tag_history | grep -vE " v?\d+.\d+.\d+-dev" ; wait ; )
158
+ { run_enumerate_tag_history | grep -vE " v?\d+.\d+.\d+-dev" ; wait ; } >> " ${CHGLOG_GIT_LOG_CACHE_FILE} " ; wait ;
159
+ cache=$( cat < " ${CHGLOG_GIT_LOG_CACHE_FILE} " ; wait ; )
139
160
fi ;
140
161
printf " %s\n" " ${cache} " ;
141
162
wait ;
@@ -148,11 +169,74 @@ function enumerate_tag_history() {
148
169
# Results:
149
170
# returns the parent tag of the specified input tag or commit.
150
171
function id_parent_tag() {
151
- local INPUT=" ${1} "
152
- enumerate_tag_history | grep -A 1 -F -f <( id_current_tag ${INPUT} ) | tail -n1
153
- wait ;
172
+ local INPUT=" $1 " ;
173
+ # Define regular expression pattern (copied from the improved regex in GHI #273)
174
+ local GIT_REF_PATTERN=' ^[a-zA-Z0-9][-a-zA-Z0-9_\+\./]*$' ;
175
+ local SANITIZED_TAG_PATTERN=' [^-,\.\+_[:alnum:]]' ;
176
+ # Validate the input to ensure it matches expected tag patterns
177
+ if printf " %s\n" " $INPUT " | grep -qE -e " ${GIT_REF_PATTERN} " ; then
178
+ # Capture the first line of the output from id_current_tag
179
+ local first_tag ;
180
+ first_tag=$( id_current_tag " $INPUT " | head -n1) ;
181
+ local sanitized_tag ;
182
+ # Sanitize the first_tag to ensure it only contains valid characters
183
+ sanitized_tag=$( printf " %s\n" " $first_tag " | sed -E " s/$SANITIZED_TAG_PATTERN //g" ) ;
184
+ # Check if the sanitized tag starts with "v", "V", "head", or "HEAD" (case insensitive)
185
+ if printf " %s\n" " $sanitized_tag " | grep -qE -e " $GIT_REF_PATTERN " 2> /dev/null; then
186
+ enumerate_tag_history | grep -A 1 -F -f <( printf " %s\n" " $sanitized_tag " ) | tail -n1 ;
187
+ wait ;
188
+ fi ;
189
+ fi ;
154
190
} # end id_parent_tag()
155
191
192
+ # USAGE:
193
+ # ~$ format_changes_by_flag FLAG_NAME CHANGELOG_BUFFER_FILE
194
+ # Arguments:
195
+ # FLAG_NAME (Required) -- The flag type to filter and group (e.g., FEATURE, FIX)
196
+ # CHANGELOG_BUFFER_FILE (Required) -- Path to the changelog buffer file
197
+ # Results:
198
+ # outputs formatted changelog entries grouped by the specified flag type
199
+ function format_changes_by_flag() {
200
+ local FLAG_NAME=" ${1} "
201
+ local BUFFER_FILE=" ${2} "
202
+
203
+ # Validate inputs
204
+ test -z " ${FLAG_NAME} " && { printf " Error: FLAG_NAME is required\n" >&2 ; return 64 ; }
205
+ test -z " ${BUFFER_FILE} " && { printf " Error: BUFFER_FILE is required\n" >&2 ; return 64 ; }
206
+ test -f " ${BUFFER_FILE} " || { printf " Error: Buffer file not found\n" >&2 ; return 65 ; }
207
+ test -r " ${BUFFER_FILE} " || { printf " Error: Buffer file not found\n" >&2 ; return 77 ; }
208
+
209
+ # Execute the AWK processing
210
+ awk -v RS=' \n' -v ORS=' \n\n\n' -v flagname=" ${FLAG_NAME} " '
211
+ {
212
+ # Check if the block contains a valid change-log entry
213
+ if ($0 ~ /([\[][A-Z]+[]]){1}/ && $0 ~ flagname) {
214
+ # Ensure that there is no content before the match
215
+ if ($0 ~ /^[a-f0-9]{7,7}[[:space:]]*(([\[][A-Z]+[]]){1})/) {
216
+ # Extract the header (first line) and the content
217
+ header = $0;
218
+ hash_line = substr(header, 1, 7); # Get the hash
219
+ header_line = substr(header, index(header, "[") + 1, (index(header, "]") - index(header, "[")) - 1); # Get the header
220
+ if (header_line ~ flagname) {
221
+ content = " * " hash_line " --" substr(header, index(header, "]") + 1); # Get the content after the header
222
+ # Combine the header and content
223
+ combined[header_line] = (combined[header_line] ? combined[header_line] "\n" : "") content;
224
+ }
225
+ }
226
+ }
227
+ }
228
+ END {
229
+ # Print combined entries
230
+ for (h in combined) {
231
+ print h ":\n" combined[h]
232
+ }
233
+ }
234
+ ' < " ${BUFFER_FILE} " | sort -id | uniq | sort -id -k 4
235
+ } # end format_changes_by_flag()
236
+
237
+ # Export function for sub-shells
238
+ export -f format_changes_by_flag
239
+
156
240
# step 1: is designed to determine the current and previous Git tags and
157
241
# then construct a Git range based on these tags. If no argument is provided, it defaults to
158
242
# using the tags.
@@ -166,15 +250,16 @@ if [[ -z "${1}" ]] ; then
166
250
" ${0} " " ${FALLBACK_GIT_RANGE} " || : ; wait ;
167
251
fi ;
168
252
done ;
253
+ rm -f " ${CHGLOG_GIT_LOG_CACHE_FILE} " || : ;
169
254
unset FALLBACK_GIT_RANGE 2> /dev/null || : ;
170
255
unset GIT_PREVIOUS_TAG 2> /dev/null || : ;
171
256
exit 0 ;
172
257
fi ;
173
258
GIT_RANGE=" ${1} "
174
259
175
260
# cache the git full log
176
- CHANGELOG_BUFFER=" ${TMPDIR:-/ tmp} /.chagelog_buffer .txt"
177
- cat <( git log " ${GIT_RANGE} " --reverse --pretty=format:" COMMIT_START%n%h%n%B%nCOMMIT_END" ) > " ${CHANGELOG_BUFFER} " ; wait ;
261
+ CHANGELOG_BUFFER=" ${TMPDIR:-/ tmp} /.changelog_buffer .txt"
262
+ git log " ${GIT_RANGE} " --reverse --pretty=format:" COMMIT_START%n%h%n%B%nCOMMIT_END" > " ${CHANGELOG_BUFFER} " ; wait ;
178
263
179
264
RAW_FLAGS_LIST=$( cat < " ${CHANGELOG_BUFFER} " | grep -oE " ([\[][A-Z]+[]]){1}" | sort -id | uniq -c | sort -rid | grep -oE " ([A-Z]+){1}" | sort -id | uniq | sort -rd ; wait ; )
180
265
@@ -202,54 +287,26 @@ if [[ ( -n "${RAW_DEL_FILES}" ) ]] ; then
202
287
fi ;
203
288
204
289
if [[ ( ${VERBOSE_CHANGE_LOG:- 0} -gt 0 ) ]] ; then
205
- # flags sub-header
206
- printf " \n### Changes by Kind\n" ;
207
-
208
- # cache the git log summaries with hashes
209
- CHANGELOG_BUFFER_SHORT=" ${TMPDIR:-/ tmp} /.short chagelog_buffer.txt"
210
- cat <( git log " ${GIT_RANGE} " --reverse --grep=" ([\[][A-Z]+[]]){1}" -E --pretty=format:" COMMIT_START%n%h %s%nCOMMIT_END" ) > " ${CHANGELOG_BUFFER_SHORT} " ; wait ;
211
-
212
- # auto-collect by flags
213
- for FLAG_ID in $( printf " %s\n" " ${RAW_FLAGS_LIST} " ) ; do
214
-
215
- if [[ ( -n " $FLAG_ID " ) ]] ; then
216
-
217
- awk -v RS=' \n' -v ORS=' \n\n\n' -v flagname=" $FLAG_ID " '
218
- {
219
- # Check if the block contains a valid change-log entry
220
- if ($0 ~ /([\[][A-Z]+[]]){1}/ && $0 ~ flagname) {
221
- # Ensure that there is no content before the match
222
- if ($0 ~ /^[a-f0-9]{7,7}[[:space:]]*(([\[][A-Z]+[]]){1})/) {
223
- # Extract the header (first line) and the content
224
- header = $0;
225
- hash_line = substr(header, 1, 7); # Get the hash
226
- header_line = substr(header, index(header, "[") + 1, (index(header, "]") - index(header, "[")) - 1); # Get the header
227
- if (header_line ~ flagname) {
228
- content = " * " hash_line " --" substr(header, index(header, "]") + 1); # Get the content after the header
229
- # Combine the header and content
230
- combined[header_line] = (combined[header_line] ? combined[header_line] "\n" : "") content;
231
- }
232
- }
233
- }
234
- }
235
- END {
236
- # Print combined entries
237
- for (h in combined) {
238
- print h ":\n" combined[h]
239
- }
240
- }
241
- ' < " ${CHANGELOG_BUFFER_SHORT} " | sort -id | uniq | sort -id -k 4
290
+ # flags sub-header
291
+ printf " \n### Changes by Kind\n" ;
242
292
243
- fi ;
293
+ # cache the git log summaries with hashes
294
+ CHANGELOG_BUFFER_SHORT=" ${TMPDIR:-/ tmp} /.short_changelog_buffer.txt"
295
+ git log " ${GIT_RANGE} " --reverse --grep=" ([\[][A-Z]+[]]){1}" -E --pretty=format:" COMMIT_START%n%h %s%nCOMMIT_END" > " ${CHANGELOG_BUFFER_SHORT} " ; wait ;
244
296
245
- done ;
297
+ # auto-collect by flags
298
+ for FLAG_ID in $( printf " %s\n" " ${RAW_FLAGS_LIST} " ) ; do
299
+ if [[ ( -n " $FLAG_ID " ) ]] ; then
300
+ format_changes_by_flag " ${FLAG_ID} " " ${CHANGELOG_BUFFER_SHORT} "
301
+ fi ;
302
+ done ;
246
303
247
- rm -f " ${CHANGELOG_BUFFER_SHORT} " 2> /dev/null || : ;
248
- # files sub-header
249
- printf " \n### Changes by file\n" ;
304
+ rm -f " ${CHANGELOG_BUFFER_SHORT} " 2> /dev/null || : ;
305
+ # files sub-header
306
+ printf " \n### Changes by file\n" ;
250
307
else
251
- # NO sub-headers
252
- printf " \n\n" ;
308
+ # NO sub-headers
309
+ printf " \n\n" ;
253
310
fi ;
254
311
255
312
# auto-collect gitlog
0 commit comments