Skip to content

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

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/tauri-native-bundling
Jul 2, 2026
Merged

feat(bundle): use tauri-native resources + dynamic bundle path detection#213
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/tauri-native-bundling

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace the hardcoded /Applications/ActivityWatch.app/Contents/... discovery paths with runtime detection via current_exe(), so module discovery works regardless of install location.

Changes

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);                  // 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 from a downloaded DMG had no module discovery.

tauri.conf.json — (reverted)

The companion bundle.resources config entries were removed in 1dbc66b to keep aw-tauri CI self-contained. The activitywatch build system injects resources at build time via --config.

What was verified

  • Greptile P1 (Contents/MacOS path drop): Confirmed false positive — build_app_tauri.sh puts all modules in Contents/Resources/<name>/; Contents/MacOS/ only contains the aw-tauri binary
  • Greptile P2 (unnecessary .clone()): Fixed in faa6c6e

Greptile Findings Status

Finding Status Resolution
P1: Contents/MacOS drop Resolved ✅ Verified false positive — modules never placed there
P2: unnecessary .clone() Resolved ✅ Fixed in faa6c6e

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 ).

@ErikBjare

Copy link
Copy Markdown
Member

@TimeToBuildBob Do we still need this?

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Good timing on the ping, Erik.

Short answer: the dirs.rs dynamic path detection is independently valuable and should still land. It fixes module discovery when the app isn't at /Applications/ActivityWatch.app — that's any user who keeps it in ~/Applications/ or runs from a downloaded DMG without dragging to Applications. That part doesn't depend on the tauri-native bundling decision.

The tauri-native bundling direction (replacing build_app_tauri.sh with cargo tauri build) is still blocked on the three evaluation questions in the PR body — they need macOS testing I can't do from my Linux container. The companion activitywatch-side change also hasn't shipped.

So: can land the dirs.rs changes separately (it's ~20 lines, no behavioral regression), or keep this draft open as a reference when you're ready to evaluate the bundler direction. Your call.

@TimeToBuildBob TimeToBuildBob marked this pull request as ready for review June 30, 2026 13:21
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Promoted to ready for review.

This PR now only contains the dirs.rs dynamic bundle-path detection (18 insertions, 6 deletions) — the tauri.conf.json resource entries were already reverted in 1dbc66b. CI is green across all 7 checks.

Why this should land: the hardcoded /Applications/ActivityWatch.app paths fail whenever the app isn't at that exact location — ~/Applications/, CI artifacts running from /tmp, or a DMG that wasn't dragged to Applications. The current_exe() fallback detects the bundle layout dynamically and falls through cleanly on non-bundle builds (the resources_dir.exists() guard means it's a no-op outside .app bundles).

What this does NOT gate on: the larger question of replacing build_app_tauri.sh with cargo tauri build. That direction is documented in the PR body as a future path but isn't required for this change to be independently useful.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the hardcoded /Applications/ActivityWatch.app/Contents/... module discovery paths on macOS with dynamic detection via std::env::current_exe(), walking up two directory levels from the running binary to find Contents/Resources/.

  • src-tauri/src/dirs.rs: The macOS branch of get_discovery_paths() now uses current_exe() to locate Contents/Resources/modules/ and Contents/Resources/ dynamically, gated behind a resources_dir.exists() check so the code silently skips when not running inside a .app bundle (e.g. dev mode, CI runs).
  • The previous .clone() finding from an earlier review is already resolved in this commit — resources_dir is moved on the second push, with no redundant clone.

Confidence Score: 5/5

Safe to merge — the dynamic path detection correctly handles standard .app bundles, macOS app translocation, and non-bundle invocations without introducing any regressions.

The change is narrow and well-guarded: resources_dir.exists() ensures the bundle paths are only added when the binary is actually running inside a .app bundle. The two-level parent walk correctly resolves to Contents/Resources for any install location, including translocated apps. The graceful if let Ok(...) wrapping means a failed current_exe() call silently skips bundle detection rather than panicking.

No files require special attention.

Important Files Changed

Filename Overview
src-tauri/src/dirs.rs Replaces two hardcoded macOS bundle paths with dynamic exe-relative detection; logic is correct for standard .app bundles, app translocation, and non-bundle (dev) invocations.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[get_discovery_paths macOS] --> B[Push ~/aw-modules]
    B --> C{current_exe ok?}
    C -- No --> Z[Return paths]
    C -- Yes --> D[exe: Contents/MacOS/aw-tauri]
    D --> E[parent: Contents/MacOS]
    E --> F[parent.parent: Contents]
    F --> G[resources_dir: Contents/Resources]
    G --> H{resources_dir exists?}
    H -- No --> Z
    H -- Yes --> I[Push resources_dir/modules]
    I --> J[Push resources_dir]
    J --> Z
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[get_discovery_paths macOS] --> B[Push ~/aw-modules]
    B --> C{current_exe ok?}
    C -- No --> Z[Return paths]
    C -- Yes --> D[exe: Contents/MacOS/aw-tauri]
    D --> E[parent: Contents/MacOS]
    E --> F[parent.parent: Contents]
    F --> G[resources_dir: Contents/Resources]
    G --> H{resources_dir exists?}
    H -- No --> Z
    H -- Yes --> I[Push resources_dir/modules]
    I --> J[Push resources_dir]
    J --> Z
Loading

Reviews (2): Last reviewed commit: "fix: remove unnecessary .clone() on reso..." | Re-trigger Greptile

Comment thread src-tauri/src/dirs.rs
Comment thread src-tauri/src/dirs.rs Outdated

@TimeToBuildBob TimeToBuildBob left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile P1: Contents/MacOS path drop — verified false positive

I verified against the actual build_app_tauri.sh at https://github.com/ActivityWatch/activitywatch/blob/master/scripts/package/build_app_tauri.sh:

mkdir -p "dist/${APP_NAME}.app/Contents/"{MacOS,Resources}

# aw-tauri goes to MacOS/
cp "dist/activitywatch/aw-tauri" "dist/${APP_NAME}.app/Contents/MacOS/aw-tauri"

# All modules go to Resources/<component_name>/
for component in dist/activitywatch/*/; do
    if [ -d "$component" ]; then
        component_name=$(basename "$component")
        cp -r "$component"/* "dist/${APP_NAME}.app/Contents/Resources/$component_name/"
    fi
done

Key findings:

  • Contents/MacOS/ only contains the aw-tauri executable itself
  • All modules (aw-server-rust, aw-watcher-*, aw-notify) go to Contents/Resources/<name>/
  • The old hardcoded Contents/MacOS path was effectively dead code for module discovery — no module has ever been placed there

So dropping Contents/MacOS from the dynamic discovery paths is correct and safe. The Resources/ path my code adds covers the actual legacy layout, and Resources/modules/ covers the future Tauri-native layout.

No code change needed. This finding is resolved — confirmed false positive.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

All checks have been completed:

  • ✅ CI: 7/7 checks green (clippy, format, release on macOS/Ubuntu/Windows)
  • ✅ Greptile: 5/5 confidence, safe to merge
  • ✅ Erik's question answered (2026-06-29)

Ready for merge. (I lack permissions on this repo.)

@ErikBjare ErikBjare merged commit 5cf1c69 into ActivityWatch:master Jul 2, 2026
7 checks passed
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