@@ -60,6 +60,12 @@ violations = []
6060checks = []
6161require_release_tag = os.environ.get("REQUIRE_RELEASE_TAG", "0") == "1"
6262expected_release_tag = os.environ.get("EXPECTED_RELEASE_TAG", "").strip()
63+ release_tag_regex = re.compile(r"^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$")
64+
65+
66+ def semver_core(version: str) -> str:
67+ m = re.match(r"^([0-9]+\.[0-9]+\.[0-9]+)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$", version or "")
68+ return m.group(1) if m else ""
6369
6470
6571def rel(p: Path) -> str:
118124 raw_tags = subprocess.check_output(["git", "-C", str(repo_root), "tag", "--points-at", "HEAD"], text=True)
119125 for line in raw_tags.splitlines():
120126 t = line.strip()
121- if re .match(r"^v[0-9]+\.[0-9]+\.[0-9]+$", t):
127+ if release_tag_regex .match(t):
122128 head_tags.append(t)
123129except Exception as ex:
124130 fail("git.tag_lookup", "command succeeds", "failed", ".git", f"Failed to resolve HEAD tags: {ex}")
@@ -128,8 +134,8 @@ expected_version = ""
128134# workflow_dispatch releases provide EXPECTED_RELEASE_TAG explicitly.
129135# tag-push releases derive the expected version from the exact release tag on HEAD.
130136if expected_release_tag:
131- if not re .match(r"^v[0-9]+\.[0-9]+\.[0-9]+$", expected_release_tag):
132- fail("svt.expected_release_tag", "vX.Y.Z", expected_release_tag, "env:EXPECTED_RELEASE_TAG", "Invalid EXPECTED_RELEASE_TAG format")
137+ if not release_tag_regex .match(expected_release_tag):
138+ fail("svt.expected_release_tag", "vX.Y.Z[-prerelease] ", expected_release_tag, "env:EXPECTED_RELEASE_TAG", "Invalid EXPECTED_RELEASE_TAG format")
133139 else:
134140 expected_version = expected_release_tag[1:]
135141 checks.append({
@@ -141,9 +147,9 @@ if expected_release_tag:
141147 })
142148elif len(head_tags) == 0:
143149 if require_release_tag:
144- fail("svt.head_tag", "exactly one tag vX.Y.Z on HEAD", "none", ".git", "No exact release tag on HEAD")
150+ fail("svt.head_tag", "exactly one tag vX.Y.Z[-prerelease] on HEAD", "none", ".git", "No exact release tag on HEAD")
145151elif len(head_tags) > 1:
146- fail("svt.head_tag", "exactly one tag vX.Y.Z on HEAD", ",".join(head_tags), ".git", "Multiple release tags on HEAD")
152+ fail("svt.head_tag", "exactly one tag vX.Y.Z[-prerelease] on HEAD", ",".join(head_tags), ".git", "Multiple release tags on HEAD")
147153else:
148154 expected_version = head_tags[0][1:]
149155# --- repo SSOT version consistency (no mixed versions) ---
@@ -179,7 +185,13 @@ else:
179185 fail("repo.ssot.RepoVersion.semver", "X.Y.Z", repo_version, rel(repo_props), "RepoVersion is not semver X.Y.Z")
180186 # if tag defines the release version, RepoVersion must match exactly
181187 if expected_version:
182- check("repo.ssot.RepoVersion", expected_version, repo_version, rel(repo_props))
188+ expected_core_version = semver_core(expected_version)
189+ if expected_core_version == "":
190+ fail("repo.ssot.RepoVersion.tag.semver", "X.Y.Z[-prerelease]", expected_version, "env:EXPECTED_RELEASE_TAG", "Expected release version is not semver-compatible")
191+ elif "-" in expected_version:
192+ check("repo.ssot.RepoVersion.core", expected_core_version, repo_version, rel(repo_props))
193+ else:
194+ check("repo.ssot.RepoVersion", expected_version, repo_version, rel(repo_props))
183195
184196check_csproj_uses_repo_version(repo_root / "samples" / "PortableConsumer" / "PortableConsumer.csproj", "PortableConsumerPackageVersion")
185197check_csproj_uses_repo_version(repo_root / "tests" / "PackageBacked.Tests" / "PackageBacked.Tests.csproj", "PackageBackedVersion")
@@ -210,8 +222,15 @@ if isinstance(project_files, list) and len(project_files) > 0:
210222 check("vbproj.Version_vs_PackageVersion", vbproj_version, vbproj_package_version, rel(project_path))
211223
212224if expected_version and project_path is not None and project_path.exists():
213- check("svt.tag_vs_vbproj.Version", expected_version, vbproj_version, rel(project_path))
214- check("svt.tag_vs_vbproj.PackageVersion", expected_version, vbproj_package_version, rel(project_path))
225+ expected_core_version = semver_core(expected_version)
226+ if expected_core_version == "":
227+ fail("svt.expected_version.semver", "X.Y.Z[-prerelease]", expected_version, "env:EXPECTED_RELEASE_TAG", "Derived expected version is not semver-compatible")
228+ elif "-" in expected_version:
229+ check("svt.tag_core_vs_vbproj.Version", expected_core_version, vbproj_version, rel(project_path))
230+ check("svt.tag_core_vs_vbproj.PackageVersion", expected_core_version, vbproj_package_version, rel(project_path))
231+ else:
232+ check("svt.tag_vs_vbproj.Version", expected_version, vbproj_version, rel(project_path))
233+ check("svt.tag_vs_vbproj.PackageVersion", expected_version, vbproj_package_version, rel(project_path))
215234
216235nupkg_dir = repo_root / "artifacts" / "nuget"
217236nupkg_files = sorted([p for p in nupkg_dir.glob("*.nupkg") if not p.name.endswith(".snupkg")])
0 commit comments