157 lines
5.1 KiB
Python
157 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
||
"""Проверяет целостность восстановленного репозитория без запуска Windows-кода."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
import re
|
||
import struct
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
|
||
REQUIRED_PATHS = (
|
||
"README.md",
|
||
"docs/assets/zapret2-console.png",
|
||
"zapret-console.bat",
|
||
"service.bat",
|
||
"exe/winws2.exe",
|
||
"exe/WinDivert.dll",
|
||
"exe/WinDivert32.sys",
|
||
"exe/WinDivert64.sys",
|
||
"exe/cygwin1.dll",
|
||
"presets/Default v5.txt",
|
||
"lists/discord-updates.txt",
|
||
"lists/googlevideo.txt",
|
||
"lists/tankix.txt",
|
||
"lua/zapret-custom.lua",
|
||
)
|
||
|
||
FORBIDDEN_TRACKED_PATHS = (
|
||
".github/workflows",
|
||
"lists/service_config.txt",
|
||
"utils/current_preset.txt",
|
||
"utils/preset-active.txt",
|
||
)
|
||
|
||
OWN_GITHUB_REFERENCES = re.compile(
|
||
rb"(?:github\.com/(?:youtubediscord|zapretdiscordyoutube|loop-uh)(?:/|\b)"
|
||
rb"|gist\.github\.com/loop-uh(?:/|\b)"
|
||
rb"|github\.com/user-attachments(?:/|\b))",
|
||
re.IGNORECASE,
|
||
)
|
||
|
||
REFERENCE_PATTERN = re.compile(
|
||
r"(?:@|=)((?:bin|lua|lists|windivert\.filter)/[^\s\"';,]+)",
|
||
re.IGNORECASE,
|
||
)
|
||
|
||
TEXT_SUFFIXES = {
|
||
".bat",
|
||
".cmd",
|
||
".lua",
|
||
".md",
|
||
".ps1",
|
||
".py",
|
||
".txt",
|
||
".vbs",
|
||
".yaml",
|
||
".yml",
|
||
}
|
||
|
||
|
||
def png_size(path: Path) -> tuple[int, int]:
|
||
data = path.read_bytes()
|
||
if data[:8] != b"\x89PNG\r\n\x1a\n" or data[12:16] != b"IHDR":
|
||
raise ValueError("файл не является корректным PNG")
|
||
return struct.unpack(">II", data[16:24])
|
||
|
||
|
||
def iter_repository_files():
|
||
for path in ROOT.rglob("*"):
|
||
if not path.is_file():
|
||
continue
|
||
relative = path.relative_to(ROOT)
|
||
if relative.parts[0] in {".git", "dist"} or "__pycache__" in relative.parts:
|
||
continue
|
||
yield path
|
||
|
||
|
||
def main() -> int:
|
||
errors: list[str] = []
|
||
|
||
for relative in REQUIRED_PATHS:
|
||
if not (ROOT / relative).is_file():
|
||
errors.append(f"нет обязательного файла: {relative}")
|
||
|
||
for relative in FORBIDDEN_TRACKED_PATHS:
|
||
if (ROOT / relative).exists():
|
||
errors.append(f"в репозиторий попало локальное или GitHub-состояние: {relative}")
|
||
|
||
image = ROOT / "docs/assets/zapret2-console.png"
|
||
if image.is_file():
|
||
try:
|
||
if png_size(image) != (980, 519):
|
||
errors.append("локальная картинка README имеет неожиданный размер")
|
||
except ValueError as exc:
|
||
errors.append(f"локальная картинка README повреждена: {exc}")
|
||
|
||
engine = ROOT / "exe/winws2.exe"
|
||
if engine.is_file():
|
||
data = engine.read_bytes()
|
||
digest = hashlib.sha256(data).hexdigest()
|
||
if data[:2] != b"MZ":
|
||
errors.append("exe/winws2.exe не является Windows PE-файлом")
|
||
if b"v1.0.3" not in data or b"b78b52c4cd7f843da3ff0848a3430afbd401bdf2" not in data:
|
||
errors.append(
|
||
"exe/winws2.exe не совпадает с восстановленным zapret2 v1.0.3 "
|
||
f"(sha256={digest})"
|
||
)
|
||
|
||
for path in iter_repository_files():
|
||
data = path.read_bytes()
|
||
match = OWN_GITHUB_REFERENCES.search(data)
|
||
if match:
|
||
relative = path.relative_to(ROOT)
|
||
errors.append(f"осталась ссылка собственного проекта на GitHub: {relative}")
|
||
|
||
referenced_from = list((ROOT / "presets").glob("*.txt"))
|
||
referenced_from.extend((ROOT / "utils").glob("*.txt"))
|
||
for source in referenced_from:
|
||
text = source.read_text(encoding="utf-8-sig", errors="replace")
|
||
for reference in REFERENCE_PATTERN.findall(text):
|
||
if "$" in reference or "%" in reference:
|
||
continue
|
||
if not (ROOT / reference).is_file():
|
||
errors.append(
|
||
f"{source.relative_to(ROOT)} ссылается на отсутствующий файл: {reference}"
|
||
)
|
||
|
||
text_files = [path for path in iter_repository_files() if path.suffix.lower() in TEXT_SUFFIXES]
|
||
if not text_files:
|
||
errors.append("не найдены текстовые файлы проекта")
|
||
for path in text_files:
|
||
for line_number, line in enumerate(path.read_bytes().splitlines(), start=1):
|
||
if line.endswith((b" ", b"\t")):
|
||
errors.append(
|
||
f"хвостовой пробел: {path.relative_to(ROOT)}:{line_number}"
|
||
)
|
||
|
||
if errors:
|
||
print("Проверка не пройдена:")
|
||
for error in sorted(set(errors)):
|
||
print(f"- {error}")
|
||
return 1
|
||
|
||
preset_count = len(list((ROOT / "presets").glob("*.txt")))
|
||
print("Проверка пройдена.")
|
||
print(f"Пресетов: {preset_count}")
|
||
print(f"Файлов в репозитории: {sum(1 for _ in iter_repository_files())}")
|
||
print(f"SHA256 winws2.exe: {hashlib.sha256(engine.read_bytes()).hexdigest()}")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|