2026-10-12 13:14:08 +03:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""Статические проверки без запуска лаунчера. Ненулевой код возврата при ошибке.
|
|
|
|
|
|
|
|
|
|
|
|
Запуск: python3 tests/scripts/static_checks.py <папка сборки с BILAUNCHER_BUILD_TESTS=ON>
|
|
|
|
|
|
|
|
|
|
|
|
1. tools/check_translations.py — каталог переводов цел и совпадает с кодом.
|
|
|
|
|
|
2. qmllint по модулю BiLauncher (цель BiLauncher_qmllint, её создаёт qt_add_qml_module).
|
|
|
|
|
|
В коде уже есть предупреждения, а код ради тестов не правится, поэтому
|
|
|
|
|
|
проверка работает «храповиком»: падает, только если предупреждений стало
|
|
|
|
|
|
больше, чем записано в tests/qmllint-baseline.txt. Стало меньше — просит
|
|
|
|
|
|
опустить планку, чтобы достигнутое не откатилось.
|
|
|
|
|
|
3. CHANGELOG.md содержит раздел для версии из project(BiLauncher VERSION …):
|
|
|
|
|
|
без него release.yml создаст релиз с пустым описанием (только предупреждение).
|
2026-10-17 23:38:49 +03:00
|
|
|
|
4. Скрипты .ps1 с не-ASCII символами сохранены в UTF-8 с BOM: без BOM Windows
|
|
|
|
|
|
PowerShell 5.1 читает их в ANSI-кодировке и падает на разборе кириллицы.
|
2026-10-12 13:14:08 +03:00
|
|
|
|
"""
|
2026-10-17 23:38:49 +03:00
|
|
|
|
import glob, os, re, subprocess, sys
|
2026-10-12 13:14:08 +03:00
|
|
|
|
|
|
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
BASELINE = os.path.join(ROOT, "tests", "qmllint-baseline.txt")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def step(title):
|
|
|
|
|
|
print(f"\n=== {title}", flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_translations():
|
|
|
|
|
|
step("Каталог переводов")
|
|
|
|
|
|
return subprocess.call([sys.executable, os.path.join(ROOT, "tools", "check_translations.py")]) == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_qmllint(build_dir):
|
|
|
|
|
|
step("qmllint (BiLauncher_qmllint)")
|
|
|
|
|
|
cmake = os.environ.get("CMAKE", "cmake")
|
|
|
|
|
|
proc = subprocess.run([cmake, "--build", build_dir, "--target", "BiLauncher_qmllint"],
|
|
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True,
|
|
|
|
|
|
encoding="utf-8", errors="replace")
|
|
|
|
|
|
output = proc.stdout
|
|
|
|
|
|
with open(os.path.join(build_dir, "qmllint.log"), "w", encoding="utf-8") as log:
|
|
|
|
|
|
log.write(output)
|
|
|
|
|
|
warnings = [line for line in output.splitlines() if line.startswith("Warning:")]
|
|
|
|
|
|
errors = [line for line in output.splitlines() if line.startswith("Error:")]
|
|
|
|
|
|
for line in warnings + errors:
|
|
|
|
|
|
print(line)
|
|
|
|
|
|
|
|
|
|
|
|
baseline = int(open(BASELINE, encoding="utf-8").read().split()[0])
|
|
|
|
|
|
print(f"\nпредупреждений: {len(warnings)}, ошибок: {len(errors)}, планка: {baseline}"
|
|
|
|
|
|
f" (полный вывод — {os.path.join(build_dir, 'qmllint.log')})")
|
|
|
|
|
|
if proc.returncode != 0 and not warnings and not errors:
|
|
|
|
|
|
print("qmllint не запустился — см. лог")
|
|
|
|
|
|
return False
|
|
|
|
|
|
if errors:
|
|
|
|
|
|
print("ОШИБКА: qmllint нашёл ошибки")
|
|
|
|
|
|
return False
|
|
|
|
|
|
if len(warnings) > baseline:
|
|
|
|
|
|
print(f"ОШИБКА: предупреждений стало больше ({len(warnings)} > {baseline}). "
|
|
|
|
|
|
"Исправьте новые или, если они осознанные, поднимите tests/qmllint-baseline.txt.")
|
|
|
|
|
|
return False
|
|
|
|
|
|
if len(warnings) < baseline:
|
|
|
|
|
|
print(f"Хорошо: предупреждений меньше планки — опустите tests/qmllint-baseline.txt до {len(warnings)}.")
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_changelog():
|
|
|
|
|
|
step("CHANGELOG.md и версия проекта")
|
|
|
|
|
|
cmake_text = open(os.path.join(ROOT, "CMakeLists.txt"), encoding="utf-8").read()
|
|
|
|
|
|
match = re.search(r"^project\(BiLauncher VERSION ([0-9]+\.[0-9]+\.[0-9]+)", cmake_text, re.M)
|
|
|
|
|
|
if not match:
|
|
|
|
|
|
print("ОШИБКА: не найден project(BiLauncher VERSION x.y.z)")
|
|
|
|
|
|
return False
|
|
|
|
|
|
version = match.group(1)
|
|
|
|
|
|
changelog = open(os.path.join(ROOT, "CHANGELOG.md"), encoding="utf-8").read()
|
2026-10-24 23:16:35 +03:00
|
|
|
|
if re.search(r"^## \[" + re.escape(version) + r"(-rc[0-9]+)?\]", changelog, re.M):
|
2026-10-12 13:14:08 +03:00
|
|
|
|
print(f"версия {version}: раздел в CHANGELOG.md есть")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"ПРЕДУПРЕЖДЕНИЕ: в CHANGELOG.md нет раздела ## [{version}] — описание релиза будет пустым")
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-17 23:38:49 +03:00
|
|
|
|
def check_ps1_bom():
|
|
|
|
|
|
step("Кодировка .ps1")
|
|
|
|
|
|
ok = True
|
|
|
|
|
|
for path in glob.glob(os.path.join(ROOT, "**", "*.ps1"), recursive=True):
|
|
|
|
|
|
if os.path.relpath(path, ROOT).startswith("build"): # папки сборки
|
|
|
|
|
|
continue
|
|
|
|
|
|
data = open(path, "rb").read()
|
|
|
|
|
|
if not data.startswith(b"\xef\xbb\xbf") and any(b > 0x7F for b in data):
|
|
|
|
|
|
print(f"ОШИБКА: {os.path.relpath(path, ROOT)} содержит не-ASCII, но сохранён без UTF-8 BOM")
|
|
|
|
|
|
ok = False
|
|
|
|
|
|
if ok:
|
|
|
|
|
|
print("все .ps1 читаются Windows PowerShell 5.1")
|
|
|
|
|
|
return ok
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-12 13:14:08 +03:00
|
|
|
|
def main():
|
|
|
|
|
|
if len(sys.argv) != 2:
|
|
|
|
|
|
print(__doc__)
|
|
|
|
|
|
return 2
|
|
|
|
|
|
build_dir = os.path.abspath(sys.argv[1])
|
2026-10-17 23:38:49 +03:00
|
|
|
|
results = [check_translations(), check_qmllint(build_dir), check_changelog(), check_ps1_bom()]
|
2026-10-12 13:14:08 +03:00
|
|
|
|
print("\nИТОГ:", "OK" if all(results) else "ЕСТЬ ОШИБКИ")
|
|
|
|
|
|
return 0 if all(results) else 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
sys.exit(main())
|