Complete local voxel MVP with server, client, MCP, packages and world interchange
This commit is contained in:
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package a clean, committed source tree and optional Linux executables.
|
||||
|
||||
No user worlds, tokens, Cargo caches, Minecraft JAR, or untracked files enter
|
||||
an archive. Every extracted file is checked against the in-archive manifest.
|
||||
"""
|
||||
import argparse
|
||||
import gzip
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import subprocess
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
def git(*args):
|
||||
return subprocess.check_output(['git', *args], cwd=ROOT)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--binaries', action='store_true')
|
||||
args = parser.parse_args()
|
||||
if git('status', '--porcelain').strip():
|
||||
raise SystemExit('Commit the reviewed tree before packaging.')
|
||||
revision = git('rev-parse', 'HEAD').decode().strip()
|
||||
epoch = int(git('show', '-s', '--format=%ct', 'HEAD'))
|
||||
paths = [Path(p.decode()) for p in git('ls-files', '-z').split(b'\0') if p]
|
||||
if args.binaries:
|
||||
paths += [Path('target/release') / name for name in
|
||||
['shacraft-server', 'shacraft-mcp', 'shacraft-compat', 'shacraft-tools']]
|
||||
files = []
|
||||
for relative in sorted(paths):
|
||||
path = ROOT / relative
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise SystemExit(f'Only regular files are distributable: {relative}')
|
||||
if relative.name.endswith('.token') or relative.name == '.env':
|
||||
raise SystemExit(f'Secret filename cannot be distributed: {relative}')
|
||||
data = path.read_bytes()
|
||||
files.append((relative.as_posix(), data, 0o755 if path.stat().st_mode & 0o111 else 0o644))
|
||||
manifest = {'project':'Shacraft Core','git_revision':revision,'source_epoch':epoch,
|
||||
'kind':'linux-x86_64' if args.binaries else 'source',
|
||||
'license':'MIT OR Apache-2.0',
|
||||
'files':{name:{'sha256':hashlib.sha256(data).hexdigest(),'size':len(data)} for name,data,_ in files}}
|
||||
files.append(('RELEASE.json', (json.dumps(manifest,indent=2)+'\n').encode(), 0o644))
|
||||
output = ROOT / 'artifacts' / f"shacraft-core-mvp-{revision[:8]}-{manifest['kind']}.tar.gz"
|
||||
output.parent.mkdir(exist_ok=True)
|
||||
with output.open('wb') as raw, gzip.GzipFile(fileobj=raw,mode='wb',mtime=0) as zipped:
|
||||
with tarfile.open(fileobj=zipped,mode='w') as archive:
|
||||
for name,data,mode in files:
|
||||
info=tarfile.TarInfo('shacraft-core/'+name)
|
||||
info.size=len(data);info.mode=mode;info.mtime=epoch
|
||||
archive.addfile(info,io.BytesIO(data))
|
||||
digest=hashlib.sha256(output.read_bytes()).hexdigest()
|
||||
output.with_name(output.name+'.sha256').write_text(f'{digest} {output.name}\n')
|
||||
with tarfile.open(output,'r:gz') as archive:
|
||||
for name,data,_ in files:
|
||||
actual=archive.extractfile('shacraft-core/'+name).read()
|
||||
if actual!=data:
|
||||
raise SystemExit(f'Archive verification failed for {name}')
|
||||
print(json.dumps({'archive':str(output),'sha256':digest,'bytes':output.stat().st_size,
|
||||
'git_revision':revision,'verified_files':len(files)},indent=2))
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user