diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml new file mode 100644 index 000000000..62b399f2f --- /dev/null +++ b/.github/workflows/build-tauri.yml @@ -0,0 +1,312 @@ +name: Build Tauri + +on: + push: + branches: [master] + tags: + - v* + pull_request: + branches: [master] + #release: + # types: [published] + +jobs: + build: + name: ${{ matrix.os }}, py-${{ matrix.python_version }}, node-${{ matrix.node_version }} + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} + env: + # Wether 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 + 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] + tauri_build: [true] + #include: + # - os: ubuntu-latest + # python_version: 3.9 + # node_version: 20 + # experimental: true + + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + fetch-depth: 0 # fetch all branches and tags + + # Build in release mode if: (longer build times) + # - on a tag (release) + # - on the master branch (nightly) + - name: Set environment variables + run: | + echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> $GITHUB_ENV + echo "TAURI_BUILD=${{ matrix.tauri_build }}" >> $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 + 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 + # 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: ${{ 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 + # Unsure which of these are actually necessary... + 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 + # run: | + # 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 # output Python packages, useful for debugging dependency versions + + - 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 # output Python packages, useful for debugging dependency versions + + - 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 }} + + # Don't run integration tests on Windows, doesn't work for some reason + # - name: Run integration tests + # if: runner.os != 'Windows' + # run: | + # source venv/bin/activate || source venv/Scripts/activate + # make test-integration + + - name: Package + run: | + source venv/bin/activate || source venv/Scripts/activate + poetry install # run again to ensure we have the correct version of PyInstaller + make package SKIP_SERVER_RUST=${{ matrix.skip_rust }} + + - name: Package dmg + if: runner.os == 'macOS' + run: | + # Load certificates + # Only load key & sign if env vars for signing exists + if [ -n "$APPLE_EMAIL" ]; then + ./scripts/ci/import-macos-p12.sh + fi + + # Build .app and .dmg + source venv/bin/activate + make dist/ActivityWatch.dmg + + # codesign and notarize + if [ -n "$APPLE_EMAIL" ]; then + codesign --verbose -s ${APPLE_PERSONALID} dist/ActivityWatch.dmg + + # Run prechecks + brew install akeru-inc/tap/xcnotary + xcnotary precheck dist/ActivityWatch.app + xcnotary precheck dist/ActivityWatch.dmg + + # Notarize + 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_PERSONAL_ID == APPLE_TEAM_ID for personal accounts + APPLE_TEAMID: ${{ secrets.APPLE_TEAMID }} + CERTIFICATE_MACOS_P12_BASE64: ${{ secrets.CERTIFICATE_MACOS_P12_BASE64 }} + CERTIFICATE_MACOS_P12_PASSWORD: ${{ secrets.CERTIFICATE_MACOS_P12_PASSWORD }} + + # - name: Package AppImage + # if: startsWith(runner.os, 'linux') + # run: | + # ./scripts/package/package-appimage.sh + + # - name: Package deb + # if: startsWith(runner.os, 'linux') + # run: | + # # The entire process is deferred to a shell file for consistency. + # ./scripts/package/package-deb.sh + + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: builds-${{ matrix.os }}-py${{ matrix.python_version }} + path: dist/activitywatch-*.* + + release-notes: + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') # only on runs triggered from tag + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + fetch-depth: 0 # fetch all branches and tags + + - 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 }}" + + # TODO: Move rename build_changelog and move into there + - name: Rename + run: | + mv changelog.md release_notes.md + + - name: Upload release notes + uses: actions/upload-artifact@v4 + with: + name: release_notes + path: release_notes.md + + release: + needs: [build, release-notes] + if: startsWith(github.ref, 'refs/tags/v') # only run on tag + runs-on: ubuntu-latest + steps: + # Will download all artifacts to path + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + path: dist + + - name: Display structure of downloaded files + run: ls -R + working-directory: dist + + # detect if version tag is stable/beta + - uses: ActivityWatch/check-version-format-action@v2 + id: version + with: + prefix: "v" + + # create a release + - name: Release + uses: softprops/action-gh-release@v1 + with: + draft: true + files: dist/*/activitywatch-*.* + body_path: dist/release_notes/release_notes.md + prerelease: ${{ !(steps.version.outputs.is_stable == 'true') }} # must compare to true, since boolean outputs are actually just strings, and "false" is truthy since it's not empty: https://github.com/actions/runner/issues/1483#issuecomment-994986996 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e003ca684..9f610955d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,9 +26,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.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 \ - qt5-default \ + 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: 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..22d800cb6 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,32 @@ 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 +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) SUBMODULES := $(filter-out aw-server-rust,$(SUBMODULES)) endif + +# Build in release mode by default, unless RELEASE=false +ifeq ($(RELEASE), false) + targetdir := debug +else + targetdir := release +endif + + +#Include awatcher on linux +ifeq ($(OS),Linux) + SUBMODULES := $(SUBMODULES) awatcher +endif + # Include extras if AW_EXTRAS is true ifeq ($(AW_EXTRAS),true) SUBMODULES := $(SUBMODULES) aw-notify aw-watcher-input @@ -34,6 +54,10 @@ 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)) +ifeq ($(TAURI_BUILD),true) + PACKAGEABLES := $(filter-out aw-server-rust aw-server, $(PACKAGEABLES)) +endif + # The `build` target # ------------------ # @@ -44,17 +68,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 @@ -62,18 +82,6 @@ build: aw-core/.git # Needed to ensure that the server has the correct version set python -c "import aw_server; print(aw_server.__version__)" - -# Install -# ------- -# -# Installs things like desktop/menu shortcuts. -# Might in the future configure autostart on the system. -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) - # Update # ------ # @@ -120,10 +128,6 @@ 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 @@ -147,7 +151,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 +172,15 @@ package: make --directory=$$dir package; \ cp -r $$dir/dist/$$dir dist/activitywatch; \ done +ifeq ($(TAURI_BUILD),true) + 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-core b/aw-core index 6a58ec59c..e13c33c6d 160000 --- a/aw-core +++ b/aw-core @@ -1 +1 @@ -Subproject commit 6a58ec59c16c57933fce7bad4453449168c83ebd +Subproject commit e13c33c6d7349ae6482c666e267c4d48c475bd79 diff --git a/aw-notify b/aw-notify index 092337a13..bcea3cd1a 160000 --- a/aw-notify +++ b/aw-notify @@ -1 +1 @@ -Subproject commit 092337a1354fdc3da96e08eb5ab87487bc731652 +Subproject commit bcea3cd1a7ffd1ffcb721b952dfa75d11c3bef91 diff --git a/aw-server-rust b/aw-server-rust index a0cdef90c..c6409796f 160000 --- a/aw-server-rust +++ b/aw-server-rust @@ -1 +1 @@ -Subproject commit a0cdef90cf86cd8d2cc89723f5751c1123ae7e2b +Subproject commit c6409796f3859c1c610339682d90e0a03659ccdd diff --git a/aw-tauri b/aw-tauri new file mode 160000 index 000000000..f2ed9e74b --- /dev/null +++ b/aw-tauri @@ -0,0 +1 @@ +Subproject commit f2ed9e74b8ab6726bb589c78758e03649e4942a2 diff --git a/aw-watcher-afk b/aw-watcher-afk index 2dd99ca13..403a331f6 160000 --- a/aw-watcher-afk +++ b/aw-watcher-afk @@ -1 +1 @@ -Subproject commit 2dd99ca13c8ef9b14f891a8ddcc74b123482951e +Subproject commit 403a331f6f626afe18094cf61aeed235b75e537c diff --git a/awatcher b/awatcher new file mode 160000 index 000000000..0cdf79f85 --- /dev/null +++ b/awatcher @@ -0,0 +1 @@ +Subproject commit 0cdf79f857a65d5f43329c46a7039a166d280d7e diff --git a/scripts/changelog_contributors.csv b/scripts/changelog_contributors.csv index 34efc5543..f268181cd 100644 --- a/scripts/changelog_contributors.csv +++ b/scripts/changelog_contributors.csv @@ -1,3 +1,4 @@ +Oxbrayo vukubrian@gmail.com 2e3s 2e3s19@gmail.com 750 37119951+750@users.noreply.github.com Alwinator 39517491+Alwinator@users.noreply.github.com @@ -25,7 +26,6 @@ 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 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..009c78492 --- /dev/null +++ b/scripts/package/README.txt @@ -0,0 +1,3 @@ +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 i.e `.sh`. + +In the aw-tauri folder there is appimage, rpm and deb binaries. Choose the appropriate one for your distro, if in doubt use the appimage works on most linux systems. If you decide on the appimage copy it 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..e454883ce --- /dev/null +++ b/scripts/package/aw-tauri.iss @@ -0,0 +1,63 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +#define MyAppName "ActivityWatch" +#define MyAppVersion GetEnv('AW_VERSION') + "-tauri" +#define MyAppPublisher "ActivityWatch Contributors" +#define MyAppURL "https://activitywatch.net/" +#define MyAppExeName "aw-tauri.exe" +#define RootDir "..\.." +#define DistDir "..\..\dist" + +#pragma verboselevel 9 + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +; NOTE: the double {{ are used to escape the { character (needed for the AppId) +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}\{#MyAppName} +DisableProgramGroupPage=yes +; Uncomment the following line to run in non administrative install mode (install for current user only.) +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 +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[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 + +; Removes the previously installed version before installing the new one +; NOTE: Doesn't work? And also discouraged by the docs +;[InstallDelete] +;Type: filesandordirs; Name: "{app}\" diff --git a/scripts/package/build_app_tauri.sh b/scripts/package/build_app_tauri.sh new file mode 100755 index 000000000..d2abf224b --- /dev/null +++ b/scripts/package/build_app_tauri.sh @@ -0,0 +1,145 @@ +#!/bin/bash +set -e + +# Configuration +APP_NAME="ActivityWatch" +BUNDLE_ID="net.activitywatch.ActivityWatch" +VERSION="0.1.0" +ICON_PATH="aw-tauri/src-tauri/icons/icon.icns" + +# Check if running on macOS +if [[ "$(uname)" != "Darwin" ]]; then + echo "This script is designed to run on macOS only." + exit 1 +fi + +# Check if dist/activitywatch exists +if [ ! -d "dist/activitywatch" ]; then + echo "Error: dist/activitywatch directory not found. Please build the project first." + exit 1 +fi + +# Check if aw-tauri binary exists +if [ ! -f "dist/activitywatch/aw-tauri" ]; then + echo "Error: aw-tauri binary not found in dist/activitywatch/" + exit 1 +fi + +# Clean previous build +echo "Cleaning previous builds..." +rm -rf "dist/${APP_NAME}.app" +mkdir -p "dist" + +# Create app bundle structure +echo "Creating app bundle structure..." +mkdir -p "dist/${APP_NAME}.app/Contents/"{MacOS,Resources} + +# Copy aw-tauri as the main executable +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" + +# Copy all other components to Resources in organized directories +echo "Copying all 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 + +# Make all aw-* executables within Resources executable +echo "Setting executable permissions..." +find "dist/${APP_NAME}.app/Contents/Resources" -type f -name "aw-*" -exec chmod +x {} \; + +# Copy app icon +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 + +# Create Info.plist +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 + +# Create PkgInfo file +echo "Creating PkgInfo..." +echo "APPL????" > "dist/${APP_NAME}.app/Contents/PkgInfo" + +# Code signing (if APPLE_PERSONALID is set) +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 environment variable not set. Skipping code signing." +fi + +echo "" +echo "✅ App bundle created successfully at: dist/${APP_NAME}.app" +echo "" +echo "App Bundle Structure:" +echo "├── Contents/" +echo "│ ├── MacOS/" +echo "│ │ └── aw-tauri (main executable)" +echo "│ ├── Resources/" +for dir in "dist/${APP_NAME}.app/Contents/Resources/"*/; do + if [ -d "$dir" ] && [[ $(basename "$dir") == aw-* ]]; then + component_name=$(basename "$dir") + echo "│ │ ├── $component_name/" + # Show the main executable in each component + main_exec=$(find "$dir" -maxdepth 1 -name "aw-*" -type f 2>/dev/null | head -1) + if [ -n "$main_exec" ]; then + exec_name=$(basename "$main_exec") + echo "│ │ │ ├── $exec_name (executable)" + fi + # Show other important files + other_files=$(find "$dir" -maxdepth 2 -name "*.jxa" -o -name "Python" -o -name "*.dylib" 2>/dev/null | wc -l | tr -d ' ') + if [ "$other_files" -gt 0 ]; then + echo "│ │ │ └── ... (+ dependencies & libraries)" + fi + fi +done +echo "│ │ └── icon.icns" +echo "│ ├── Info.plist" +echo "│ └── PkgInfo" +echo "" +echo "All executables are properly packaged and accessible by aw-tauri." +echo "To test the app, run: open 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 100644 index 000000000..bae0565d2 --- /dev/null +++ b/scripts/package/move-to-aw-modules.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash + +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 -r . ~/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 diff --git a/scripts/package/package-all.sh b/scripts/package/package-all.sh index 967c6a459..cb949a718 100755 --- a/scripts/package/package-all.sh +++ b/scripts/package/package-all.sh @@ -41,6 +41,11 @@ function get_arch() { platform=$(get_platform) version=$(get_version) arch=$(get_arch) + +if [[ $platform == "linux" && $TAURI_BUILD == "true" ]]; then + cp scripts/package/README.txt scripts/package/move-to-aw-modules.sh dist/activitywatch +fi + echo "Platform: $platform, arch: $arch, version: $version" function build_zip() { @@ -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!" } @@ -85,4 +94,3 @@ echo "-------------------------------------" echo "Contents of ./dist" ls -l dist echo "-------------------------------------" -