сборка манифеста для лаунчера
This commit is contained in:
+3
-1
@@ -1 +1,3 @@
|
||||
.env
|
||||
.env
|
||||
__pycache__/
|
||||
manifest.json
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
IGNORED_TOP_LEVEL_DIRS = {
|
||||
"Cache",
|
||||
"Errors",
|
||||
"Logs",
|
||||
"Screenshots",
|
||||
"WTF",
|
||||
}
|
||||
|
||||
ENV_VAR_NAME = "WOW_HOME"
|
||||
|
||||
|
||||
def sha256_bytes(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
|
||||
h = hashlib.sha256()
|
||||
with path.open("rb") as f:
|
||||
while chunk := f.read(chunk_size):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def is_ignored(rel_path: Path) -> bool:
|
||||
if not rel_path.parts:
|
||||
return False
|
||||
return rel_path.parts[0] in IGNORED_TOP_LEVEL_DIRS
|
||||
|
||||
|
||||
def load_dotenv(env_path: Path) -> dict[str, str]:
|
||||
values: dict[str, str] = {}
|
||||
|
||||
if not env_path.exists():
|
||||
return values
|
||||
|
||||
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
if key.startswith("export "):
|
||||
key = key.removeprefix("export ").strip()
|
||||
|
||||
if not key:
|
||||
continue
|
||||
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
||||
value = value[1:-1]
|
||||
|
||||
values[key] = value
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def resolve_root(explicit_dir: Optional[str]) -> Optional[Path]:
|
||||
if explicit_dir:
|
||||
return Path(explicit_dir).resolve()
|
||||
|
||||
env_value = os.environ.get(ENV_VAR_NAME)
|
||||
if env_value:
|
||||
return Path(env_value).expanduser().resolve()
|
||||
|
||||
project_root = Path(__file__).resolve().parent
|
||||
dotenv_values = load_dotenv(project_root / ".env")
|
||||
env_value = dotenv_values.get(ENV_VAR_NAME)
|
||||
|
||||
if env_value:
|
||||
os.environ.setdefault(ENV_VAR_NAME, env_value)
|
||||
return Path(env_value).expanduser().resolve()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def compute_build_hash(files: list[dict]) -> str:
|
||||
lines = [
|
||||
f'{item["path"]}:{item["size"]}:{item["sha256"]}'
|
||||
for item in sorted(files, key=lambda x: x["path"])
|
||||
]
|
||||
payload = "\n".join(lines).encode("utf-8")
|
||||
return sha256_bytes(payload)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Collect files into manifest.json")
|
||||
parser.add_argument(
|
||||
"--dir",
|
||||
help=f"Directory to scan. Defaults to {ENV_VAR_NAME} from env or .env",
|
||||
)
|
||||
parser.add_argument("--output", default="manifest.json", help="Output file")
|
||||
args = parser.parse_args()
|
||||
|
||||
root = resolve_root(args.dir)
|
||||
output = Path(args.output).resolve()
|
||||
|
||||
if root is None:
|
||||
print(
|
||||
f"ERROR: directory is not set. Pass --dir or define {ENV_VAR_NAME} in env/.env",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
if not root.exists() or not root.is_dir():
|
||||
print(f"ERROR: directory not found: {root}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
files = []
|
||||
|
||||
for file_path in sorted(root.rglob("*")):
|
||||
if not file_path.is_file():
|
||||
continue
|
||||
|
||||
if file_path.resolve() == output:
|
||||
continue
|
||||
|
||||
rel_path_obj = file_path.relative_to(root)
|
||||
|
||||
if is_ignored(rel_path_obj):
|
||||
continue
|
||||
|
||||
rel_path = rel_path_obj.as_posix()
|
||||
files.append({
|
||||
"path": rel_path,
|
||||
"size": file_path.stat().st_size,
|
||||
"sha256": sha256_file(file_path),
|
||||
})
|
||||
|
||||
build_hash = compute_build_hash(files)
|
||||
|
||||
manifest = {
|
||||
"buildHash": build_hash,
|
||||
"files": files
|
||||
}
|
||||
|
||||
with output.open("w", encoding="utf-8", newline="\n") as f:
|
||||
json.dump(manifest, f, ensure_ascii=False, indent=2)
|
||||
f.write("\n")
|
||||
|
||||
print(f"OK: written {output}")
|
||||
print(f"Files: {len(files)}")
|
||||
print(f"Build hash: {build_hash}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user