← Files Ad Craftiness - صنعة الإعلانARCHIVED FILE
scripts/package.py
3.79 KB · Nov 3, 2026 · 19:28 UTC
#!/usr/bin/env python3
"""Build deterministic release package for OpenAI ChatGPT / Codex plugin."""
from __future__ import annotations
import hashlib
import json
import os
import shutil
import stat
import sys
import tempfile
import zipfile
from pathlib import Path
FIXED_TIME = (1980, 1, 1, 0, 0, 0)
ROOT = Path(__file__).resolve().parents[1]
def build_package(output_zip: Path) -> dict:
with tempfile.TemporaryDirectory() as tmpdir:
stage = Path(tmpdir) / "bundle"
stage.mkdir(parents=True, exist_ok=True)
# Copy required plugin files
(stage / ".codex-plugin").mkdir(parents=True, exist_ok=True)
shutil.copy2(ROOT / ".codex-plugin" / "plugin.json", stage / ".codex-plugin" / "plugin.json")
shutil.copy2(ROOT / "plugin.json", stage / "plugin.json")
shutil.copy2(ROOT / "README.md", stage / "README.md")
shutil.copy2(ROOT / "LICENSE", stage / "LICENSE")
shutil.copytree(ROOT / "skills", stage / "skills")
shutil.copytree(ROOT / "assets", stage / "assets")
shutil.copytree(ROOT / "evals", stage / "evals")
shutil.copytree(ROOT / "scripts", stage / "scripts")
if (ROOT / "docs").exists():
shutil.copytree(ROOT / "docs", stage / "docs")
# Clean OS / temporary files
for p in stage.rglob(".DS_Store"):
p.unlink()
for p in stage.rglob("__pycache__"):
shutil.rmtree(p)
for p in stage.rglob("*.py[cod]"):
p.unlink()
# Collect directories and files sorted for deterministic archive
directories: set[str] = set()
files: list[str] = []
for current, dirs, names in os.walk(stage, topdown=True, followlinks=False):
cur = Path(current)
for name in sorted(dirs):
rel = (cur / name).relative_to(stage).as_posix() + "/"
directories.add(rel)
for name in sorted(names):
rel = (cur / name).relative_to(stage).as_posix()
files.append(rel)
parts = rel.split("/")[:-1]
for i in range(1, len(parts) + 1):
directories.add("/".join(parts[:i]) + "/")
output_zip.parent.mkdir(parents=True, exist_ok=True)
tmp_zip = output_zip.with_name(output_zip.name + ".tmp")
if tmp_zip.exists():
tmp_zip.unlink()
with zipfile.ZipFile(tmp_zip, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
for rel in sorted(directories):
info = zipfile.ZipInfo(rel, FIXED_TIME)
info.create_system = 3
info.external_attr = (stat.S_IFDIR | 0o755) << 16
archive.writestr(info, b"")
for rel in sorted(files):
file_path = stage.joinpath(*rel.split("/"))
info = zipfile.ZipInfo(rel, FIXED_TIME)
info.create_system = 3
info.compress_type = zipfile.ZIP_DEFLATED
executable = bool(file_path.stat().st_mode & stat.S_IXUSR)
mode = 0o755 if executable else 0o644
info.external_attr = (stat.S_IFREG | mode) << 16
archive.writestr(info, file_path.read_bytes(), compress_type=zipfile.ZIP_DEFLATED, compresslevel=9)
tmp_zip.replace(output_zip)
digest = hashlib.sha256(output_zip.read_bytes()).hexdigest()
return {
"ok": True,
"outputPath": str(output_zip.resolve()),
"sha256": digest,
"bytes": output_zip.stat().st_size,
"entries": len(directories) + len(files),
}
def main():
dest = ROOT / "dist" / "ask-farouk-about-creative-thinking-v1.0.0.zip"
if len(sys.argv) > 1:
dest = Path(sys.argv[1]).resolve()
result = build_package(dest)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
SHA-256: 3f5fc14a220a673ba3af5f2adde01b1234bf6bd6211d4d8f2f2209a16ba5c100