From b915c2a99686db14b2346c3a72bf261e2932073b Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 16:14:16 +0000 Subject: [PATCH 1/7] ci(build): modernize runners, node, cache keys for aw-qt CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ubuntu 22.04 → 24.04, add macos-latest alongside macOS-13 - Node 20 → 22 - Fix macOS Python setup condition (matrix.os instead of runner.os) - Use matrix.os in cache keys for unique per-variant caching - Enable cargo cache on macOS (was disabled) - Fix qt5-default → qt5-qmake + qtbase5-dev (ubuntu-24.04) - Fix artifact upload name collision (matrix.os) - Fix typo: 'Wether' → 'Whether' --- .github/workflows/build.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c6ef167c..f3e59585d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} env: - # Wether to build and include extras (like aw-notify and aw-watcher-input) + # Whether to build and include extras (like aw-notify and aw-watcher-input) AW_EXTRAS: true # sets the macOS version target, see: https://users.rust-lang.org/t/compile-rust-binary-for-older-versions-of-mac-osx/38695 MACOSX_DEPLOYMENT_TARGET: 10.9 @@ -26,9 +26,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, windows-latest, macOS-13] + os: [ubuntu-24.04, windows-latest, macOS-13, macos-latest] python_version: [3.9] - node_version: [20] + node_version: [22] skip_rust: [false] skip_webui: [false] experimental: [false] @@ -53,14 +53,14 @@ jobs: echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV - name: Set up Python - if: runner.os != 'macOS' + if: matrix.os != 'macOS-13' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) - name: Set up Python for macOS - if: runner.os == 'macOS' + if: matrix.os == 'macOS-13' run: | curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" @@ -103,21 +103,21 @@ jobs: cache-name: node with: path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + key: ${{ matrix.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-${{ env.cache-name }}- + ${{ matrix.os }}-${{ env.cache-name }}- - name: Cache cargo build uses: actions/cache@v4 - if: ${{ !matrix.skip_rust && (runner.os != 'macOS') }} # cache doesn't seem to behave nicely on macOS, see: https://github.com/ActivityWatch/aw-server-rust/issues/180 + # if: ${{ !matrix.skip_rust && (runner.os != 'macOS') }} # cache doesn't seem to behave nicely on macOS, see: https://github.com/ActivityWatch/aw-server-rust/issues/180 env: cache-name: cargo-build-target with: path: aw-server-rust/target # key needs to contain rustc_hash due to https://github.com/ActivityWatch/aw-server-rust/issues/180 - key: ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} + key: ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | - ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}- + ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}- - name: Install APT dependencies if: runner.os == 'Linux' @@ -126,7 +126,8 @@ jobs: # Unsure which of these are actually necessary... sudo apt-get install -y \ appstream \ - qtbase5-dev qt5-qmake \ + qt5-qmake \ + qtbase5-dev \ qtwayland5 \ libqt5x11extras5 \ libfontconfig1 \ @@ -223,7 +224,7 @@ jobs: - name: Upload packages uses: actions/upload-artifact@v4 with: - name: builds-${{ runner.os }}-py${{ matrix.python_version }} + name: builds-${{ matrix.os }}-py${{ matrix.python_version }} path: dist/activitywatch-*.* release-notes: From f98f4c93e760ea9b7d34bdf7ae619878f096c535 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 16:14:26 +0000 Subject: [PATCH 2/7] feat: add aw-tauri and awatcher submodules aw-tauri is the Tauri-based replacement for aw-qt (system tray). awatcher is used on Linux for Wayland-compatible window watching. --- .gitmodules | 6 ++++++ aw-tauri | 1 + awatcher | 1 + 3 files changed, 8 insertions(+) create mode 160000 aw-tauri create mode 160000 awatcher diff --git a/.gitmodules b/.gitmodules index 8e7d1bdaf..e5e7a8947 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,9 @@ [submodule "aw-watcher-input"] path = aw-watcher-input url = https://github.com/ActivityWatch/aw-watcher-input.git +[submodule "aw-tauri"] + path = aw-tauri + url = https://github.com/activitywatch/aw-tauri +[submodule "awatcher"] + path = awatcher + url = https://github.com/2e3s/awatcher diff --git a/aw-tauri b/aw-tauri new file mode 160000 index 000000000..89e5ddf73 --- /dev/null +++ b/aw-tauri @@ -0,0 +1 @@ +Subproject commit 89e5ddf732c39caba1a333cf5af001de0cc3a4bf diff --git a/awatcher b/awatcher new file mode 160000 index 000000000..678d7fd08 --- /dev/null +++ b/awatcher @@ -0,0 +1 @@ +Subproject commit 678d7fd0867462f7925c1e4771994bba5f3da0c7 From 88700449b52d0942cac6acb0c0879cd6cccae983 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 16:17:54 +0000 Subject: [PATCH 3/7] feat: add aw-tauri CI workflow, Makefile support, and packaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a separate build-tauri.yml CI workflow that builds aw-tauri alongside the existing aw-qt builds. The two build pipelines coexist independently. Makefile changes: - TAURI_BUILD=true flag switches between aw-qt and aw-tauri submodules - aw-server-rust builds only aw-sync in Tauri mode (not full server) - ICON points to aw-tauri icons when TAURI_BUILD is set - Conditional packaging: aw-tauri app bundle on macOS, Inno Setup on Windows Packaging: - build_app_tauri.sh: macOS .app bundle creator for Tauri - aw-tauri.iss: Windows Inno Setup with different AppId/AppName to avoid colliding with existing aw-qt installations (addresses review comment) - move-to-aw-modules.sh: Linux module discovery helper (rsync -a, proper line continuation) - README.txt: Linux end-user instructions (typos fixed) - package-all.sh: Tauri-aware zip/installer selection Build-tauri.yml features: - Multi-OS: ubuntu-24.04, ubuntu-24.04-arm, windows-latest, macOS-13, macos-latest - Retry wrapper (nick-fields/retry) for flaky builds - Separate artifact names (builds-tauri-*) to not collide with aw-qt - Tauri-specific APT deps (libgtk-3-dev, libwebkit2gtk-4.1-dev, etc.) Also updates contributor CSV files (brayo-pip → 0xbrayo). Based on work by @0xbrayo in PR #1163. --- .github/workflows/build-tauri.yml | 260 +++++++++++++++++++++ Makefile | 61 +++-- scripts/changelog_contributors.csv | 2 +- scripts/changelog_contributors_twitter.csv | 2 +- scripts/package/README.txt | 10 + scripts/package/aw-tauri.iss | 57 +++++ scripts/package/build_app_tauri.sh | 105 +++++++++ scripts/package/move-to-aw-modules.sh | 27 +++ scripts/package/package-all.sh | 11 +- 9 files changed, 517 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/build-tauri.yml create mode 100644 scripts/package/README.txt create mode 100644 scripts/package/aw-tauri.iss create mode 100755 scripts/package/build_app_tauri.sh create mode 100755 scripts/package/move-to-aw-modules.sh diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml new file mode 100644 index 000000000..ab4f7d702 --- /dev/null +++ b/.github/workflows/build-tauri.yml @@ -0,0 +1,260 @@ +name: Build Tauri + +on: + push: + branches: [master] + tags: + - v* + pull_request: + branches: [master] + +jobs: + build: + name: ${{ matrix.os }}, py-${{ matrix.python_version }}, node-${{ matrix.node_version }} + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} + env: + # Whether to build and include extras (like aw-notify and aw-watcher-input) + AW_EXTRAS: true + TAURI_BUILD: true + # sets the macOS version target, see: https://users.rust-lang.org/t/compile-rust-binary-for-older-versions-of-mac-osx/38695 + MACOSX_DEPLOYMENT_TARGET: 10.9 + defaults: + run: + shell: bash + strategy: + fail-fast: false + max-parallel: 5 + matrix: + os: + [ + ubuntu-24.04, + ubuntu-24.04-arm, + windows-latest, + macOS-13, + macos-latest, + ] + python_version: [3.9] + node_version: [22] + skip_rust: [false] + skip_webui: [false] + experimental: [false] + + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + fetch-depth: 0 + + - name: Set environment variables + run: | + echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV + echo "TAURI_BUILD=true" >> $GITHUB_ENV + + - name: Set up Python + if: matrix.os != 'macOS-13' + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + + # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) + - name: Set up Python for macOS + if: matrix.os == 'macOS-13' + run: | + curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" + sudo installer -pkg python.pkg -target / + echo "/Library/Frameworks/Python.framework/Versions/${{ matrix.python_version }}/bin" >> $GITHUB_PATH + "/Applications/Python ${{ matrix.python_version }}/Install Certificates.command" + env: + PYTHON_VERSION: ${{ matrix.python_version }}.13 + + - name: Set up Node + if: ${{ !matrix.skip_webui }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node_version }} + + - name: Set up Rust + if: ${{ !matrix.skip_rust }} + uses: dtolnay/rust-toolchain@master + id: toolchain + with: + toolchain: stable + + - name: Get npm cache dir + id: npm-cache-dir + run: | + echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v4 + name: Cache npm + if: ${{ !matrix.skip_webui }} + env: + cache-name: node + with: + path: ${{ steps.npm-cache-dir.outputs.dir }} + key: ${{ matrix.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ matrix.os }}-${{ env.cache-name }}- + + - name: Cache cargo build + uses: actions/cache@v4 + env: + cache-name: cargo-build-target + with: + path: aw-server-rust/target + key: ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}- + + - name: Install APT dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + libgtk-3-dev \ + libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + libjavascriptcoregtk-4.1-dev \ + libsoup-3.0-dev \ + xdg-utils + + - name: Install dependencies + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + choco install innosetup + fi + pip3 install poetry==1.3.2 + + - name: Build + uses: nick-fields/retry@v3 + with: + timeout_minutes: 60 + max_attempts: 3 + shell: bash + command: | + python3 -m venv venv + source venv/bin/activate || source venv/Scripts/activate + poetry install + make build SKIP_WEBUI=${{ matrix.skip_webui }} SKIP_SERVER_RUST=${{ matrix.skip_rust }} + pip freeze + + - name: Run tests + uses: nick-fields/retry@v3 + with: + timeout_minutes: 60 + max_attempts: 3 + shell: bash + command: | + source venv/bin/activate || source venv/Scripts/activate + make test SKIP_SERVER_RUST=${{ matrix.skip_rust }} + + - name: Package + run: | + source venv/bin/activate || source venv/Scripts/activate + poetry install + make package SKIP_SERVER_RUST=${{ matrix.skip_rust }} + + - name: Package dmg + if: runner.os == 'macOS' + run: | + if [ -n "$APPLE_EMAIL" ]; then + ./scripts/ci/import-macos-p12.sh + fi + + source venv/bin/activate + make dist/ActivityWatch.dmg + + if [ -n "$APPLE_EMAIL" ]; then + codesign --verbose -s ${APPLE_PERSONALID} dist/ActivityWatch.dmg + + brew install akeru-inc/tap/xcnotary + xcnotary precheck dist/ActivityWatch.app + xcnotary precheck dist/ActivityWatch.dmg + + make dist/notarize + fi + mv dist/ActivityWatch.dmg dist/activitywatch-$(scripts/package/getversion.sh)-macos-x86_64.dmg + env: + APPLE_EMAIL: ${{ secrets.APPLE_EMAIL }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_PERSONALID: ${{ secrets.APPLE_TEAMID }} + APPLE_TEAMID: ${{ secrets.APPLE_TEAMID }} + CERTIFICATE_MACOS_P12_BASE64: ${{ secrets.CERTIFICATE_MACOS_P12_BASE64 }} + CERTIFICATE_MACOS_P12_PASSWORD: ${{ secrets.CERTIFICATE_MACOS_P12_PASSWORD }} + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: builds-tauri-${{ matrix.os }}-py${{ matrix.python_version }} + path: dist/activitywatch-*.* + + release-notes: + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + fetch-depth: 0 + + - uses: ActivityWatch/check-version-format-action@v2 + id: version + with: + prefix: "v" + + - name: Echo version + run: | + echo "${{ steps.version.outputs.full }} (stable: ${{ steps.version.outputs.is_stable }})" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install deps + run: | + pip install requests + + - name: Generate release notes + run: | + LAST_RELEASE=`STABLE_ONLY=${{ steps.version.output.is_stable }} ./scripts/get_latest_release.sh` + ./scripts/build_changelog.py --range "$LAST_RELEASE...${{ steps.version.outputs.full }}" + + - name: Rename + run: | + mv changelog.md release_notes.md + + - name: Upload release notes + uses: actions/upload-artifact@v4 + with: + name: release_notes_tauri + path: release_notes.md + + release: + needs: [build, release-notes] + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + path: dist + + - name: Display structure of downloaded files + run: ls -R + working-directory: dist + + - uses: ActivityWatch/check-version-format-action@v2 + id: version + with: + prefix: "v" + + - name: Release + uses: softprops/action-gh-release@v1 + with: + draft: true + files: dist/*/activitywatch-*.* + body_path: dist/release_notes_tauri/release_notes.md + prerelease: ${{ !(steps.version.outputs.is_stable == 'true') }} diff --git a/Makefile b/Makefile index 67d8fdd27..9b2a5c23c 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,17 @@ SHELL := /usr/bin/env bash -SUBMODULES := aw-core aw-client aw-qt aw-server aw-server-rust aw-watcher-afk aw-watcher-window +OS := $(shell uname -s) + +ifeq ($(TAURI_BUILD),true) + SUBMODULES := aw-core aw-client aw-server aw-server-rust aw-watcher-afk aw-watcher-window aw-tauri + # Include awatcher on Linux (Wayland-compatible window watcher) + ifeq ($(OS),Linux) + SUBMODULES := $(SUBMODULES) awatcher + endif +else + SUBMODULES := aw-core aw-client aw-qt aw-server aw-server-rust aw-watcher-afk aw-watcher-window +endif # Exclude aw-server-rust if SKIP_SERVER_RUST is true ifeq ($(SKIP_SERVER_RUST),true) @@ -34,6 +44,19 @@ PACKAGEABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),package)) LINTABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),lint)) TYPECHECKABLES := $(foreach dir,$(SUBMODULES),$(call has_target,$(dir),typecheck)) +# When building with Tauri, aw-server-rust is built as aw-sync only (not full server), +# so exclude it from the standard package target +ifeq ($(TAURI_BUILD),true) + PACKAGEABLES := $(filter-out aw-server-rust aw-server, $(PACKAGEABLES)) +endif + +# Build mode: release vs debug +ifeq ($(RELEASE), false) + targetdir := debug +else + targetdir := release +endif + # The `build` target # ------------------ # @@ -44,17 +67,13 @@ build: aw-core/.git # needed due to https://github.com/pypa/setuptools/issues/1963 # would ordinarily be specified in pyproject.toml, but is not respected due to https://github.com/pypa/setuptools/issues/1963 pip install 'setuptools>49.1.1' - @if [ "$(SKIP_SERVER_RUST)" = "false" ]; then \ - if (which cargo); then \ - echo 'Rust found!'; \ - else \ - echo 'ERROR: Rust not found, try running with SKIP_SERVER_RUST=true'; \ - exit 1; \ - fi \ - fi for module in $(SUBMODULES); do \ echo "Building $$module"; \ - make --directory=$$module build SKIP_WEBUI=$(SKIP_WEBUI) || { echo "Error in $$module build"; exit 2; }; \ + if [ "$$module" = "aw-server-rust" ] && [ "$(TAURI_BUILD)" = "true" ]; then \ + make --directory=$$module aw-sync SKIP_WEBUI=$(SKIP_WEBUI) || { echo "Error in $$module aw-sync"; exit 2; }; \ + else \ + make --directory=$$module build SKIP_WEBUI=$(SKIP_WEBUI) || { echo "Error in $$module build"; exit 2; }; \ + fi; \ done # The below is needed due to: https://github.com/ActivityWatch/activitywatch/issues/173 make --directory=aw-client build @@ -68,11 +87,13 @@ build: aw-core/.git # # Installs things like desktop/menu shortcuts. # Might in the future configure autostart on the system. +ifneq ($(TAURI_BUILD),true) install: make --directory=aw-qt install # Installation is already happening in the `make build` step currently. # We might want to change this. # We should also add some option to install as user (pip3 install --user) +endif # Update # ------ @@ -120,15 +141,15 @@ test-integration: # aw-server-python @echo "== Integration testing aw-server ==" @pytest ./scripts/tests/integration_tests.py ./aw-server/tests/ -v - # aw-server-rust - @echo "== Integration testing aw-server-rust ==" - @export PATH=aw-server-rust/target/release:aw-server-rust/target/debug:${PATH}; \ - pytest ./scripts/tests/integration_tests.py ./aw-server/tests/ -v %/.git: git submodule update --init --recursive -ICON := "aw-qt/media/logo/logo.png" +ifeq ($(TAURI_BUILD),true) + ICON := "aw-tauri/src-tauri/icons/icon.png" +else + ICON := "aw-qt/media/logo/logo.png" +endif aw-qt/media/logo/logo.icns: mkdir -p build/MyIcon.iconset @@ -147,7 +168,11 @@ aw-qt/media/logo/logo.icns: mv build/MyIcon.icns aw-qt/media/logo/logo.icns dist/ActivityWatch.app: aw-qt/media/logo/logo.icns +ifeq ($(TAURI_BUILD),true) + scripts/package/build_app_tauri.sh +else pyinstaller --clean --noconfirm aw.spec +endif dist/ActivityWatch.dmg: dist/ActivityWatch.app # NOTE: This does not codesign the dmg, that is done in the CI config @@ -164,10 +189,16 @@ package: make --directory=$$dir package; \ cp -r $$dir/dist/$$dir dist/activitywatch; \ done +ifeq ($(TAURI_BUILD),true) +# Copy aw-sync binary for Tauri builds + mkdir -p dist/activitywatch/aw-server-rust + cp aw-server-rust/target/$(targetdir)/aw-sync dist/activitywatch/aw-server-rust/aw-sync +else # Move aw-qt to the root of the dist folder mv dist/activitywatch/aw-qt aw-qt-tmp mv aw-qt-tmp/* dist/activitywatch rmdir aw-qt-tmp +endif # Remove problem-causing binaries rm -f dist/activitywatch/libdrm.so.2 # see: https://github.com/ActivityWatch/activitywatch/issues/161 rm -f dist/activitywatch/libharfbuzz.so.0 # see: https://github.com/ActivityWatch/activitywatch/issues/660#issuecomment-959889230 diff --git a/scripts/changelog_contributors.csv b/scripts/changelog_contributors.csv index 34efc5543..f3d753257 100644 --- a/scripts/changelog_contributors.csv +++ b/scripts/changelog_contributors.csv @@ -25,7 +25,7 @@ aaayushsingh ayush-_-singh@live.com alclary 9044153+alclary@users.noreply.github.com alialamine ali@towbe.com alwinator accounts@alwinschuster.at -brayo-pip 62670517+brayo-pip@users.noreply.github.com +0xbrayo vukubrian@gmail.com chaoky levimanga@gmail.com chengyuhui chengyuhui1@gmail.com davidfraser davidfraser@users.noreply.github.com diff --git a/scripts/changelog_contributors_twitter.csv b/scripts/changelog_contributors_twitter.csv index bbc14c5a2..d0560c107 100644 --- a/scripts/changelog_contributors_twitter.csv +++ b/scripts/changelog_contributors_twitter.csv @@ -1,4 +1,4 @@ -brayo-pip subrupt +0xbrayo subrupt chaoky chaokyer erikbjare erikbjare iloveitaly mike_bianco diff --git a/scripts/package/README.txt b/scripts/package/README.txt new file mode 100644 index 000000000..085add795 --- /dev/null +++ b/scripts/package/README.txt @@ -0,0 +1,10 @@ +Run move-to-aw-modules.sh to copy all modules except aw-tauri to ~/aw-modules/. +aw-tauri (replaces aw-qt) will use this directory to discover new modules. +You can add your own modules and scripts to this directory. The modules should +start with the aw- prefix and should not have an extension (e.g. no .sh). + +In the aw-tauri folder there are AppImage, RPM, and DEB binaries. Choose the +appropriate one for your Linux distribution. If in doubt, use the AppImage as +it works on most Linux systems. If you use the AppImage, copy it to a permanent +folder like ~/bin or /usr/local/bin, since autostart relies on the AppImage +being in the same location each time. diff --git a/scripts/package/aw-tauri.iss b/scripts/package/aw-tauri.iss new file mode 100644 index 000000000..0cbd9f4ec --- /dev/null +++ b/scripts/package/aw-tauri.iss @@ -0,0 +1,57 @@ +; Inno Setup script for ActivityWatch (Tauri edition) +; +; This is separate from activitywatch-setup.iss (aw-qt) to avoid +; installation collisions. Uses a different AppId, install directory, +; and display name. + +#define MyAppName "ActivityWatch (Tauri)" +#define MyAppVersion GetEnv('AW_VERSION') +#define MyAppPublisher "ActivityWatch Contributors" +#define MyAppURL "https://activitywatch.net/" +#define MyAppExeName "aw-tauri.exe" +#define RootDir "..\.." +#define DistDir "..\..\dist" + +#pragma verboselevel 9 + +[Setup] +; IMPORTANT: Different AppId from aw-qt to allow side-by-side installation +AppId={{983D0855-08C8-46BD-AEFB-3924581C6703} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL="https://github.com/ActivityWatch/activitywatch/issues" +AppUpdatesURL="https://github.com/ActivityWatch/activitywatch/releases" +DefaultDirName={autopf}\ActivityWatch-Tauri +DisableProgramGroupPage=yes +PrivilegesRequired=lowest +PrivilegesRequiredOverridesAllowed=dialog +OutputDir={#DistDir} +OutputBaseFilename=activitywatch-setup +SetupIconFile="{#RootDir}\aw-tauri\src-tauri\icons\icon.ico" +UninstallDisplayName={#MyAppName} +UninstallDisplayIcon={app}\{#MyAppExeName} +Compression=lzma +SolidCompression=yes +WizardStyle=modern + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked +Name: "StartMenuEntry" ; Description: "Start ActivityWatch when Windows starts"; GroupDescription: "Windows Startup"; MinVersion: 4,4; + +[Files] +Source: "{#DistDir}\activitywatch\aw-tauri.exe"; DestDir: "{app}\aw-tauri"; Flags: ignoreversion +Source: "{#DistDir}\activitywatch\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon +Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: StartMenuEntry; + +[Run] +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent diff --git a/scripts/package/build_app_tauri.sh b/scripts/package/build_app_tauri.sh new file mode 100755 index 000000000..77e28b9db --- /dev/null +++ b/scripts/package/build_app_tauri.sh @@ -0,0 +1,105 @@ +#!/bin/bash +set -e + +# Build a macOS .app bundle for the Tauri-based ActivityWatch. +# This replaces the PyInstaller-based bundling used by aw-qt. + +APP_NAME="ActivityWatch" +BUNDLE_ID="net.activitywatch.ActivityWatch" +VERSION="0.1.0" +ICON_PATH="aw-tauri/src-tauri/icons/icon.icns" + +if [[ "$(uname)" != "Darwin" ]]; then + echo "This script is designed to run on macOS only." + exit 1 +fi + +if [ ! -d "dist/activitywatch" ]; then + echo "Error: dist/activitywatch directory not found. Please build the project first." + exit 1 +fi + +if [ ! -f "dist/activitywatch/aw-tauri" ]; then + echo "Error: aw-tauri binary not found in dist/activitywatch/" + exit 1 +fi + +echo "Cleaning previous builds..." +rm -rf "dist/${APP_NAME}.app" +mkdir -p "dist" + +echo "Creating app bundle structure..." +mkdir -p "dist/${APP_NAME}.app/Contents/"{MacOS,Resources} + +echo "Copying aw-tauri as main executable..." +cp "dist/activitywatch/aw-tauri" "dist/${APP_NAME}.app/Contents/MacOS/aw-tauri" +chmod +x "dist/${APP_NAME}.app/Contents/MacOS/aw-tauri" + +echo "Copying components to Resources..." +for component in dist/activitywatch/*/; do + if [ -d "$component" ]; then + component_name=$(basename "$component") + echo " Copying $component_name..." + mkdir -p "dist/${APP_NAME}.app/Contents/Resources/$component_name" + cp -r "$component"/* "dist/${APP_NAME}.app/Contents/Resources/$component_name/" + fi +done + +echo "Setting executable permissions..." +find "dist/${APP_NAME}.app/Contents/Resources" -type f -name "aw-*" -exec chmod +x {} \; + +echo "Copying app icon..." +if [ -f "$ICON_PATH" ]; then + cp "$ICON_PATH" "dist/${APP_NAME}.app/Contents/Resources/icon.icns" +else + echo "Warning: Icon file not found at $ICON_PATH" +fi + +echo "Creating Info.plist..." +cat > "dist/${APP_NAME}.app/Contents/Info.plist" << EOF + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + aw-tauri + CFBundleIconFile + icon.icns + CFBundleIdentifier + ${BUNDLE_ID} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${APP_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${VERSION} + CFBundleVersion + ${VERSION} + NSAppleEventsUsageDescription + ActivityWatch needs access to monitor application usage + NSHighResolutionCapable + + NSPrincipalClass + NSApplication + LSMinimumSystemVersion + 10.14 + + +EOF + +echo "Creating PkgInfo..." +echo "APPL????" > "dist/${APP_NAME}.app/Contents/PkgInfo" + +if [ -n "$APPLE_PERSONALID" ]; then + echo "Signing app with identity: $APPLE_PERSONALID" + codesign --deep --force --sign "$APPLE_PERSONALID" "dist/${APP_NAME}.app" + echo "App signing complete." +else + echo "APPLE_PERSONALID not set. Skipping code signing." +fi + +echo "App bundle created at: dist/${APP_NAME}.app" diff --git a/scripts/package/move-to-aw-modules.sh b/scripts/package/move-to-aw-modules.sh new file mode 100755 index 000000000..f3f79ca39 --- /dev/null +++ b/scripts/package/move-to-aw-modules.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copy all AW modules to ~/aw-modules/ for aw-tauri to discover. +# aw-tauri uses this directory to find and launch AW components. +set -e + +mkdir -p ~/aw-modules/ + +if [[ -n "$XDG_SESSION_TYPE" && "$XDG_SESSION_TYPE" == "wayland" ]]; then + rsync -a . ~/aw-modules/ \ + --exclude=aw-tauri \ + --exclude=aw-server-rust \ + --exclude=awatcher \ + --exclude=move-to-aw-modules.sh \ + --exclude=README.txt + cp ./awatcher/aw-awatcher ~/aw-modules/ + cp ./aw-server-rust/aw-sync ~/aw-modules/ +else + rsync -a . ~/aw-modules/ \ + --exclude=aw-tauri \ + --exclude=awatcher \ + --exclude=aw-server-rust \ + --exclude=move-to-aw-modules.sh \ + --exclude=README.txt + cp ./aw-server-rust/aw-sync ~/aw-modules/ +fi + +echo "Modules copied to ~/aw-modules/" diff --git a/scripts/package/package-all.sh b/scripts/package/package-all.sh index 967c6a459..ae286c94a 100755 --- a/scripts/package/package-all.sh +++ b/scripts/package/package-all.sh @@ -43,6 +43,11 @@ version=$(get_version) arch=$(get_arch) echo "Platform: $platform, arch: $arch, version: $version" +# For Tauri Linux builds, include helper scripts and README +if [[ $platform == "linux" && $TAURI_BUILD == "true" ]]; then + cp scripts/package/README.txt scripts/package/move-to-aw-modules.sh dist/activitywatch/ +fi + function build_zip() { echo "Zipping executables..." pushd dist; @@ -70,7 +75,11 @@ function build_setup() { # Windows installer version should not include 'v' prefix, see: https://github.com/microsoft/winget-pkgs/pull/17564 version_no_prefix="$(echo $version | sed -e 's/^v//')" - env AW_VERSION=$version_no_prefix "$innosetupdir/iscc.exe" scripts/package/activitywatch-setup.iss + if [[ $TAURI_BUILD == "true" ]]; then + env AW_VERSION=$version_no_prefix "$innosetupdir/iscc.exe" scripts/package/aw-tauri.iss + else + env AW_VERSION=$version_no_prefix "$innosetupdir/iscc.exe" scripts/package/activitywatch-setup.iss + fi mv dist/activitywatch-setup.exe dist/$filename echo "Setup built!" } From c23e606f71053291178063d629940982aee205d3 Mon Sep 17 00:00:00 2001 From: Bob Date: Sun, 22 Feb 2026 09:28:20 +0000 Subject: [PATCH 4/7] ci: fix macOS-13 runner label casing (macOS-13 -> macos-13) GitHub Actions runner labels are case-sensitive. `macOS-13` (capital OS) is not a valid runner label and causes CI failures. The correct label is `macos-13` (all lowercase). Fix this in both build.yml and build-tauri.yml. --- .github/workflows/build-tauri.yml | 6 +++--- .github/workflows/build.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml index ab4f7d702..a9cb3aa38 100644 --- a/.github/workflows/build-tauri.yml +++ b/.github/workflows/build-tauri.yml @@ -31,7 +31,7 @@ jobs: ubuntu-24.04, ubuntu-24.04-arm, windows-latest, - macOS-13, + macos-13, macos-latest, ] python_version: [3.9] @@ -52,14 +52,14 @@ jobs: echo "TAURI_BUILD=true" >> $GITHUB_ENV - name: Set up Python - if: matrix.os != 'macOS-13' + if: matrix.os != 'macos-13' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) - name: Set up Python for macOS - if: matrix.os == 'macOS-13' + if: matrix.os == 'macos-13' run: | curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" sudo installer -pkg python.pkg -target / diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3e59585d..b2d0b589e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, windows-latest, macOS-13, macos-latest] + os: [ubuntu-24.04, windows-latest, macos-13, macos-latest] python_version: [3.9] node_version: [22] skip_rust: [false] @@ -53,14 +53,14 @@ jobs: echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV - name: Set up Python - if: matrix.os != 'macOS-13' + if: matrix.os != 'macos-13' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) - name: Set up Python for macOS - if: matrix.os == 'macOS-13' + if: matrix.os == 'macos-13' run: | curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" From f179ea68d36e3481bb656116d40cd35a6fa457d4 Mon Sep 17 00:00:00 2001 From: TimeToBuildBob Date: Sun, 22 Feb 2026 12:55:03 +0000 Subject: [PATCH 5/7] ci: replace deprecated macos-13 with macos-14 runner macos-13 (x86_64) is deprecated by GitHub Actions. Replace with macos-14 (ARM64/Apple Silicon) in both build.yml and build-tauri.yml. Also remove the macos-13-specific Python workaround (custom pkg install for macOS 10.9 compatibility) since macos-14 uses standard actions/setup-python. --- .github/workflows/build-tauri.yml | 14 +------------- .github/workflows/build.yml | 23 +---------------------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml index a9cb3aa38..02b7a3527 100644 --- a/.github/workflows/build-tauri.yml +++ b/.github/workflows/build-tauri.yml @@ -31,7 +31,7 @@ jobs: ubuntu-24.04, ubuntu-24.04-arm, windows-latest, - macos-13, + macos-14, macos-latest, ] python_version: [3.9] @@ -52,22 +52,10 @@ jobs: echo "TAURI_BUILD=true" >> $GITHUB_ENV - name: Set up Python - if: matrix.os != 'macos-13' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) - - name: Set up Python for macOS - if: matrix.os == 'macos-13' - run: | - curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" - sudo installer -pkg python.pkg -target / - echo "/Library/Frameworks/Python.framework/Versions/${{ matrix.python_version }}/bin" >> $GITHUB_PATH - "/Applications/Python ${{ matrix.python_version }}/Install Certificates.command" - env: - PYTHON_VERSION: ${{ matrix.python_version }}.13 - - name: Set up Node if: ${{ !matrix.skip_webui }} uses: actions/setup-node@v4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2d0b589e..4ce00a845 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, windows-latest, macos-13, macos-latest] + os: [ubuntu-24.04, windows-latest, macos-14, macos-latest] python_version: [3.9] node_version: [22] skip_rust: [false] @@ -53,31 +53,10 @@ jobs: echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV - name: Set up Python - if: matrix.os != 'macos-13' uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - # Setup Python version built for older macOS (https://github.com/actions/virtual-environments/issues/1256) - - name: Set up Python for macOS - if: matrix.os == 'macos-13' - run: | - curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macosx10.9.pkg -o "python.pkg" - - # Python 3.11+ only has *macos11.pkg, so no more *macosx10.9.pkg - # the 'macos11' naming seems to suggest it only supports macos11 and up, - # but the release page says "for macOS 10.9 and later", - # unclear what the resulting binary compatibility will be. - # - # curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o "python.pkg" - - sudo installer -pkg python.pkg -target / - echo "/Library/Frameworks/Python.framework/Versions/${{ matrix.python_version }}/bin" >> $GITHUB_PATH - "/Applications/Python ${{ matrix.python_version }}/Install Certificates.command" - env: - # Add the patch number to the Python version (for FTP download link) - PYTHON_VERSION: ${{ matrix.python_version }}.13 - - name: Set up Node if: ${{ !matrix.skip_webui }} uses: actions/setup-node@v4 From 278a8648f8e74f1b9a9a67f6867c2d3f0604737b Mon Sep 17 00:00:00 2001 From: Bob Date: Sun, 22 Feb 2026 15:02:17 +0000 Subject: [PATCH 6/7] ci: upgrade Poetry from 1.3.2 to 1.4.2 to fix Windows race condition Poetry 1.3.x has a known Windows file-locking race condition when installing packages (tilde-prefixed dist-info temp files cause OSError). Version 1.4.0+ fixes this. Using 1.4.2 (last release in 1.4.x series) to stay on same lockfile format as 1.3.x. Fixes: Windows CI failure in jobs that run 'poetry install' --- .github/workflows/build-tauri.yml | 2 +- .github/workflows/build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml index 02b7a3527..ab3a8aa99 100644 --- a/.github/workflows/build-tauri.yml +++ b/.github/workflows/build-tauri.yml @@ -113,7 +113,7 @@ jobs: if [ "$RUNNER_OS" == "Windows" ]; then choco install innosetup fi - pip3 install poetry==1.3.2 + pip3 install poetry==1.4.2 - name: Build uses: nick-fields/retry@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ce00a845..9ba4f58db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,7 @@ jobs: if [ "$RUNNER_OS" == "Windows" ]; then choco install innosetup fi - pip3 install poetry==1.3.2 + pip3 install poetry==1.4.2 - name: Build run: | From 7fea151786a57b3af18a164c6af8728ffb3bccf7 Mon Sep 17 00:00:00 2001 From: Bob Date: Sun, 22 Feb 2026 15:06:30 +0000 Subject: [PATCH 7/7] ci: cache node_modules and aw-tauri cargo target to reduce build times - Cache node_modules directly instead of npm global cache. The global cache only saves download time, but npm ci still does a full install into node_modules every run (~5-7 min per npm ci call). Caching node_modules directly skips this entirely on cache hit. - Cache aw-tauri/src-tauri/target alongside aw-server-rust/target in the Tauri workflow. The Tauri Cargo release build takes ~8 min uncached and was not being cached previously. - Also cache aw-tauri/node_modules in the Tauri workflow (has its own package-lock.json separate from aw-webui). Expected improvement: ~15-20 min savings on cached Windows Tauri builds (from ~34 min to ~15 min). --- .github/workflows/build-tauri.yml | 23 ++++++++++------------- .github/workflows/build.yml | 17 +++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml index ab3a8aa99..02c2df728 100644 --- a/.github/workflows/build-tauri.yml +++ b/.github/workflows/build-tauri.yml @@ -69,28 +69,25 @@ jobs: with: toolchain: stable - - name: Get npm cache dir - id: npm-cache-dir - run: | - echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Cache npm + - name: Cache node_modules + uses: actions/cache@v4 if: ${{ !matrix.skip_webui }} - env: - cache-name: node with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ matrix.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + path: | + aw-server-rust/aw-webui/node_modules + aw-tauri/node_modules + key: ${{ matrix.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ matrix.os }}-${{ env.cache-name }}- + ${{ matrix.os }}-node_modules- - name: Cache cargo build uses: actions/cache@v4 env: cache-name: cargo-build-target with: - path: aw-server-rust/target + path: | + aw-server-rust/target + aw-tauri/src-tauri/target key: ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ matrix.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}- diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ba4f58db..86058b6ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,21 +70,14 @@ jobs: with: toolchain: stable - - name: Get npm cache dir - id: npm-cache-dir - run: | - echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Cache npm + - name: Cache node_modules + uses: actions/cache@v4 if: ${{ !matrix.skip_webui }} - env: - cache-name: node with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ matrix.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + path: aw-server-rust/aw-webui/node_modules + key: ${{ matrix.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ matrix.os }}-${{ env.cache-name }}- + ${{ matrix.os }}-node_modules- - name: Cache cargo build uses: actions/cache@v4