Isolate manual artifact validation without package publication
This commit is contained in:
1 parent
98976747c3
commit
d02f1f0b55
75 files changed
+3895
-634
No files matched your search
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fetch pinned native cross-build dependencies without cloning Git history."""
|
||||
import argparse
|
||||
import ast
|
||||
import json
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# These dependencies correspond to features explicitly disabled by this recipe.
|
||||
# Unknown/new dependencies stay enabled; do not infer a skip from their names.
|
||||
DISABLED = {'dng_sdk', 'icu', 'dawn', 'angle2', 'swiftshader', 'emsdk'}
|
||||
|
||||
|
||||
def evaluate(node, variables):
|
||||
if isinstance(node, ast.Constant):
|
||||
return node.value
|
||||
if isinstance(node, ast.Dict):
|
||||
return {evaluate(k, variables): evaluate(v, variables) for k, v in zip(node.keys, node.values)}
|
||||
if isinstance(node, (ast.List, ast.Tuple)):
|
||||
return [evaluate(n, variables) for n in node.elts]
|
||||
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
|
||||
return evaluate(node.left, variables) + evaluate(node.right, variables)
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'Var' and len(node.args) == 1:
|
||||
return variables[evaluate(node.args[0], variables)]
|
||||
raise ValueError('Unsupported DEPS expression: ' + ast.dump(node))
|
||||
|
||||
|
||||
def dependencies(source):
|
||||
values = {}
|
||||
for node in ast.parse((source / 'DEPS').read_text()).body:
|
||||
if isinstance(node, ast.Assign):
|
||||
for name in node.targets:
|
||||
if isinstance(name, ast.Name) and name.id in ('vars', 'deps'):
|
||||
values[name.id] = evaluate(node.value, values.get('vars', {}))
|
||||
if not values.get('deps'):
|
||||
raise ValueError('No dependency map in DEPS')
|
||||
return values['deps']
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--source', type=Path, required=True)
|
||||
p.add_argument('--cache', type=Path, help='Immutable dependency checkouts shared across version profiles')
|
||||
p.add_argument('--no-fetch-gn', action='store_true', help='Use an existing verified GN for a local test')
|
||||
a = p.parse_args()
|
||||
source = a.source.resolve()
|
||||
records = {}
|
||||
for relative, value in dependencies(source).items():
|
||||
if not isinstance(value, str):
|
||||
continue # GN/Ninja CIPD packages are handled by upstream fetch-gn.
|
||||
if Path(relative).name in DISABLED:
|
||||
continue
|
||||
if '..' in Path(relative).parts:
|
||||
raise ValueError('Dependency path escapes source: ' + relative)
|
||||
link = source / relative
|
||||
path = link.absolute()
|
||||
if not path.is_relative_to(source):
|
||||
raise ValueError('Dependency path escapes source: ' + relative)
|
||||
url, revision = value.rsplit('@', 1)
|
||||
if not re.fullmatch(r'[0-9a-f]{40}', revision):
|
||||
raise ValueError('Dependency is not commit-pinned: ' + value)
|
||||
if a.cache:
|
||||
key = hashlib.sha256((url + '@' + revision).encode()).hexdigest()
|
||||
cached = a.cache.resolve() / key
|
||||
if link.is_symlink():
|
||||
link.unlink()
|
||||
if not link.exists():
|
||||
link.parent.mkdir(parents=True, exist_ok=True)
|
||||
link.symlink_to(cached, target_is_directory=True)
|
||||
path = cached
|
||||
if not (path / '.git').exists():
|
||||
subprocess.run(['git', 'init', '-q', str(path)], check=True)
|
||||
subprocess.run(['git', '-C', str(path), 'remote', 'add', 'origin', url], check=True)
|
||||
current = subprocess.run(['git', '-C', str(path), 'rev-parse', 'HEAD'], capture_output=True, text=True)
|
||||
if current.stdout.strip() != revision:
|
||||
subprocess.run(['git', '-C', str(path), 'remote', 'set-url', 'origin', url], check=True)
|
||||
subprocess.run(['git', '-C', str(path), 'fetch', '--depth=1', 'origin', revision], check=True)
|
||||
subprocess.run(['git', '-C', str(path), 'checkout', '--detach', revision], check=True)
|
||||
records[relative] = revision
|
||||
if not a.no_fetch_gn:
|
||||
subprocess.run([sys.executable, str(source / 'bin/fetch-gn')], cwd=source, check=True)
|
||||
(source / 'native-cross-deps.json').write_text(json.dumps(records, indent=2) + '\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user