Compare commits

..
20 Commits
Author SHA1 Message Date
Laurens Valk f1ef421676 v4.0.0b8 2026-05-30 11:02:40 +02:00
Laurens Valk 608c3f0d98 npm/jedi: Further trim down build logic.
We used to separately build jedi and then use another Python step to package it.

We can just do it from the same environment to avoid packaging issues.
2026-05-30 11:02:12 +02:00
Laurens Valk b65443f1ec v4.0.0b7 2026-05-30 10:47:00 +02:00
Laurens Valk 88b901833c npm/jedi: Use pybricks-api from source. 2026-05-30 10:43:15 +02:00
Laurens Valk d61e452a04 v4.0.0b6 2026-05-30 10:01:23 +02:00
Laurens Valk f880b821c0 CHANGELOG: Explain synchronization with firmware. 2026-05-30 10:00:18 +02:00
Laurens Valk acda5b21cb npm/jedi: Build pybricks-api from source.
Now that we use a single release pipeline, the API itself might not yet be on PyPI by the time we build jedi, so we build it locally.

We'll still keep the API docs on PyPI so users can have local autocomplete, but that path is no longer a dependency for building jedi.
2026-05-30 09:40:09 +02:00
Laurens Valk a780b8d69e v4.0.0b5 2026-05-29 22:04:02 +02:00
Laurens Valk c1054edf02 .github/workflows: Use npm tag for beta versions. 2026-05-29 22:03:23 +02:00
Laurens Valk dcd4d3a45c v4.0.0b4 2026-05-29 21:58:24 +02:00
Laurens Valk 48cd9db78a .github/workflows: Use node 24. 2026-05-29 21:57:20 +02:00
Laurens Valk 719e587517 v4.0.0b3 2026-05-29 21:46:03 +02:00
Laurens Valk eab61dc6ac .github/workflows: Fix permissions. 2026-05-29 21:45:25 +02:00
Laurens Valk c78eb16660 v4.0.0b2 2026-05-29 21:35:04 +02:00
Laurens Valk fa7a336fd1 .github/workflows: Use trusted publishing. 2026-05-29 21:34:14 +02:00
Laurens Valk d5f6a1ad00 v4.0.0b1 2026-05-29 21:15:09 +02:00
Laurens Valk 5bef252078 .github/workflows: Build on v4 tags. 2026-05-29 21:15:03 +02:00
Laurens Valk f12a6a26ac .github/workflows: Convert python to npm semver style. 2026-05-29 21:06:31 +02:00
Laurens Valk 4e78de9d2a .github/workflows: Update actions. 2026-05-29 20:57:04 +02:00
Laurens Valk 95b168355e npm: Simplify release pipeline.
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.
2026-05-29 20:38:37 +02:00
5 changed files with 168 additions and 10 deletions
+2 -1
View File
@@ -38,6 +38,7 @@ jobs:
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- run: python jedi/build.py "$NPM_VERSION"
- run: poetry run python build.py "$NPM_VERSION"
working-directory: jedi
- run: npm publish --tag "$NPM_TAG"
working-directory: jedi/npm-build
+1 -1
View File
@@ -27,7 +27,7 @@ poetry run pytest -vv
# @pybricks/jedi npm package
echo "==> Building @pybricks/jedi"
cd "$REPO_ROOT/jedi"
python3 build.py "$NPM_VERSION"
poetry run python build.py "$NPM_VERSION"
# @pybricks/ide-docs npm package
echo "==> Building @pybricks/ide-docs"
+9 -7
View File
@@ -1,13 +1,14 @@
#!/usr/bin/env python3
# Run with: poetry run python build.py <npm-version>
# Builds the @pybricks/jedi npm package into npm-build/.
import email.parser
import importlib.metadata
import json
import pathlib
import shutil
import subprocess
import sys
import tomllib
import zipfile
if len(sys.argv) != 2:
@@ -54,12 +55,12 @@ subprocess.check_call(["poetry", "build", "--format=wheel"], cwd=ROOT_DIR)
for whl in (ROOT_DIR / "dist").glob("pybricks_jedi-*.whl"):
shutil.copy(whl, BUILD_DIR)
# download transitive dependencies using versions pinned in poetry.lock
# download transitive dependencies using the versions already installed in the venv
# (installed by poetry from the lockfile, so versions are pinned correctly)
transitive_packages = ["jedi", "parso", "docstring-parser", "typing-extensions"]
with open(ROOT_DIR / "poetry.lock", "rb") as f:
lock = tomllib.load(f)
lock_versions = {pkg["name"]: pkg["version"] for pkg in lock["package"]}
transitive = [f"{pkg}=={lock_versions[pkg]}" for pkg in transitive_packages]
transitive = [
f"{pkg}=={importlib.metadata.version(pkg)}" for pkg in transitive_packages
]
subprocess.check_call(
[sys.executable, "-m", "pip", "download", "--only-binary=any"] + transitive,
cwd=BUILD_DIR,
@@ -143,4 +144,5 @@ with open(BUILD_DIR / "LICENSE", "w") as f:
# copy additional files
shutil.copy(ROOT_DIR / "README.md", BUILD_DIR / "README.md")
for file in ("README.md",):
shutil.copy(ROOT_DIR / file, BUILD_DIR / file)
+155
View File
@@ -0,0 +1,155 @@
#!/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()
REPO_ROOT_DIR = (ROOT_DIR / ".." / "..").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": "git+https://github.com/pybricks/pybricks-api.git",
"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 api wheel from local source so pip uses it instead of fetching from PyPI
subprocess.check_call(["poetry", "build", "--format=wheel"], cwd=REPO_ROOT_DIR)
# copy locally built pybricks wheel to build dir
for whl in (REPO_ROOT_DIR / "dist").glob("pybricks-*.whl"):
shutil.copy(whl, BUILD_DIR)
# 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 using the versions already installed in the venv
# (installed by poetry from the lockfile, so versions are pinned correctly)
transitive_packages = ["jedi", "parso", "docstring-parser", "typing-extensions"]
# use the jedi venv's Python to query metadata since packages are installed there
jedi_venv_python = JEDI_SRC_DIR / ".venv" / "bin" / "python"
pkg_versions = json.loads(
subprocess.check_output(
[
jedi_venv_python,
"-c",
f"import importlib.metadata, json; print(json.dumps({{p: importlib.metadata.version(p) for p in {transitive_packages!r}}}))",
]
)
)
transitive = [f"{pkg}=={pkg_versions[pkg]}" for pkg in transitive_packages]
subprocess.check_call(
[sys.executable, "-m", "pip", "download", "--only-binary=any"] + transitive,
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)
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pybricks"
version = "4.0.0b10"
version = "4.0.0b8"
description = "Documentation and user-API stubs for Pybricks MicroPython"
authors = ["The Pybricks Authors <team@pybricks.com>"]
maintainers = ["Laurens Valk <laurens@pybricks.com>", "David Lechner <david@pybricks.com>" ]