Skip to content

feat(packaging): ship awatcher in the non-Tauri Linux bundle - #1531

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/aw-qt-bundle-awatcher
Oct 18, 2026
Merged

ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/aw-qt-bundle-awatcher

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

What

Builds the pinned awatcher submodule for Linux non-Tauri builds and ships it in the PyInstaller bundle as a top-level aw-awatcher executable.

Why

Erik on #92 (2026-10-17): "Get on implementing Wayland support in aw-qt, should be as easy as bundling awatcher or something and adding the detection to aw-qt."

The two halves are currently split:

  • Detection: feat(wayland): auto-detect Wayland session and start aw-watcher-window-wayland aw-qt#142 detects Wayland and substitutes a module named aw-watcher-window-wayland, degrading with a warning when none is installed.
  • Bundling: the PyInstaller bundle (aw.spec) merges only aw-watcher-window, aw-watcher-input and aw-notify. The root Makefile adds awatcher to SUBMODULES only under TAURI_BUILD=true, staged into aw-tauri/src-tauri/modules/.

So a stock Linux aw-qt install on Wayland hits the #142 warning path and records nothing. This PR closes the bundling half.

How

  • Add awatcher to SUBMODULES for Linux non-Tauri builds (macOS/Windows unchanged; Tauri unchanged).
  • Copy the built binary to dist/activitywatch/aw-awatcher in the package target. aw-qt discovers bundled modules by scanning its own directory for executables named aw-*, so a top-level aw-awatcher is picked up with no aw-qt change.
  • Fail closed if the binary is missing, so a Linux bundle cannot silently ship without a Wayland-capable watcher.
  • Exclude awatcher from the generic PACKAGEABLES loop (it is copied explicitly) and from SUBMODULES when SKIP_SERVER_RUST=true (those CI legs skip the Rust toolchain).
  • Cache awatcher/target next to aw-server-rust/target in the two jobs that build it.

Verification

  • make -n package emits the copy + test -x guard; make -n package TAURI_BUILD=true and SKIP_SERVER_RUST=true emit neither.
  • make -pn build shows SUBMODULES ending in awatcher for the Linux non-Tauri case and unchanged for Tauri.
  • CI: the Linux Qt artifact and the manylinux AppImage/deb legs should now contain aw-awatcher. This PR's own run exercises the build + package path.

Follow-up (not in this PR)

ActivityWatch/aw-qt needs a small change so _apply_wayland_substitutions() accepts aw-awatcher as well as aw-watcher-window-wayland — without it the bundled binary is present but not selected. Tracked in the local task; will open separately against aw-qt (based on #142 if it has not merged).

Refs #92

The PyInstaller aw-qt bundle only merged aw-watcher-window,
aw-watcher-input and aw-notify, so a stock Linux install on Wayland had
no watcher that can report window activity. aw-qt#142 detects Wayland
and substitutes a module named aw-watcher-window-wayland, but the
bundle shipped neither that module nor awatcher, so it degraded to a
warning and recorded nothing.

Build the pinned awatcher submodule for Linux non-Tauri builds and copy
it into dist/activitywatch as a top-level `aw-awatcher` executable,
where aw-qt's bundled-module discovery finds it (executables named
aw-* next to aw-qt). The copy fails closed so a Linux bundle can never
silently ship without it.

awatcher is Rust, so it is excluded when SKIP_SERVER_RUST=true (those
CI legs skip the Rust toolchain). Tauri builds are unchanged: they
already stage the same binary into aw-tauri/src-tauri/modules/.

Cache awatcher/target alongside aw-server-rust/target in the two jobs
that build it, to keep the extra Rust build off the critical path.

Refs ActivityWatch#92
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Note on the red Tauri legs: they fail at the pin guard, not in this change.

ERROR: inconsistent aw-server build inputs:
  - Tauri locks aw-server-rust 04e8d666cb65, but the release submodule is 4b4a1d662349

master's top-level aw-server-rust moved to 4b4a1d6623 in the 2026-10-16 nightly submodule bump while aw-tauri's Cargo.lock stayed at 04e8d666. #1529 aligns both sides (aw-tauri 44b92d458 / aw-server-rust fe558c4f) and clears this on every open PR once it merges; #1530 is red for the same reason. This PR touches only the non-Tauri Linux build path.

@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[High impact] Adds a new Rust component to the Linux release build pipeline.

Fix the Linux Tauri build with both skip flags before merging.

Findings

  1. P1 Tauri requires a skipped binary ▶

Summary

This PR builds awatcher for non-Tauri Linux bundles and copies it into the bundle as aw-awatcher.

  • Linux bundles now include the aw-awatcher executable.

TimeToBuildBob explicitly deferred teaching aw-qt to select aw-awatcher on Wayland to a separate PR. That acknowledged gap is not reported here.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Linux build] --> B{Tauri build?}
  B -->|No| C[Build awatcher unless Rust is skipped]
  C --> D[Copy as aw-awatcher unless Rust is skipped]
  B -->|Yes| E{Rust skipped?}
  E -->|No| F[Build awatcher]
  E -->|Yes| G[Skip awatcher]
  F --> H[Stage watcher for Tauri]
  G --> I[Staging fails if binary is missing]
Loading

Reviews (1) · Last reviewed commit: "feat(packaging): ship awatcher in the no..." · Reviewed by Greptile

Comment thread Makefile
The Tauri staging block required awatcher/target/<targetdir>/awatcher
unconditionally, but awatcher is now filtered out of SUBMODULES whenever
SKIP_SERVER_RUST=true (it is Rust too). A Linux Tauri build with that flag
therefore stopped before building aw-tauri even though aw-sync already
degrades gracefully in the same situation.

Mirror the aw-sync branch: stage awatcher when the binary exists (e.g. from
the build cache), skip silently when SKIP_SERVER_RUST=true, and keep failing
closed otherwise. Verified with make -n for Tauri/non-Tauri and skip/no-skip,
plus a shell replay of the staging loop (missing binary + skip -> build
proceeds; missing binary without skip -> exit 1; cached binary + skip ->
staged).

Git-Session-Id: 3a4e72d5-a63f-50a9-8cd3-149e50e744e5
awatcher's cctk dependency builds smithay-client-toolkit, whose build
script runs pkg-config for xkbcommon. The Qt runner and the manylinux
container do not ship the headers, so the awatcher build fails with:

  pkg-config exited with status code 1
  > pkg-config --libs --cflags xkbcommon

Add libxkbcommon-dev (apt) and libxkbcommon-devel (dnf) alongside the
other X11/Qt development packages.

Git-Session-Id: fd0e9fee-f902-4513-956c-6d49e7501af8
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Two follow-ups pushed after the first run:

  • cb9d345 (sibling session) — the Tauri staging block still required the awatcher binary even when SKIP_SERVER_RUST=true filters it out of SUBMODULES. Mirrored the aw-sync branch: stage when present, skip silently under SKIP_SERVER_RUST, fail closed otherwise.
  • 59c7f2c — the Qt build failed on pkg-config --libs --cflags xkbcommon: awatcher's cctk dependency builds smithay-client-toolkit, which needs the xkbcommon headers. Added libxkbcommon-dev (apt, Qt job) and libxkbcommon-devel (dnf, manylinux container).

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

The Qt legs now pass with the awatcher build in place, and the artifact is verified:

01:21:03 Building awatcher
01:27:18 cp awatcher/target/debug/awatcher dist/activitywatch/aw-awatcher
01:27:26   adding: activitywatch/aw-awatcher (deflated 77%)
01:28:06   inflating: activitywatch/aw-awatcher
  • Build Qt artifacts (ubuntu-22.04) — pass (25m17s), binary present in the packaged zip
  • Build Qt artifacts (macos-latest) — pass (unchanged path; awatcher is Linux-only here)
  • Build Qt artifacts (windows-latest) — still running

The Greptile P1 thread is resolved: cb9d345 fixed the Tauri staging regression it described.

The only red checks are the three Tauri legs, and they fail at the pin guard, not in this diff:

ERROR: inconsistent aw-server build inputs:
  - Tauri locks aw-server-rust 04e8d666cb65, but the release submodule is 4b4a1d662349

Master's top-level submodule moved in the 2026-10-16 nightly bump without aw-tauri's Cargo.lock. #1529 realigns both gitlinks to fe558c4f, is fully green (including all three Tauri legs) and is ready to merge.

Merge order: #1529 first, then this branch needs a rebase onto master so its gitlink matches the Tauri lock; after that the Tauri legs here should go green without any further code change.

@TimeToBuildBob

TimeToBuildBob commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 AI code review

Safe to merge — no P0/P1 findings on latest review

Updated after inline dispositions on finding threads — this is the current state; the verdict below is frozen at review time and is kept as the historical record of that pass.

Finding disposition
Finding Severity State
.github/workflows/release.yml:668 P2 rejected
Makefile:311 P2 accepted-tradeoff

Adds awatcher to the Linux non-Tauri build's SUBMODULES, builds it, and copies the resulting binary into dist/activitywatch as aw-awatcher during packaging. Excludes awatcher from PACKAGEABLES and from SUBMODULES when SKIP_SERVER_RUST is true. Caches awatcher/target in two release-workflow jobs and adds libxkbcommon development packages.

Needs a look — P2 only

Confidence 4/5

2 findings · ⚠️ 2 P2

⚠️ P2 medium — Makefile:311

The package recipe copies the awatcher binary to dist/activitywatch/aw-awatcher (line 311), but the PR description itself states that aw-qt's Wayland substitution (aw-qt#142) currently only matches a module named aw-watcher-window-wayland and does not recognize aw-awatcher until a separate follow-up change lands. As a result, the shipped binary is never selected by aw-qt, so a Wayland user of the Linux non-Tauri bundle still records no window/AFK data. The PR's verification checks only that the binary is present, not that aw-qt consumes it; merging this alone delivers no user-visible fix until the aw-qt change lands.

How this was verified: Checked the PR description's 'Follow-up (not in this PR)' section, which explicitly says aw-qt needs a change to accept aw-awatcher; without that change the binary is not selected. Also checked the Makefile package target lines 301-314 where the copy is added.

⚠️ P2 medium — .github/workflows/release.yml:668

The CI cache key for the cargo build cache uses hashFiles('**/Cargo.lock'). Adding awatcher/target to the cache path means the cache now also stores awatcher's build artifacts, but the key does not include any awatcher-specific input. If awatcher's Cargo.lock changes without a change to aw-server-rust's Cargo.lock, the cache key stays the same and a stale awatcher binary from a previous build is restored. The build step then reuses that stale binary, potentially shipping an outdated awatcher in the bundle. The same issue applies to the manylinux job. The cache should include awatcher's Cargo.lock hash or a separate cache entry for awatcher.

How this was verified: Checked the cache step in build-qt (lines 660-670) and build-qt-manylinux-2-28 (lines 1022-1032). Both use the same key pattern with hashFiles('/Cargo.lock'), which covers all Cargo.lock files in the repo, so a change in awatcher/Cargo.lock would change the hash. However, if awatcher's Cargo.lock is not present or not tracked, the hash may not reflect it. The awatcher submodule is a separate repository with its own Cargo.lock, and hashFiles('/Cargo.lock') should include it if the submodule is checked out. But the cache key also depends on the toolchain cachekey and the matrix OS, so a stale cache could still be restored if the lock file hash matches. The concern is that the cache path now includes awatcher/target but the key does not specifically track awatcher's source changes beyond the lock file. If awatcher's source changes without a lock file change, the cache is stale. This is a real risk because the build step may not rebuild awatcher if the target directory is restored from cache and the source is newer than the binary.

Files changed (2) — the diff as I read it
  • .github/workflows/release.yml — Adds awatcher/target to the cargo cache path in the Qt and manylinux jobs, and adds libxkbcommon-dev/libxkbcommon-devel packages.
  • Makefile — Adds awatcher to SUBMODULES for Linux non-Tauri builds, filters it from PACKAGEABLES and from SKIP_SERVER_RUST builds, stages it in Tauri only when not skipping Rust, and copies it into dist/activitywatch as aw-awatcher.

Reviewed 59c7f2c1a79c · openrouter/deepseek/deepseek-v4-flash-0731 · llm engine · 1189s · about this reviewer

Maintainer commands

@TimeToBuildBob review (own line) — fresh review · @TimeToBuildBob fix — a worker acts on the findings. Once per comment; 👀 = received.

Comment thread Makefile
Comment thread .github/workflows/release.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants