diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml new file mode 100644 index 000000000..02c2df728 --- /dev/null +++ b/.github/workflows/build-tauri.yml @@ -0,0 +1,245 @@ +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-14, + 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 + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + + - 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: Cache node_modules + uses: actions/cache@v4 + if: ${{ !matrix.skip_webui }} + with: + 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 }}-node_modules- + + - name: Cache cargo build + uses: actions/cache@v4 + env: + cache-name: cargo-build-target + with: + 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 }}- + + - 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.4.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/.github/workflows/build.yml b/.github/workflows/build.yml index 4c6ef167c..86058b6ff 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-14, macos-latest] python_version: [3.9] - node_version: [20] + node_version: [22] skip_rust: [false] skip_webui: [false] experimental: [false] @@ -53,31 +53,10 @@ jobs: echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV - name: Set up Python - if: runner.os != 'macOS' 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' - 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 @@ -91,33 +70,26 @@ 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: ${{ runner.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: | - ${{ runner.os }}-${{ env.cache-name }}- + ${{ matrix.os }}-node_modules- - 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 +98,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 \ @@ -147,7 +120,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: | @@ -223,7 +196,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: 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/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/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 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!" }