fix: чинит разбор workflow — описание релиза вынесено в скрипт
build / build (push) Successful in 24s
build / build (push) Successful in 24s
Многострочный python3 -c внутри блока run начинался с нулевого отступа
и обрывал YAML-скаляр: Gitea отказывалась читать файл целиком
("line 137: could not find expected ':'").
Генерация markdown уехала в tools/release_notes.py — в CI остался один
вызов без экранирования, а сам рендер покрыт тестами.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
61c0ea3558
commit
60c7141f88
@@ -124,25 +124,7 @@ jobs:
|
|||||||
set -eu
|
set -eu
|
||||||
tag=$(date -u '+%Y.%m.%d-%H%M')
|
tag=$(date -u '+%Y.%m.%d-%H%M')
|
||||||
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
||||||
{
|
python3 tools/release_notes.py "$(git rev-parse --short HEAD)" > /tmp/notes.md
|
||||||
echo "Собрано из \`$(git rev-parse --short HEAD)\`."
|
|
||||||
echo
|
|
||||||
echo '| Категория | Доменов |'
|
|
||||||
echo '| --- | ---: |'
|
|
||||||
python3 -c "import json; [print(f'| {k} | {v} |') for k, v in json.load(open('dist/metadata.json'))['categories'].items()]"
|
|
||||||
echo
|
|
||||||
echo '| IP-категория | Сетей | Ограничения |'
|
|
||||||
echo '| --- | ---: | --- |'
|
|
||||||
python3 -c "
|
|
||||||
import json
|
|
||||||
for k, v in json.load(open('dist/metadata.json'))['ip_categories'].items():
|
|
||||||
limits = ' '.join(filter(None, ['/'.join(v.get('network', [])), v.get('ports', '')])) or '—'
|
|
||||||
print(f\"| {k} | {v['count']} | {limits} |\")
|
|
||||||
"
|
|
||||||
echo
|
|
||||||
echo "sha256(discord-geosite.dat) = \`$(cut -d' ' -f1 dist/discord-geosite.dat.sha256sum)\`"
|
|
||||||
echo "sha256(discord-geoip.dat) = \`$(cut -d' ' -f1 dist/discord-geoip.dat.sha256sum)\`"
|
|
||||||
} > /tmp/notes.md
|
|
||||||
cat /tmp/notes.md
|
cat /tmp/notes.md
|
||||||
|
|
||||||
- name: Публикация в ветку release
|
- name: Публикация в ветку release
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Печатает описание релиза в markdown по dist/metadata.json.
|
||||||
|
|
||||||
|
python3 tools/release_notes.py <короткий-sha> [каталог-сборки]
|
||||||
|
|
||||||
|
Отдельным скриптом, а не строчкой в CI: внутри YAML такой markdown
|
||||||
|
пришлось бы экранировать до нечитаемости.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def render(meta: dict, revision: str) -> str:
|
||||||
|
lines = [f"Собрано из `{revision}`.", ""]
|
||||||
|
|
||||||
|
lines += ["| Категория | Доменов |", "| --- | ---: |"]
|
||||||
|
for name, count in meta["categories"].items():
|
||||||
|
lines.append(f"| {name} | {count} |")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines += ["| IP-категория | Сетей | Ограничения |", "| --- | ---: | --- |"]
|
||||||
|
for name, info in meta["ip_categories"].items():
|
||||||
|
limits = " ".join(filter(None, ["/".join(info.get("network", [])), info.get("ports", "")]))
|
||||||
|
lines.append(f"| {name} | {info['count']} | {limits or '—'} |")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines += [
|
||||||
|
f"sha256(discord-geosite.dat) = `{meta['geosite_dat_sha256']}`",
|
||||||
|
"",
|
||||||
|
f"sha256(discord-geoip.dat) = `{meta['geoip_dat_sha256']}`",
|
||||||
|
]
|
||||||
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str]) -> int:
|
||||||
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
||||||
|
if not 2 <= len(argv) <= 3:
|
||||||
|
print(f"использование: {argv[0]} <короткий-sha> [каталог-сборки]", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
out_dir = Path(argv[2]) if len(argv) == 3 else Path("dist")
|
||||||
|
meta = json.loads((out_dir / "metadata.json").read_text(encoding="utf-8"))
|
||||||
|
sys.stdout.write(render(meta, argv[1]))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main(sys.argv))
|
||||||
@@ -11,6 +11,7 @@ import unittest
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import build
|
import build
|
||||||
|
import release_notes
|
||||||
import sync
|
import sync
|
||||||
from build import BuildError, Net, Options, Rule
|
from build import BuildError, Net, Options, Rule
|
||||||
|
|
||||||
@@ -336,6 +337,34 @@ class SyncTest(unittest.TestCase):
|
|||||||
self.assertTrue(sync.is_generated(Path(tmp) / "new.txt"))
|
self.assertTrue(sync.is_generated(Path(tmp) / "new.txt"))
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseNotesTest(unittest.TestCase):
|
||||||
|
META = {
|
||||||
|
"geosite_dat_sha256": "aa",
|
||||||
|
"geoip_dat_sha256": "bb",
|
||||||
|
"categories": {"discord": 34},
|
||||||
|
"ip_categories": {
|
||||||
|
"discord": {"count": 140},
|
||||||
|
"discord-voice": {"count": 13, "network": ["udp"], "ports": "50000-50099"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_renders_both_tables(self):
|
||||||
|
notes = release_notes.render(self.META, "abc1234")
|
||||||
|
self.assertIn("Собрано из `abc1234`.", notes)
|
||||||
|
self.assertIn("| discord | 34 |", notes)
|
||||||
|
self.assertIn("| discord | 140 | — |", notes)
|
||||||
|
self.assertIn("| discord-voice | 13 | udp 50000-50099 |", notes)
|
||||||
|
self.assertIn("sha256(discord-geoip.dat) = `bb`", notes)
|
||||||
|
|
||||||
|
def test_reads_real_metadata(self):
|
||||||
|
with tempfile.TemporaryDirectory() as out:
|
||||||
|
out_dir = Path(out) / "dist"
|
||||||
|
with data(a="a.com\n", ip={"n": "1.2.3.0/24\n"}):
|
||||||
|
build.build(out_dir, None, None, "proxy")
|
||||||
|
meta = json.loads((out_dir / "metadata.json").read_text(encoding="utf-8"))
|
||||||
|
self.assertIn("| n | 1 | — |", release_notes.render(meta, "abc1234"))
|
||||||
|
|
||||||
|
|
||||||
class CheckTest(unittest.TestCase):
|
class CheckTest(unittest.TestCase):
|
||||||
def test_clean_sources_pass(self):
|
def test_clean_sources_pass(self):
|
||||||
with data(a="a.com\nb.com\n", ip={"a": "1.2.3.0/24\n"}):
|
with data(a="a.com\nb.com\n", ip={"a": "1.2.3.0/24\n"}):
|
||||||
|
|||||||
Reference in New Issue
Block a user