@@ -10,11 +10,15 @@ permissions:
1010jobs :
1111 increment-and-notify :
1212 runs-on : ubuntu-latest
13+ env :
14+ RETRY_MAX : 3
15+ COMMIT_LIMIT : 50 # max commits to include in Telegram message
1316 steps :
1417 - name : Checkout pushed ref (full)
1518 uses : actions/checkout@v4
1619 with :
17- fetch-depth : 0 # needed to list tags and read history
20+ fetch-depth : 0
21+ token : ${{ secrets.GITHUB_TOKEN }}
1822
1923 - name : Configure git user
2024 run : |
@@ -27,12 +31,12 @@ jobs:
2731 # Pattern: rev-<number>
2832 latest=$(git tag --list 'rev-*' --sort=-v:refname | head -n1 || true)
2933 if [ -z "$latest" ]; then
30- echo "::set-output name= latest:: "
31- echo "::set-output name= num:: "
34+ echo "latest=" >> "$GITHUB_OUTPUT "
35+ echo "num=" >> "$GITHUB_OUTPUT "
3236 else
3337 num=${latest#rev-}
34- echo "::set-output name= latest:: $latest"
35- echo "::set-output name= num:: $num"
38+ echo "latest= $latest" >> "$GITHUB_OUTPUT "
39+ echo "num= $num" >> "$GITHUB_OUTPUT "
3640 fi
3741
3842 - name : Determine next rev number
@@ -50,46 +54,150 @@ jobs:
5054 fi
5155 fi
5256 tag="rev-${next}"
53- echo "::set-output name= tag:: $tag"
54- echo "::set-output name= number:: $next"
57+ echo "tag= $tag" >> "$GITHUB_OUTPUT "
58+ echo "number= $next" >> "$GITHUB_OUTPUT "
5559
56- - name : Create and push rev tag
60+ - name : Create and push rev tag (with retry to avoid race)
61+ id : push_tag
5762 env :
5863 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
5964 run : |
60- tag="${{ steps.next_rev.outputs.tag }}"
61- echo "Creating tag $tag at $GITHUB_SHA"
62- git tag "$tag" "$GITHUB_SHA"
63- git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" "refs/tags/${tag}"
65+ set -euo pipefail
66+ attempt=0
67+ # Determine target SHA to tag (prefer GITHUB_SHA if valid, else HEAD)
68+ target_sha="${GITHUB_SHA:-}"
69+ if ! git rev-parse --verify "$target_sha" >/dev/null 2>&1; then
70+ target_sha=$(git rev-parse --verify HEAD)
71+ fi
72+
73+ while [ $attempt -lt "$RETRY_MAX" ]; do
74+ attempt=$((attempt + 1))
75+ # Recompute latest to reduce window-of-time races
76+ latest=$(git tag --list 'rev-*' --sort=-v:refname | head -n1 || true)
77+ if [ -z "$latest" ]; then
78+ num=0
79+ else
80+ num=${latest#rev-}
81+ if ! echo "$num" | grep -Eq '^[0-9]+$'; then
82+ num=0
83+ fi
84+ fi
85+ next=$((num + 1))
86+ tag="rev-${next}"
87+ echo "Attempt $attempt: creating tag $tag at $target_sha"
88+
89+ # Create annotated tag (safer metadata) and push
90+ git tag -a "$tag" -m "Auto increment rev tag ${tag}" "$target_sha"
91+ if git push origin "refs/tags/${tag}"; then
92+ echo "pushed_tag=$tag" >> "$GITHUB_OUTPUT"
93+ echo "pushed=1" >> "$GITHUB_OUTPUT"
94+ exit 0
95+ else
96+ echo "Push failed (possible collision). Deleting local tag and retrying."
97+ git tag -d "$tag" || true
98+ sleep 1
99+ fi
100+ done
101+
102+ echo "pushed=" >> "$GITHUB_OUTPUT"
103+ echo "pushed_tag=" >> "$GITHUB_OUTPUT"
104+ echo "Failed to create tag after $RETRY_MAX attempts."
105+ exit 1
64106
65107 - name : Find latest rev tag (by creation date for notification)
66108 id : latest_for_notify
67109 run : |
68- # find latest rev-* tag by creation date; leave empty if none
110+ # Ensure tags are available
111+ git fetch --tags --prune
112+ # find latest rev-* tag by creation date; prefer annotated tag taggerdate, fall back to commit date
69113 entry=$(git for-each-ref --sort=-creatordate --format='%(refname:short) %(creatordate:unix)' refs/tags | grep '^rev-[0-9]\+' | head -n1 || true)
70114 if [ -z "$entry" ]; then
71- echo "::set-output name= tag:: "
72- echo "::set-output name=ts:: "
115+ echo "tag=" >> "$GITHUB_OUTPUT "
116+ echo "ts=" >> "$GITHUB_OUTPUT "
73117 else
74118 tag=$(echo "$entry" | awk '{print $1}')
75119 ts=$(echo "$entry" | awk '{print $2}')
76- echo "::set-output name= tag:: $tag"
77- echo "::set-output name=ts:: $ts"
120+ echo "tag= $tag" >> "$GITHUB_OUTPUT "
121+ echo "ts= $ts" >> "$GITHUB_OUTPUT "
78122 fi
79123
80124 - name : Build Telegram message
81125 id : message
82126 run : |
127+ set -euo pipefail
83128 repo="${GITHUB_REPOSITORY}"
84- branch="${GITHUB_REF#refs/heads/}"
85- sha_full="${GITHUB_SHA}"
86- sha="${sha_full::7}"
87- author="$(git --no-pager show -s --format='%an' $GITHUB_SHA || echo '')"
88- msg="$(git --no-pager show -s --format='%s' $GITHUB_SHA || echo '')"
129+ repo_url="https://github.com/${repo}"
130+ ref="${GITHUB_REF:-}"
131+ branch=""
132+ pushed_tag=""
133+ if echo "$ref" | grep -q '^refs/heads/'; then
134+ branch="${ref#refs/heads/}"
135+ elif echo "$ref" | grep -q '^refs/tags/'; then
136+ pushed_tag="${ref#refs/tags/}"
137+ branch=""
138+ else
139+ branch="$ref"
140+ fi
141+
142+ sha_full="${GITHUB_SHA:-$(git rev-parse --verify HEAD)}"
143+ sha_short="${sha_full::7}"
144+
89145 latest_tag="${{ steps.latest_for_notify.outputs.tag }}"
90- tag_part="Rev: ${latest_tag:-(none)}"
91- body="Repo: ${repo}%0ABranch: ${branch}%0ACommit: ${sha}%0AAuthor: ${author}%0AMessage: ${msg}%0A${tag_part}"
92- echo "::set-output name=body::$body"
146+
147+ before="${GITHUB_EVENT_BEFORE:-}"
148+ after="${GITHUB_EVENT_AFTER:-$GITHUB_SHA}"
149+
150+ # Build commits list safely using a unit separator
151+ if [ -n "$before" ] && [ "$before" != "0000000000000000000000000000000000000000" ]; then
152+ range="${before}..${after}"
153+ git_cmd=(git --no-pager log --pretty=format:'%h%x1F%an%x1F%s' "${range}" --max-count=${COMMIT_LIMIT})
154+ else
155+ git_cmd=(git --no-pager log -n 1 --pretty=format:'%h%x1F%an%x1F%s' "${after}")
156+ fi
157+
158+ commits_raw=$("${git_cmd[@]}" || true)
159+
160+ commits_text=""
161+ if [ -z "$commits_raw" ]; then
162+ commits_text="(no commits found)"
163+ else
164+ # iterate lines separated by newline
165+ while IFS= read -r line; do
166+ # split on unit separator
167+ IFS=$'\x1F' read -r shorthash author message <<< "$line"
168+ # resolve full hash where possible
169+ fullhash=$(git rev-parse "${shorthash}" 2>/dev/null || echo "${shorthash}")
170+ commit_url="${repo_url}/commit/${fullhash}"
171+ # escape HTML entities for Telegram parse_mode=HTML
172+ esc_msg=$(printf '%s' "$message" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g')
173+ esc_author=$(printf '%s' "$author" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g')
174+ commits_text="${commits_text}${shorthash} - ${esc_msg} (${esc_author})%0A${commit_url}%0A"
175+ done <<< "$commits_raw"
176+ fi
177+
178+ repo_link="<a href=\"${repo_url}\">${repo}</a>"
179+ commit_link="<a href=\"${repo_url}/commit/${sha_full}\">${sha_short}</a>"
180+
181+ tag_link="(none)"
182+ if [ -n "${latest_tag}" ]; then
183+ tag_link="<a href=\"${repo_url}/releases/tag/${latest_tag}\">${latest_tag}</a>"
184+ fi
185+
186+ branch_info=""
187+ if [ -n "$branch" ]; then
188+ branch_info="Branch: ${branch}%0A"
189+ elif [ -n "$pushed_tag" ]; then
190+ branch_info="Tag push: ${pushed_tag}%0A"
191+ fi
192+
193+ body="Repo: ${repo_link}%0A${branch_info}Commit: ${commit_link}%0ATag: ${tag_link}%0A%0ACommits:%0A${commits_text}"
194+ # Truncate body if excessively long (Telegram has limits ~4096 chars)
195+ maxlen=3800
196+ if [ "${#body}" -gt "$maxlen" ]; then
197+ body="${body:0:$maxlen}%0A...(truncated)"
198+ fi
199+
200+ echo "body=$body" >> "$GITHUB_OUTPUT"
93201
94202 - name : Send Telegram notification
95203 env :
@@ -108,4 +216,4 @@ jobs:
108216
109217 - name : Output created tag
110218 run : |
111- echo "Created tag: ${{ steps.next_rev .outputs.tag }}"
219+ echo "Created tag: ${{ steps.push_tag .outputs.pushed_tag }}"
0 commit comments