Skip to content

Commit 87b7161

Browse files
committed
Fix tags filtering resulting in paths filter being ignored
Signed-off-by: C85297 <95289555+C85297@users.noreply.github.com>
1 parent b0ee669 commit 87b7161

File tree

4 files changed

+226
-49
lines changed

4 files changed

+226
-49
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Tracks the commits in a [git](http://git-scm.com/) repository.
6363
compatible (extended matching enabled, matches entire lines only). Ignored if
6464
`tag_filter` is also specified.
6565

66+
* `tag_behaviour`: *Optional.* If `match_tagged` (the default), then the resource will only detect commits that are tagged with a tag matching `tag_regex` and `tag_filter`, and match all other filters. If `match_tag_ancestors`, then the resource will only detect commits matching all other filters and that are ancestors of a commit that are tagged with a tag matching `tag_regex` and `tag_filter`.
67+
6668
* `fetch_tags`: *Optional.* If `true` the flag `--tags` will be used to fetch
6769
all tags in the repository. If `false` no tags will be fetched.
6870

@@ -146,7 +148,7 @@ Tracks the commits in a [git](http://git-scm.com/) repository.
146148
skipped
147149
* `include_all_match`: *Optional.* Boolean wheater it should match all the include filters "AND", default: false
148150

149-
**Note**: *You must escape any regex sensitive characters, since the string is used as a regex filter.*
151+
**Note**: *You must escape any regex sensitive characters, since the string is used as a regex filter.*
150152
For example, using `[skip deploy]` or `[deploy skip]` to skip non-deployment related commits in a deployment pipeline:
151153

152154
```yaml
@@ -397,7 +399,7 @@ will stop the build.
397399
Run the tests with the following command:
398400

399401
```sh
400-
docker build -t git-resource --target tests --build-arg base_image=paketobuildpacks/run-jammy-base:latest .
402+
docker build -t git-resource --target tests --build-arg base_image=paketobuildpacks/run-noble-base:latest .
401403
402404
```
403405

assets/check

+84-40
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ paths="$(jq -r '(.source.paths // ["."])[]' <<< "$payload")" # those "'s are imp
2424
ignore_paths="$(jq -r '":!" + (.source.ignore_paths // [])[]' <<< "$payload")" # these ones too
2525
tag_filter=$(jq -r '.source.tag_filter // ""' <<< "$payload")
2626
tag_regex=$(jq -r '.source.tag_regex // ""' <<< "$payload")
27+
tag_behaviour=$(jq -r '.source.tag_behaviour // "match_tagged"' <<< "$payload")
2728
git_config_payload=$(jq -r '.source.git_config // []' <<< "$payload")
2829
ref=$(jq -r '.version.ref // ""' <<< "$payload")
2930
skip_ci_disabled=$(jq -r '.source.disable_ci_skip // false' <<< "$payload")
@@ -65,12 +66,20 @@ then
6566
fi
6667

6768
tagflag=""
68-
if [ -n "$tag_filter" ] || [ -n "$tag_regex" ] ; then
69+
if [ -n "$tag_filter" ] && [ -n "$tag_regex" ] ; then
70+
echo "Cannot provide both tag_filter and tag_regex"
71+
exit 1
72+
elif [ -n "$tag_filter" ] || [ -n "$tag_regex" ] ; then
6973
tagflag="--tags"
7074
else
7175
tagflag="--no-tags"
7276
fi
7377

78+
if [ "$tag_behaviour" != "match_tagged" ] && [ "$tag_behaviour" != "match_tag_ancestors" ]; then
79+
echo "Invalid tag_behaviour"
80+
exit 1
81+
fi
82+
7483
for filter in "$filter_include" "$filter_exclude"
7584
do
7685
if jq -e 'type != "array"' <<<"$filter" &>/dev/null
@@ -81,6 +90,11 @@ do
8190
fi
8291
done
8392

93+
if [ "$version_depth" -le 0 ]; then
94+
echo "Invalid version_depth"
95+
exit 1
96+
fi
97+
8498
# We're just checking for commits; we don't ever need to fetch LFS files here!
8599
export GIT_LFS_SKIP_SMUDGE=1
86100

@@ -167,58 +181,88 @@ lines_including_and_after() {
167181
sed -ne "/$escaped_string/,$ p"
168182
}
169183

170-
get_commit(){
171-
for tag in $*; do
172-
commit=$(git rev-list -n 1 $tag)
173-
jq -n '{ref: $tag, commit: $commit}' --arg tag $tag --arg commit $commit
174-
done
175-
}
176-
177184
#if no range is selected just grab the last commit that fits the filter
178-
if [ -z "$log_range" ]
185+
if [ -z "$log_range" ] && [ -z "$tag_filter" ] && [ -z "$tag_regex" ]
179186
then
180187
list_command+="| git rev-list --stdin --date-order --no-walk=unsorted -$version_depth --reverse"
181188
fi
182189

183-
if [ "$reverse" == "true" ]
190+
if [ "$reverse" == "true" ] && [ -z "$tag_filter" ] && [ -z "$tag_regex" ]
184191
then
185192
list_command+="| git rev-list --stdin --date-order --first-parent --no-walk=unsorted --reverse"
186193
fi
187194

188-
if [ -n "$tag_filter" ]; then
189-
{
190-
if [ -n "$ref" ] && [ -n "$branch" ]; then
191-
tags=$(git tag --list "$tag_filter" --sort=creatordate --contains $ref --merged $branch)
192-
get_commit $tags
193-
elif [ -n "$ref" ]; then
194-
tags=$(git tag --list "$tag_filter" --sort=creatordate | lines_including_and_after $ref)
195-
get_commit $tags
196-
else
197-
branch_flag=
198-
if [ -n "$branch" ]; then
199-
branch_flag="--merged $branch"
195+
get_tags_matching_filter() {
196+
local list_command=$1
197+
local tags=$2
198+
for tag in $tags; do
199+
# We turn the tag ref (e.g. v1.0.0) into the object name
200+
# (e.g. 1a410efbd13591db07496601ebc7a059dd55cfe9) and use grep to check it is in the output
201+
# of list_command - if it isn't, it doesn't pass one of the other filters and shouldn't be
202+
# outputted.
203+
local commit=$(git rev-list -n 1 $tag)
204+
local this_list_command="$list_command | grep -cFx \"$commit\""
205+
local list_output="$(set -f; eval "$this_list_command"; set +f)"
206+
if [ "$list_output" -ge 1 ]; then
207+
jq -cn '{ref: $tag, commit: $commit}' --arg tag $tag --arg commit $commit
208+
fi
209+
done
210+
}
211+
212+
get_tags_match_ancestors_filter() {
213+
local list_command=$1
214+
local tags=$2
215+
216+
# Sort commits so that we look at the oldest commits first
217+
local this_list_command="$list_command | git rev-list --stdin --date-order --first-parent --no-walk=unsorted --reverse"
218+
local list_output="$(set -f; eval "$this_list_command"; set +f)"
219+
for commit in $list_output; do
220+
# Output the commit if it is an ancestor of any of the matching tags
221+
local is_ancestor=false
222+
for tag in $tags; do
223+
tag_commit=$(git rev-list -n 1 $tag)
224+
if [ "$tag_commit" == "$commit" ] || git merge-base --is-ancestor "$commit" "$tag_commit"; then
225+
is_ancestor=true
226+
break
200227
fi
201-
tag=$(git tag --list "$tag_filter" --sort=creatordate $branch_flag | tail -$version_depth)
202-
get_commit $tag
228+
done
229+
if [ "$is_ancestor" = true ]; then
230+
jq -cn '{ref: $commit}' --arg commit $commit
203231
fi
204-
} | jq -s "map(.)" >&3
205-
elif [ -n "$tag_regex" ]; then
206-
{
207-
if [ -n "$ref" ] && [ -n "$branch" ]; then
208-
tags=$(git tag --list --sort=creatordate --contains $ref --merged $branch | grep -Ex "$tag_regex")
209-
get_commit $tags
210-
elif [ -n "$ref" ]; then
211-
tags=$(git tag --list --sort=creatordate | grep -Ex "$tag_regex" | lines_including_and_after $ref)
212-
get_commit $tags
232+
done
233+
}
234+
235+
if [ -n "$tag_filter" ] || [ -n "$tag_regex" ]; then
236+
# Create a suffix to "git tag" that will apply the tag filter
237+
if [ -n "$tag_filter" ]; then
238+
tag_filter_cmd="--list \"$tag_filter\""
239+
elif [ -n "$tag_regex" ]; then
240+
tag_filter_cmd="| grep -Ex \"$tag_regex\""
241+
fi
242+
243+
# Build a list of tag refs (e.g. v1.0.0) that match the filter
244+
if [ -n "$ref" ] && [ -n "$branch" ]; then
245+
tags=$(set -f; eval "git tag --sort=creatordate --contains $ref --merged $branch $tag_filter_cmd"; set +f)
246+
elif [ -n "$ref" ]; then
247+
tags=$(set -f; eval "git tag --sort=creatordate $tag_filter_cmd | lines_including_and_after $ref"; set +f)
248+
else
249+
branch_flag=
250+
if [ -n "$branch" ]; then
251+
branch_flag="--merged $branch"
252+
fi
253+
tags=$(set -f; eval "git tag --sort=creatordate $branch_flag $tag_filter_cmd"; set +f)
254+
fi
255+
256+
# Only proceed if we actually found any tags
257+
if [ -n "$tags" ]; then
258+
if [ "$tag_behaviour" == "match_tagged" ]; then
259+
get_tags_matching_filter "$list_command" "$tags" | tail "-$version_depth" | jq -s "map(.)" >&3
213260
else
214-
branch_flag=
215-
if [ -n "$branch" ]; then
216-
branch_flag="--merged $branch"
217-
fi
218-
tag=$(git tag --list --sort=creatordate $branch_flag | grep -Ex "$tag_regex" | tail -$version_depth)
219-
get_commit $tag
261+
get_tags_match_ancestors_filter "$list_command" "$tags" | tail "-$version_depth" | jq -s "map(.)" >&3
220262
fi
221-
} | jq -s "map(.)" >&3
263+
else
264+
jq -n "[]" >&3
265+
fi
222266
else
223267
{
224268
set -f

test/check.sh

+108-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ set -e
44

55
source $(dirname $0)/helpers.sh
66

7+
check_jq_functionality() {
8+
# Ensure JQ correctly treats empty input as invalid
9+
set +e
10+
jq -e 'type == "string"' </dev/null
11+
if [ $? -eq 0 ]; then
12+
echo "WARNING - Outdated JQ - please update! Some tests may have incorrectly pass"
13+
fi
14+
set -e
15+
}
16+
717
it_can_check_from_head() {
818
local repo=$(init_repo)
919
local ref=$(make_commit $repo)
@@ -644,6 +654,97 @@ it_can_check_with_tag_filter() {
644654
"
645655
}
646656

657+
it_can_check_with_tag_and_path_match_filter() {
658+
local repo=$(init_repo)
659+
local ref1=$(make_commit_to_file $repo file-a)
660+
local ref2=$(make_annotated_tag $repo "1.0-staging" "tag 1" true)
661+
local ref3=$(make_commit_to_file $repo file-b)
662+
local ref4=$(make_annotated_tag $repo "1.0-production" "tag 2" true)
663+
local ref5=$(make_annotated_tag $repo "2.0-staging" "tag 3" true)
664+
local ref6=$(make_commit_to_file $repo file-c)
665+
local ref7=$(make_annotated_tag $repo "2.0-staging" "tag 5" true)
666+
local ref8=$(make_commit_to_file $repo file-b)
667+
local ref9=$(make_annotated_tag $repo "2.0-production" "tag 4" true)
668+
local ref10=$(make_commit_to_file $repo file-c)
669+
670+
check_uri_with_tag_and_path_filter $repo "2*" "match_tagged" file-c | jq -e "
671+
. == [{ref: \"2.0-staging\", commit: \"$ref6\"}]
672+
"
673+
674+
# No matching files. ref3's tag was replaced by the tag on ref6 - which does not path match
675+
check_uri_with_tag_and_path_filter $repo "*-staging" "match_tagged" file-b | jq -e "
676+
. == []
677+
"
678+
679+
# Although multiple tagged commits modify the files, ref8 is the latest (and version_depth is default 1)
680+
check_uri_with_tag_and_path_filter $repo "*" "match_tagged" file-b file-c | jq -e "
681+
. == [
682+
{ref: \"2.0-production\", commit: \"$ref8\"}
683+
]
684+
"
685+
686+
# file-f was never created
687+
check_uri_with_tag_and_path_filter $repo "*" "match_tagged" file-f | jq -e "
688+
. == []
689+
"
690+
691+
# no tags matching 4.0-*
692+
check_uri_with_tag_and_path_filter $repo "4.0-*" "match_tagged" file-c | jq -e "
693+
. == []
694+
"
695+
}
696+
697+
it_can_check_with_tag_and_path_match_ancestors_filter() {
698+
local repo=$(init_repo)
699+
local ref1=$(make_commit_to_file $repo file-a)
700+
local ref2=$(make_annotated_tag $repo "1.0-staging" "tag 1" true)
701+
local ref3=$(make_commit_to_file $repo file-b)
702+
local ref4=$(make_annotated_tag $repo "1.0-production" "tag 2" true)
703+
local ref5=$(make_annotated_tag $repo "2.0-staging" "tag 3" true)
704+
local ref6=$(make_commit_to_file $repo file-c)
705+
local ref7=$(make_annotated_tag $repo "2.0-staging" "tag 5" true)
706+
local ref8=$(make_commit_to_file $repo file-b)
707+
local ref9=$(make_annotated_tag $repo "2.0-production" "tag 4" true)
708+
local ref10=$(make_commit_to_file $repo file-c)
709+
710+
# ref10 is the most recent to modify file-c, but the latest 2* tag is before it. Therefore ref6 matches.
711+
check_uri_with_tag_and_path_filter $repo "2*" "match_tag_ancestors" file-c | jq -e "
712+
. == [{ref: \"$ref6\"}]
713+
"
714+
715+
# ref3 is the most recent commit to modify file-b, and following commits have -staging tags
716+
check_uri_with_tag_and_path_filter $repo "*-staging" "match_tag_ancestors" file-b | jq -e "
717+
. == [{ref: \"$ref3\"}]
718+
"
719+
720+
# although no 2.* tagged commits modified file-a, they follow on from commits that did
721+
check_uri_with_tag_and_path_filter $repo "2.*" "match_tag_ancestors" file-a | jq -e "
722+
. == [{ref: \"$ref1\"}]
723+
"
724+
725+
# no commits creating file-c are followed by 1.* tags
726+
check_uri_with_tag_and_path_filter $repo "1.*" "match_tag_ancestors" file-c | jq -e "
727+
. == []
728+
"
729+
730+
# Although multiple tagged commits modify the files, ref8 is the latest (and version_depth is default 1)
731+
check_uri_with_tag_and_path_filter $repo "*" "match_tag_ancestors" file-b file-c | jq -e "
732+
. == [
733+
{ref: \"$ref8\"}
734+
]
735+
"
736+
737+
# file-f was never created
738+
check_uri_with_tag_and_path_filter $repo "*" "match_tag_ancestors" file-f | jq -e "
739+
. == []
740+
"
741+
742+
# no tags matching 4.0-*
743+
check_uri_with_tag_and_path_filter $repo "4.0-*" "match_tag_ancestors" file-c | jq -e "
744+
. == []
745+
"
746+
}
747+
647748
it_can_check_with_tag_regex() {
648749
local repo=$(init_repo)
649750
local ref1=$(make_commit $repo)
@@ -679,7 +780,7 @@ it_can_check_with_tag_filter_with_cursor() {
679780
local ref13=$(make_commit $repo)
680781

681782
x=$(check_uri_with_tag_filter_from $repo "*-staging" "2.0-staging")
682-
check_uri_with_tag_filter_from $repo "*-staging" "2.0-staging" | jq -e "
783+
check_uri_with_tag_filter_from $repo "*-staging" "2.0-staging" 2 | jq -e "
683784
. == [{ref: \"2.0-staging\", commit: \"$ref5\"}, {ref: \"3.0-staging\", commit: \"$ref9\"}]
684785
"
685786
}
@@ -701,7 +802,7 @@ it_can_check_with_tag_regex_with_cursor() {
701802
local ref13=$(make_commit $repo)
702803

703804
x=$(check_uri_with_tag_regex_from $repo ".*-staging" "2.0-staging")
704-
check_uri_with_tag_regex_from $repo ".*-staging" "2.0-staging" | jq -e "
805+
check_uri_with_tag_regex_from $repo ".*-staging" "2.0-staging" 2 | jq -e "
705806
. == [{ref: \"2.0-staging\", commit: \"$ref5\"}, {ref: \"3.0-staging\", commit: \"$ref9\"}]
706807
"
707808
}
@@ -765,7 +866,7 @@ it_can_check_with_tag_filter_over_all_branches_with_cursor() {
765866
local ref13=$(make_annotated_tag $repo "3.0-production" "tag 6")
766867
local ref14=$(make_commit_to_branch $repo branch-a)
767868

768-
check_uri_with_tag_filter_from $repo "*-staging" "2.0-staging" | jq -e "
869+
check_uri_with_tag_filter_from $repo "*-staging" "2.0-staging" 2 | jq -e "
769870
. == [{ref: \"2.0-staging\", commit: \"$ref6\"}, {ref: \"3.0-staging\", commit: \"$ref10\"}]
770871
"
771872
}
@@ -787,7 +888,7 @@ it_can_check_with_tag_regex_over_all_branches_with_cursor() {
787888
local ref13=$(make_annotated_tag $repo "3.0-production" "tag 6")
788889
local ref14=$(make_commit_to_branch $repo branch-a)
789890

790-
check_uri_with_tag_regex_from $repo ".*-staging" "2.0-staging" | jq -e "
891+
check_uri_with_tag_regex_from $repo ".*-staging" "2.0-staging" 2 | jq -e "
791892
. == [{ref: \"2.0-staging\", commit: \"$ref6\"}, {ref: \"3.0-staging\", commit: \"$ref10\"}]
792893
"
793894
}
@@ -1014,6 +1115,7 @@ it_checks_uri_with_tag_filter_and_version_depth() {
10141115
]"
10151116
}
10161117

1118+
run check_jq_functionality
10171119
run it_can_check_from_head
10181120
run it_can_check_from_a_ref
10191121
run it_can_check_from_a_first_commit_in_repo
@@ -1054,6 +1156,8 @@ run it_can_check_with_tag_filter_with_bogus_ref
10541156
run it_can_check_with_tag_regex_with_bogus_ref
10551157
run it_can_check_with_tag_filter_with_replaced_tags
10561158
run it_can_check_with_tag_regex_with_replaced_tags
1159+
run it_can_check_with_tag_and_path_match_filter
1160+
run it_can_check_with_tag_and_path_match_ancestors_filter
10571161
run it_can_check_from_head_only_fetching_single_branch
10581162
run it_can_check_and_set_git_config
10591163
run it_can_check_from_a_ref_and_only_show_merge_commit

0 commit comments

Comments
 (0)