| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- """Host-side enterprise -> pinned Gogs checkout mapping and post-initialization entry."""
- import argparse
- import json
- import os
- from pathlib import Path
- import re
- import subprocess
- import sys
- import uuid
- sys.dont_write_bytecode = True
- from bootstrap import COMPANY, Stopped, atomic_create, encode, need, plain, private_json, sealed, sha
- from host_bootstrap import initialize, run
- def register(registry, checkout, commit, apply=False, runner=subprocess.run):
- registry, checkout = plain(registry), plain(checkout)
- cfg, _ = sealed(checkout)
- need(re.fullmatch('[a-f0-9]{40}', commit), 'PINNED_COMMIT_REQUIRED')
- need(run(['git', '-C', str(checkout), 'rev-parse', 'HEAD'], runner).decode().strip() == commit,
- 'CONFIGURATION_COMMIT_MISMATCH')
- remote = run(['git', '-C', str(checkout), 'remote', 'get-url', 'origin'], runner).decode().strip()
- expected = 'https://git.fmode.cn/' + cfg['organization'] + '/' + cfg['repository'] + '.git'
- need(remote == expected, 'CONFIGURATION_REPOSITORY_MISMATCH')
- need(not run(['git', '-C', str(checkout), 'status', '--porcelain'], runner).strip(),
- 'CONFIGURATION_CHECKOUT_CHANGED')
- before = registry.read_bytes() if registry.exists() else None
- mapping = private_json(registry) if before is not None else {'schema': 'ksf.enterprise-registry.v1', 'companies': {}}
- need(set(mapping) == {'schema', 'companies'} and mapping['schema'] == 'ksf.enterprise-registry.v1'
- and isinstance(mapping['companies'], dict), 'INVALID_ENTERPRISE_REGISTRY')
- need(checkout != registry and checkout not in registry.parents, 'REGISTRY_INSIDE_CHECKOUT')
- entry = {'repositoryURL': expected, 'checkout': str(checkout), 'commit': commit,
- 'packageManifestSha256': sha((checkout / 'FILES.sha256').read_bytes())}
- changed = mapping['companies'].get(COMPANY) != entry
- mapping['companies'][COMPANY] = entry
- if changed and apply:
- if before is not None:
- atomic_create(registry.with_name(registry.name + '.before-' + uuid.uuid4().hex), before)
- temp = registry.with_name('.' + registry.name + '.' + uuid.uuid4().hex)
- try:
- atomic_create(temp, encode(mapping))
- need((registry.read_bytes() if registry.exists() else None) == before, 'REGISTRY_CONCURRENT_CHANGE')
- os.replace(temp, registry)
- finally:
- if temp.exists():
- temp.unlink()
- return {'ok': True, 'planOnly': not apply, 'mappingChanged': changed and apply,
- 'companyObjectId': COMPANY, 'configuration': entry, 'businessWrites': 0}
- def after_twin(registry, container, apply=False, runner=subprocess.run):
- need(re.fullmatch('[a-zA-Z0-9][a-zA-Z0-9_.-]{0,100}', container), 'INVALID_CONTAINER_NAME')
- current = json.loads(run(['docker', 'exec', '--user', '10000:10000', container,
- 'cat', '/opt/data/.fmode/config/enterprise/current.json'], runner))
- mapping = private_json(registry)
- need(mapping.get('schema') == 'ksf.enterprise-registry.v1', 'INVALID_ENTERPRISE_REGISTRY')
- entry = mapping.get('companies', {}).get(current.get('companyObjectId'))
- if entry is None:
- return {'ok': True, 'skipped': True, 'reason': 'NO_ENTERPRISE_WORKSHOP_MAPPING', 'businessWrites': 0}
- need(current.get('companyObjectId') == COMPANY, 'UNSUPPORTED_ENTERPRISE')
- root = plain(entry['checkout'])
- need(sha((root / 'FILES.sha256').read_bytes()) == entry['packageManifestSha256'], 'REGISTERED_PACKAGE_CHANGED')
- return initialize(root, entry['commit'], container, apply, runner)
- def main():
- parser = argparse.ArgumentParser(description=__doc__)
- sub = parser.add_subparsers(dest='command', required=True)
- bind = sub.add_parser('bind')
- bind.add_argument('--checkout', type=Path, required=True)
- bind.add_argument('--commit', required=True)
- hook = sub.add_parser('after-twin')
- hook.add_argument('--container', required=True)
- for p in (bind, hook):
- p.add_argument('--registry', type=Path, required=True)
- p.add_argument('--apply', action='store_true')
- args = parser.parse_args()
- need(args.registry.is_absolute(), 'ABSOLUTE_PATH_REQUIRED')
- if args.command == 'bind':
- need(args.checkout.is_absolute(), 'ABSOLUTE_PATH_REQUIRED')
- result = register(args.registry, args.checkout, args.commit, args.apply)
- else:
- result = after_twin(args.registry, args.container, args.apply)
- print(json.dumps(result, ensure_ascii=False, indent=2))
- if __name__ == '__main__':
- try:
- main()
- except (Stopped, OSError, ValueError, KeyError, subprocess.TimeoutExpired) as error:
- print(json.dumps({'ok': False, 'error': {'code': str(error) if isinstance(error, Stopped)
- else 'ENTERPRISE_REGISTRY_STOPPED'}}), file=sys.stderr)
- sys.exit(1)
|