77 workflow_dispatch :
88
99permissions :
10- contents : read
10+ contents : write
1111
1212jobs :
1313 publish :
2222 steps :
2323 - name : Checkout
2424 uses : actions/checkout@v4
25+ with :
26+ fetch-depth : 0
2527
2628 - name : Setup Java
2729 uses : actions/setup-java@v4
@@ -41,13 +43,16 @@ jobs:
4143 run : |
4244 set -euo pipefail
4345
46+ python3 -m pip install --disable-pip-version-check --quiet semver
47+
4448 python3 <<'PY' >> "$GITHUB_OUTPUT"
4549 import base64
4650 import os
4751 import sys
4852 import urllib.request
4953 import urllib.error
5054 import xml.etree.ElementTree as ET
55+ import semver
5156
5257 ns = {"m": "http://maven.apache.org/POM/4.0.0"}
5358 pom = ET.parse("pom.xml").getroot()
@@ -72,28 +77,22 @@ jobs:
7277 except Exception:
7378 latest = "0.0.0"
7479
75- def parse_semver(value: str):
76- core = value.split("+", 1)[0]
77- main, _, prerelease = core.partition("-")
78- major, minor, patch = (int(part) for part in main.split(".")[:3])
79- return major, minor, patch, prerelease
80-
81- def is_greater(left: str, right: str) -> bool:
82- left_parts = parse_semver(left)
83- right_parts = parse_semver(right)
84- if left_parts[:3] != right_parts[:3]:
85- return left_parts[:3] > right_parts[:3]
86- left_pre = left_parts[3]
87- right_pre = right_parts[3]
88- if left_pre == right_pre:
89- return False
90- if not left_pre:
91- return True
92- if not right_pre:
93- return False
94- return left_pre > right_pre
95-
96- publish = is_greater(version, latest)
80+ def to_semver(value: str):
81+ try:
82+ return semver.Version.parse(value)
83+ except ValueError:
84+ return None
85+
86+ current_semver = to_semver(version)
87+ latest_semver = to_semver(latest) or semver.Version.parse("0.0.0")
88+ if current_semver is None:
89+ print(f"current_version={version}")
90+ print(f"latest_version={latest}")
91+ print("publish=false")
92+ print("reason=current version is not valid semver")
93+ sys.exit(0)
94+
95+ publish = current_semver > latest_semver
9796 reason = "current version is newer than published version"
9897 if publish:
9998 # If a deployment for this exact version is already staged/ongoing in the Portal,
@@ -133,6 +132,82 @@ jobs:
133132 if : steps.gate.outputs.publish == 'true'
134133 run : mvn -B -ntp -DskipTests -DpublishRelease=true deploy
135134
135+ - name : Ensure release tag is in sync
136+ if : steps.gate.outputs.publish == 'true' && steps.publish_step.outcome == 'success'
137+ env :
138+ VERSION : ${{ steps.gate.outputs.current_version }}
139+ shell : bash
140+ run : |
141+ set -euo pipefail
142+ tag="v${VERSION}"
143+ current_sha="$(git rev-parse HEAD)"
144+
145+ if git rev-parse "$tag" >/dev/null 2>&1; then
146+ tag_sha="$(git rev-list -n 1 "$tag")"
147+ if [ "$tag_sha" != "$current_sha" ]; then
148+ echo "::error::Tag $tag already exists at $tag_sha, expected $current_sha"
149+ exit 1
150+ fi
151+ echo "Tag $tag already in sync with $current_sha"
152+ exit 0
153+ fi
154+
155+ git config user.name "github-actions[bot]"
156+ git config user.email "github-actions[bot]@users.noreply.github.com"
157+ git tag -a "$tag" -m "Release $tag"
158+ git push origin "$tag"
159+ echo "Created and pushed tag $tag"
160+
161+ - name : Create or update GitHub release notes
162+ if : steps.gate.outputs.publish == 'true' && steps.publish_step.outcome == 'success'
163+ env :
164+ GH_TOKEN : ${{ github.token }}
165+ VERSION : ${{ steps.gate.outputs.current_version }}
166+ shell : bash
167+ run : |
168+ set -euo pipefail
169+ tag="v${VERSION}"
170+ notes_file="/tmp/release_notes.md"
171+
172+ python3 - <<'PY'
173+ import os
174+ from pathlib import Path
175+
176+ version = os.environ["VERSION"]
177+ header = f"## [{version}]"
178+ changelog = Path("CHANGELOG.md")
179+ output = Path("/tmp/release_notes.md")
180+
181+ if not changelog.exists():
182+ output.write_text(f"Release {version}", encoding="utf-8")
183+ raise SystemExit(0)
184+
185+ lines = changelog.read_text(encoding="utf-8").splitlines()
186+ in_target = False
187+ captured = []
188+ for line in lines:
189+ if line.startswith(header):
190+ in_target = True
191+ continue
192+ if in_target and line.startswith("## ["):
193+ break
194+ if in_target:
195+ captured.append(line)
196+
197+ notes = "\n".join(captured).strip()
198+ if not notes:
199+ notes = f"Release {version}"
200+ output.write_text(notes + "\n", encoding="utf-8")
201+ PY
202+
203+ if gh release view "$tag" >/dev/null 2>&1; then
204+ gh release edit "$tag" --title "$tag" --notes-file "$notes_file"
205+ echo "Updated release $tag"
206+ else
207+ gh release create "$tag" --title "$tag" --notes-file "$notes_file"
208+ echo "Created release $tag"
209+ fi
210+
136211 - name : Notify Slack (success)
137212 if : steps.gate.outputs.publish == 'true' && steps.publish_step.outcome == 'success'
138213 env :
@@ -161,29 +236,30 @@ jobs:
161236
162237 changelog = Path("CHANGELOG.md")
163238 latest_changes = "See CHANGELOG for details."
239+ section_header = f"## [{version}]"
164240 if changelog.exists():
165241 lines = changelog.read_text(encoding="utf-8").splitlines()
166- in_latest = False
242+ in_target = False
167243 bullets = []
168244 for line in lines:
169- if line.startswith("## [") and not in_latest :
170- in_latest = True
245+ if line.startswith(section_header) :
246+ in_target = True
171247 continue
172- if in_latest and line.startswith("## ["):
248+ if in_target and line.startswith("## ["):
173249 break
174- if in_latest and line.startswith("- "):
250+ if in_target and line.startswith("- "):
175251 bullets.append(line[2:].strip())
176252 if bullets:
177253 latest_changes = "\n".join(f"• {b}" for b in bullets[:5])
178254
179255 payload = {
180- "text": f"🚀 Facturapi Java SDK {version} published ",
256+ "text": f"🚀 Facturapi Java SDK {version} validated and submitted ",
181257 "blocks": [
182258 {
183259 "type": "header",
184260 "text": {
185261 "type": "plain_text",
186- "text": f"🚀 Java SDK {version} is live on Maven Central"
262+ "text": f"🚀 Java SDK {version} validated and submitted to Maven Central"
187263 }
188264 },
189265 {
@@ -202,8 +278,9 @@ jobs:
202278 "text": (
203279 "*Useful links*\n"
204280 f"• Maven Central: <https://central.sonatype.com/artifact/io.facturapi/facturapi-java/{version}|View artifact>\n"
281+ f"• Central deployment status: <https://central.sonatype.com/publishing/deployments|Check progress>\n"
205282 f"• Workflow run: <{server_url}/{repo}/actions/runs/{run_id}|Open run>\n"
206- f"• Changelog: <{server_url}/{repo}/blob/main /CHANGELOG.md|Read changes>"
283+ f"• Changelog: <{server_url}/{repo}/blob/{sha} /CHANGELOG.md|Read changes>"
207284 )
208285 }
209286 },
0 commit comments