mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-18 04:43:55 +00:00
138 lines
5.1 KiB
Python
138 lines
5.1 KiB
Python
"""Manual artifact release only; never invoked by the application build."""
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
from functools import cache
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse
|
|
from urllib.request import urlopen
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SOURCE = ROOT / 'crypto/encryption'
|
|
OUTPUT = ROOT / 'src/lib/encryption/vendor'
|
|
|
|
|
|
def run(*args, **kwargs):
|
|
return subprocess.run(args, cwd=SOURCE, check=True, **kwargs)
|
|
|
|
|
|
run('cargo', 'test', '--locked')
|
|
run('cargo', 'build', '--locked', '--release', '--target', 'wasm32-unknown-unknown')
|
|
run(
|
|
'wasm-bindgen',
|
|
'--target',
|
|
'web',
|
|
'--out-name',
|
|
'encryption',
|
|
'--out-dir',
|
|
str(OUTPUT),
|
|
str(SOURCE / 'target/wasm32-unknown-unknown/release/encryption.wasm'),
|
|
)
|
|
# Use Cargo's actual target build graph, excluding tests and unused optional crates.
|
|
metadata = json.loads(run('cargo', 'metadata', '--locked', '--format-version', '1', capture_output=True).stdout)
|
|
tree = run(
|
|
'cargo',
|
|
'tree',
|
|
'--locked',
|
|
'--target',
|
|
'wasm32-unknown-unknown',
|
|
'--edges',
|
|
'normal,build',
|
|
'--prefix',
|
|
'none',
|
|
'--format',
|
|
'{p}',
|
|
capture_output=True,
|
|
).stdout.decode()
|
|
used = {tuple(line.split()[:2]) for line in tree.splitlines()}
|
|
metadata['packages'] = [p for p in metadata['packages'] if (p['name'], 'v' + p['version']) in used]
|
|
assert {(p['name'], 'v' + p['version']) for p in metadata['packages']} == used
|
|
|
|
|
|
@cache
|
|
def download(url):
|
|
with urlopen(url, timeout=30) as response:
|
|
return response.read().decode('utf-8')
|
|
|
|
|
|
texts = {}
|
|
notices = [
|
|
'Third-Party Licenses',
|
|
'===================',
|
|
'',
|
|
'Scope: Encryption WebAssembly artifact and its build-time dependencies.',
|
|
'Packages are listed alphabetically with references to the full license and notice texts below.',
|
|
'Identical texts are included once; package-specific copyright notices are preserved.',
|
|
'Source code, including MPL-2.0 dependencies, is available at each Source URL below.',
|
|
]
|
|
for package in sorted(metadata['packages'], key=lambda p: (p['name'], p['version'])):
|
|
if package['source'] is None:
|
|
continue # The local wrapper is not a third-party dependency.
|
|
license = package['license'] or ''
|
|
choices = {
|
|
'MIT',
|
|
'Apache-2.0',
|
|
'BSD-2-Clause',
|
|
'BSD-3-Clause',
|
|
'ISC',
|
|
'Unicode-3.0',
|
|
'Zlib',
|
|
'CC0-1.0',
|
|
'Unlicense',
|
|
'MPL-2.0',
|
|
}
|
|
if not any(x.strip(' ()') in choices for x in license.replace('/', ' OR ').split(' OR ')):
|
|
raise RuntimeError(f'Review dependency license: {package["name"]} {license}')
|
|
directory = Path(package['manifest_path']).parent
|
|
documents = {
|
|
path.name: path.read_text()
|
|
for path in sorted(directory.iterdir())
|
|
if path.is_file() and path.name.upper().startswith(('LICENSE', 'COPYING', 'NOTICE'))
|
|
}
|
|
if package.get('license_file'):
|
|
path = directory / package['license_file']
|
|
documents[path.name] = path.read_text()
|
|
if not documents:
|
|
# Some published crates omit the workspace license; use their exact source revision.
|
|
if license == 'MPL-2.0':
|
|
url = 'https://www.mozilla.org/media/MPL/2.0/index.txt'
|
|
expected = 'Mozilla Public License Version 2.0'
|
|
else:
|
|
repository = urlparse(package['repository'])
|
|
if repository.netloc != 'github.com' or license not in ('MIT', 'Apache-2.0'):
|
|
raise RuntimeError(f'Missing license notice: {package["name"]}')
|
|
repo = '/'.join(repository.path.strip('/').split('/')[:2])
|
|
revision = json.loads((directory / '.cargo_vcs_info.json').read_text())['git']['sha1']
|
|
url = f'https://raw.githubusercontent.com/{repo}/{revision}/LICENSE'
|
|
expected = 'Permission is hereby granted' if license == 'MIT' else 'Apache License'
|
|
documents[url] = download(url)
|
|
if expected not in documents[url]:
|
|
raise RuntimeError(f'Unexpected license notice: {url}')
|
|
references = []
|
|
for name, text in documents.items():
|
|
text = text.strip()
|
|
if not text:
|
|
raise RuntimeError(f'Empty license notice: {package["name"]}/{name}')
|
|
number = texts.setdefault(text, len(texts) + 1)
|
|
references.append(f'{name}: [{number}]')
|
|
notices.append(
|
|
f'\nPackage: {package["name"]}\nVersion: {package["version"]}\nLicense: {license}\n'
|
|
f'Source: https://crates.io/api/v1/crates/{package["name"]}/{package["version"]}/download\n'
|
|
+ '\n'.join(references)
|
|
)
|
|
notices.extend(
|
|
f'\nLicense and notice text [{number}]\n--------------------------------\n{text}' for text, number in texts.items()
|
|
)
|
|
(OUTPUT / 'THIRD_PARTY_LICENSES.txt').write_text('\n'.join(notices) + '\n')
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json') as temp:
|
|
json.dump(metadata, temp)
|
|
temp.flush()
|
|
env = dict(
|
|
os.environ,
|
|
CRYPTO_SOURCE_REVISION=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=ROOT).decode().strip(),
|
|
)
|
|
subprocess.run(
|
|
['node', str(ROOT / 'scripts/encryption/artifact.mjs'), '--write', '--metadata', temp.name], check=True, env=env
|
|
)
|