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
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ updates:
update-types: ["minor", "patch"]
ignore:
# The Rust side of the Tauri pairs above; bumped with them, by hand.
# Build the Linux installer before taking a plugin bump: for example,
# tauri-plugin-deep-link 2.6.2 does not compile against rust-ini 0.21.3.
- dependency-name: "tauri"
- dependency-name: "tauri-*"
- package-ecosystem: "github-actions"
Expand Down
71 changes: 37 additions & 34 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
outputs:
run_sidecar: ${{ steps.scope.outputs.run_sidecar }}
run_native: ${{ steps.scope.outputs.run_native }}
native_matrix: ${{ steps.scope.outputs.native_matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -62,10 +63,36 @@ jobs:
fi
fi
fi
if [ "${EVENT_NAME}" = "pull_request" ]; then
# The checkout is the merge commit; its first parent is the base.
git diff --relative --name-only HEAD^1 HEAD > "${changed_paths}"
fi
python scripts/classify_build_changes.py \
--event "${EVENT_NAME}" \
--paths-file "${changed_paths}" \
--github-output "${GITHUB_OUTPUT}"
# A pull request builds the Linux installer only; main and dispatches
# build every platform.
python - "${EVENT_NAME}" >> "${GITHUB_OUTPUT}" <<'PY'
import json, sys
native = [
{"label": "macOS arm64", "os": "macos-15", "platform": "macos",
"architecture": "arm64", "target": "aarch64-apple-darwin",
"bundles": "dmg", "signing": "adhoc"},
{"label": "macOS Intel", "os": "macos-15-intel", "platform": "macos",
"architecture": "x86_64", "target": "x86_64-apple-darwin",
"bundles": "dmg", "signing": "adhoc"},
{"label": "Windows x86_64", "os": "windows-2022", "platform": "windows",
"architecture": "x86_64", "target": "x86_64-pc-windows-msvc",
"bundles": "msi,nsis", "signing": "unsigned"},
{"label": "Linux x86_64", "os": "ubuntu-22.04", "platform": "linux",
"architecture": "x86_64", "target": "x86_64-unknown-linux-gnu",
"bundles": "deb,appimage", "signing": "unsigned"},
]
if sys.argv[1] == "pull_request":
native = [entry for entry in native if entry["platform"] == "linux"]
print("native_matrix=" + json.dumps(native))
PY

frontend:
name: Frontend behavior and build
Expand Down Expand Up @@ -133,10 +160,11 @@ jobs:
strategy:
fail-fast: false
matrix:
# PRs prove the frozen-engine contract once on Linux. Main repeats the
# complete matrix for dependency, toolchain, packaging, and native
# changes. Immutable native release tags rebuild every supported OS.
os: ${{ fromJSON(github.event_name == 'pull_request' && '["ubuntu-22.04"]' || '["ubuntu-22.04", "macos-14", "windows-latest"]') }}
# PRs prove the frozen-engine contract on Linux and macOS (macOS freezes
# the PyObjC wheels, whose license notices the build must find). Main
# repeats the complete matrix for dependency, toolchain, packaging, and
# native changes. Immutable native release tags rebuild every supported OS.
os: ${{ fromJSON(github.event_name == 'pull_request' && '["ubuntu-22.04", "macos-14"]' || '["ubuntu-22.04", "macos-14", "windows-latest"]') }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -182,43 +210,18 @@ jobs:
name: Native installer (${{ matrix.label }})
needs: artifact-scope
# Main runs this evidence for dependency, toolchain, packaging, and native
# changes. Application-only merges already pass the PR checks and the
# changes, and a pull request that changes the native shell builds the
# Linux installer. Application-only merges already pass the PR checks and the
# exact-main frontend, distribution, and cross-platform test jobs. Every
# immutable desktop-v* release tag rebuilds and tests all installers in the
# separate Native Installer Release workflow.
if: needs.artifact-scope.outputs.run_native == 'true'
strategy:
fail-fast: false
matrix:
include:
- label: macOS arm64
os: macos-15
platform: macos
architecture: arm64
target: aarch64-apple-darwin
bundles: dmg
signing: adhoc
- label: macOS Intel
os: macos-15-intel
platform: macos
architecture: x86_64
target: x86_64-apple-darwin
bundles: dmg
signing: adhoc
- label: Windows x86_64
os: windows-2022
platform: windows
architecture: x86_64
target: x86_64-pc-windows-msvc
bundles: msi,nsis
signing: unsigned
- label: Linux x86_64
os: ubuntu-22.04
platform: linux
architecture: x86_64
target: x86_64-unknown-linux-gnu
bundles: deb,appimage
signing: unsigned
# Built by the artifact-scope job: every platform on main and dispatch,
# Linux only on a pull request that changes the native shell.
include: ${{ fromJSON(needs.artifact-scope.outputs.native_matrix) }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/desktop-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
#
# Called from packages.yml when a change touches packages/desktop, so its jobs
# feed the required packages-gate. Pull requests run the Linux lane on Python
# 3.11 and 3.12. A push to main repeats the same contract on macOS and Windows,
# so a release always rests on the complete cross-platform matrix. Ported from
# openadapt-desktop's test.yml.
# 3.11 and 3.12 and one Windows lane, because Windows checks files out with CRLF
# and has its own paths. A push to main repeats the same contract on macOS and
# Windows, so a release always rests on the complete cross-platform matrix.
# Ported from openadapt-desktop's test.yml.
name: Desktop test

on:
Expand All @@ -23,8 +24,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ${{ fromJSON(github.event_name == 'pull_request' && '["ubuntu-latest"]' || '["ubuntu-latest", "macos-latest", "windows-latest"]') }}
os: ${{ fromJSON(github.event_name == 'pull_request' && '["ubuntu-latest", "windows-latest"]' || '["ubuntu-latest", "macos-latest", "windows-latest"]') }}
python-version: ["3.11", "3.12"]
exclude: ${{ fromJSON(github.event_name == 'pull_request' && '[{"os":"windows-latest","python-version":"3.11"}]' || '[]') }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
Expand Down
12 changes: 12 additions & 0 deletions packages/desktop/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# CHANGELOG


## v0.21.1 (2026-10-19)

0.21.0 was never published, because main's macOS, Windows and Linux builds of it failed. This release ships the 0.21.0 changes below together with these fixes.

### Bug Fixes

- **desktop**: go back to tauri-plugin-deep-link 2.6.1 so the Linux shell compiles
- **desktop**: supply the reviewed PyObjC license for the 12.2.2 wheels
- **desktop**: prepare a release on a CRLF checkout



## v0.21.0 (2026-10-18)

### Features
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/openadapt_desktop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
+-- backends/ Storage backend plugins
"""

__version__ = "0.21.0"
__version__ = "0.21.1"
4 changes: 2 additions & 2 deletions packages/desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openadapt-desktop",
"version": "0.21.0",
"version": "0.21.1",
"description": "Installed companion for OpenAdapt authoring, teaching, and local pairing",
"private": true,
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "openadapt-desktop"
version = "0.21.0"
version = "0.21.1"
description = "The OpenAdapt desktop app and local engine: record a data-entry task, run it on a list, and check each saved record"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
19 changes: 17 additions & 2 deletions packages/desktop/scripts/classify_build_changes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Classify whether a Desktop change needs full native artifact builds.

Pull requests keep the representative Linux frozen-sidecar build. Release
Pull requests keep the representative frozen-sidecar builds, and a pull
request that changes the Tauri shell or its JavaScript locks also builds the
Linux installer, so a native dependency bump compiles before it merges. Release
candidates and explicit dispatches keep the complete platform matrix. A push
to protected ``main`` repeats the expensive matrix only when a dependency,
toolchain, packaging, or native-runtime input changed. The immutable native
Expand Down Expand Up @@ -51,6 +53,12 @@
"scripts/verify_build_artifact.py",
}

# A pull request that changes one of these also builds the Linux installer.
# The Rust shell and its plugins compile per platform, and the npm and cargo
# sides of each Tauri plugin must move together.
NATIVE_PR_FILES = {"package-lock.json", "package.json"}
NATIVE_PR_PREFIXES = ("src-tauri/",)

# These paths get the normal PR checks and the exact-main frontend, Python
# distribution, and cross-platform test jobs. They do not change the native
# packaging contract. Everything not named here fails toward the full matrix.
Expand Down Expand Up @@ -105,12 +113,19 @@ def classify_build_scope(event: str, changed_paths: Iterable[str]) -> BuildScope

if event == "workflow_dispatch":
return BuildScope(True, True, "explicit release-candidate build")
paths = tuple(path for raw in changed_paths if (path := _normalize(raw)))
if event == "pull_request":
native = tuple(
path
for path in paths
if path in NATIVE_PR_FILES or path.startswith(NATIVE_PR_PREFIXES)
)
if native:
return BuildScope(True, True, f"pull request changes the native shell: {native[0]}")
return BuildScope(True, False, "representative pull-request sidecar")
if event != "push":
return BuildScope(True, True, f"unknown event {event!r}; fail toward full evidence")

paths = tuple(path for raw in changed_paths if (path := _normalize(raw)))
if not paths:
return BuildScope(True, True, "no changed paths; fail toward full evidence")

Expand Down
42 changes: 42 additions & 0 deletions packages/desktop/scripts/frozen_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,48 @@ def capture_floor_is_satisfied(candidate: str, floor: str) -> bool:
"e5dcffe836b6ec8a58e492419b550e65fb8cbdc308503979e5dacb33ac7ea3b7"
),
},
("pyobjc-core", "12.2.2"): {
"relative_path": "pyobjc/LICENSE.txt",
"source_url": (
"https://github.com/ronaldoussoren/pyobjc/blob/"
"38cdf73fc049ef2d176973eff4ac8d5c76b0a34b/"
"pyobjc-core/License.txt"
),
"source_commit": "38cdf73fc049ef2d176973eff4ac8d5c76b0a34b",
"sha256": "0ca04b07928d4872b9d9bb22187ca0426dd8bfab08f26eada0999a71dc81aaff",
"license_expression": "MIT",
"license_evidence_sha256": (
"e5dcffe836b6ec8a58e492419b550e65fb8cbdc308503979e5dacb33ac7ea3b7"
),
},
("pyobjc-framework-coremedia", "12.2.2"): {
"relative_path": "pyobjc/LICENSE.txt",
"source_url": (
"https://github.com/ronaldoussoren/pyobjc/blob/"
"38cdf73fc049ef2d176973eff4ac8d5c76b0a34b/"
"pyobjc-framework-CoreMedia/License.txt"
),
"source_commit": "38cdf73fc049ef2d176973eff4ac8d5c76b0a34b",
"sha256": "0ca04b07928d4872b9d9bb22187ca0426dd8bfab08f26eada0999a71dc81aaff",
"license_expression": "MIT",
"license_evidence_sha256": (
"e5dcffe836b6ec8a58e492419b550e65fb8cbdc308503979e5dacb33ac7ea3b7"
),
},
("pyobjc-framework-screencapturekit", "12.2.2"): {
"relative_path": "pyobjc/LICENSE.txt",
"source_url": (
"https://github.com/ronaldoussoren/pyobjc/blob/"
"38cdf73fc049ef2d176973eff4ac8d5c76b0a34b/"
"pyobjc-framework-ScreenCaptureKit/License.txt"
),
"source_commit": "38cdf73fc049ef2d176973eff4ac8d5c76b0a34b",
"sha256": "0ca04b07928d4872b9d9bb22187ca0426dd8bfab08f26eada0999a71dc81aaff",
"license_expression": "MIT",
"license_evidence_sha256": (
"e5dcffe836b6ec8a58e492419b550e65fb8cbdc308503979e5dacb33ac7ea3b7"
),
},
("rapidocr-onnxruntime", "1.4.4"): {
"relative_path": "rapidocr/LICENSE",
"source_url": (
Expand Down
11 changes: 8 additions & 3 deletions packages/desktop/scripts/native_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@ def set_native_version(version: str, root: Path = ROOT) -> dict[str, str]:
result with the Git blobs of the candidate tag. Text-mode I/O would break
that: on Windows it rewrites every ``\\n`` as ``\\r\\n``, and an omitted
encoding decodes with the locale default. All reads and writes below are
therefore explicit UTF-8 bytes with LF endings.
therefore explicit UTF-8 bytes with LF endings. The Cargo patterns also
accept CRLF, because Git on Windows checks files out with CRLF by default,
and they keep whatever ending each line already has.
"""

if not VERSION_PATTERN.fullmatch(version):
Expand Down Expand Up @@ -301,15 +303,18 @@ def set_lock_versions(lock: dict) -> None:

cargo_toml = root / "src-tauri" / "Cargo.toml"
text, replaced = re.subn(
r'(?m)^version = "[^"]+"$', f'version = "{version}"', read_source(cargo_toml), count=1
r'(?m)^version = "[^"]+"(?=\r?$)',
f'version = "{version}"',
read_source(cargo_toml),
count=1,
)
if replaced != 1:
raise ValueError(f"could not rewrite package version in {cargo_toml}")
write_source(cargo_toml, text)

cargo_lock = root / "src-tauri" / "Cargo.lock"
text, replaced = re.subn(
r'(name = "openadapt-desktop"\nversion = ")[^"]+(")',
r'(name = "openadapt-desktop"\r?\nversion = ")[^"]+(")',
rf"\g<1>{version}\g<2>",
read_source(cargo_lock),
count=1,
Expand Down
10 changes: 5 additions & 5 deletions packages/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "openadapt-desktop"
version = "0.21.0"
version = "0.21.1"
description = "Installed companion for OpenAdapt authoring, teaching, and local pairing"
authors = ["OpenAdapt AI"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/nicoverbruggen/tauri-v2-schema/main/schema.json",
"productName": "OpenAdapt Desktop",
"version": "0.21.0",
"version": "0.21.1",
"identifier": "ai.openadapt.desktop",
"build": {
"frontendDist": "../dist",
Expand Down
9 changes: 9 additions & 0 deletions packages/desktop/tests/test_build_change_classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ def test_pull_request_keeps_one_representative_sidecar() -> None:
)


@pytest.mark.parametrize(
"path", ["src-tauri/Cargo.lock", "src-tauri/src/main.rs", "package.json", "package-lock.json"]
)
def test_pull_request_that_changes_the_native_shell_builds_an_installer(path: str) -> None:
scope = classify_build_scope("pull_request", ["src/App.tsx", path])
assert scope.run_sidecar and scope.run_native
assert path in scope.reason


def test_explicit_release_candidate_keeps_the_complete_matrix() -> None:
assert classify_build_scope("workflow_dispatch", []) == BuildScope(
run_sidecar=True,
Expand Down
Loading
Loading