Download source/scripts/deployment/package_handoff.py from Voltline/vimeml-tiny-ja-v2.1: direct link, hf CLI and curl.
- Browser
- Download file 6.41 kB
-
https://huggingface.co/Voltline/vimeml-tiny-ja-v2.1/resolve/main/source/scripts/deployment/package_handoff.py
- Command line
-
hf download hf://Voltline/vimeml-tiny-ja-v2.1/source/scripts/deployment/package_handoff.py
-
curl -L -o package_handoff.py https://huggingface.co/Voltline/vimeml-tiny-ja-v2.1/resolve/main/source/scripts/deployment/package_handoff.py
6.41 kB
| """Archive Mac project snapshots, experiment evidence and Vime client for local handoff.""" | |
| import argparse | |
| import hashlib | |
| import json | |
| import subprocess | |
| import zipfile | |
| from pathlib import Path | |
| from datetime import date | |
| SKIP_PARTS = {'.git', '__pycache__', '.pytest_cache', '.build', '.swiftpm', 'xcuserdata'} | |
| SKIP_NAMES = {'.DS_Store'} | |
| def digest(source): | |
| if isinstance(source, bytes): | |
| return hashlib.sha256(source).hexdigest() | |
| with source.open('rb') as stream: | |
| return hashlib.file_digest(stream, 'sha256').hexdigest() | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument('--project', type=Path, required=True) | |
| parser.add_argument('--client', type=Path, required=True) | |
| parser.add_argument('--output', type=Path, required=True) | |
| args = parser.parse_args() | |
| root, client, out = args.project.resolve(), args.client.resolve(), args.output.resolve() | |
| if out.exists(): | |
| raise SystemExit('Output already exists; use a new handoff version.') | |
| out.mkdir(parents=True) | |
| manifest = {'format': 'vimeml_mac_handoff_v1', 'date': date.today().isoformat(), | |
| 'vime_git_head': subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=client, text=True).strip(), | |
| 'vime_git_status': subprocess.check_output(['git', 'status', '--short'], cwd=client, text=True), | |
| 'archives': {}, 'excluded': [], | |
| 'policy': 'Local transfer only; snapshots are not automatic merge instructions or Git commits.'} | |
| def entries(base, roots, prefix): | |
| files = [] | |
| for name in roots: | |
| source = base / name | |
| if not source.exists(): | |
| continue | |
| paths = [source] if source.is_file() else sorted(source.rglob('*')) | |
| for path in paths: | |
| relative = path.relative_to(base) | |
| if not path.is_file(): | |
| continue | |
| if (SKIP_PARTS.intersection(relative.parts) or path.name in SKIP_NAMES or path.suffix == '.pyc' | |
| or path.name == '.env' or path.name.startswith('.env.') | |
| or path.name in {'.pypirc', '.envrc'}): | |
| manifest['excluded'].append(str(Path(prefix) / relative)) | |
| continue | |
| if path.is_symlink(): | |
| raise SystemExit(f'Unexpected symlink requires review: {path}') | |
| files.append(((Path(prefix) / relative).as_posix(), path)) | |
| return files | |
| def archive(name, files): | |
| indexed = [] | |
| with zipfile.ZipFile(out / name, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=3) as zip_file: | |
| for relative, data in files: | |
| value = digest(data) | |
| if isinstance(data, bytes): | |
| zip_file.writestr(relative, data) | |
| size = len(data) | |
| else: | |
| zip_file.write(data, relative) | |
| size = data.stat().st_size | |
| indexed.append({'path': relative, 'bytes': size, 'sha256': value}) | |
| # Verify every entry against its source hash, including models and raw trace files. | |
| with zipfile.ZipFile(out / name) as zip_file: | |
| for entry in indexed: | |
| with zip_file.open(entry['path']) as stream: | |
| value = hashlib.file_digest(stream, 'sha256').hexdigest() | |
| if value != entry['sha256']: | |
| raise SystemExit(f'Archive verification failed: {entry["path"]}') | |
| manifest['archives'][name] = {'file_count': len(indexed), | |
| 'uncompressed_bytes': sum(e['bytes'] for e in indexed), | |
| 'archive_bytes': (out / name).stat().st_size, | |
| 'entries': indexed} | |
| print(f'{name}: {len(indexed)} files, {(out / name).stat().st_size / 1048576:.1f} MiB; SHA256 verified', flush=True) | |
| source_roots = ['annotations', 'configs', 'docs', 'examples', 'scripts', 'src', 'tests'] | |
| source_roots += [p.name for p in root.iterdir() if p.is_file() and p.name not in SKIP_NAMES] | |
| archive('VimeML-source.zip', entries(root, source_roots, 'VimeML')) | |
| results_roots = ['outputs', 'runs'] + [str(p.relative_to(root)) for p in (root / 'artifacts').iterdir() | |
| if p.name not in {'models', 'token-data'}] | |
| archive('VimeML-results.zip', entries(root, results_roots, 'VimeML')) | |
| archive('VimeML-training-inputs.zip', entries(root, ['artifacts/models', 'artifacts/token-data'], 'VimeML')) | |
| client_paths = subprocess.check_output(['git', 'ls-files', '-z', '--cached', '--others', '--exclude-standard'], cwd=client).decode().split('\0') | |
| client_entries = entries(client, sorted(set(p for p in client_paths if p)), 'Vime') | |
| patch = subprocess.check_output(['git', 'diff', '--binary', 'HEAD', '--', '.', ':(exclude)**/xcuserdata/**'], cwd=client) | |
| client_entries += [('CLIENT_TRACKED_CHANGES.patch', patch), | |
| ('CLIENT_GIT_STATUS.txt', manifest['vime_git_status'].encode()), | |
| ('CLIENT_GIT_HEAD.txt', (manifest['vime_git_head'] + '\n').encode()), | |
| ('Vime/Docs/MacStageHandoff.md', (root / 'docs/reports/mac-20261006/handoff.md').read_bytes()), | |
| ('Vime/Docs/iPhoneKeyboardMemory.md', (root / 'docs/reports/mac-20261006/iphone-keyboard-memory.md').read_bytes())] | |
| archive('Vime-client.zip', client_entries) | |
| manifest['not_archived_roots'] = ['VimeML/venv (rebuild from requirements and environment snapshot)', | |
| 'VimeML/handoff (this generated output)', | |
| 'Vime/.git (base commit and patch preserved instead)'] | |
| (out / 'README.md').write_bytes((root / 'docs/reports/mac-20261006/handoff.md').read_bytes()) | |
| (out / 'MANIFEST.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n') | |
| sums = [] | |
| for path in sorted(out.iterdir()): | |
| if path.is_file(): | |
| with path.open('rb') as stream: | |
| value = hashlib.file_digest(stream, 'sha256').hexdigest() | |
| sums.append(f'{value} {path.name}') | |
| (out / 'SHA256SUMS.txt').write_text('\n'.join(sums) + '\n') | |
| print(f'Handoff ready: {out}', flush=True) | |
| if __name__ == '__main__': | |
| main() | |