chore: streamline compatibility tracking and documentation
This commit is contained in:
1 parent
92d2ba9889
commit
23f0456622
16 files changed
+476
-259
No files matched your search
@@ -19,4 +19,4 @@ DOCS
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog:** [v1.0.1...v1.27.2](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.0.1...v1.27.2)
|
||||
**Full Changelog:** [v1.0.1...v1.27.2](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/compare/v1.0.1...v1.27.2)
|
||||
@@ -19,4 +19,4 @@ DOCS
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog:** [v1.27.2...v1.27.3](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.27.2...v1.27.3)
|
||||
**Full Changelog:** [v1.27.2...v1.27.3](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/compare/v1.27.2...v1.27.3)
|
||||
@@ -18,4 +18,4 @@ DOCS
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog:** [v1.27.3...v28.0.0](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.27.3...v28.0.0)
|
||||
**Full Changelog:** [v1.27.3...v28.0.0](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/compare/v1.27.3...v28.0.0)
|
||||
@@ -0,0 +1,137 @@
|
||||
"""Offline tests for the compatibility tracker."""
|
||||
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).with_name("track_gitea_release.py")
|
||||
SPEC = importlib.util.spec_from_file_location("track_gitea_release", SCRIPT)
|
||||
TRACKER = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(TRACKER)
|
||||
|
||||
|
||||
class TrackerTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.directory = tempfile.TemporaryDirectory(prefix="tracker-test-", dir=TRACKER.ROOT)
|
||||
self.addCleanup(self.directory.cleanup)
|
||||
self.root = Path(self.directory.name)
|
||||
self.paths = ("COMPATIBILITY.md", "README.md", "AGENTS.md", "docs/README.zh-CN.md")
|
||||
self.originals = {}
|
||||
for name in self.paths:
|
||||
destination = self.root / name
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
content = (TRACKER.ROOT / name).read_text(encoding="utf-8")
|
||||
destination.write_text(content, encoding="utf-8")
|
||||
self.originals[name] = content
|
||||
|
||||
def read(self, name):
|
||||
return (self.root / name).read_text(encoding="utf-8")
|
||||
|
||||
def assert_unchanged(self):
|
||||
for name, content in self.originals.items():
|
||||
self.assertEqual(content, self.read(name), name)
|
||||
|
||||
def test_new_release_adds_only_pending_rows(self):
|
||||
changed = TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
self.assertEqual({self.root / name for name in self.paths}, set(changed))
|
||||
compatibility = self.read("COMPATIBILITY.md")
|
||||
self.assertIn("| 28.2.0 | [PENDING] Compatibility verification", compatibility)
|
||||
self.assertIn("| **28.2.0** | 2026-11-01 | [PENDING]", compatibility)
|
||||
self.assertIn("| **v28.0.0** | **28.0.0** | **28.0.0** | [PASS] Active |", compatibility)
|
||||
self.assertIn("Latest verified:** Release v28.0.0", compatibility)
|
||||
self.assertIn("Gitea-28.2.0%20%5BPENDING%5D%20%7C%2028.0.0%20tested-yellow", self.read("README.md"))
|
||||
self.assertIn("Gitea 28.2.0:** [PENDING]", self.read("README.md"))
|
||||
self.assertIn("Gitea 28.2.0:** [PENDING]", self.read("docs/README.zh-CN.md"))
|
||||
self.assertIn("Gitea release: 28.2.0 [PENDING]", self.read("AGENTS.md"))
|
||||
self.assertIn("Latest tested:** Gitea 28.0.0", self.read("README.md"))
|
||||
self.assertIn("最新测试:** Gitea 28.0.0", self.read("docs/README.zh-CN.md"))
|
||||
self.assertEqual([], TRACKER.apply_release(self.root, "28.2.0", "2026-11-01"))
|
||||
|
||||
def test_existing_version_is_idempotent(self):
|
||||
self.assertEqual([], TRACKER.apply_release(self.root, "28.1.0", "2026-10-06"))
|
||||
self.assert_unchanged()
|
||||
|
||||
def test_partial_update_is_repaired(self):
|
||||
compatibility = self.root / "COMPATIBILITY.md"
|
||||
partial = self.read("COMPATIBILITY.md").replace("| 28.1.0 | [PENDING] Compatibility verification; no tested template release yet |\n", "")
|
||||
compatibility.write_text(partial, encoding="utf-8")
|
||||
self.assertEqual([compatibility], TRACKER.apply_release(self.root, "28.1.0", "2026-10-06"))
|
||||
self.assertIn("| 28.1.0 | [PENDING] Compatibility verification", self.read("COMPATIBILITY.md"))
|
||||
|
||||
def test_older_version_does_not_rewrite_history(self):
|
||||
self.assertEqual([], TRACKER.apply_release(self.root, "28.0.0", "2026-09-29"))
|
||||
self.assert_unchanged()
|
||||
|
||||
def test_dry_run_does_not_write(self):
|
||||
self.assertEqual(4, len(TRACKER.apply_release(self.root, "28.2.0", "2026-11-01", dry_run=True)))
|
||||
self.assert_unchanged()
|
||||
|
||||
def test_missing_marker_fails_without_writing(self):
|
||||
compatibility = self.root / "COMPATIBILITY.md"
|
||||
compatibility.write_text(self.read("COMPATIBILITY.md").replace("<!-- TRACKER:HISTORY -->", ""), encoding="utf-8")
|
||||
with self.assertRaises(ValueError):
|
||||
TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
self.assertEqual(self.originals["README.md"], self.read("README.md"))
|
||||
|
||||
def test_unknown_marker_fails_without_writing(self):
|
||||
news = self.root / "docs/NEWS.md"
|
||||
news.write_text("Gitea 28.1.0 [PENDING] <!-- TRACKER:FUTURE-UNKNOWN -->\n", encoding="utf-8")
|
||||
with self.assertRaisesRegex(ValueError, "Unknown TRACKER marker"):
|
||||
TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
self.assert_unchanged()
|
||||
|
||||
def test_new_marked_document_is_discovered(self):
|
||||
news = self.root / "docs/NEWS.md"
|
||||
news.write_text("Gitea 28.1.0 [PENDING] <!-- TRACKER:UPSTREAM -->\n", encoding="utf-8")
|
||||
changed = TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
self.assertIn(news, changed)
|
||||
self.assertEqual("Gitea 28.2.0 [PENDING] <!-- TRACKER:UPSTREAM -->\n", news.read_text(encoding="utf-8"))
|
||||
|
||||
def test_disagreeing_tested_versions_fail(self):
|
||||
chinese = self.root / "docs/README.zh-CN.md"
|
||||
chinese.write_text(self.read("docs/README.zh-CN.md").replace("**最新测试:** Gitea 28.0.0", "**最新测试:** Gitea 28.1.0"), encoding="utf-8")
|
||||
with self.assertRaisesRegex(ValueError, "disagree"):
|
||||
TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
|
||||
def test_after_verification_new_version_becomes_pending(self):
|
||||
english = self.root / "README.md"
|
||||
chinese = self.root / "docs/README.zh-CN.md"
|
||||
agents = self.root / "AGENTS.md"
|
||||
english.write_text(self.read("README.md")
|
||||
.replace("**Latest tested:** Gitea 28.0.0", "**Latest tested:** Gitea 28.1.0")
|
||||
.replace("Gitea-28.1.0%20%5BPENDING%5D%20%7C%2028.0.0%20tested-yellow", "Gitea-28.1.0%20%7C%2028.1.0%20tested-blue")
|
||||
.replace("**Upstream Gitea 28.1.0:** [PENDING]", "**Upstream Gitea 28.1.0:** [PASS]"), encoding="utf-8")
|
||||
chinese.write_text(self.read("docs/README.zh-CN.md")
|
||||
.replace("**最新测试:** Gitea 28.0.0", "**最新测试:** Gitea 28.1.0")
|
||||
.replace("**上游 Gitea 28.1.0:** [PENDING]", "**上游 Gitea 28.1.0:** [PASS]"), encoding="utf-8")
|
||||
agents.write_text(self.read("AGENTS.md").replace("28.1.0 [PENDING]", "28.1.0 [PASS]"), encoding="utf-8")
|
||||
TRACKER.apply_release(self.root, "28.2.0", "2026-11-01")
|
||||
self.assertIn("Gitea-28.2.0%20%5BPENDING%5D%20%7C%2028.1.0%20tested-yellow", self.read("README.md"))
|
||||
self.assertIn("Gitea 28.2.0:** [PENDING]", self.read("README.md"))
|
||||
self.assertIn("Latest tested:** Gitea 28.1.0", self.read("README.md"))
|
||||
self.assertIn("最新测试:** Gitea 28.1.0", self.read("docs/README.zh-CN.md"))
|
||||
|
||||
def test_invalid_version_rejected(self):
|
||||
with self.assertRaises(ValueError):
|
||||
TRACKER.apply_release(self.root, "28.2.0-rc1", "2026-11-01")
|
||||
|
||||
def test_release_api_uses_published_date(self):
|
||||
payload = {"tag_name": "v28.2.0", "published_at": "2026-11-01T08:15:00Z", "draft": False, "prerelease": False}
|
||||
with patch.object(TRACKER, "urlopen", return_value=io.BytesIO(json.dumps(payload).encode("utf-8"))) as mocked:
|
||||
self.assertEqual(("28.2.0", "2026-11-01"), TRACKER.fetch_release("28.2.0"))
|
||||
self.assertEqual(f"{TRACKER.RELEASES_API}/tags/v28.2.0", mocked.call_args.args[0].full_url)
|
||||
|
||||
def test_prerelease_is_rejected(self):
|
||||
payload = {"tag_name": "v28.2.0", "published_at": "2026-11-01T08:15:00Z", "prerelease": True}
|
||||
with patch.object(TRACKER, "urlopen", return_value=io.BytesIO(json.dumps(payload).encode("utf-8"))):
|
||||
with self.assertRaises(ValueError):
|
||||
TRACKER.fetch_release(None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Record a new upstream Gitea release without changing verified compatibility."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
RELEASES_API = "https://api.github.com/repos/go-gitea/gitea/releases"
|
||||
VERSION_RE = re.compile(r"v?(\d+)\.(\d+)\.(\d+)\Z")
|
||||
ROW_RE = re.compile(r"^\|\s*(?:\*\*)?(\d+\.\d+\.\d+)(?:\*\*)?\s*\|")
|
||||
INLINE_VERSION_RE = re.compile(r"(?<!\d)\d+\.\d+\.\d+(?!\d)")
|
||||
STATUS_RE = re.compile(r"\[(?:PENDING|PASS|WARN|FAIL|UNSUPPORTED)\]")
|
||||
MARKER_RE = re.compile(r"<!-- TRACKER:([A-Z][A-Z0-9-]*) -->")
|
||||
BADGE_RE = re.compile(r"Gitea-(\d+\.\d+\.\d+)(?:%20(?:pending|%5BPENDING%5D))?%20%7C%20(\d+\.\d+\.\d+)%20tested-(?:blue|yellow)")
|
||||
TABLES = {
|
||||
"VERSION-MAP": "| Gitea version | Template release |",
|
||||
"HISTORY": "| Gitea | Release Date | Mail Template Changes | Breaking? |",
|
||||
}
|
||||
AUTO_MARKERS = frozenset((*TABLES, "BADGE", "UPSTREAM"))
|
||||
MANUAL_MARKERS = frozenset(("LATEST-TESTED", "LATEST-VERIFIED"))
|
||||
REQUIRED_MARKERS = {"VERSION-MAP": 1, "HISTORY": 1, "BADGE": 1, "UPSTREAM": 3, "LATEST-TESTED": 2, "LATEST-VERIFIED": 1}
|
||||
|
||||
|
||||
def version_key(value):
|
||||
match = VERSION_RE.fullmatch(value)
|
||||
if not match:
|
||||
raise ValueError(f"Expected a stable X.Y.Z version, got {value!r}")
|
||||
return tuple(int(part) for part in match.groups())
|
||||
|
||||
|
||||
def fetch_release(requested_version):
|
||||
endpoint = f"{RELEASES_API}/tags/v{requested_version}" if requested_version else f"{RELEASES_API}/latest"
|
||||
request = Request(endpoint, headers={"Accept": "application/vnd.github+json", "User-Agent": "gitea-mail-templates-tracker"})
|
||||
token = os.environ.get("GITHUB_TOKEN")
|
||||
if token:
|
||||
request.add_header("Authorization", f"Bearer {token}")
|
||||
with urlopen(request, timeout=20) as response:
|
||||
release = json.load(response)
|
||||
if release.get("draft") or release.get("prerelease"):
|
||||
raise ValueError("Draft and prerelease Gitea versions are not tracked")
|
||||
version = str(release["tag_name"]).removeprefix("v")
|
||||
version_key(version)
|
||||
if requested_version and version != requested_version:
|
||||
raise ValueError(f"Release tag mismatch: expected {requested_version}, got {version}")
|
||||
published = release.get("published_at")
|
||||
if not published:
|
||||
raise ValueError(f"Release v{version} has no published_at date")
|
||||
release_date = date.fromisoformat(published[:10]).isoformat()
|
||||
return version, release_date
|
||||
|
||||
|
||||
def table(lines, marker_index, expected_header):
|
||||
header = marker_index + 1
|
||||
if header + 1 >= len(lines) or lines[header].strip() != expected_header:
|
||||
raise ValueError(f"Unexpected table header after {lines[marker_index].strip()}")
|
||||
if not re.fullmatch(r"\|[\s|:-]+\|", lines[header + 1].strip()):
|
||||
raise ValueError(f"Missing Markdown table separator after {lines[marker_index].strip()}")
|
||||
first = header + 2
|
||||
end = first
|
||||
while end < len(lines) and lines[end].startswith("|"):
|
||||
end += 1
|
||||
versions = set()
|
||||
for line in lines[first:end]:
|
||||
match = ROW_RE.match(line)
|
||||
if not match:
|
||||
if expected_header == TABLES["HISTORY"] and re.match(r"^\|\s*\*\*≤\s*\d+\.\d+\.x\*\*\s*\|", line):
|
||||
continue
|
||||
raise ValueError(f"Malformed version row after {lines[marker_index].strip()}: {line.strip()}")
|
||||
if match.group(1) in versions:
|
||||
raise ValueError(f"Duplicate version after {lines[marker_index].strip()}: {match.group(1)}")
|
||||
versions.add(match.group(1))
|
||||
if not versions:
|
||||
raise ValueError(f"Empty version table after {lines[marker_index].strip()}")
|
||||
return first, versions
|
||||
|
||||
|
||||
def documents(root):
|
||||
"""Discover Markdown docs; new docs opt in by adding a known TRACKER marker."""
|
||||
paths = sorted((*root.glob("*.md"), *root.glob("docs/**/*.md")))
|
||||
result = {path: path.read_text(encoding="utf-8").splitlines(keepends=True) for path in paths}
|
||||
markers = []
|
||||
for path, lines in result.items():
|
||||
for index, line in enumerate(lines):
|
||||
for match in MARKER_RE.finditer(line):
|
||||
name = match.group(1)
|
||||
if name not in AUTO_MARKERS | MANUAL_MARKERS:
|
||||
raise ValueError(f"Unknown TRACKER marker {name} in {path}")
|
||||
markers.append((path, index, name))
|
||||
for name, minimum in REQUIRED_MARKERS.items():
|
||||
count = sum(item[2] == name for item in markers)
|
||||
if count < minimum or (name in TABLES or name == "BADGE") and count != minimum:
|
||||
raise ValueError(f"Expected {minimum} TRACKER:{name} marker(s), found {count}")
|
||||
return result, markers
|
||||
|
||||
|
||||
def latest_tested(documents_by_path, markers):
|
||||
versions = set()
|
||||
for path, index, name in markers:
|
||||
if name == "LATEST-TESTED":
|
||||
match = re.search(r"Gitea\s+(\d+\.\d+\.\d+)", documents_by_path[path][index])
|
||||
if not match:
|
||||
raise ValueError(f"Missing Gitea version beside TRACKER:LATEST-TESTED in {path}")
|
||||
versions.add(match.group(1))
|
||||
if len(versions) != 1:
|
||||
raise ValueError(f"LATEST-TESTED markers disagree: {sorted(versions)}")
|
||||
return versions.pop()
|
||||
|
||||
|
||||
def update_marker(lines, index, name, version, release_date, tested):
|
||||
if name in TABLES:
|
||||
first, versions = table(lines, index, TABLES[name])
|
||||
if version not in versions:
|
||||
if name == "VERSION-MAP":
|
||||
row = f"| {version} | [PENDING] Compatibility verification; no tested template release yet |\n"
|
||||
else:
|
||||
row = f"| **{version}** | {release_date} | [PENDING] Mail-template compatibility verification | TBD |\n"
|
||||
lines.insert(first, row)
|
||||
elif name == "UPSTREAM":
|
||||
versions = list(INLINE_VERSION_RE.finditer(lines[index]))
|
||||
if len(STATUS_RE.findall(lines[index])) != 1 or len(versions) != 1:
|
||||
raise ValueError("TRACKER:UPSTREAM needs exactly one status and version on the same line")
|
||||
line = INLINE_VERSION_RE.sub(version, lines[index], count=1)
|
||||
lines[index] = STATUS_RE.sub("[PENDING]", line, count=1)
|
||||
elif name == "BADGE":
|
||||
match = BADGE_RE.search(lines[index])
|
||||
if not match or match.group(2) != tested:
|
||||
raise ValueError("TRACKER:BADGE has an unexpected format or tested version")
|
||||
badge = f"Gitea-{version}%20%5BPENDING%5D%20%7C%20{tested}%20tested-yellow"
|
||||
lines[index] = BADGE_RE.sub(badge, lines[index], count=1)
|
||||
|
||||
|
||||
def apply_release(root, version, release_date, dry_run=False):
|
||||
target = version_key(version)
|
||||
date.fromisoformat(release_date)
|
||||
originals, markers = documents(root)
|
||||
tested = latest_tested(originals, markers)
|
||||
history_path, history_index, _ = next(item for item in markers if item[2] == "HISTORY")
|
||||
history_first, _ = table(originals[history_path], history_index, TABLES["HISTORY"])
|
||||
latest = version_key(ROW_RE.match(originals[history_path][history_first]).group(1))
|
||||
if target < latest or target <= version_key(tested):
|
||||
return []
|
||||
|
||||
updated = {path: lines.copy() for path, lines in originals.items()}
|
||||
for path, index, name in sorted(markers, key=lambda item: (str(item[0]), item[1]), reverse=True):
|
||||
if name in AUTO_MARKERS:
|
||||
update_marker(updated[path], index, name, version, release_date, tested)
|
||||
changed = [path for path in updated if updated[path] != originals[path]]
|
||||
if not dry_run:
|
||||
for path in changed:
|
||||
path.write_text("".join(updated[path]), encoding="utf-8")
|
||||
return changed
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--version", default=os.environ.get("GITEA_VERSION"), help="Specific stable Gitea version; omit for latest release")
|
||||
parser.add_argument("--release-date", help="YYYY-MM-DD; use with --version for offline checks")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Check without changing repository documentation")
|
||||
args = parser.parse_args()
|
||||
if args.release_date and not args.version:
|
||||
parser.error("--release-date requires --version")
|
||||
if args.version:
|
||||
args.version = args.version.removeprefix("v")
|
||||
version_key(args.version)
|
||||
if args.release_date:
|
||||
version, release_date = args.version, date.fromisoformat(args.release_date).isoformat()
|
||||
else:
|
||||
version, release_date = fetch_release(args.version)
|
||||
changed = apply_release(ROOT, version, release_date, dry_run=args.dry_run)
|
||||
print(f"Gitea {version}: {', '.join(str(path.relative_to(ROOT)) for path in changed) if changed else 'already tracked'}")
|
||||
output_path = os.environ.get("GITHUB_OUTPUT")
|
||||
if output_path:
|
||||
with open(output_path, "a", encoding="utf-8") as output:
|
||||
output.write(f"version={version}\nchanged={'true' if changed else 'false'}\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except (KeyError, OSError, ValueError) as error:
|
||||
sys.exit(f"Tracker failed: {error}")
|
||||
@@ -2,12 +2,11 @@ name: Gitea Version Tracker
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Daily at 08:00 UTC — check for new Gitea releases
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Gitea version (e.g. 1.27.0) — leave empty to auto-detect'
|
||||
description: 'Stable Gitea version (e.g. 28.2.0); empty means latest'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
@@ -20,103 +19,32 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Determine latest Gitea version
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Test tracker
|
||||
run: python -B -m unittest discover -s .github/scripts -p 'test_*.py'
|
||||
|
||||
- name: Record pending Gitea release
|
||||
id: version
|
||||
run: |
|
||||
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||
NEW_VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
# Fetch latest release from GitHub API (go-gitea/gitea mirror)
|
||||
RELEASE_JSON=$(curl -sSf https://api.github.com/repos/go-gitea/gitea/releases/latest)
|
||||
NEW_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name')
|
||||
fi
|
||||
# Strip 'v' prefix if present (API returns v1.27.0, we use 1.27.0)
|
||||
NEW_VERSION="${NEW_VERSION#v}"
|
||||
echo "New Gitea version: $NEW_VERSION"
|
||||
|
||||
# Parse marker line AND embedded OFFSET to locate the data row
|
||||
MARKER_INFO=$(grep -n 'TRACKER:QUICK-REF-MAX' COMPATIBILITY.md)
|
||||
MARKER_LINE=$(echo "$MARKER_INFO" | cut -d: -f1)
|
||||
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
|
||||
CURRENT=$(sed -n "$((MARKER_LINE + OFFSET))p" COMPATIBILITY.md | awk -F'|' '{gsub(/\*| /, "", $4); print $4}')
|
||||
echo "Current max tested: $CURRENT"
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "current_version=$CURRENT" >> $GITHUB_OUTPUT
|
||||
|
||||
if [ "$NEW_VERSION" = "$CURRENT" ]; then
|
||||
echo "No new Gitea release detected. Skipping."
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "New version detected: $CURRENT → $NEW_VERSION"
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: COMPATIBILITY.md — Quick Reference (max tested version)
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
NEW="${{ steps.version.outputs.new_version }}"
|
||||
# Parse marker line and embedded OFFSET to locate the data row
|
||||
MARKER_INFO=$(grep -n 'TRACKER:QUICK-REF-MAX' COMPATIBILITY.md)
|
||||
MARKER=$(echo "$MARKER_INFO" | cut -d: -f1)
|
||||
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
|
||||
TARGET=$((MARKER + OFFSET))
|
||||
# Update column 4 (Max Tested) and column 5 (Status) in the pipe-separated row
|
||||
awk -i inplace -F'|' -v OFS='|' -v ver="${NEW}" '
|
||||
NR == '$TARGET' { $4 = " **" ver "** "; $5 = " ⏳ Pending Verification " }
|
||||
{ print }
|
||||
' COMPATIBILITY.md
|
||||
|
||||
- name: COMPATIBILITY.md — Version History table
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
NEW="${{ steps.version.outputs.new_version }}"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
# Parse marker line and embedded OFFSET for the insertion point
|
||||
MARKER_INFO=$(grep -n 'TRACKER:VERSION-INSERT' COMPATIBILITY.md)
|
||||
MARKER=$(echo "$MARKER_INFO" | cut -d: -f1)
|
||||
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
|
||||
INSERT=$((MARKER + OFFSET))
|
||||
ROW="| **${NEW}** | ${TODAY} | ⏳ Pending Verification | TBD |"
|
||||
sed -i "${INSERT}a ${ROW}" COMPATIBILITY.md
|
||||
|
||||
- name: README.md — pending badge
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
NEW="${{ steps.version.outputs.new_version }}"
|
||||
# Badge: update version, change label tested→pending, color blue→yellow
|
||||
sed -i "/TRACKER:BADGE/s/%20[0-9a-z.-]*%20tested-blue/%20${NEW}%20pending-yellow/" README.md
|
||||
run: python -B .github/scripts/track_gitea_release.py
|
||||
env:
|
||||
GITEA_VERSION: ${{ github.event.inputs.version }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
if: steps.version.outputs.changed == 'true'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
branch: track/gitea-${{ steps.version.outputs.new_version }}
|
||||
commit-message: "docs: track Gitea ${{ steps.version.outputs.new_version }} — pending verification"
|
||||
title: "Track Gitea ${{ steps.version.outputs.new_version }} — ⏳ Pending Verification"
|
||||
add-paths: '*.md'
|
||||
branch: track/gitea-${{ steps.version.outputs.version }}
|
||||
commit-message: "docs: track Gitea ${{ steps.version.outputs.version }} pending verification"
|
||||
title: "Track Gitea ${{ steps.version.outputs.version }} compatibility"
|
||||
body: |
|
||||
Gitea **${{ steps.version.outputs.new_version }}** has been released.
|
||||
Gitea **${{ steps.version.outputs.version }}** is recorded as [PENDING] across the marked documentation. Verified/tested ranges remain unchanged.
|
||||
|
||||
This PR:
|
||||
- Updates the compatibility matrix in `COMPATIBILITY.md`
|
||||
- Marks the new version as pending in the `README.md` badge
|
||||
- Marks the new version as **⏳ Pending Verification**
|
||||
|
||||
## Verification Checklist
|
||||
- [ ] Check [Gitea changelog](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) for mail template changes
|
||||
- [ ] Review diff of official templates: `git diff v${{ steps.version.outputs.current_version }}..v${{ steps.version.outputs.new_version }} -- templates/mail/ services/mailer/`
|
||||
- [ ] Run `cd tools && go run . preview all` to confirm templates still render
|
||||
- [ ] Run `cd tools && go test ./...` to verify push-notification data paths
|
||||
- [ ] Update status from ⏳ to ✅ (compatible) or ❌ (breaking) in COMPATIBILITY.md
|
||||
- [ ] Update the `Mail Template Changes` and `Breaking?` columns with accurate notes
|
||||
- [ ] After verification, update `TRACKER:LATEST-VERIFIED` and the English/Chinese `TRACKER:LATEST-TESTED` lines; switch the README badge from pending to tested
|
||||
|
||||
## Semantic tracking
|
||||
Once verified, update the tracked Gitea version:
|
||||
- [ ] If templates changed, mark the top **Template Release** row `Unreleased` until the fix is tagged; if no change is needed, retain the verified release tag
|
||||
- [ ] Update the README release status and compatibility matrix to distinguish source fixes from published packages
|
||||
- [ ] Write `.github/release-notes/vX.Y.Z.md` before tagging; the release workflow uses this file for the published notes
|
||||
- [ ] Tag a new release when shipping template fixes — `git tag vX.Y.Z && git push origin vX.Y.Z` (the release workflow packages automatically)
|
||||
labels: compatibility, automated
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- [ ] Review upstream mail templates, mailer data, functions, and translation keys.
|
||||
- [ ] Run `cd tools && go test ./...` and `go run . preview all`.
|
||||
- [ ] Record findings in `COMPATIBILITY.md`; update tested ranges and status text only after verification.
|
||||
- [ ] If template changes are needed, release them separately with reviewed notes and assets.
|
||||
@@ -34,16 +34,26 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Verify release notes
|
||||
run: test -s ".github/release-notes/${GITHUB_REF_NAME}.md"
|
||||
|
||||
- name: Generate static preview for the archive
|
||||
working-directory: tools
|
||||
run: go run . preview all
|
||||
|
||||
- name: Package themes/
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
ARCHIVE="gitea-mail-templates-${VERSION}"
|
||||
mkdir -p "dist/${ARCHIVE}/docs"
|
||||
mkdir -p "dist/${ARCHIVE}/docs" "dist/${ARCHIVE}/preview"
|
||||
cp -r themes LICENSE README.md CONTRIBUTING.md COMPATIBILITY.md "dist/${ARCHIVE}/"
|
||||
cp docs/README.zh-CN.md docs/CONTRIBUTING.zh-CN.md "dist/${ARCHIVE}/docs/"
|
||||
cp -r docs/images "dist/${ARCHIVE}/docs/"
|
||||
cp preview/index.html preview/rendered.js "dist/${ARCHIVE}/preview/"
|
||||
cd dist
|
||||
zip -r "${ARCHIVE}.zip" "${ARCHIVE}"
|
||||
tar -czf "${ARCHIVE}.tar.gz" "${ARCHIVE}"
|
||||
|
||||
Reference in new issue
Block a user