Commit Graph
156 Commits
Author SHA1 Message Date
Bot of Thomas Güttler 9081b452f3 feat: add structured search with visual filter builder (#469) 2026-06-07 04:38:28 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 156ccae83b fix(ci): forward SSH tunnel directly to dagger engine socket
Eliminates the socat bridge dependency by using OpenSSH's built-in
Unix socket forwarding (-L port:socket_path). The dagger user already
owns /run/dagger/engine.sock so no intermediate TCP listener is needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:15:19 +02:00
Bot of Thomas Güttler 145346c18a refactor: build Android bundle locally via fvm instead of Dagger (#463) 2026-06-06 09:04:13 +02:00
d86ce7766c feat: add undo log detail view (#461)
## Summary

- Tapping a row in the Undo Log list opens a new `UndoLogDetailScreen`
- Detail screen shows: account ID, action type (with icon/colour), timestamp, source folder, destination folder (move only), and a list of all emails in the transaction (subject + sender)
- Navigation uses go_router nested route `/accounts/undo-log/:actionId` with `state.extra` to pass the `UndoAction` object
- AppBar has an **Undo** button that calls the existing undo service and pops back

## Also fixed

- `flake.nix`: replaced the broken dagger/nix 0.20.8 Nix wrapper (infinite self-exec loop) with a direct 0.21.4 `fetchurl` derivation; wired `DAGGER_HOST` so the pre-commit `dart-check` hook can reach the running engine
- `pubspec.lock`: bumped `meta` 1.17→1.18 and `test` 1.30→1.31 to match what the CI resolver picks up (eliminates spurious generated-files drift in CI)

## Verification

- `task test` — all 492 unit/widget tests pass
- `dart analyze --fatal-infos` — clean (no warnings or infos)
- Pre-commit hooks (including `dart-check` via Dagger) — all passed on commit

Closes #450

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/461
2026-06-06 05:43:17 +02:00
f88d14f362 fix: register SOPS-decrypted secrets for CI log redaction (#460)
## Summary

- The Forgejo/GitHub Actions runner only redacts values it has been explicitly told about. Secrets exported via `$GITHUB_ENV` in `setup_dagger_remote.sh` were never registered, so they could appear in plain text in CI log output.
- Added `::add-mask::` calls for every secret exported by `export_secret()`, and for the two inline variables `DAGGER_SSH_KEY` and `DAGGER_ENGINE_HOST` that bypass that function.
- Multiline values (e.g. SSH private keys, JSON key files) are masked line-by-line, since `::add-mask::` covers a single line at a time.

## Test plan

- [ ] Trigger a `workflow_dispatch` run of `deploy.yml` and confirm no secret values appear in plain text in the "Setup Dagger Remote Engine" step or any subsequent steps.
- [ ] Confirm the existing `[secrets] exported NAME (N chars)` log lines still appear (they log only the name and length, not the value).

Closes #434

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/460
2026-06-06 05:38:47 +02:00
985bac7022 refactor: migrate deploy-android-bundle to Dagger (#449)
## Summary

- Deletes `scripts/build_android_bundle_local.sh`, which required a host Android SDK and failed with `No Android SDK found`
- Removes the `build-android-bundle-local` Taskfile task that invoked it
- Rewrites `deploy-android-bundle` to call the existing Dagger `publish-android` pipeline (build → stamp versionCode → sign → upload) via `sops exec-env` for local secret injection — no local Android SDK needed

The `publish-android` Dagger function (`ci/main.go`) already handles everything the old script did (keystore decode, AAB build, signing) plus version-code stamping, so no changes to `ci/main.go` are required.

Closes #444

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/449
2026-06-05 22:43:22 +02:00
Bot of Thomas Güttler 8446b05601 feat: add per-email notes stored on IMAP/JMAP server (#443) 2026-06-05 19:31:35 +02:00
Bot of Thomas Güttler bcece9f0af refactor: unify mail display with shared ThreadTile widget (#445) 2026-06-05 19:06:29 +02:00
Bot of Thomas Güttler 9ca7089c50 fix: enforce non-root execution in Taskfile and shell scripts (#433) 2026-06-05 18:41:36 +02:00
Bot of Thomas Güttler adef2e9f80 feat: unify thread list views via shared EmailThreadTile widget (#431) 2026-06-05 18:11:28 +02:00
Bot of Thomas Güttler 2788a43dda feat: dedicated page for allowed image-sender addresses (#420) 2026-06-05 17:53:48 +02:00
8718339b4e ci: add timeouts to all CI/CD jobs, Dagger tasks, and runner scripts (#432)
Closes #415

## Summary

- Adds missing `timeout-minutes` to `ci.yml` (`check` job, 60 min) and `windows-nightly.yml` (90 min, ready for when the Windows runner is registered)
- Wraps `ssh-keyscan` and `ssh -f -N -L` tunnel creation in `setup_dagger_remote.sh` with `timeout 30`; emits a `::warning::` annotation when either takes more than 10 s
- Adds `timeout --kill-after=10 <N>` to all bare `dagger call` invocations in `Taskfile.yml`: 600 s for test/query tasks, 1800 s for build/deploy tasks, 60 s for `ci-graph`; `stalwart` and `check-dagger` (already protected) left untouched
- Adds `timeout --kill-after=10 2400` per attempt in `run_firebase_test.sh`; emits `::warning::` on exit 124 instead of silently retrying

## Test plan

- CI passes on this PR (the `check` job now has `timeout-minutes: 60` and will self-enforce)
- All `dagger call` lines in `Taskfile.yml` now have a `timeout` prefix (visible in the diff)
- `setup_dagger_remote.sh` logic is unchanged — only the two network calls are wrapped

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/432
2026-06-05 11:49:30 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 0cefc8f8e7 load android signing secrets from SOPS for local builds
Keystore is decoded into /dev/shm (tmpfs, RAM-only) during the build
and cleaned up on exit — never written to physical disk. ANDROID_KEYSTORE_PATH
is now required with no fallback; missing it fails loudly. Dagger CI path
updated to write to /tmp and set ANDROID_KEYSTORE_PATH accordingly.

Also fix check_ci_images.sh: filter out incomplete image tags ending in ':'
that arise from dynamic From("image:"+variable) concatenations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 09:00:26 +02:00
Thomas Güttler 68950e6888 Merge branch 'issue-421-bug-report' 2026-06-04 22:18:00 +02:00
Thomas Güttler 59a9ed9109 Implement bug report uploading backend and Flutter client UI (#421) 2026-06-04 22:14:04 +02:00
3d2288ab9f fix: downgrade Flutter to 3.44.0 — cirruslabs image for 3.44.1 not published (#428)
## Summary

- Downgrades `.fvmrc` from Flutter `3.44.1` back to `3.44.0` — `ghcr.io/cirruslabs/flutter:3.44.1` does not exist on GHCR so every Dagger-based deploy job fails with "not found"
- Extends `scripts/check_ci_images.sh` to also validate the Flutter image derived from `.fvmrc` (previously only literal `From("...")` calls in `ci/main.go` were checked, so Renovate bumps to non-existent images went undetected)
- Updates `.pre-commit-config.yaml` to trigger the `ci-image-exists` hook on `.fvmrc` changes as well as `ci/main.go`

## Root cause

Recent run logs showed:
```
! ghcr.io/cirruslabs/flutter:3.44.1: not found
Error: failed to resolve image "ghcr.io/cirruslabs/flutter:3.44.1"
```
Renovate bumped Flutter to 3.44.1 (#411) but cirruslabs has not published that image — the latest available is `3.44.0`. Same root cause as #409, but the pre-commit guard only watched `ci/main.go`, not `.fvmrc`.

Closes #427

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/428
2026-06-04 22:05:18 +02:00
b631bdae24 feat: validate ci/main.go container images in pre-commit (#413)
## Summary

- Adds `scripts/check_ci_images.sh`: extracts every `From("...")` image reference from `ci/main.go` and runs `skopeo inspect --no-creds` on each one (manifest-only, no layer pull, no daemon required)
- Adds `task check-ci-images` task in `Taskfile.yml` that runs the script
- Adds `ci-image-exists` hook to `.pre-commit-config.yaml` that fires only when `ci/main.go` is staged (using `files: ^ci/main\.go$` rather than `always_run`, to avoid a network round-trip on every unrelated commit)
- Adds `skopeo` to the Nix devShell so the tool is on PATH when the hook runs via `nix develop --command`

This catches a bad image tag (like `ghcr.io/cirruslabs/flutter:3.44.1` not yet published) at commit time, before the push reaches CI.

## Test plan

- Stage a change to `ci/main.go` bumping a `From("...")` tag to a non-existent version → hook rejects commit with NOT FOUND
- Stage a change with valid image tags → hook prints OK for each image and allows the commit
- Stage a change to any other file → `ci-image-exists` hook is skipped entirely

Closes #407

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/413
2026-06-04 17:34:17 +02:00
Bot of Thomas Güttler c1d314a621 feat: combined inbox as the default startup view (#376) (#379) 2026-06-04 02:46:59 +02:00
87244de7da feat: group email headers in full-screen dialog (#374)
Closes #372

## What changed

- **New widget** `lib/ui/widgets/email_headers_dialog.dart`: full-screen header browser that organises headers into collapsible groups:
  - **Headers** — all standard headers (expanded by default)
  - **List- Headers** — all `List-*` headers grouped together (expanded)
  - **Received** — all `Received` headers, **collapsed by default**; shows the inter-hop duration between consecutive entries and highlights delays in colour (green < 30 s, orange < 5 min, red >= 5 min)
  - **ARC- Headers** — all `ARC-*` headers (above X-, expanded)
  - **X-Prefix Headers** — X- headers split by their second component (e.g. `X-Google-*` → "X-Google Headers"), sorted alphabetically, at the very bottom

- **`email_detail_screen.dart`**: `_showHeaders` now uses `EmailHeadersDialog`; `_showStructure` converted from `AlertDialog` to `Dialog.fullscreen()` — satisfying "Make popup windows full screen."

- **`scripts/check_coverage.dart`**: new widget file added to the `_excluded` set (UI widgets are covered by integration tests, not unit tests).

## Verified

`task check` passes (analyze: no issues, 491 unit tests pass, coverage >= 80 %).

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/374
2026-06-03 22:14:14 +02:00
29c2c7e96c fix: three deploy failures from run #1424 (#369)
## Summary

Fixes three distinct failures from CI deploy run #1424 and concurrent website update failures.

- **Play Store job**: `pip install google-auth requests` fails on Ubuntu 24.04 with PEP 668. Fixed by using `python3 -m venv` for an isolated install.
- **SSH key error (APK, Linux, website jobs)**: All SSH/rsync steps fail with `Load key "/root/.ssh/id_ed25519": error in libcrypto` inside the Dagger Alpine 3.21 container. This is the first time these jobs actually ran (all previous deploy runs had every job skipped). Two fixes:
  - `setup_dagger_remote.sh`: `export_secret` was appending an extra trailing newline to values (like SSH private keys) that already end with `\n`. Now only adds one when needed.
  - `ci/main.go` `Deployer`: mounts the key at a `.raw` path, strips Windows-style CRLF endings with `tr -d '\r'`, then writes the normalised key to `id_ed25519`. CRLF bytes cause "error in libcrypto" in Alpine's LibreSSL-backed openssh.

## Test plan
- [ ] Deploy run triggers after merge; all three deploy jobs complete
- [ ] Play Store verification step passes
- [ ] SSH commands in Alpine load the key without `error in libcrypto`

Closes #366

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/369
2026-06-03 21:23:13 +02:00
6a097976d3 fix: correct LAST_DEPLOYED_SHA detection so Play Store always gets updated (#364)
Closes #361

Three bugs in the hourly deploy workflow's change-detection logic caused the Play Store to silently fall behind whenever a deploy failed or all-android jobs were skipped.

**Bug 1 (primary): commit_sha → head_sha**
Forgejo's API returns head_sha; commit_sha was always None. This meant LAST_DEPLOYED_SHA was always empty, so the diff fell back to HEAD~1..HEAD — only the single most recent commit was inspected. If android changes landed in an earlier commit, they were silently missed.

**Bug 2: Skipped runs counted as 'deployed'**
A workflow run where deploy-playstore was skipped (android=false) has status=success, so it was treated as a successful deploy. Now the code queries each run's job results and only trusts a run where the 'Build & Deploy to Play Store' job's own conclusion=success.

**Bug 3: Narrow fallback when SHA unknown**
When LAST_DEPLOYED_SHA could not be determined the workflow diffed HEAD~1..HEAD — potentially missing many commits. Now it defaults to android=true / linux=true (deploy everything) as the safe fallback.

Additional changes:
- ::error:: / ::warning:: / ::notice:: annotations so skip/failure reasons surface in the Actions UI.
- scripts/verify_playstore_deploy.py: new post-deploy check that queries the internal track and fails if the latest version code is more than 1 hour old. (Version codes are Unix timestamps set by ci/main.go's PublishAndroid.) Catches silent deploy failures the upload API did not reject.
- scripts/test_verify_playstore_deploy.py: 5 unit tests for the verify script (all pass).

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/364
2026-06-03 19:26:00 +02:00
Bot of Thomas Güttler 2747c4e63d chore: migrate CI secrets from Forgejo to SOPS (#354) 2026-06-03 06:37:07 +02:00
Thomas Güttler ed247baaac fix: use more robust Dagger connection verification 2026-06-02 16:55:18 +02:00
Thomas Güttler 69bd7f5962 fix: use SSH tunnel for Dagger remote connection 2026-06-02 16:52:16 +02:00
Thomas Güttler e0ecac20aa fix: ensure remote DAGGER_HOST is set and use more robust SSH setup 2026-06-02 16:24:56 +02:00
Thomas Güttler f9e0fadb68 fix: use ssh-keyscan to populate known_hosts for Dagger 2026-06-02 16:21:49 +02:00
Thomas Güttler aebc1e508e fix: use ssh-agent for Dagger remote connection 2026-06-02 16:18:06 +02:00
Thomas Güttler 375fd18f9f fix: use full SSH URL for Dagger remote to avoid config include issues 2026-06-02 16:14:51 +02:00
Thomas Güttler ba21b802eb fix: use _EXPERIMENTAL_DAGGER_RUNNER_HOST for Dagger SSH redirection 2026-06-02 13:31:11 +02:00
Thomas Güttler 7974c28102 fix: use absolute path for dagger in ssh wrapper 2026-06-02 13:23:41 +02:00
Thomas Güttler e5c5dc9db8 fix: add IdentitiesOnly=yes to SSH config for Dagger 2026-06-02 13:20:20 +02:00
Thomas Güttler 6703ffd69b fix: use explicit ssh wrapper for dagger commands 2026-06-02 13:19:16 +02:00
Thomas Güttler ee1fccf340 fix: use _EXPERIMENTAL_DAGGER_RUNNER_HOST for SSH redirection 2026-06-02 13:16:33 +02:00
Thomas Güttler 5757176937 debug: add SSH connection test to setup_dagger_remote.sh 2026-06-02 12:51:41 +02:00
Thomas Güttler 8ee411d1c8 fix: use --output-type json for SOPS decryption 2026-06-02 12:45:34 +02:00
Thomas Güttler 1e2d1b6063 chore: migrate to SOPS and SSH for Dagger engine access 2026-06-02 11:10:29 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 ea5d119706 fix: add timeouts to dagger query, docker info, and portfile loop (#347)
Three unguarded blocking calls caused CI to hang until the 60-min timeout:
- dagger query prune steps had no timeout; || true only catches errors, not hangs
- docker info (added in d905cd6) had no timeout if Docker socket is unresponsive
- until portfile loop in check-dagger spun forever if otel-receiver.py crashed

Fixes: timeout 120 on all dagger query prune calls, timeout 30 on docker info,
and a kill -0 process-alive guard on the portfile until loop with fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 21:43:07 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 968db75c69 feat: replace agent_loop.py with agentloop
Switch from the bespoke 1136-line Python orchestrator to the community
agentloop tool (https://github.com/guettli/agentloop). The new tool
handles the issue → agent → PR pipeline via a label state machine using
loop/plan and loop/code labels, running every 5 minutes via cron.

Removes: scripts/agent_loop.py, scripts/test_agent_loop.py
Removes: .forgejo/workflows/monitor.yml (no heartbeat concept in agentloop)
Updates: AGENTS.md to document the new loop/ label workflow

agentloop config lives in ~/agentloop/loop/sharedinbox/ on the host.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 09:20:48 +02:00
Bot of Thomas Güttler d905cd653f fix: check Docker availability before falling back to local Dagger engine (#329) (#333) 2026-05-29 23:19:14 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 e21cde0a3c fix: allow forgejo-actions as issue author in agent loop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 21:52:56 +02:00
Bot of Thomas Güttler 50a6678ec2 feat: reimplement user preferences, archive, configurable navigation (#315) (#324) 2026-05-29 19:08:12 +02:00
Bot of Thomas Güttler adc4eb6f6d feat: remove publish-website from deploy.yml, schedule website.yml hourly (#325) (#330) 2026-05-29 12:53:18 +02:00
Bot of Thomas Güttler c45775be92 fix: move sync health report to own row below each account (#311) (#322) 2026-05-28 06:53:11 +02:00
Bot of Thomas Güttler a5928c1aa6 fix: add _tea_get and merged-PR catch-up to close issues on merge (#305) (#310) 2026-05-28 00:07:13 +02:00
Bot of Thomas Güttler 7f3cd43d6e feat: add --dangerously-skip-permissions to claude --resume output (#304) (#309) 2026-05-27 23:48:12 +02:00
Bot of Thomas Güttler 41550eb4b5 feat: configurable menu bar position for mailbox view (#298) (#303) 2026-05-27 22:07:12 +02:00
Bot of Thomas Güttler 5ddfe68467 feat: catch up Renovate PRs with passing CI in agent loop (#289) (#293) 2026-05-27 20:09:13 +02:00
Bot of Thomas Güttler c0dd13be5d feat: align single and multi-mail actions, add archive (#287) (#291) 2026-05-27 19:36:13 +02:00
Bot of Thomas Güttler 73bbfd2694 fix: add explicit note that app settings are never uploaded (#280) (#281) 2026-05-27 08:25:20 +02:00
Thomas SharedInbox 49e6b335d9 better err msg in agent-loop. 2026-05-27 08:14:42 +02:00