Skip to content

feat(bundle): use tauri-native resources + dynamic bundle path detection#213

Draft
TimeToBuildBob wants to merge 2 commits intoActivityWatch:masterfrom
TimeToBuildBob:feat/tauri-native-bundling
Draft

feat(bundle): use tauri-native resources + dynamic bundle path detection#213
TimeToBuildBob wants to merge 2 commits intoActivityWatch:masterfrom
TimeToBuildBob:feat/tauri-native-bundling

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Contributor

@TimeToBuildBob TimeToBuildBob commented Apr 9, 2026

Summary

Draft PR to evaluate Tauri-native .app bundling as a replacement for build_app_tauri.sh in the activitywatch repo.

Raised by @ErikBjare in ErikBjare/bob#546:

"Why aren't we using Tauri's way of producing .app bundles and including the "modules" as sidecars or similar?"

Changes

tauri.conf.json — resources injected at build time (not statically configured)

Initially this PR added static bundle.resources to tauri.conf.json, but that broke standalone aw-tauri CI because the paths (../../dist/activitywatch/*) only exist when aw-tauri is used as a submodule inside the activitywatch repo.

Per 1dbc66b, those static entries were removed. Instead, the activitywatch build system injects resources at build time via --config:

cargo tauri build --config '{"bundle":{"resources":{
  "../../dist/activitywatch/aw-server-rust": "modules/aw-server-rust",
  "../../dist/activitywatch/aw-watcher-afk": "modules/aw-watcher-afk",
  "../../dist/activitywatch/aw-watcher-window": "modules/aw-watcher-window",
  "../../dist/activitywatch/aw-watcher-input": "modules/aw-watcher-input",
  "../../dist/activitywatch/aw-notify": "modules/aw-notify"
}}}'

This keeps aw-tauri self-contained (CI-friendly) while still letting activitywatch drive the bundling.

dirs.rs — dynamic resource path detection

Replace the hardcoded /Applications/ActivityWatch.app/Contents/... discovery paths with runtime detection via current_exe():

// Contents/MacOS/aw-tauri  →  go up two levels  →  Contents/Resources
if let Ok(exe_path) = std::env::current_exe() {
    if let Some(contents_dir) = exe_path.parent().and_then(|p| p.parent()) {
        let resources_dir = contents_dir.join("Resources");
        if resources_dir.exists() {
            discovery_paths.push(resources_dir.join("modules")); // tauri-native layout
            discovery_paths.push(resources_dir.clone());          // legacy compat
        }
    }
}

Why this matters: The old code only worked when installed at /Applications/ActivityWatch.app. Users who move the app to ~/Applications/ or run it from a downloaded DMG had no module discovery. This change is independently valuable regardless of the bundler decision.

What's NOT changed here

The Python.framework signing complexity (the #1254–#1256 series) is independent of the bundler. PyInstaller copies Python.framework as separate inodes → needs canonical signing regardless of whether build_app_tauri.sh or tauri build creates the .app.

What's still needed (activitywatch side)

The companion change in activitywatch would replace build_app_tauri.sh with cargo tauri build (with the --config flag above):

# Makefile: replace
scripts/package/build_app_tauri.sh
# with
cd aw-tauri && cargo tauri build --target universal-apple-darwin --config '{"bundle":{"resources":{...}}}'

CI would also need updating to use Tauri's signing/notarization flow (or keep the existing xcnotary approach, applied after tauri build).

Evaluation questions

  1. Does bundle.resources with directory paths work correctly with PyInstaller output (which includes nested Python.framework)?
  2. Does Tauri's bundler auto-generate Info.plist fields we currently set manually in build_app_tauri.sh (CFBundleURLTypes, LSUIElement, etc.)?
  3. Is cargo tauri build fast enough vs the current shell script approach?

Draft status — not ready to merge, but enough to evaluate the direction.

Draft exploration of Tauri-native module bundling instead of build_app_tauri.sh.

**tauri.conf.json**: Add `bundle.resources` to include pre-built Python watcher
modules in `Contents/Resources/modules/` during `tauri build`. Paths assume the
repo is used as a submodule inside activitywatch (`../../dist/activitywatch/`).

**dirs.rs** (macOS): Replace hardcoded `/Applications/ActivityWatch.app/...` paths
with dynamic detection via `current_exe()`. The new logic:
1. Gets the executable path (Contents/MacOS/aw-tauri)
2. Walks up to Contents/Resources
3. Adds Resources/modules/ (for tauri-native layout) and Resources/ (legacy compat)

This fixes discovery for any install location — not just /Applications.

### Trade-offs vs build_app_tauri.sh
- Gains: standard Tauri tooling, dynamic resource path, auto-generated Info.plist
- Unchanged: Python.framework signing complexity (PyInstaller issue, not bundler issue)
- Still needed: activitywatch Makefile/CI changes to run `cargo tauri build` instead
  of `build_app_tauri.sh` — see ActivityWatch/activitywatch for the CI side

This is a draft for evaluation — the CI integration is not yet done.
Comment thread src-tauri/tauri.conf.json Outdated
The hardcoded ../../dist/activitywatch/* paths broke standalone aw-tauri CI
since they only exist when aw-tauri is used as a submodule inside activitywatch.

Instead, the activitywatch build system should inject resources at build time:

  cargo tauri build --config '{"bundle":{"resources":{...}}}'

This keeps aw-tauri self-contained and CI-friendly. The dirs.rs dynamic
discovery change is independently valuable and unaffected.

Addresses ErikBjare's review: 'Maybe these should be conditionally added?'
@ErikBjare
Copy link
Copy Markdown
Member

Update the description to note 1dbc66b

@TimeToBuildBob
Copy link
Copy Markdown
Contributor Author

Done — updated the description to note commit 1dbc66b and explain the new approach (build-time injection vs static resources in ).

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