| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- """Package a complete first-install kit; preserve sealed incremental releases."""
- from pathlib import Path
- import argparse
- import hashlib
- import io
- import json
- import re
- import tarfile
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- OUT = ROOT / 'docs/workshop-onboarding-20261109'
- RELEASE = 'ksf-workshop-onboarding-20261109-r1'
- sha = lambda raw: hashlib.sha256(raw).hexdigest()
- encode = lambda value: (json.dumps(value, ensure_ascii=False, indent=2) + '\n').encode('utf-8')
- def stage():
- workshop_dir = ROOT / 'skills/ksf-workshop-runtime'
- prior = ROOT / 'docs/workshop-skill-release-20261109-r7'
- ready = json.loads((prior / 'release-ready.json').read_bytes())
- assert sha((prior / ready['zip']).read_bytes()) == ready['sha256']
- with zipfile.ZipFile(prior / ready['zip']) as z:
- r7 = json.loads(z.read('release.json'))['candidate']
- assert len(r7) == 43
- workshop = {}
- package = {}
- for name, expected in r7.items():
- raw = (workshop_dir / name).read_bytes()
- assert sha(raw) == expected, name
- assert not any(term in name.lower() for term in ['credential-binding', 'private.pem', 'envelope'])
- workshop[name] = expected
- package['skills/ksf-workshop-runtime/' + name] = raw
- source = json.loads((OUT / 'source-audit/voc-source.json').read_bytes())
- raw = (OUT / 'source-audit/voc-skill-0.3.42.tgz').read_bytes()
- assert sha(raw) == source['sha256'] and len(raw) == source['bytes']
- voc_files = {}
- with tarfile.open(fileobj=io.BytesIO(raw), mode='r:gz') as archive:
- for member in archive.getmembers():
- assert member.isfile() and member.name.startswith('package/')
- name = member.name[len('package/'):]
- assert name not in voc_files and '..' not in Path(name).parts and not name.startswith('/')
- voc_files[name] = sha(archive.extractfile(member).read())
- assert len(voc_files) == 169
- links = 0
- for name in workshop:
- if name.endswith('.md'):
- p = workshop_dir / name
- for target in re.findall(r'\]\(([^)]+)\)', p.read_text(encoding='utf-8')):
- if '://' in target or target.startswith('#'):
- continue
- resolved = (p.parent / target.split('#', 1)[0]).resolve()
- assert resolved.is_file() and resolved.is_relative_to(workshop_dir.resolve())
- links += 1
- meta = {'schema': 'ksf.workshop-onboarding.v1', 'releaseId': RELEASE,
- 'workshop': {'name': 'ksf-workshop-runtime', 'version': 'r7', 'files': workshop,
- 'sourceReleaseSha256': ready['sha256'], 'relativeLinks': links},
- 'voc': {**{k: source[k] for k in ['name', 'version', 'url', 'integrity', 'sha256', 'bytes']},
- 'files': voc_files, 'distribution': 'download-from-official-npm-no-third-party-artifact-included'},
- 'installation': {'freshInstallSupported': True, 'existingMatchingVersion': 'verify-only',
- 'existingDifferentVersion': 'stop-preserve-and-use-host-upgrade-path',
- 'hostSkillsDirectory': 'must-be-provided-by-host',
- 'hostConfigurationModifiedByInstaller': False},
- 'credentialsIncluded': False, 'databaseIncluded': False, 'remoteInstalled': False,
- 'doubaoNativeSupportVerified': False}
- package.update({'release.json': encode(meta), 'README.md': (OUT / 'README.md').read_bytes(),
- 'install.py': (ROOT / 'scripts/workshop-onboarding/install.py').read_bytes(),
- 'check-voc.cjs': (ROOT / 'scripts/workshop-onboarding/check-voc.cjs').read_bytes(),
- '初始化提示词.txt': (OUT / '一键初始化正文.txt').read_bytes()})
- validation = OUT / 'local-validation.json'
- if validation.exists():
- value = json.loads(validation.read_bytes())
- assert value['ok'] and value['installerSha256'] == sha(package['install.py'])
- assert value['runtimeCheckSha256'] == sha(package['check-voc.cjs'])
- package[validation.name] = validation.read_bytes()
- package['FILES.sha256'] = ''.join(sha(raw) + ' ' + name + '\n' for name, raw in sorted(package.items())).encode()
- for name, raw in package.items():
- p = OUT / 'package' / name
- p.parent.mkdir(parents=True, exist_ok=True)
- p.write_bytes(raw)
- return package, meta
- def main():
- parser = argparse.ArgumentParser()
- parser.add_argument('--pack', action='store_true')
- args = parser.parse_args()
- package, meta = stage()
- if not args.pack:
- print(json.dumps({'ok': True, 'staged': len(package), 'workshopFiles': 43, 'vocOriginalFiles': 169}))
- return
- assert 'local-validation.json' in package, 'Validation required before release'
- target = OUT / (RELEASE + '.zip')
- assert not target.exists(), 'Never overwrite a released archive'
- with zipfile.ZipFile(target, 'x', zipfile.ZIP_DEFLATED) as z:
- for name, raw in sorted(package.items()):
- info = zipfile.ZipInfo(name, (2026, 11, 9, 0, 0, 0))
- info.external_attr = 0o100600 << 16
- z.writestr(info, raw, compress_type=zipfile.ZIP_DEFLATED)
- with zipfile.ZipFile(target) as z:
- assert z.testzip() is None and set(z.namelist()) == set(package)
- assert all(z.read(name) == raw for name, raw in package.items())
- receipt = {'ok': True, 'releaseId': RELEASE, 'filename': target.name, 'bytes': target.stat().st_size,
- 'sha256': sha(target.read_bytes()), 'packageEntries': len(package),
- 'workshopFiles': 43, 'vocVersion': meta['voc']['version'], 'vocDownloadedFromOfficialSource': True,
- 'thirdPartyArtifactIncluded': False, 'gitPublished': False, 'remoteInstalled': False,
- 'doubaoActualInstallationVerified': False}
- (OUT / 'package-receipt.json').write_bytes(encode(receipt))
- print(json.dumps(receipt, ensure_ascii=False, indent=2))
- if __name__ == '__main__':
- main()
|