mirror of
https://github.com/pybricks/pybricks-api.git
synced 2026-07-27 19:57:02 +00:00
Releasing API updates is quite a long and error prone process, so the point that it was limiting our ability to push frequent updates in practice. There were 5 tag, commit, wait, and proceed steps and you'd have to start over or force push if a mistake was made. This keeps all the npm packages as they were, but versions everything from a single source of truth, which is this project's main version. Everything builds on a tag on the main repo, much like we did for a regular IDE docs RTD release. It also skips the PyPI for jedi as an intermediate step, which isn't really needed and required us to have this strict order of publication steps.
142 lines
4.1 KiB
Python
Executable File
142 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import email.parser
|
|
import json
|
|
import pathlib
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import zipfile
|
|
|
|
if len(sys.argv) != 2:
|
|
print(f"Usage: {sys.argv[0]} <version>", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
VERSION = sys.argv[1]
|
|
|
|
ROOT_DIR = pathlib.Path(__file__).parent.resolve()
|
|
JEDI_SRC_DIR = (ROOT_DIR / ".." / ".." / "jedi").resolve()
|
|
BUILD_DIR = (ROOT_DIR / "build").resolve()
|
|
|
|
package_json = {
|
|
"name": "@pybricks/jedi",
|
|
"version": VERSION,
|
|
"description": "Binary distribution of pybricks-jedi Python package and dependencies for use with Pyodide.",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "https://github.com/pybricks/pybricks-api",
|
|
"directory": "npm/jedi",
|
|
},
|
|
"publishConfig": {"registry": "https://registry.npmjs.org", "access": "public"},
|
|
}
|
|
|
|
license_identifiers: set[str] = set()
|
|
license_text: dict[str, str] = {}
|
|
whl_map: dict[str, str] = {}
|
|
|
|
# ensure empty build directory so we don't end up with stale files
|
|
shutil.rmtree(BUILD_DIR, True)
|
|
BUILD_DIR.mkdir()
|
|
|
|
# build pybricks-jedi wheel from local source
|
|
subprocess.check_call(["poetry", "build", "--format=wheel"], cwd=JEDI_SRC_DIR)
|
|
|
|
# copy locally built wheel to build dir
|
|
for whl in (JEDI_SRC_DIR / "dist").glob("pybricks_jedi-*.whl"):
|
|
shutil.copy(whl, BUILD_DIR)
|
|
|
|
# download transitive dependencies from PyPI, using the local wheel to satisfy pybricks-jedi itself
|
|
subprocess.check_call(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pip",
|
|
"download",
|
|
"--only-binary=any",
|
|
f"--find-links={BUILD_DIR}",
|
|
"pybricks-jedi",
|
|
],
|
|
cwd=BUILD_DIR,
|
|
)
|
|
|
|
# extract info from wheel files to generate javascript package files
|
|
for whl in BUILD_DIR.glob("*.whl"):
|
|
with zipfile.ZipFile(whl) as f:
|
|
|
|
def is_dist_info_metadata(info: zipfile.ZipInfo):
|
|
return info.filename.endswith(".dist-info/METADATA")
|
|
|
|
def is_dist_info_license(info: zipfile.ZipInfo):
|
|
return ".dist-info" in info.filename and "LICENSE" in info.filename
|
|
|
|
metadata = next(filter(is_dist_info_metadata, f.filelist))
|
|
|
|
with f.open(metadata.filename) as mf:
|
|
meta = email.parser.BytesParser().parse(mf)
|
|
|
|
# extract package name from metadata
|
|
name = meta["Name"]
|
|
|
|
if not name:
|
|
raise RuntimeError(f"missing 'Name' in {whl.name} METADATA")
|
|
|
|
whl_map[name] = whl.name
|
|
|
|
# extract license identifier from metadata
|
|
|
|
license = meta["License"]
|
|
|
|
if not license:
|
|
# some packages are missing license in metadata
|
|
if whl.name.startswith("typing_extensions-"):
|
|
license = "Python-2.0"
|
|
else:
|
|
raise RuntimeError(f"missing 'License' in {whl.name} METADATA")
|
|
|
|
license_identifiers.add(license)
|
|
|
|
if whl.name.startswith("pybricks_jedi-"):
|
|
with open(JEDI_SRC_DIR / "LICENSE") as lf:
|
|
license_text[whl.name] = lf.read()
|
|
else:
|
|
try:
|
|
license = next(filter(is_dist_info_license, f.filelist))
|
|
except StopIteration:
|
|
raise RuntimeError(f"missing license for {whl.name}")
|
|
|
|
with f.open(license) as lf:
|
|
license_text[whl.name] = lf.read().decode()
|
|
|
|
|
|
# generate package.json file
|
|
|
|
# create "license" item from collected license identifiers
|
|
package_json["license"] = (
|
|
license_identifiers[0]
|
|
if len(license_identifiers) < 2
|
|
else f"({' AND '.join(license_identifiers)})"
|
|
)
|
|
|
|
# create "exports" item from collect whl map
|
|
package_json["exports"] = {f"./{k}.whl": f"./{v}" for k, v in whl_map.items()}
|
|
|
|
with open(BUILD_DIR / "package.json", "w") as f:
|
|
json.dump(package_json, f, indent=2)
|
|
|
|
# generate LICENSE file
|
|
|
|
NEWLINE = "\n"
|
|
DIVIDER = "=" * 80 + NEWLINE
|
|
with open(BUILD_DIR / "LICENSE", "w") as f:
|
|
for whl, text in license_text.items():
|
|
f.write(DIVIDER)
|
|
f.write(whl)
|
|
f.writelines([NEWLINE, DIVIDER, NEWLINE])
|
|
f.write(text)
|
|
f.write(NEWLINE)
|
|
|
|
# copy additional files
|
|
|
|
for file in ("README.md",):
|
|
shutil.copy(ROOT_DIR / file, BUILD_DIR / file)
|