Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
180 lines
5.7 KiB
Python
Executable File
180 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import base64
|
|
import hashlib
|
|
import os.path
|
|
import shutil
|
|
import tempfile
|
|
import zipfile
|
|
from typing import NamedTuple
|
|
|
|
|
|
class Wheel(NamedTuple):
|
|
src: str
|
|
plat: str
|
|
exe: str = 'sentry-cli'
|
|
|
|
|
|
WHEELS = (
|
|
Wheel(
|
|
src='sentry-cli-Darwin-arm64',
|
|
plat='macosx_11_0_arm64',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Darwin-universal',
|
|
plat='macosx_11_0_universal2',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Darwin-x86_64',
|
|
plat='macosx_10_15_x86_64',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Linux-aarch64',
|
|
plat='manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_2_aarch64',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Linux-armv7',
|
|
plat='manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_2_armv7l',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Linux-i686',
|
|
plat='manylinux_2_17_i686.manylinux2014_i686.musllinux_1_2_i686',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Linux-x86_64',
|
|
plat='manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_2_x86_64',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Windows-i686.exe',
|
|
plat='win32',
|
|
exe='sentry-cli.exe',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Windows-x86_64.exe',
|
|
plat='win_amd64',
|
|
exe='sentry-cli.exe',
|
|
),
|
|
Wheel(
|
|
src='sentry-cli-Windows-aarch64.exe',
|
|
plat='win_arm64',
|
|
exe='sentry-cli.exe',
|
|
),
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--binaries', required=True)
|
|
parser.add_argument('--base', required=True)
|
|
parser.add_argument('--dest', required=True)
|
|
args = parser.parse_args()
|
|
|
|
expected = {wheel.src for wheel in WHEELS}
|
|
received = set(os.listdir(args.binaries))
|
|
if expected < received:
|
|
raise SystemExit(
|
|
f'Unexpected binaries:\n\n'
|
|
f'- extra: {", ".join(sorted(received - expected))}\n'
|
|
f'- missing: {", ".join(sorted(expected - received))}'
|
|
)
|
|
|
|
sdist_path = wheel_path = None
|
|
for fname in os.listdir(args.base):
|
|
if fname.endswith('.tar.gz'):
|
|
sdist_path = os.path.join(args.base, fname)
|
|
elif fname.endswith('.whl'):
|
|
wheel_path = os.path.join(args.base, fname)
|
|
else:
|
|
raise SystemExit(f'unexpected file in `--base`: {fname}')
|
|
|
|
if sdist_path is None or wheel_path is None:
|
|
raise SystemExit('expected wheel and sdist in `--base`')
|
|
|
|
os.makedirs(args.dest, exist_ok=True)
|
|
shutil.copy(sdist_path, args.dest)
|
|
|
|
for wheel in WHEELS:
|
|
binary_src = os.path.join(args.binaries, wheel.src)
|
|
binary_size = os.stat(binary_src).st_size
|
|
with open(binary_src, 'rb') as bf:
|
|
digest = hashlib.sha256(bf.read()).digest()
|
|
digest_b64 = base64.urlsafe_b64encode(digest).rstrip(b'=').decode()
|
|
|
|
basename = os.path.basename(wheel_path)
|
|
wheelname, _ = os.path.splitext(basename)
|
|
name, version, py, abi, plat = wheelname.split('-')
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
with zipfile.ZipFile(wheel_path) as zipf:
|
|
zipf.extractall(tmp)
|
|
|
|
distinfo = os.path.join(tmp, f'{name}-{version}.dist-info')
|
|
scripts = os.path.join(tmp, f'{name}-{version}.data', 'scripts')
|
|
|
|
# replace the script binary with our copy
|
|
os.remove(os.path.join(scripts, 'sentry-cli'))
|
|
shutil.copy(binary_src, os.path.join(scripts, wheel.exe))
|
|
|
|
# rewrite RECORD to include the new file
|
|
record_fname = os.path.join(distinfo, 'RECORD')
|
|
with open(record_fname) as f:
|
|
record_lines = list(f)
|
|
|
|
record = f'{name}-{version}.data/scripts/sentry-cli,'
|
|
for i, line in enumerate(record_lines):
|
|
if line.startswith(record):
|
|
record_lines[i] = (
|
|
f'{name}-{version}.data/scripts/{wheel.exe},'
|
|
f'sha256={digest_b64},'
|
|
f'{binary_size}\n'
|
|
)
|
|
break
|
|
else:
|
|
raise SystemExit(f'could not find {record!r} in RECORD')
|
|
|
|
with open(record_fname, 'w') as f:
|
|
f.writelines(record_lines)
|
|
|
|
# rewrite WHEEL to have the new tags
|
|
wheel_fname = os.path.join(distinfo, 'WHEEL')
|
|
with open(wheel_fname) as f:
|
|
wheel_lines = list(f)
|
|
|
|
for i, line in enumerate(wheel_lines):
|
|
if line.startswith('Tag: '):
|
|
wheel_lines[i:i + 1] = [
|
|
f'Tag: {py}-{abi}-{plat}\n'
|
|
for plat in wheel.plat.split('.')
|
|
]
|
|
break
|
|
else:
|
|
raise SystemExit("could not find 'Tag: ' in WHEEL")
|
|
|
|
with open(wheel_fname, 'w') as f:
|
|
f.writelines(wheel_lines)
|
|
|
|
# write out the final zip
|
|
new_basename = f'{name}-{version}-{py}-{abi}-{wheel.plat}.whl'
|
|
tmp_new_wheel = os.path.join(tmp, new_basename)
|
|
fnames = sorted(
|
|
os.path.join(root, fname)
|
|
for root, _, fnames in os.walk(tmp)
|
|
for fname in fnames
|
|
)
|
|
with zipfile.ZipFile(tmp_new_wheel, 'w') as zipf:
|
|
for fname in fnames:
|
|
zinfo = zipfile.ZipInfo(os.path.relpath(fname, tmp))
|
|
if '/scripts/' in zinfo.filename:
|
|
zinfo.external_attr = 0o100755 << 16
|
|
with open(fname, 'rb') as fb:
|
|
zipf.writestr(zinfo, fb.read())
|
|
|
|
# move into dest
|
|
shutil.move(tmp_new_wheel, args.dest)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|