diff --git a/AGENTS.md b/AGENTS.md index 10fdfe6..ef60b5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,15 +19,19 @@ ## Running +Flutter build dependencies (libgtk-3-dev, libepoxy-dev, libsecret-1-dev, etc.) are installed via apt — see the Flutter Linux docs. The nix dev shell provides only tools: `task`, `fvm`, `stalwart-mail`. + +Enter the nix dev shell first: `nix develop` + ```bash # Code generation (must run after schema changes) -dart run build_runner build --delete-conflicting-outputs +task codegen # Desktop -flutter run -d linux +task run # Tests -flutter test +task test ``` ## Adding a screen diff --git a/Taskfile.yml b/Taskfile.yml index 38f5155..20e3685 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -18,14 +18,23 @@ tasks: run: once deps: [_nix-check] cmds: - - cmd: command -v flutter >/dev/null 2>&1 || fvm install + - cmd: fvm install --skip-pub-get _pub-get: internal: true run: once deps: [_flutter-check] cmds: - - flutter pub get --suppress-analytics 2>/dev/null + - fvm flutter pub get --suppress-analytics + + _linux-deps-check: + internal: true + run: once + preconditions: + - sh: command -v clang >/dev/null 2>&1 + msg: "Linux desktop deps missing. Run: sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libstdc++-12-dev" + - sh: pkg-config --exists gtk+-3.0 2>/dev/null + msg: "Linux desktop deps missing. Run: sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libstdc++-12-dev" install-hooks: desc: Install pre-commit hooks (requires pre-commit; see .pre-commit-config.yaml) @@ -36,7 +45,7 @@ tasks: desc: Generate Drift DB code (run after any schema change) deps: [_nix-check, _pub-get] cmds: - - flutter pub run build_runner build --delete-conflicting-outputs + - fvm flutter pub run build_runner build --delete-conflicting-outputs analyze: desc: Static analysis (flutter analyze) @@ -48,7 +57,7 @@ tasks: desc: Auto-fix lint issues with dart fix --apply deps: [_nix-check] cmds: - - dart fix --apply + - fvm dart fix --apply test: desc: Unit tests + coverage gate (fails if any non-excluded lib/ file is missing) @@ -66,7 +75,7 @@ tasks: desc: Full Flutter test suite (unit + widget + integration) deps: [_nix-check] cmds: - - flutter test + - fvm flutter test integration: desc: Integration tests against a local Stalwart mail server @@ -74,48 +83,23 @@ tasks: cmds: - stalwart-dev/test.sh - _check-libsecret: - internal: true - cmds: - - | - if pkg-config --exists libsecret-1; then - exit 0 - elif dpkg -s libsecret-1-dev >/dev/null 2>&1; then - echo "Error: libsecret-1-dev is installed but pkg-config cannot find it." - echo "Your nix shell was opened before the package was installed." - echo "Fix: exit the nix shell and re-enter with: nix develop" - else - echo "Error: libsecret-1-dev is not installed." - echo "Fix: sudo apt install libsecret-1-dev" - fi - exit 1 - build-linux: desc: Build the Linux desktop app (debug) - deps: [_nix-check, _pub-get, _check-libsecret] + deps: [_nix-check, _linux-deps-check, _pub-get] cmds: - - flutter build linux --debug --no-pub - - | - binary=build/linux/x64/debug/bundle/sharedinbox - missing=$(LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu ldd "$binary" 2>/dev/null | grep 'not found' || true) - if [ -n "$missing" ]; then - echo "Error: built binary has unresolvable runtime dependencies:" - echo "$missing" - echo "Fix: sudo apt install libsecret-1-0" - exit 1 - fi + - fvm flutter build linux --debug --no-pub build-android: desc: Build a release APK (output in build/app/outputs/flutter-apk/) deps: [_nix-check, _pub-get] cmds: - - flutter build apk --release --no-pub + - fvm flutter build apk --release --no-pub run: desc: Run the app on Linux desktop - deps: [_nix-check, _pub-get] + deps: [_nix-check, _linux-deps-check, _pub-get] cmds: - - flutter run -d linux --no-pub + - fvm flutter run -d linux --no-pub check: desc: All fast checks — analyze + unit tests + widget tests + build-linux + integration in parallel diff --git a/flake.lock b/flake.lock index f6d9478..5a1fd9d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,49 +1,5 @@ { "nodes": { - "android-nixpkgs": { - "inputs": { - "devshell": "devshell", - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1776286015, - "narHash": "sha256-Is4Efj9Jzdy+vo0xoS7ak5i6yYPhviYsRG9ZijzkAXE=", - "owner": "tadfisher", - "repo": "android-nixpkgs", - "rev": "d2793eb50d8fd8e9ad2937cfeafa8e87f08480d8", - "type": "github" - }, - "original": { - "owner": "tadfisher", - "ref": "stable", - "repo": "android-nixpkgs", - "type": "github" - } - }, - "devshell": { - "inputs": { - "nixpkgs": [ - "android-nixpkgs", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1768818222, - "narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=", - "owner": "numtide", - "repo": "devshell", - "rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -62,24 +18,6 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1776221942, @@ -98,8 +36,7 @@ }, "root": { "inputs": { - "android-nixpkgs": "android-nixpkgs", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } }, @@ -117,21 +54,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index f65356f..ab9104c 100644 --- a/flake.nix +++ b/flake.nix @@ -3,63 +3,25 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; - android-nixpkgs = { - url = "github:tadfisher/android-nixpkgs/stable"; - inputs.nixpkgs.follows = "nixpkgs"; - }; flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, android-nixpkgs, flake-utils }: + outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let - pkgs = import nixpkgs { - inherit system; - config.allowUnfree = true; - config.android_sdk.accept_license = true; - }; - - androidSdk = android-nixpkgs.sdk.${system} (s: with s; [ - cmdline-tools-latest - build-tools-35-0-0 - platform-tools - platforms-android-36 - platforms-android-35 - emulator - ]); - + pkgs = import nixpkgs { inherit system; }; in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ - # Android - androidSdk - - # Linux desktop build deps (Flutter GTK backend) - pkg-config - cmake - ninja - clang - gtk3 - glib - pcre2 - libepoxy - at-spi2-atk - at-spi2-core - # libsecret intentionally omitted from Nix inputs: the binary-cache - # build of libgpg-error (a transitive dep) requires GLIBC_2.42 which - # is absent on non-NixOS hosts (nixpkgs issue, glibc 2.40 vs 2.42). - # Use the system package instead: - # sudo apt install libsecret-1-dev - - # Local IMAP/SMTP dev server for integration tests - stalwart-mail - # Task runner go-task - # Flutter version manager (fvm install downloads the pinned Flutter SDK) + # Flutter version manager — needed for host builds (task build-linux, task run) fvm + # Local IMAP/SMTP dev server for integration tests + stalwart-mail + # Utilities git curl @@ -69,23 +31,6 @@ ]; shellHook = '' - export ANDROID_HOME="${androidSdk}/share/android-sdk" - export ANDROID_SDK_ROOT="$ANDROID_HOME" - export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH" - - # flutter_secure_storage_linux needs libsecret-1 via pkg-config. - # We use the system package (sudo apt install libsecret-1-dev) because - # the Nix binary-cache build of libgpg-error (a transitive dep) requires - # GLIBC_2.42 which is absent in this closure's glibc 2.40. - export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:''${PKG_CONFIG_PATH:-}" - - # Nix sets TMPDIR/TMP/TEMP to a per-shell dir that is removed when the - # shell exits. Flutter refuses to start if that dir no longer exists - # (https://github.com/flutter/flutter/issues/74042). Reset to /tmp. - export TMPDIR=/tmp - export TMP=/tmp - export TEMP=/tmp - # Disable Flutter telemetry inside dev shell export FLUTTER_SUPPRESS_ANALYTICS=true diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 6d57dbc..38c24ab 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -6,7 +6,6 @@ set(APPLICATION_ID "de.sharedinbox.sharedinbox") cmake_policy(SET CMP0063 NEW) -# Load bundled libraries from the lib/ directory relative to the binary. set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") # Build mode defaults. @@ -50,14 +49,6 @@ target_compile_definitions(${BINARY_NAME} PRIVATE FLUTTER_VERSION_PATCH=${FLUTTE target_compile_definitions(${BINARY_NAME} PRIVATE FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}) target_link_libraries(${BINARY_NAME} PRIVATE flutter PkgConfig::GTK) -# nixos-25.11 packages are compiled against glibc 2.42 from the Nix toolchain. -# On non-NixOS hosts with an older glibc (e.g. 2.40) the static linker cannot -# resolve versioned symbols such as __inet_pton_chk@GLIBC_2.42 that live in -# Nix-built transitive dependencies (libgpg-error → libsecret → -# flutter_secure_storage_linux). Those .so files carry their own RPATH to the -# Nix glibc, so the symbols ARE present at runtime — the link-time check is a -# false alarm. --allow-shlib-undefined suppresses it. -target_link_options(${BINARY_NAME} PRIVATE -Wl,--allow-shlib-undefined) add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/pubspec.yaml b/pubspec.yaml index 4548cc7..6890696 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: go_router: ^14.8.1 # Secure credential storage (passwords) - flutter_secure_storage: ^9.2.4 + flutter_secure_storage: ^10.0.0 # Date formatting intl: any diff --git a/scripts/run_analyze.sh b/scripts/run_analyze.sh index bec4d32..222d464 100755 --- a/scripts/run_analyze.sh +++ b/scripts/run_analyze.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -euo pipefail START=$(date +%s) -flutter analyze --no-pub +fvm flutter analyze --no-pub END=$(date +%s) echo "analyze: $((END - START))s" diff --git a/scripts/run_unit_tests.sh b/scripts/run_unit_tests.sh index 6250c1b..75e9cce 100755 --- a/scripts/run_unit_tests.sh +++ b/scripts/run_unit_tests.sh @@ -3,7 +3,7 @@ set -euo pipefail START=$(date +%s) tmp=$(mktemp) trap 'rm -f "$tmp"' EXIT -if flutter test test/unit/ test/widget/ --coverage --no-pub --reporter expanded >"$tmp" 2>&1; then +if fvm flutter test test/unit/ test/widget/ --coverage --no-pub --reporter expanded >"$tmp" 2>&1; then # Success: show only the summary line grep -E "^All [0-9]+ tests passed" "$tmp" || tail -1 "$tmp" else @@ -11,6 +11,6 @@ else cat "$tmp" exit 1 fi -dart run scripts/check_coverage.dart +fvm dart run scripts/check_coverage.dart END=$(date +%s) echo "test: $((END - START))s" diff --git a/scripts/run_widget_tests.sh b/scripts/run_widget_tests.sh index 477093d..9b88b5d 100755 --- a/scripts/run_widget_tests.sh +++ b/scripts/run_widget_tests.sh @@ -3,7 +3,7 @@ set -euo pipefail START=$(date +%s) tmp=$(mktemp) trap 'rm -f "$tmp"' EXIT -if flutter test test/widget/ --no-pub --reporter expanded >"$tmp" 2>&1; then +if fvm flutter test test/widget/ --no-pub --reporter expanded >"$tmp" 2>&1; then grep -E "^All [0-9]+ tests passed" "$tmp" || tail -1 "$tmp" else cat "$tmp" diff --git a/stalwart-dev/test.sh b/stalwart-dev/test.sh index 62d252e..90deb8a 100755 --- a/stalwart-dev/test.sh +++ b/stalwart-dev/test.sh @@ -9,7 +9,8 @@ export STALWART_PASS_B="${STALWART_PASS_B:-secret}" export STALWART_USER_C="${STALWART_USER_C:-bob}" export STALWART_PASS_C="${STALWART_PASS_C:-secret}" export STALWART_RANDOM_PORTS=1 -export STALWART_TMPDIR="$(mktemp -d /tmp/stalwart-dev-XXXXXX)" +STALWART_TMPDIR="$(mktemp -d /tmp/stalwart-dev-XXXXXX)" +export STALWART_TMPDIR command -v stalwart >/dev/null || { echo "stalwart not in PATH — run inside nix develop" @@ -60,6 +61,6 @@ export STALWART_IMAP_HOST="127.0.0.1" export STALWART_SMTP_HOST="127.0.0.1" START=$(date +%s) -flutter test test/integration/ +fvm flutter test test/integration/ END=$(date +%s) echo "integration: $((END - START))s" diff --git a/test/unit/db_test_helper.dart b/test/unit/db_test_helper.dart index 642c256..12adce0 100644 --- a/test/unit/db_test_helper.dart +++ b/test/unit/db_test_helper.dart @@ -1,21 +1,11 @@ -import 'dart:ffi'; -import 'dart:io'; - import 'package:drift/drift.dart'; import 'package:drift/native.dart'; -import 'package:sqlite3/open.dart'; import 'package:sharedinbox/data/db/database.dart'; /// Call once per test file (e.g. in setUpAll) before creating any AppDatabase. void configureSqliteForTests() { driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; - if (Platform.isLinux) { - open.overrideFor( - OperatingSystem.linux, - () => DynamicLibrary.open('/usr/lib/x86_64-linux-gnu/libsqlite3.so.0'), - ); - } } AppDatabase openTestDatabase() => AppDatabase(NativeDatabase.memory());