Rust clippy diagnostic / detmir-portal-clippy-diagnostic (push) Waiting to run
144 lines
4.6 KiB
Python
144 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Create a GitHub Actions release package from Rust release binaries."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import shutil
|
|
import stat
|
|
import tarfile
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
SKIP_DIRS = {"deps", "build", "examples", "incremental"}
|
|
SKIP_SUFFIXES = {".d", ".rlib", ".rmeta"}
|
|
|
|
|
|
def sha256(path: Path) -> str:
|
|
digest = hashlib.sha256()
|
|
with path.open("rb") as handle:
|
|
while True:
|
|
chunk = handle.read(1024 * 1024)
|
|
if not chunk:
|
|
break
|
|
digest.update(chunk)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def is_binary(path: Path) -> bool:
|
|
if not path.is_file():
|
|
return False
|
|
if path.name in SKIP_DIRS:
|
|
return False
|
|
if path.suffix in SKIP_SUFFIXES:
|
|
return False
|
|
return bool(path.stat().st_mode & stat.S_IXUSR)
|
|
|
|
|
|
def collect(release_dir: Path) -> list[Path]:
|
|
items = [item for item in sorted(release_dir.iterdir()) if is_binary(item)]
|
|
if not items:
|
|
raise SystemExit(f"No release binaries found in {release_dir}")
|
|
return items
|
|
|
|
|
|
def write(path: Path, text: str) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
|
|
|
|
def copy_release_file(src: Path, dst: Path) -> Path:
|
|
"""Copy file contents without preserving metadata that some mounts reject."""
|
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(src, dst)
|
|
try:
|
|
dst.chmod(src.stat().st_mode & 0o777)
|
|
except PermissionError:
|
|
# Some removable/network filesystems reject chmod/utime metadata changes.
|
|
# The package remains valid because the archive manifest/checksums are
|
|
# based on file contents, not filesystem timestamps.
|
|
pass
|
|
return dst
|
|
|
|
|
|
def write_archive_checksum(archive: Path) -> None:
|
|
write(archive.with_suffix(archive.suffix + ".sha256"), f"{sha256(archive)} {archive.name}\n")
|
|
|
|
|
|
def create_compatibility_aliases(out_dir: Path, archive: Path) -> None:
|
|
"""Create both linux-x86_64 and linux_x86_64 artifact paths."""
|
|
out_alias = Path(str(out_dir).replace("linux-x86_64", "linux_x86_64"))
|
|
if out_alias != out_dir:
|
|
if out_alias.exists():
|
|
shutil.rmtree(out_alias)
|
|
out_alias.mkdir(parents=True)
|
|
for item in out_dir.iterdir():
|
|
if item.is_file():
|
|
copy_release_file(item, out_alias / item.name)
|
|
|
|
archive_alias = Path(str(archive).replace("linux-x86_64", "linux_x86_64"))
|
|
if archive_alias != archive:
|
|
copy_release_file(archive, archive_alias)
|
|
write_archive_checksum(archive_alias)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--release-dir", type=Path, required=True)
|
|
parser.add_argument("--out-dir", type=Path, required=True)
|
|
parser.add_argument("--archive", type=Path, required=True)
|
|
parser.add_argument("--target", default="linux-x86_64")
|
|
parser.add_argument("--commit", default="unknown")
|
|
parser.add_argument("--ref", default="unknown")
|
|
parser.add_argument("--run-id", default="unknown")
|
|
args = parser.parse_args()
|
|
|
|
release_dir = args.release_dir.resolve()
|
|
out_dir = args.out_dir.resolve()
|
|
archive = args.archive.resolve()
|
|
|
|
if out_dir.exists():
|
|
shutil.rmtree(out_dir)
|
|
out_dir.mkdir(parents=True)
|
|
|
|
binaries = collect(release_dir)
|
|
for binary in binaries:
|
|
copy_release_file(binary, out_dir / binary.name)
|
|
|
|
names = [binary.name for binary in binaries]
|
|
write(out_dir / "BINARIES.txt", "\n".join(names) + "\n")
|
|
|
|
checksum_lines = []
|
|
manifest_binaries = []
|
|
for name in names:
|
|
packaged = out_dir / name
|
|
digest = sha256(packaged)
|
|
checksum_lines.append(f"{digest} {name}")
|
|
manifest_binaries.append(
|
|
{"name": name, "size_bytes": packaged.stat().st_size, "sha256": digest}
|
|
)
|
|
write(out_dir / "SHA256SUMS.txt", "\n".join(checksum_lines) + "\n")
|
|
|
|
manifest = {
|
|
"project": "AWatch-rus",
|
|
"target": args.target,
|
|
"commit": args.commit,
|
|
"ref": args.ref,
|
|
"run_id": args.run_id,
|
|
"build_time_utc": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
|
"binaries": manifest_binaries,
|
|
}
|
|
write(out_dir / "BUILD_MANIFEST.json", json.dumps(manifest, ensure_ascii=False, indent=2) + "\n")
|
|
|
|
archive.parent.mkdir(parents=True, exist_ok=True)
|
|
with tarfile.open(archive, "w:gz") as tar:
|
|
tar.add(out_dir, arcname=out_dir.name)
|
|
write_archive_checksum(archive)
|
|
create_compatibility_aliases(out_dir, archive)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|