Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 The Pybricks Authors

name: Release

# One package per tag, because the packages are released independently: the
# tag pybricks-core-v1.0.0 publishes pybricks-core 1.0.0 and nothing else.
# Tag pybricks-core first, then pybricks-client and pybricks-service, then
# pybricks-cli, so that each one's requirements are on PyPI before it is.
on:
push:
tags: ["pybricks-*-v*"]

permissions:
contents: read

env:
UV_FROZEN: "1"

jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: { persist-credentials: false }
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
# A tag that disagrees with the package it names is refused before
# anything is built. A version on PyPI can never be uploaded again, so a
# mistake caught after the upload is a version number lost.
- name: Check the tag against the package
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
package="${TAG%-v*}"
version="${TAG##*-v}"
dir="packages/pb-${package#pybricks-}"
test -f "$dir/pyproject.toml" || { echo "::error::$TAG names no package"; exit 1; }
actual="$(uv version --package "$package" --short)"
test "$actual" = "$version" || { echo "::error::$TAG, but $package is $actual"; exit 1; }
grep -qF "## [$version]" "$dir/CHANGELOG.md" || { echo "::error::$dir/CHANGELOG.md has no [$version] section"; exit 1; }
echo "PACKAGE=$package" >> "$GITHUB_ENV"
- run: uv build --package "$PACKAGE" --out-dir dist
- run: uvx twine check --strict dist/*
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with: { name: dist, path: dist/ }

publish:
needs: build
# The PyPI trusted publisher for each package names this workflow and this
# environment, so a token is never stored anywhere.
environment:
name: pypi
permissions:
id-token: write # OIDC, which is how PyPI knows the upload is from here
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with: { name: dist, path: dist }
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- run: uv publish --trusted-publishing always dist/*
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The format is based on

- A uv workspace of four packages, `src` layout throughout, with `py.typed`.
- ruff, pyright strict, pytest and a strict docs build, gated from commit one.
- CI on Linux, macOS, Windows and Windows ARM64, across Python 3.11 to 3.13.
- CI on Linux, macOS, Windows and Windows ARM64, across Python 3.11 to 3.14.
- Documentation with the API and command references generated from the code.

### Notes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ async with pb_client.connect() as pb:
await pb.run_program(await pb_client.compile_script(Path("my_robot.py")))
```

> **Status:** early development. Nothing is stable yet.
> **Status:** beta. Commands and APIs may still change before 1.0.0.

[Documentation](docs/index.md) · [Contributing](CONTRIBUTING.md)
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Run Python programs on your [Pybricks](https://pybricks.com) hub from your
computer — from the command line, or from your own Python code.

!!! note "Status"
Early development. Nothing here is stable yet.
Beta. Commands and APIs may still change before 1.0.0.

## Install

Expand Down
1 change: 1 addition & 0 deletions packages/pb-cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ Notable changes to `pybricks-cli`, the `pb` command. The format is based on
- Data on stdout and everything else on stderr, so every command can be piped.
- One-line errors with a suggested fix; a traceback only when it is our bug.
- An arrow-key picker wherever the tool would otherwise have to guess.
- `--transport usb`, `ble` or `rfcomm`, the last for EV3 and NXT hubs.
2 changes: 1 addition & 1 deletion packages/pb-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ starts instantly, and so does the hundredth.
`pb repl` talks to the hub's interpreter a line at a time, and `pb run -c` runs
a line or two of Python without a file. `pb --help` lists the rest.

> **Status:** early development. Nothing is stable yet.
> **Status:** beta. Commands and APIs may still change before 1.0.0.
11 changes: 6 additions & 5 deletions packages/pb-cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pybricks-cli"
version = "0.1.0"
version = "1.0.0b1"
description = "The Pybricks command line interface"
readme = "README.md"
license = "MIT"
Expand All @@ -10,6 +10,7 @@ maintainers = [{ name = "David Lechner", email = "david@pybricks.com" }]
requires-python = ">=3.11"
keywords = ["pybricks", "lego", "micropython", "bluetooth", "robotics"]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Education",
Expand All @@ -19,9 +20,9 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"pybricks-core",
"pybricks-client",
"pybricks-service",
"pybricks-core>=1.0.0b1",
"pybricks-client>=1.0.0b1",
"pybricks-service>=1.0.0b1",
"anyio>=4.14",
"cyclopts>=4.22",
"rich>=13.7",
Expand All @@ -35,7 +36,7 @@ pb = "pb_cli.cli:main"
[project.urls]
homepage = "https://pybricks.com"
repository = "https://github.com/pybricks/pybricks-tools"
documentation = "https://pybricks.github.io/pybricks-tools/cli/"
documentation = "https://pybricks.com/pybricks-tools/cli/"
"Changelog" = "https://github.com/pybricks/pybricks-tools/blob/main/packages/pb-cli/CHANGELOG.md"
"Support" = "https://github.com/pybricks/support/discussions"
"Issues" = "https://github.com/pybricks/support/issues"
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-cli/src/pb_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

"""The Pybricks command line interface."""

__version__ = "0.1.0"
__version__ = "1.0.0b1"

__all__ = ["__version__"]
1 change: 1 addition & 0 deletions packages/pb-client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Notable changes to `pybricks-client`. The format is based on

- `connect()` gives an async handle to the service, starting one if needed.
- Ongoing events are async context managers yielding async iterators.
- `watching_adapters()` says which Bluetooth adapters a search cannot use.
- No function takes a `timeout`; callers scope cancellation themselves.
- Compiles a script and every module of its own that it imports.
- Passes a precompiled `.mpy` through, for modules built for native code.
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ everything it opens is closed for you when the `async with` block ends.
**Looking for the command line?** That is `pybricks-cli`, which installs `pb`
and uses this library underneath.

> **Status:** early development. Nothing is stable yet.
> **Status:** beta. Commands and APIs may still change before 1.0.0.
7 changes: 4 additions & 3 deletions packages/pb-client/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pybricks-client"
version = "0.1.0"
version = "1.0.0b1"
description = "Client library for the Pybricks service"
readme = "README.md"
license = "MIT"
Expand All @@ -10,6 +10,7 @@ maintainers = [{ name = "David Lechner", email = "david@pybricks.com" }]
requires-python = ">=3.11"
keywords = ["pybricks", "lego", "micropython", "bluetooth", "robotics"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Natural Language :: English",
Expand All @@ -18,7 +19,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"pybricks-core",
"pybricks-core>=1.0.0b1",
"anyio>=4.14",
"mpy-cross-v6>=1.1.1",
"typing-extensions>=4.15",
Expand All @@ -27,7 +28,7 @@ dependencies = [
[project.urls]
homepage = "https://pybricks.com"
repository = "https://github.com/pybricks/pybricks-tools"
documentation = "https://pybricks.github.io/pybricks-tools/reference/"
documentation = "https://pybricks.com/pybricks-tools/reference/"
"Changelog" = "https://github.com/pybricks/pybricks-tools/blob/main/packages/pb-client/CHANGELOG.md"
"Support" = "https://github.com/pybricks/support/discussions"
"Issues" = "https://github.com/pybricks/support/issues"
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-client/src/pb_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[move_on_after][anyio.move_on_after] instead.
"""

__version__ = "0.1.0"
__version__ = "1.0.0b1"

from pb_client.compile import (
SOURCE_NAME,
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ file locations all of them use.
`pybricks-client`, which is the one to reach for if you want to drive a hub from
your own Python.

> **Status:** early development. Nothing is stable yet.
> **Status:** beta. Commands and APIs may still change before 1.0.0.
5 changes: 3 additions & 2 deletions packages/pb-core/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pybricks-core"
version = "0.1.0"
version = "1.0.0b1"
description = "Shared protocol, IPC transport and logging for the Pybricks tools"
readme = "README.md"
license = "MIT"
Expand All @@ -10,6 +10,7 @@ maintainers = [{ name = "David Lechner", email = "david@pybricks.com" }]
requires-python = ">=3.11"
keywords = ["pybricks", "lego", "micropython", "bluetooth", "robotics"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Natural Language :: English",
Expand All @@ -26,7 +27,7 @@ dependencies = [
[project.urls]
homepage = "https://pybricks.com"
repository = "https://github.com/pybricks/pybricks-tools"
documentation = "https://pybricks.github.io/pybricks-tools/design/"
documentation = "https://pybricks.com/pybricks-tools/design/"
"Changelog" = "https://github.com/pybricks/pybricks-tools/blob/main/packages/pb-core/CHANGELOG.md"
"Support" = "https://github.com/pybricks/support/discussions"
"Issues" = "https://github.com/pybricks/support/issues"
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-core/src/pb_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
platform paths. It deliberately depends on neither ``pb_service`` nor ``pb_client``.
"""

__version__ = "0.1.0"
__version__ = "1.0.0b1"

__all__ = ["__version__"]
32 changes: 22 additions & 10 deletions packages/pb-core/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 The Pybricks Authors

import shutil
import socket
import sys
import tempfile
import uuid
from collections.abc import Iterator

import pytest

Expand Down Expand Up @@ -31,9 +34,7 @@ def _schemes() -> list[str]:


@pytest.fixture(params=_schemes())
def endpoint(
request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory
) -> Endpoint:
def endpoint(request: pytest.FixtureRequest) -> Iterator[Endpoint]:
"""A unique, unused endpoint for each transport under test.

Uniqueness matters: named pipe names are machine-global, so a fixed name
Expand All @@ -43,14 +44,25 @@ def endpoint(
unique = uuid.uuid4().hex[:12]

if scheme == "memory":
return Endpoint("memory", f"test-{unique}")
yield Endpoint("memory", f"test-{unique}")
return

if scheme == "tcp":
return Endpoint("tcp", f"127.0.0.1:{_free_tcp_port()}")
yield Endpoint("tcp", f"127.0.0.1:{_free_tcp_port()}")
return

if scheme == "pipe":
return Endpoint("pipe", f"pb-test-{unique}")

# AF_UNIX: keep the path short, since sun_path is only 104 bytes on macOS.
directory = tmp_path_factory.mktemp("s")
return Endpoint("unix", str(directory / f"{unique}.sock"))
yield Endpoint("pipe", f"pb-test-{unique}")
return

# AF_UNIX: under /tmp rather than pytest's tmp_path, since sun_path is only
# 104 bytes on macOS. The per-user temporary directory there is 57 bytes
# once resolved, and with pytest's own two levels a socket in it measures
# 104 as soon as pytest's run counter reaches three digits -- one too many,
# counting the terminator. tests/e2e/conftest.py's short_tmp_path does the
# same for the end-to-end tests.
directory = tempfile.mkdtemp(prefix="pb-", dir="/tmp")
try:
yield Endpoint("unix", f"{directory}/{unique}.sock")
finally:
shutil.rmtree(directory, ignore_errors=True)
4 changes: 4 additions & 0 deletions packages/pb-service/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ Notable changes to `pybricks-service`. The format is based on
- Single-instance lock, shared-secret authentication, and a startup log line.
- Exits when idle, but never while a hub is still connected.
- Bluetooth through bleak, behind a seam that USB and RFCOMM can reuse.
- EV3 and NXT hubs over Bluetooth Classic, found among the paired devices.
- Says when macOS is asking for Bluetooth permission, and waits for the answer.
- A serial port that will not open is reported, not taken for a missing hub.
- The Pybricks hub protocol, gated by profile version wherever it changed.
- Speaks every Pybricks Profile version up to v1.7.0.
- Scanning that reports each hub as it is heard, with type and signal strength.
- Remembers the hub chosen with `pb use`, in the user's state directory.
- A simulated hub, so the whole service can be exercised without a radio.
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ session and a program watching the same hub do not get in each other's way.
**You do not install this yourself.** It comes with `pybricks-cli`, and it
starts when something needs it and stops when nothing has for a while.

> **Status:** early development. Nothing is stable yet.
> **Status:** beta. Commands and APIs may still change before 1.0.0.
7 changes: 4 additions & 3 deletions packages/pb-service/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pybricks-service"
version = "0.1.0"
version = "1.0.0b1"
description = "The Pybricks service: manages hub connections"
readme = "README.md"
license = "MIT"
Expand All @@ -10,6 +10,7 @@ maintainers = [{ name = "David Lechner", email = "david@pybricks.com" }]
requires-python = ">=3.11"
keywords = ["pybricks", "lego", "micropython", "bluetooth", "robotics"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Natural Language :: English",
Expand All @@ -18,7 +19,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"pybricks-core",
"pybricks-core>=1.0.0b1",
"anyio>=4.14",
"bleak>=1.1.0",
"packaging>=22",
Expand All @@ -34,7 +35,7 @@ pb-service = "pb_service.cli:main"
[project.urls]
homepage = "https://pybricks.com"
repository = "https://github.com/pybricks/pybricks-tools"
documentation = "https://pybricks.github.io/pybricks-tools/design/"
documentation = "https://pybricks.com/pybricks-tools/design/"
"Changelog" = "https://github.com/pybricks/pybricks-tools/blob/main/packages/pb-service/CHANGELOG.md"
"Support" = "https://github.com/pybricks/support/discussions"
"Issues" = "https://github.com/pybricks/support/issues"
Expand Down
2 changes: 1 addition & 1 deletion packages/pb-service/src/pb_service/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
once.
"""

__version__ = "0.1.0"
__version__ = "1.0.0b1"

__all__ = ["__version__"]
2 changes: 1 addition & 1 deletion properdocs.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
site_name: Pybricks Tools
site_description: A service, library and CLI for talking to Pybricks hubs
site_url: https://pybricks.github.io/pybricks-tools/
site_url: https://pybricks.com/pybricks-tools/
repo_url: https://github.com/pybricks/pybricks-tools
edit_uri: edit/main/docs/

Expand Down
Loading
Loading