Skip to content

Commit 973af35

Browse files
update script
1 parent 3d287e3 commit 973af35

File tree

2 files changed

+64
-127
lines changed

2 files changed

+64
-127
lines changed

pkgs/src/mytool/1.0.0/manifest.acl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ manifest {
1111
commit = "abcdef0123456789";
1212
date = "2025-10-12";
1313
}
14-
string archive_sha256 = "03909b5b45720b0af771f0233eb5a71bcfaed4340841944df147c408febd069a";
14+
string archive_sha256 = "9326ae5d58c78791eb57503e9c488135b6fa2f34cd63c79016ec07efb0c0a48b";
1515
string pkg_format = "pandora-v1";
1616
}

scripts/create_pkg.py

Lines changed: 63 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
#!/usr/bin/env python3
22
# scripts/create_package.py
33
"""
4-
Create a deterministic .pkg from a package source dir, using local `arch` if available.
5-
If `arch` is missing, this script will attempt to compile src/arch.c into a local arch binary.
4+
Create a deterministic .pkg from a package source dir, always using local build/arch.
65
76
Usage:
8-
scripts/create_package.py <src_dir> [--out-root pkgs] [--keep-temp] [--arch-cmd PATH]
9-
10-
Examples:
11-
scripts/create_package.py pkgs/src/mytool/3.0.0 --out-root pkgs
7+
scripts/create_package.py <src_dir> [--out-root pkgs] [--keep-temp]
128
139
Behavior:
1410
- Expects a manifest.acl file at the top of <src_dir>.
1511
- Produces <out_root>/<name>/<ver>/<name>-<ver>.pkg
16-
- If 'arch' executable is found in PATH or provided via --arch-cmd, calls: arch <src_dir> <out_pkg>
17-
- Otherwise attempts to compile src/arch.c to ./build/arch and use it; if compilation fails, falls back to deterministic tar.gz.
12+
- ALWAYS uses build/arch. If build/arch is missing, attempts to compile src/arch.c -> build/arch.
13+
- If compilation or arch invocation fails, the script exits with non-zero (no tar fallback).
1814
- Computes SHA256 and updates (or inserts) archive_sha256 in manifest.acl.
1915
"""
2016
from __future__ import annotations
@@ -47,13 +43,12 @@ def find_manifest(src: Path) -> Path:
4743
def update_manifest_archive_sha(manifest_path: Path, sha_hex: str) -> None:
4844
text = manifest_path.read_text(encoding="utf-8")
4945
new_line = f' string archive_sha256 = "{sha_hex}";'
50-
# replace existing archive_sha or archive_sha256 line if present
5146
pat = re.compile(r'^\s*(string\s+)?archive_sha(?:256)?\s*=\s*".*"\s*;\s*$', re.MULTILINE)
5247
if pat.search(text):
5348
text = pat.sub(new_line, text, count=1)
5449
else:
55-
# attempt to insert before closing brace of first top-level block
56-
m = re.search(r'^[a-zA-Z_][a-zA-Z0-9_]*\s*\{', text, re.MULTILINE)
50+
# insert before the first top-level closing brace if possible
51+
m = re.search(r'^[A-Za-z_][A-Za-z0-9_]*\s*\{', text, re.MULTILINE)
5752
if m:
5853
start = m.end()
5954
depth = 1
@@ -76,114 +71,65 @@ def update_manifest_archive_sha(manifest_path: Path, sha_hex: str) -> None:
7671
def ensure_parent(path: Path) -> None:
7772
path.parent.mkdir(parents=True, exist_ok=True)
7873

79-
# -------- arch build / invocation --------
80-
81-
def try_find_arch(provided: Optional[str]) -> Optional[Path]:
82-
# If user provided explicit arch-cmd, prefer that (verify executable)
83-
if provided:
84-
p = Path(provided)
85-
if p.is_file() and os.access(p, os.X_OK):
86-
return p
87-
# maybe it's in PATH; shutil.which handles that
88-
which = shutil.which(provided)
89-
if which:
90-
return Path(which)
91-
return None
92-
# look in PATH
93-
which = shutil.which("arch")
94-
if which:
95-
return Path(which)
96-
# try local build/arch
97-
local = Path("build") / "arch"
98-
if local.is_file() and os.access(local, os.X_OK):
99-
return local
100-
# try .local/bin/arch
101-
local2 = Path(".local") / "bin" / "arch"
102-
if local2.is_file() and os.access(local2, os.X_OK):
103-
return local2
104-
return None
74+
# -------- arch build / invocation (always use build/arch) --------
10575

106-
def attempt_build_arch(src_dir: Path) -> Optional[Path]:
76+
def arch_executable() -> Path:
10777
"""
108-
Try to compile src/arch.c into build/arch (or .local/bin/arch).
109-
Returns Path to compiled arch on success, or None on failure.
78+
Return the Path to build/arch. If missing, attempt to compile src/arch.c into it.
79+
If compilation fails, raise RuntimeError.
11080
"""
81+
out_path = Path("build") / "arch"
82+
if out_path.is_file() and os.access(out_path, os.X_OK):
83+
return out_path
84+
85+
# attempt to build from src/arch.c
11186
src_c = Path("src") / "arch.c"
11287
if not src_c.is_file():
113-
return None
114-
out_dir = Path("build")
88+
raise RuntimeError("build/arch not found and src/arch.c missing; cannot proceed")
89+
90+
out_dir = out_path.parent
11591
out_dir.mkdir(parents=True, exist_ok=True)
116-
out_path = out_dir / "arch"
92+
93+
# prefer cc/gcc/clang
11794
cc = shutil.which("cc") or shutil.which("gcc") or shutil.which("clang")
11895
if not cc:
119-
return None
96+
raise RuntimeError("C compiler not found; cannot build build/arch")
97+
12098
cmd = [cc, "-O2", "-std=c11", "-o", str(out_path), str(src_c)]
121-
print("Compiling arch:", " ".join(cmd))
122-
try:
123-
res = subprocess.run(cmd, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
124-
if res.returncode != 0:
125-
print("Compilation failed:", res.stderr.strip(), file=sys.stderr)
126-
return None
127-
# ensure executable bit
128-
out_path.chmod(0o755)
129-
return out_path
130-
except Exception as e:
131-
print("Compilation error:", e, file=sys.stderr)
132-
return None
99+
print("Compiling arch:", " ".join(cmd), file=sys.stderr)
100+
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
101+
if res.returncode != 0:
102+
print("Compilation of build/arch failed.", file=sys.stderr)
103+
print("=== stdout ===", file=sys.stderr)
104+
print(res.stdout, file=sys.stderr)
105+
print("=== stderr ===", file=sys.stderr)
106+
print(res.stderr, file=sys.stderr)
107+
raise RuntimeError("compilation failed for build/arch")
108+
out_path.chmod(0o755)
109+
return out_path
133110

134111
def call_arch(arch_path: Path, src: Path, out_pkg: Path) -> int:
135112
cmd = [str(arch_path), "pack", str(out_pkg), str(src)]
136-
print("Running arch:", " ".join(cmd))
113+
print("Running arch:", " ".join(cmd), file=sys.stderr)
137114
try:
138-
res = subprocess.run(cmd, check=False)
115+
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
116+
# print stdout/stderr for CI visibility
117+
if res.stdout:
118+
print("arch stdout:", res.stdout, file=sys.stderr)
119+
if res.stderr:
120+
print("arch stderr:", res.stderr, file=sys.stderr)
139121
return res.returncode
140122
except Exception as e:
141123
print("arch invocation failed:", e, file=sys.stderr)
142124
return 1
143125

144-
# -------- tar fallback creation --------
145-
146-
def create_tar_fallback(src: Path, out_pkg: Path) -> int:
147-
tmpdir = tempfile.mkdtemp(prefix="create_pkg_tmp_")
148-
try:
149-
tmp_tar = Path(tmpdir) / "archive.tar"
150-
# Use a shell pipeline to create deterministic tar. Keep it simple and rely on GNU tar and gzip.
151-
# Build a sorted list of files and feed to tar with --null -T - and deterministic options.
152-
# The --transform 's|^\./||' removes leading ./ entries so archive root contains package root contents.
153-
shell_cmd = (
154-
f"cd {str(src)!r} && "
155-
"find . -print0 | LC_ALL=C sort -z | "
156-
f"tar --null -T - --numeric-owner --owner=0 --group=0 --mtime='UTC 1970-01-01' -cf {str(tmp_tar)!r} --transform 's|^\\./||'"
157-
)
158-
res = subprocess.run(shell_cmd, shell=True)
159-
if res.returncode != 0:
160-
print("tar creation failed", file=sys.stderr)
161-
return res.returncode
162-
tmp_gz = Path(tmpdir) / "archive.tar.gz"
163-
gz_cmd = ["gzip", "-9", "-n", "-c", str(tmp_tar)]
164-
with tmp_gz.open("wb") as outf:
165-
proc = subprocess.Popen(gz_cmd, stdout=outf)
166-
proc.wait()
167-
if proc.returncode != 0:
168-
print("gzip failed", file=sys.stderr)
169-
return proc.returncode
170-
ensure_parent(out_pkg)
171-
shutil.move(str(tmp_gz), str(out_pkg))
172-
return 0
173-
finally:
174-
try:
175-
shutil.rmtree(tmpdir)
176-
except Exception:
177-
pass
178-
179126
# -------- main flow --------
180127

181128
def main(argv):
182-
ap = argparse.ArgumentParser(description="Create package .pkg from source tree, auto-building arch if needed")
183-
ap.add_argument("src_dir", help="source directory: docs/src/<name>/<ver>")
129+
ap = argparse.ArgumentParser(description="Create package .pkg from source tree; always use build/arch")
130+
ap.add_argument("src_dir", help="source directory: pkgs/src/<name>/<ver>")
184131
ap.add_argument("--out-root", default="pkgs", help="output root (default: pkgs)")
185132
ap.add_argument("--keep-temp", action="store_true", help="keep temporary files on error")
186-
ap.add_argument("--arch-cmd", default=None, help="custom arch command path (optional)")
187133
args = ap.parse_args(argv)
188134

189135
src = Path(args.src_dir).resolve()
@@ -212,39 +158,30 @@ def main(argv):
212158
out_pkg_dir.mkdir(parents=True, exist_ok=True)
213159
out_pkg = out_pkg_dir / f"{name}-{version}.pkg"
214160

215-
# determine arch binary
216-
arch_path = try_find_arch(args.arch_cmd)
217-
if not arch_path:
218-
print("arch binary not found. Attempting to build from src/arch.c ...")
219-
built = attempt_build_arch(Path("src"))
220-
if built:
221-
arch_path = built
222-
print("Built arch at", arch_path)
223-
else:
224-
print("Could not build arch; will use tar fallback", file=sys.stderr)
161+
# ensure parent dir exists
162+
ensure_parent(out_pkg)
225163

226-
# try arch if available
227-
if arch_path:
228-
rc = call_arch(arch_path, src, out_pkg)
229-
if rc == 0 and out_pkg.is_file():
230-
print("arch succeeded, produced:", out_pkg)
231-
else:
232-
print(f"arch returned code {rc}; falling back to deterministic tar", file=sys.stderr)
233-
rc2 = create_tar_fallback(src, out_pkg)
234-
if rc2 != 0:
235-
print("Failed to produce archive via fallback (code {})".format(rc2), file=sys.stderr)
236-
return 1
237-
print("Created archive via tar fallback:", out_pkg)
238-
else:
239-
rc2 = create_tar_fallback(src, out_pkg)
240-
if rc2 != 0:
241-
print("Failed to produce archive via fallback (code {})".format(rc2), file=sys.stderr)
242-
return 1
243-
print("Created archive via tar fallback:", out_pkg)
164+
# Always use build/arch. Try to find or compile it.
165+
try:
166+
arch_path = arch_executable()
167+
except Exception as e:
168+
print("ERROR: could not obtain build/arch:", e, file=sys.stderr)
169+
return 3
170+
171+
rc = call_arch(arch_path, src, out_pkg)
172+
if rc != 0 or not out_pkg.is_file():
173+
print(f"ERROR: build/arch failed (rc={rc}) or did not produce {out_pkg}", file=sys.stderr)
174+
return 4
175+
176+
print("arch succeeded, produced:", out_pkg, file=sys.stderr)
244177

245178
# compute sha256
246-
sha = compute_sha256(out_pkg)
247-
print("SHA256:", sha)
179+
try:
180+
sha = compute_sha256(out_pkg)
181+
print("SHA256:", sha)
182+
except Exception as e:
183+
print("ERROR computing SHA256:", e, file=sys.stderr)
184+
return 5
248185

249186
# update manifest archive_sha256
250187
try:

0 commit comments

Comments
 (0)