Skip to content

Commit 72fd5e0

Browse files
committed
safer tag-to-branch resolution
1 parent 1e6bae5 commit 72fd5e0

2 files changed

Lines changed: 84 additions & 21 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ If you submitted a detailed HTML report of the coverage to the action, replace t
2222

2323
`https://html-preview.github.io/?url=https://github.com/USERNAME/REPO/blob/coverage/BRANCH/report.html`
2424

25+
### Inputs
26+
27+
- `coverage` (required): Coverage percentage (for example `83` or `83%`).
28+
- `report` (optional): Path to an HTML report file to publish as `report.html`.
29+
- `branch` (optional): Source branch override. Recommended for tag-triggered workflows where multiple branches may contain the same tag commit.
30+
2531
## Examples
2632

2733
Inside your .github/workflows/workflow.yml file:
@@ -86,3 +92,13 @@ jobs:
8692
coverage: ${{ steps.coverage.outputs.coverage }}
8793
report: "coveragereport.html"
8894
```
95+
96+
Tag workflow example with explicit source branch:
97+
98+
```yml
99+
- name: Publish code coverage badge from tag build
100+
uses: linkdata/gitcoverage@v3
101+
with:
102+
coverage: "91%"
103+
branch: "release/1.x"
104+
```

action.yml

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ inputs:
2222
coverage:
2323
description: "Coverage percentage (e.g. 83 or 83%)"
2424
required: true
25+
branch:
26+
description: "Optional source branch name override (recommended for tag-triggered workflows)"
27+
required: false
2528
report:
2629
description: "Optional path to an HTML coverage report file to publish as report.html"
2730
required: false
@@ -39,6 +42,8 @@ runs:
3942
- name: Detect current branch (handles tags; fetches tags; robust)
4043
id: branch
4144
shell: bash
45+
env:
46+
INPUT_BRANCH: ${{ inputs.branch }}
4247
run: |
4348
set -euo pipefail
4449
@@ -61,6 +66,23 @@ runs:
6166
# Fetch all remote heads so we can query them locally
6267
git fetch origin +refs/heads/*:refs/remotes/origin/* --prune
6368
69+
# Explicit override: useful for tag workflows where multiple branches may contain the tag.
70+
BRANCH="${INPUT_BRANCH:-}"
71+
BRANCH="$(echo "$BRANCH" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
72+
if [[ -n "$BRANCH" ]]; then
73+
if [[ "$BRANCH" == "HEAD" || "$BRANCH" =~ ^\(.+\)$ ]]; then
74+
echo "Invalid explicit branch input: '$BRANCH'" >&2
75+
exit 1
76+
fi
77+
if ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
78+
echo "Explicit branch '$BRANCH' was not found on origin." >&2
79+
exit 1
80+
fi
81+
echo "Using explicit branch input: $BRANCH"
82+
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
83+
exit 0
84+
fi
85+
6486
# --- Fast paths: PR source branch or branch ref ---
6587
if [[ -n "${GITHUB_HEAD_REF:-}" ]]; then
6688
BRANCH="${GITHUB_HEAD_REF}"
@@ -74,39 +96,64 @@ runs:
7496
if [[ -z "$BRANCH" ]] && { [[ "${GITHUB_REF_TYPE:-}" == "tag" ]] || [[ "${GITHUB_REF:-}" == refs/tags/* ]]; }; then
7597
TAG="${GITHUB_REF_NAME:-${GITHUB_REF#refs/tags/}}"
7698
TAG_REF="tags/$TAG"
99+
TAG_SHA="$(git rev-list -n1 "$TAG_REF")"
77100
echo "Ref is a tag (${TAG}); resolving containing branch..."
78101
79-
# 1) Prefer remote branches that contain the tag commit
102+
# 1) Gather remote branches that contain the tag commit
80103
CANDIDATES=()
81104
while IFS= read -r r; do
82105
[[ -z "$r" || "$r" == origin/HEAD ]] && continue
83106
CANDIDATES+=("${r#origin/}")
84107
done < <(git branch -r --format "%(refname:short)" --contains "$TAG_REF" 2>/dev/null || true)
85108
86-
# 2) If none (common in shallow graphs), pick branches whose TIP == tag commit
87-
if [[ ${#CANDIDATES[@]} -eq 0 ]]; then
88-
TAG_SHA="$(git rev-list -n1 "$TAG_REF")"
89-
while IFS= read -r rb; do
90-
[[ -z "$rb" ]] && continue
91-
[[ "$rb" == origin/HEAD ]] && continue
92-
b="${rb#origin/}"
93-
BR_SHA="$(git rev-parse "$rb" 2>/dev/null || true)"
94-
[[ -n "$BR_SHA" && "$BR_SHA" == "$TAG_SHA" ]] && CANDIDATES+=("$b")
95-
done < <(git for-each-ref --format='%(refname:short)' refs/remotes/origin/)
109+
# 2) Prefer branches whose TIP exactly matches the tagged commit.
110+
TIP_MATCHES=()
111+
while IFS= read -r rb; do
112+
[[ -z "$rb" || "$rb" == origin/HEAD ]] && continue
113+
b="${rb#origin/}"
114+
BR_SHA="$(git rev-parse "$rb" 2>/dev/null || true)"
115+
[[ -n "$BR_SHA" && "$BR_SHA" == "$TAG_SHA" ]] && TIP_MATCHES+=("$b")
116+
done < <(git for-each-ref --format='%(refname:short)' refs/remotes/origin/)
117+
if [[ ${#TIP_MATCHES[@]} -gt 0 ]]; then
118+
CANDIDATES=("${TIP_MATCHES[@]}")
96119
fi
97120
98-
# 3) Choose best candidate: origin/HEAD > main/master > first
121+
# 3) Choose nearest containing branch by smallest ahead distance from tag commit.
122+
# If still ambiguous, fail instead of silently picking the wrong branch.
99123
DEF="$(resolve_default_branch)"
100-
if [[ -n "$DEF" ]]; then
101-
for b in "${CANDIDATES[@]}"; do [[ "$b" == "$DEF" ]] && BRANCH="$b" && break; done
102-
fi
103-
if [[ -z "$BRANCH" ]]; then
104-
for pref in main master; do
105-
for b in "${CANDIDATES[@]}"; do [[ "$b" == "$pref" ]] && BRANCH="$b" && break 2; done
106-
done
107-
fi
108-
if [[ -z "$BRANCH" && ${#CANDIDATES[@]} -gt 0 ]]; then
124+
if [[ ${#CANDIDATES[@]} -eq 1 ]]; then
109125
BRANCH="${CANDIDATES[0]}"
126+
elif [[ ${#CANDIDATES[@]} -gt 1 ]]; then
127+
BEST_BRANCH=""
128+
BEST_DIST=""
129+
TIED=0
130+
for b in "${CANDIDATES[@]}"; do
131+
dist="$(git rev-list --count "${TAG_REF}..origin/${b}" 2>/dev/null || echo 999999)"
132+
[[ "$dist" =~ ^[0-9]+$ ]] || dist=999999
133+
if [[ -z "$BEST_DIST" || "$dist" -lt "$BEST_DIST" ]]; then
134+
BEST_DIST="$dist"
135+
BEST_BRANCH="$b"
136+
TIED=0
137+
elif [[ "$dist" -eq "$BEST_DIST" ]]; then
138+
if [[ -n "$DEF" && "$b" == "$DEF" ]]; then
139+
BEST_BRANCH="$b"
140+
TIED=0
141+
elif [[ -n "$DEF" && "$BEST_BRANCH" == "$DEF" ]]; then
142+
:
143+
else
144+
TIED=1
145+
fi
146+
fi
147+
done
148+
149+
if [[ -n "$BEST_BRANCH" && "$TIED" -eq 0 ]]; then
150+
BRANCH="$BEST_BRANCH"
151+
else
152+
echo "Ambiguous tag-to-branch mapping for tag '${TAG}'." >&2
153+
echo "Candidates: ${CANDIDATES[*]}" >&2
154+
echo "Set input 'branch' to the intended source branch." >&2
155+
exit 1
156+
fi
110157
fi
111158
fi
112159

0 commit comments

Comments
 (0)