The old fvm-based task had the same name as the new Dagger-based one,
causing go-task to error immediately (1-second CI failure).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pass --build-number $(date +%s) to flutter build for both APK and AAB
so each CI run gets a unique version code (fixes "already been used" error)
- Extract UploadToPlayStore(aab, playStoreConfig) as its own Dagger function
so the build and upload are independently callable
- Add build-android-bundle task (exports AAB via dagger export) and
upload-android-bundle task (calls UploadToPlayStore with the local file)
- CI deploy-playstore job now has two steps: Build Android Bundle and
Upload to Play Store, so a failed upload can be retried without rebuilding
- deploy-apk also gets --build-number to avoid version code collisions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both BuildAndroidApk and BuildAndroidRelease were using the debug
signing config because the keystore and password were never forwarded
into the Dagger container. Add setupKeystore() helper that decodes
ANDROID_KEYSTORE_BASE64 into android/app/upload-keystore.jks and
sets ANDROID_KEYSTORE_PASSWORD, then wire both secrets through
DeployApk, PublishAndroid, and the Taskfile/CI env blocks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add -q (quiet) flag to all dagger call invocations to suppress INFO-level
engine messages while keeping warnings and errors visible. Set DAGGER_NO_NAG=1
globally to suppress the Dagger Cloud tracing nag line. --progress=plain
is retained on all calls as required.
Refactor the CI pipeline to use WithServiceBinding for the Stalwart mail
server, replacing legacy shell scripts and manual port management.
Introduces pre-seeded data for the Stalwart service to avoid network
hits and improves headless UI testing with Xvfb.
The builds page at /builds/ was empty because generate-build-history
only ran inside deploy-playstore; if that job failed early (e.g. Play
Store secrets not configured) the website was never updated, and the
build-linux job never triggered a website update at all.
Changes:
- generate_build_history.py: extend to cover Linux tarballs in addition
to Android APKs, capped at MAX_BUILDS_PER_PLATFORM (30) each
- Taskfile: add website-publish task (generate-build-history +
website-deploy), exclude *.tar.gz from rsync, update descriptions
- .forgejo/workflows/ci.yml: add publish-website job that waits for
both build-linux and deploy-playstore (using always() so it runs
even when deploy-playstore fails), then removes the duplicate
generate/deploy steps from deploy-playstore
- .github/workflows/ci.yml: add deploy job that deploys Linux build,
generates build history, builds Hugo site, and rsyncs to server
- .gitignore: ignore website/content/builds/_index.md (generated),
Python __pycache__, and widget test failure screenshots
- stalwart-dev/integration_ui_test.sh: use ${USER:-$(id -un)} for
robustness in environments where USER is unset
- scripts/test_generate_build_history.py: unit tests for parse_builds
and render_entries covering both platforms
Generated content (builds/_index.md and per-day pages) is not tracked
in git; it is produced at CI time and rsynced to the server.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add check-mocks task that re-runs build_runner and fails if any
*.mocks.dart file differs from what is committed. Wired into
check-fast (pre-commit) and added as an early CI step so stale
mocks are caught before the full test suite runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds build-windows-release and deploy-windows-to-server Taskfile tasks,
a build-windows CI job (requires a windows-runner self-hosted runner),
and extends updateInfoProvider to also cover Platform.isWindows.
latest.json is now extended with a 'windows' key; both deploy tasks
preserve the other platform's URL when updating the file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Build task embeds GIT_HASH via --dart-define; new deploy-linux-to-server task
packages a tar.gz and updates latest.json on the server. The account list screen
shows a MaterialBanner when a newer Linux build is available.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- scripts/generate_build_history.py: SSH into server, list APKs under
public_html/builds/YYYY/MM/DD/, fetch commit titles from Codeberg API,
and write Hugo content pages to website/content/builds/
- Taskfile: add deploy-apk-to-server and generate-build-history tasks;
add --exclude='*.apk' to website-deploy rsync so APKs survive redeploy
- CI: after Play Store deploy, set up SSH key, scp APK, generate history,
then deploy website
- .gitignore: exclude website/content/builds/ (generated at deploy time)
- website/hugo.toml: add Builds nav item
Closes#73
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The (YY-20)mmddHHMM formula generates ~605M for 2026, which is lower
than existing epoch-second deployments (~1.747B). Google Play rejects
version code regressions at commit time (403 Forbidden).
Blocked — see issue #63 for context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces epoch seconds with a compact date-based integer so the Play
Store version code is interpretable by humans while staying below the
2 100 000 000 upper bound until ~2040.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
date +%y%m%d%H%M for 2026-05-14 17:17 = 2605141717 which exceeds
Android's 2100000000 versionCode cap, aborting the build.
Epoch seconds (~1.75B today) stay under the cap and remain unique.
Human-readable build-name (yymmddhhmm) is unchanged for issue #63.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Select All button to AppBar during selection mode (#15)
- Replace Unix timestamp build number with yymmdd-hhmm format (#63)
- Gate release.yml on CI workflow success via workflow_run event
- Update golden for email_list_selection to reflect new Select All button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
git rev-list --count returns 1 in shallow CI clones. A Unix timestamp
(~1.747B, well under Android's 2.1B max) is always increasing and works
in both CI shallow checkouts and local full clones.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Play Store rejects versionCode 1 since it was already used. Deriving
the build number from `git rev-list --count HEAD` gives a monotonically
increasing versionCode on every push with no manual bumping required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Missing generated .g.dart files caused flutter build appbundle to fail.
_codegen already implies _pub-get via its dep chain.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ndk debugSymbolLevel=FULL to release build type (opt-B for debug symbols)
- Add google-api-python-client to Nix devshell
- Add scripts/deploy_playstore.py to upload AAB to internal track
- Add deploy-android-bundle task to Taskfile
- Enable release.yml (remove if:false, wire up task deploy-android-bundle)
- Fix forbidden-files pre-commit hook to run task via nix develop (like dart-check)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HUGO_PARAMS_GITVERSION was stripped when website-build called
'nix develop --command hugo' from within an already-active nix shell
(nested nix develop resets the environment). Hugo is already on PATH
from the outer 'nix develop --command task website-deploy' in CI, and
locally tasks run from within 'nix develop' per AGENTS.md convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- website/layouts/_partials/extend_head.html: injects <meta name="x-version">
using HUGO_PARAMS_GITVERSION (set by Taskfile at build time)
- Taskfile: website-build sets HUGO_PARAMS_GITVERSION=<short HEAD>;
new website-verify task runs scripts/website-verify.sh
- scripts/website-verify.sh: fetches homepage, retries 6x/10s, checks
that the deployed version hash matches HEAD
- website.yml: Verify step after Deploy; scripts/website-verify.sh added
to path trigger
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- flake.nix: replace tea with fgj (fetchurl of v0.4.0 linux_amd64 binary)
- flake.nix shellHook: export IN_NIX_SHELL=1 so that nix develop --command
sets the variable that _preflight checks (nix develop does not set it
automatically, unlike the old nix-shell)
- Taskfile.yml _preflight: use IN_NIX_SHELL check instead of DIRENV_DIR,
which never worked in CI
- scripts/ci_logs.sh + task ci-logs: fetch raw Codeberg Actions job logs
from the web-UI endpoint (API endpoint not available on Codeberg 15.0.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Overview
This PR implements several fixes and enhancements requested in the latest session:
### Fixes
1. **Issue 1: Raw Email Headers**
- Added database support for raw headers.
- Added a new Headers tab in the email detail screen with a zebra-colored table display.
2. **Issue 2: Exception on Undo of Delete**
- Added `toJson` and `fromJson` to `EmailAddress` model to fix serialization during undo.
3. **Issue 3: Crash Reporting**
- Added a button to the Crash Screen to report issues directly on Codeberg.
### Infrastructure
- Added Nix experimental features check to `Taskfile.yml` to ensure a consistent dev environment.
## Verification
- Manually verified the Headers display on Linux.
- Verified Undo for IMAP and JMAP accounts.
- Verified the Crash Screen button.
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/6
- Added .forgejo/workflows/ci.yml for thin CI orchestration.
- Configured Dockerized Codeberg runner with Nix support in codeberg-runner/.
- Added systemd service for persistent runner execution.
- Added GEMINI.md for project CI/Nix conventions.
- Added Taskfile task for Linux release builds.
- Optimize task deploy-android with marker files and source/generate tracking.
- Fix flaky Android E2E test with pumpAndSettle and safety delays.
- Implement global CrashScreen and error handlers in main.dart.
- Refactor threading to use a persistent Threads table for performance.
- Add database indexes and migration for schema v18.
- Enhance coverage gate with ghost path checks and increased coverage (82%).
- Run integration tests sequentially in Taskfile.yml to avoid port 4190 (ManageSieve) conflict.
- Add ManageSieve listener to Stalwart config for better test coverage.
- Increase Android emulator boot timeout and add software-mode flags (-accel off, -no-boot-anim, -gpu swiftshader_indirect) to accommodate environments without KVM.
- Update LATER.md with notes on software emulation performance.
- Fix IMAP attachment sizes showing as '0 B' by falling back to decoded content length.
- Fix IMAP attachment downloads failing for single-part fetches by falling back to root message.
- Implement immediate server-side sync for deletions/flags using a new onChangesQueued stream.
- Automate FVM SDK setup and add 'task setup'.
- Make MobSF optional in build-android to handle environment restrictions.
- Update test fakes to match EmailRepository interface changes.
The Android UI integration test failed at tap(aliceTile) with "0 widgets"
even though pumpUntil had just found the tile. On the slow software-rendered
emulator the route-pop animation finalises during pumpUntil's trailing 300 ms
settle, briefly leaving the tile out of the tree. Re-confirm with a second
pumpUntil before the tap.
Bundles the previously uncommitted infra changes that make task deploy-android
run end-to-end inside nix develop: Linux desktop runtime libs + GL software
rendering env in flake.nix, path_provider_android pin to <2.3 to avoid the
libdartjni SIGSEGV, deferred DB-path resolution after WidgetsFlutterBinding,
+iglx for xvfb-run, platform-tools on PATH, and a single pre-commit script
replacing the dart-format / task-check-fast pair.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Start MobSF as a dep of build-android (_mobsf-start) so it warms up during
the APK build instead of blocking afterwards (saves up to 90 s). Add
_integrations task to run integration and integration-ui in parallel since
they use random Stalwart ports and different Flutter build targets.
Also add `docker rm ... || true` before `docker run` in both _mobsf-start and
mobsf_scan.sh to handle stopped-but-not-removed containers gracefully.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On Android, the soft keyboard keeps viewInsets.bottom non-zero while the
search TextField is focused. ListView.builder is allocated near-zero
height and renders 0 items, so find.text(subject) always finds nothing
even though the IMAP search returned results. Unfocusing the primary
focus after enterText dismisses the keyboard and gives the results list
full body height before pumpUntil starts polling.
Also fix pumpUntil to use pump(300ms) instead of pumpAndSettle() so a
continuously-running animation (spinner under CPU load) never prevents
settling, and override accountConnectionStatusProvider so _AccountTile
never shows a CircularProgressIndicator during the test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CircularProgressIndicator in _AccountTile (from accountConnectionStatusProvider)
runs continuously and prevents pumpAndSettle() from ever settling on Android,
causing frame-pump storms that drop the StreamBuilder data state and make
tap(aliceTile) find 0 widgets.
Overriding the provider to return immediately means no spinner ever enters the
tree, so pumpUntil() can use pumpAndSettle() cleanly again.
Also adds task run-android (boots sharedinbox_test AVD and runs flutter run).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add INTERNET permission to main AndroidManifest.xml (was missing from
release builds, causing all network calls to fail on device)
- Add scripts/mobsf_scan.sh: uploads release APK to MobSF after each
build and asserts required permissions are declared; docker pull -q
suppresses progress-bar noise
- Wire MobSF scan into build-android task; add mobsf-stop convenience task
- Fix _AccountTile subtitle overflow on Android: replace Column([Text,Text])
with single Text('email\ntype') so ListTile can measure height correctly
- E2E test robustness on Android: use pumpUntil(find.text('Alice')) instead
of pumpUntil(FAB)+expect to handle Drift background-isolate stream delay;
add skipOffstage:false to tap; remove stale email-address assertion
- Uninstall app before each Android integration test run to clear leftover
DB state and prevent "Unable to start the app" on repeated runs
- Update widget tests to use find.textContaining for merged subtitle text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stalwart 0.14.x does not increment HIGHESTMODSEQ when new mail arrives
via SMTP delivery, so the incremental sync's CONDSTORE fast-path saw
serverModSeq == storedModSeq and returned early — silently skipping
UID SEARCH and missing any newly received messages.
Fix: remove the early-return fast-path. Incremental sync now always
runs UID SEARCH UID ${lastUid+1}:* to discover new messages. CONDSTORE
is still used for the flag-refresh gate (only runs when modseq changed),
which is its correct, narrower role.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>