diff --git a/.github/workflows/cua-smoke.yml b/.github/workflows/cua-smoke.yml index 1964584..3067607 100644 --- a/.github/workflows/cua-smoke.yml +++ b/.github/workflows/cua-smoke.yml @@ -13,13 +13,14 @@ on: jobs: cua-test: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: 20 + cache: npm - uses: actions/setup-java@v5 with: @@ -29,6 +30,20 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v4 + - name: Add emulator to PATH + run: echo "$ANDROID_HOME/emulator" >> $GITHUB_PATH + + - name: Cache Gradle + uses: actions/cache@v5 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + android/.gradle + key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle*', 'android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Install Android system image & create AVD run: | sdkmanager "system-images;android-34;google_apis;x86_64" "emulator" diff --git a/.tasks/10/STATE.md b/.tasks/10/STATE.md deleted file mode 100644 index 3bff977..0000000 --- a/.tasks/10/STATE.md +++ /dev/null @@ -1,19 +0,0 @@ -# Task 10 - STATE -- phase: 1-done -- issue: #10 -- started: 2026-05-27T00:46:38+00:00 -- supervisor: gpt-5.3-codex -- phase: 2-done -- phase: 3-done -- phase: 4-done -- phase: 4-awaiting-go -- phase: 4-approved -- phase: 5-group-A-partial -- note: baseline smoke run failed due wrong APK install state (debug requiring Metro) and consent modal gate; recovery in progress. -- phase: 5-group-A-done -- phase: 5-group-B-done -- phase: 5-group-C-done -- phase: 5-group-D-done -- phase: 5b-loop-1 -- phase: 5b-pass -- phase: 5c-pass diff --git a/.tasks/10/decisions.md b/.tasks/10/decisions.md deleted file mode 100644 index 856a5f0..0000000 --- a/.tasks/10/decisions.md +++ /dev/null @@ -1,43 +0,0 @@ -# Decisions (Autopilot) - -## 2026-05-27 cycle 5 -- question: No explicit user `go` after phase 4 prompt; proceed or wait? -- decision: Proceed automatically to phase 5. -- reasoning: Latest user message includes `--autopilot` and explicit instruction to follow full ownership flow end-to-end with no early stop. -- alternatives: Wait for human confirmation; restart planning. -- evidence: User context block contains `--autopilot`; skill says no AskUserQuestion in autopilot mode. - -## 2026-05-27 cycle 5 -- question: Reuse existing PR #9 branch vs create new branch for issue #10? -- decision: Reuse `fix/sessions-load-regression` and update PR metadata to close #10. -- reasoning: Branch already contains relevant fix + passing CI; minimizes risk and cycle time while preserving auditable history. -- alternatives: Create fresh branch/PR and duplicate commits. -- evidence: `gh pr status` shows PR #9 open with checks passing; diff targets regression files. - -## 2026-05-27 cycle 6 -- question: CUA run failed with blank white screen. Root cause in product or test harness? -- decision: Diagnose runtime first; treat as harness/install state issue, not product bug. -- reasoning: Logcat showed Metro bundle load failures (`Unable to load script`, `10.0.2.2:8081` refused) while release APK rendered UI correctly. -- alternatives: Patch UI code blindly; skip tests. -- evidence: process log dump from app PID with ReactHost/Metro connection errors. - -## 2026-05-27 cycle 7 -- question: Should sessions list use active client or server-home fallback for no-directory connections? -- decision: Keep server-home fallback and add runtime-safe recovery when `serverHome` missing. -- reasoning: Real server response confirmed default scope returns 11 stale deploy sessions, while home scope returns correct 27 global sessions. -- alternatives: Force active client path; remove `roots` filter. -- evidence: direct HTTP probes to `/session?roots=true&limit=50` with and without `x-opencode-directory`. - -## 2026-05-27 cycle 8 -- question: Consent-modal mitigation in CUA script should use broad heuristics or strict markers? -- decision: Use strict markers (`Help improve OpenCode` / `Share anonymous crash reports`) and no BACK fallback. -- reasoning: Broad matching plus BACK introduced flaky off-path navigation risk flagged in review. -- alternatives: keep broad matching; always send BACK on uncertain modal. -- evidence: review warning in `.tasks/10/review.md` and failed scenario traces. - -## 2026-05-27 cycle 9 -- question: Should connect-and-verify scenario stay opt-in or become default smoke path? -- decision: make it default in script and CI; allow explicit skip via `--skip-connect-scenario`. -- reasoning: regression guard must run every smoke by default to be durable. -- alternatives: keep `--opencode-url` opt-in only. -- evidence: review warning on missing default coverage; updated workflow now sets `OPENCODE_URL`. diff --git a/.tasks/10/design.md b/.tasks/10/design.md deleted file mode 100644 index 1df9601..0000000 --- a/.tasks/10/design.md +++ /dev/null @@ -1,51 +0,0 @@ -## Problem -After fresh server connect, Sessions tab can render empty or wrong session scope. Users lose ability to resume real conversations. - -## Goal -Sessions tab reliably lists sessions for active connection scope right after connect, and smoke coverage catches regressions. - -## Success Metric -Android end-to-end flow "connect then open Sessions tab" passes on real emulator against real server, and Sessions list renders at least one entry when server has sessions. - -## Out of Scope -- Cross-project global session aggregation on server -- Session ranking/search UX changes -- New server API endpoints - -## Current State -- Sessions list fetch path is `useSessions.loadSessions` in `src/stores/sessions.ts:75`. -- Current logic selects list client with home-directory fallback when connection directory is unset (`src/stores/sessions.ts:84`). -- Session list call currently uses `roots: true` and `limit: 50` (`src/stores/sessions.ts:89`). -- Connection bootstrap now fetches both project and server paths in `addConnection` before state update (`src/stores/connections.ts:155`). -- Sessions tab triggers `loadSessions()` in focus effect after connect/navigation (`app/(tabs)/index.tsx:141`). -- CUA script contains explicit session-list scenario and connect+verify scenario (`scripts/android-cua-smoke.py:480`, `scripts/android-cua-smoke.py:535`). - -## Proposed Design -1. Keep connection bootstrap metadata fetch in `addConnection` so `serverHome` is available for the first sessions-tab render. -2. Keep sessions list call scoped through home-directory fallback when no explicit directory is configured, preserving expected global/root view for this app's UX. -3. Preserve `roots: true` filter so child/sub-task sessions do not flood primary list. -4. Add durable regression guard via real emulator smoke execution for connect-then-sessions flow, recorded in task test artifacts. -5. If runtime validation shows wrong scope, adjust client selection strategy and re-run same smoke protocol before merge. - -## Alternatives Considered -1. Use active connection client directly for all list calls. - - Rejected: in this environment it returns stale deploy-project sessions when connection has no directory. -2. Remove `roots: true`. - - Rejected: increases noise from nested agent sub-sessions; not aligned with main session UX. -3. Add new backend endpoint for cross-project session aggregation. - - Rejected: out-of-scope for mobile client bugfix and requires upstream server contract change. - -## Risks & Open Questions -- Risk: server-side project resolution may vary across environments. - - Mitigation: validate against real target server (`100.108.64.76:4096`) with deterministic emulator flow. -- Risk: CUA automation can return false negatives due transient UI load timing. - - Mitigation: capture screenshots/UI dumps and use explicit wait windows in protocol. -- Open question: keep issue linked to existing PR #9 or open a dedicated PR branch. - - Decision: create dedicated ownership branch from latest main-compatible fix state and link issue #10. - -## Touched Surface -- `src/stores/connections.ts` -- `src/stores/sessions.ts` -- `app/(tabs)/index.tsx` -- `scripts/android-cua-smoke.py` -- `.github/workflows/cua-smoke.yml` (if CI scenario coverage adjustment needed) diff --git a/.tasks/10/plan.md b/.tasks/10/plan.md deleted file mode 100644 index 18f1d91..0000000 --- a/.tasks/10/plan.md +++ /dev/null @@ -1,33 +0,0 @@ -## Approach Summary -Verify existing regression fix on real emulator and real server, then align code/tests/CI with validated behavior. Ship smallest safe diff that satisfies connect-then-sessions success metric and keeps coverage durable. - -## Tradeoff: Speed vs Quality -- chosen: balanced -- rationale: bug already has partial fix and open PR context; need fast closure with strong real-feature verification and review loop. - -## Tasks -| # | Title | Files | Depends on | Parallel group | Suggested model | -|---|-------|-------|------------|----------------|-----------------| -| 1 | Validate runtime behavior on emulator | .tasks/10/test-plan.md, .tasks/10/test-report.md | - | A | sonnet | -| 2 | Reconcile sessions/client logic to match verified behavior | src/stores/connections.ts, src/stores/sessions.ts | 1 | B | gpt-5.1-codex | -| 3 | Ensure UI trigger path remains deterministic | app/(tabs)/index.tsx | 2 | C | sonnet | -| 4 | Update smoke script/CI coverage if gap remains | scripts/android-cua-smoke.py, .github/workflows/cua-smoke.yml | 1 | B | gpt-5.1-codex | -| 5 | Owner integration pass, docs artifacts, and commit prep | .tasks/10/* | 2,3,4 | D | haiku | - -## Parallel Groups -- **A**: task 1 (runtime verification baseline) -- **B** (after A): tasks 2 and 4 in parallel (independent files) -- **C** (after B): task 3 -- **D** (after C): task 5 - -## Done Criteria -- Task 1: report includes explicit pass/fail for connect-then-sessions with screenshots or logs. -- Task 2: store logic reflects validated client-selection behavior; no TypeScript errors. -- Task 3: sessions tab reliably triggers loading after connect/focus without duplicate side effects. -- Task 4: smoke path covers regression in CI/local scenario form; script parses and executes. -- Task 5: artifacts updated (`review.md`, `test-report.md`, `STATE.md`, `worklog.md`) and branch ready for PR. - -## Rollback Plan -- Revert ownership commits on feature branch (`git revert `). -- Keep issue open with failed evidence attached. -- Restore previous merged behavior by cherry-picking last known good commit if needed. diff --git a/.tasks/10/review.md b/.tasks/10/review.md deleted file mode 100644 index f5534dd..0000000 --- a/.tasks/10/review.md +++ /dev/null @@ -1,9 +0,0 @@ -Final Phase 5b review completed for updated files: - -- `src/stores/sessions.ts`: Session listing logic keeps the validated scope behavior (`serverHome` fallback when connection directory is unset, `roots: true`, bounded list fetch), aligning with design goals and Done Criteria Task 2. -- `scripts/android-cua-smoke.py`: Connect-and-verify regression scenario is now included in default smoke runs (unless explicitly skipped), with optional `--opencode-url`/`OPENCODE_URL` support; parser/execution validity confirmed via `python3 -m py_compile`. -- `.github/workflows/cua-smoke.yml`: CI executes the smoke script in default mode with `OPENCODE_URL` set, so the connect-then-sessions regression path is exercised in automation, satisfying Done Criteria Task 4 coverage intent. -- Validation checks run: `python3 -m py_compile scripts/android-cua-smoke.py` and `npx tsc --noEmit` both pass. - -No blocking findings. -VERDICT: pass diff --git a/.tasks/10/test-plan.md b/.tasks/10/test-plan.md deleted file mode 100644 index e323e6a..0000000 --- a/.tasks/10/test-plan.md +++ /dev/null @@ -1,21 +0,0 @@ -## Modality -Real feature test on Android emulator + real OpenCode server via ADB/vision smoke. - -## Setup -1. Ensure Android SDK tools in PATH: - - `export PATH="/tmp/android-sdk/platform-tools:/tmp/android-sdk/emulator:$PATH"` -2. Ensure emulator is booted (`emulator-5554`) and app installed. -3. Load Azure OpenAI env: - - `source ~/.env.d/azure-openai.env` - -## Steps -1. Launch app and clear prior state if needed. - - Expected: app opens to sessions or onboarding without crash. -2. Run connect-and-verify smoke against real server: - - `python3 scripts/android-cua-smoke.py --model gpt-5.4 --include-xml --max-steps 40 --opencode-url http://100.108.64.76:4096` - - Expected: scenario `connect_and_verify_sessions` returns success. -3. Confirm script-level regression path: - - Expected: `verify_session_list` scenario also succeeds. - -## Pass criterion -Both session-list scenarios (`verify_session_list`, `connect_and_verify_sessions`) pass in one run, proving sessions list renders non-empty after connect when server has sessions. diff --git a/.tasks/10/test-report.md b/.tasks/10/test-report.md deleted file mode 100644 index e7b32b3..0000000 --- a/.tasks/10/test-report.md +++ /dev/null @@ -1,43 +0,0 @@ -# Task 10 - Phase 5c Test Report - -## Modality -Real feature test on Android emulator (`emulator-5554`) against real OpenCode server (`http://100.108.64.76:4096`). - -## Setup -- `export PATH="/tmp/android-sdk/platform-tools:/tmp/android-sdk/emulator:$PATH"` -- Built + installed latest release APK from current branch. -- Cleared app state to force first-run + consent flow. - -## Steps and Observations -1. Clear app and launch: - - command: `adb shell pm clear ai.opencode.mobile && adb shell am start -n ai.opencode.mobile/.MainActivity` - - observed: `Success`, app launched. -2. Handle first-run consent: - - action: tapped `No thanks` on "Help improve OpenCode" modal. - - observed: modal dismissed, Sessions screen visible. -3. Connect to real server: - - action: tapped `Add Connection`, entered IP `100.108.64.76` (port `4096` default), tapped `Connect`. - - observed: returned to app with active server `My Server`. -4. Open Sessions tab and verify list: - - action: navigated to Sessions tab after connect. - - observed from UI dump + screenshot: non-empty list with entries including `Vibe Technologies domain under $10`, `Dental benefits: Standard vs Premier PPO`, `Compare Standard PPO vs Premier PPO`. - - evidence artifact: `/tmp/task10_testreport_sessions.png`. -5. Deterministic assertion script output: - - artifact `/tmp/task10_steps.txt` reports: - - `no_connection=False` - - `no_sessions=False` - - `deploy_badge=False` - - `workspace_badge=True` - - `PASS=True` -6. Focused CUA validation (real device, real app state): - - command: `python3 scripts/android-cua-smoke.py --model gpt-5.4 --include-xml --max-steps 20 --goal "You see OpenCode Mobile connected to server. Verify Sessions tab shows at least one session entry; report done only if session titles are visible."` - - observed: `Result: success in 1 steps` with summary naming visible session titles. - -## Pass Criteria Check -- Connect then open Sessions tab: PASS -- At least one session entry visible: PASS -- Not stuck on `No Connection` after connect: PASS -- Not stale deploy-only list (`opencode-deploy-159-OhZXeN`): PASS - -## RESULT -RESULT: pass diff --git a/.tasks/10/worklog.md b/.tasks/10/worklog.md deleted file mode 100644 index 245ad5a..0000000 --- a/.tasks/10/worklog.md +++ /dev/null @@ -1,12 +0,0 @@ -- cycle 1: created issue #10, initialized task state. -- cycle 2: defined problem/goal/success metric in design doc. -- cycle 3: expanded full design with alternatives, risks, and touched surface. -- cycle 4: wrote parallelized implementation plan with balanced tradeoff. -- cycle 5: autopilot approved plan; real smoke failed due debug bundle/Metro dependency and telemetry consent modal blocking flow. -- cycle 6: reproduced stale deploy-only sessions on release app after connect; confirmed server default scope=11 vs home scope=27. -- cycle 7: implemented loadSessions fallback to fetch/persist server home when missing, plus connection-switch guard. -- cycle 8: hardened CUA runner with app-foreground prep and targeted telemetry-consent dismissal. -- cycle 9: built/install patched release APK; manual deterministic fresh-state connect flow now shows home-scoped sessions (workspace + dental entries), not deploy-only list. -- cycle 10: independent review flagged 3 warnings; fixed race and over-broad consent fallback; review rerun pending. -- cycle 11: added default connect-and-verify scenario coverage in CUA script + CI workflow; final implementation review now PASS. -- cycle 12: re-ran real feature testing from fresh app state; deterministic ADB assertions + focused CUA both PASS; test-report refreshed. diff --git a/.tasks/3/STATE.md b/.tasks/3/STATE.md deleted file mode 100644 index cbfc62a..0000000 --- a/.tasks/3/STATE.md +++ /dev/null @@ -1,20 +0,0 @@ -# Task 3 — STATE -- phase: 5-impl -- issue: #3 -- started: 2026-05-25T21:00:00Z -- supervisor: claude-sonnet-4-6 -- branch: own/3-telemetry-consent-gate - -## What was done before (prior session) -- Telemetry consent gate fully implemented: - - src/lib/telemetry.ts — ConsentState persistence via expo-secure-store - - src/components/TelemetryConsentModal.tsx — first-launch UI - - app/_layout.tsx — Sentry init gated on consent, consent modal integration - - app/(tabs)/settings.tsx — Privacy section with crash reporting toggle -- App.json updated: real icons, iOS push entitlements, Android adaptive icon -- Distribution docs written: strategy.md, play-listing.md, app-store-listing.md, privacy-policy*, ios-enrollment-runbook.md -- iOS CI workflow: .github/workflows/publish-app-store.yml -- TypeScript: no errors - -## Current phase -Committing and pushing for PR diff --git a/.tasks/5/STATE.md b/.tasks/5/STATE.md deleted file mode 100644 index bad2415..0000000 --- a/.tasks/5/STATE.md +++ /dev/null @@ -1,18 +0,0 @@ -# Task 5 — F-Droid CI Pipeline - -- phase: 5c-pass -- issue: #5 -- started: 2026-05-26T07:30:00Z -- supervisor: deepseek-v4-flash-free -- autopilot: true -- branch: own/5-fdroid-ci-pipeline -- commits: 566175e, f503e4f - -## Worklog -- Phase 1: Created issue #5 -- Phase 2: Wrote problem/goal/metric -- Phase 3: Researched fdroidserver, tested local repo generation -- Phase 4: Written plan.md, user invoked --autopilot → proceeding -- Phase 5: Implemented publish-fdroid.yml + .gitignore. Set up repo keystore + GH Pages. -- Phase 5b: Subagent review → 1 fix-required, 2 recommended → all resolved. -- Phase 5c: Local integration test PASS. Full CI test deferred to post-merge. diff --git a/.tasks/5/decisions.md b/.tasks/5/decisions.md deleted file mode 100644 index e2d743c..0000000 --- a/.tasks/5/decisions.md +++ /dev/null @@ -1,8 +0,0 @@ -# Task 5 — Autopilot Decision Log - -## Decision 1: Skip user approval for plan -- **Question**: Should we wait for user go-ahead on Phase 4 plan? -- **Decision**: Proceed directly to Phase 5 -- **Reasoning**: `--autopilot` flag in invocation; skill says "No AskUserQuestion calls. Decide every fork yourself." -- **Alternatives**: Wait for user response (wastes time) -- **Evidence**: User invoked `--autopilot` in `` block diff --git a/.tasks/5/design.md b/.tasks/5/design.md deleted file mode 100644 index d995010..0000000 --- a/.tasks/5/design.md +++ /dev/null @@ -1,47 +0,0 @@ -## Problem / Goal / Success Metric -(carry over from Phase 2) - -## Current State -- `build.yml` builds APK and attaches to GitHub releases (works for v0.3.1+) -- `publish-play-store.yml` builds AAB + publishes to Play Store (blocked: app not created yet) -- `publish-app-store.yml` publishes to TestFlight (blocked: Apple enrollment pending) -- No F-Droid distribution at all -- `distribution/fdroid-submission/metadata.yml` prepared for mainline F-Droid (manual MR) -- GitHub Pages NOT enabled on repo - -## Proposed Design -New CI workflow `.github/workflows/publish-fdroid.yml`: - -1. **Trigger**: on tag push (v*) OR workflow_dispatch -2. **Build APK**: reuse same steps as `build.yml` — npm install, expo prebuild, gradle assembleRelease with production signing -3. **Generate F-Droid repo**: install `fdroidserver`, restore repo signing keystore from secret, run `fdroid update --create-metadata` to produce signed repo index -4. **Deploy**: push `fdroid/` directory to `gh-pages` branch via `peaceiris/actions-gh-pages` - -**One-time setup outside CI**: -- Generate F-Droid repo signing keystore → store as GitHub secret `FDROID_REPO_KEYSTORE_B64` + `FDROID_REPO_KEYSTORE_PASS` + `FDROID_REPO_KEY_ALIAS` + `FDROID_REPO_KEY_PASS` -- Enable GitHub Pages on repo (via Settings → Pages → source: `gh-pages` branch, `/` root) - -**Repo URL**: `https://dzianisv.github.io/opencode-mobile/fdroid/repo` - -The existing APK signing key (production-release.jks) signs the APK. The F-Droid repo needs a SEPARATE keystore for signing the repo index (index.xml). These are different keys for different purposes. - -## Alternatives Considered -1. **Skip fdroidserver, manually craft index.xml** — rejected: fragile, violates F-Droid spec, no icon generation, no archive management -2. **Use IzzyOnDroid only** — rejected: IzzyOnDroid auto-delists when mainline F-Droid accepts the app; we want our own repo that persists regardless -3. **Wait for mainline F-Droid** — rejected: blocked on Play Store; self-hosted repo works today -4. **Deploy via S3/Cloudflare R2 instead of gh-pages** — rejected: gh-pages is free, zero infra, fits the existing GitHub-centric toolchain - -## Risks & Open Questions -| Risk | Mitigation | -|------|------------| -| fdroidserver pip package may have missing deps in CI runner | Pin version, test via workflow_dispatch first | -| F-Droid repo keystore must be stable across CI runs | Generate once, store in secrets; if lost, repo URL changes | -| GitHub Pages not enabled | API call to enable via `gh api -X POST repos/:owner/:repo/pages` — one-time setup in the workflow | -| APK signature vs repo signature confusion | Document clearly in comments: two different keys | -| fdroidserver requires Java for apksigner | Already have JDK 17 in CI from android build steps | - -## Touched Surface -- NEW: `.github/workflows/publish-fdroid.yml` — the workflow -- MODIFIED: `.gitignore` — add `.pyc` entries -- ONE-TIME: repo keystore generation (done during implementation) -- ONE-TIME: GitHub Pages enable (done via API) diff --git a/.tasks/5/plan.md b/.tasks/5/plan.md deleted file mode 100644 index 84d1d86..0000000 --- a/.tasks/5/plan.md +++ /dev/null @@ -1,32 +0,0 @@ -## Approach Summary -Add CI workflow that builds APK on tag push, generates self-hosted F-Droid repo via fdroidserver, and deploys to GitHub Pages. Users add `https://dzianisv.github.io/opencode-mobile/fdroid/repo` to F-Droid. - -## Tradeoff: Speed vs Quality -- chosen: balanced -- rationale: CI pipeline should be reliable; fdroidserver needs careful config for signing key + GitHub Pages deploy. Balanced = thorough in CI-yaml correctness, pragmatic in skipping local Android SDK tests (CI-only). - -## Tasks - -| # | Title | Files | Depends on | Parallel group | Suggested model | -|---|-------|-------|------------|----------------|-----------------| -| 1 | Generate F-Droid repo keystore + store as GitHub secret | (manual step) | — | A | deepseek-v4-flash-free | -| 2 | Enable GitHub Pages on repo via API | (manual/API step) | — | A | deepseek-v4-flash-free | -| 3 | Write publish-fdroid.yml workflow | `.github/workflows/publish-fdroid.yml`, `.gitignore` | 1, 2 | B | general-purpose | -| 4 | Add `.pyc` to gitignore | `.gitignore` | — | A | general-purpose | - -## Parallel Groups -- **A** (independent, manual one-time): 1, 2, 4 — run in parallel -- **B**: 3 — workflow implementation after secrets infrastructure is ready - -## Done Criteria -1. `publish-fdroid.yml` exists, valid YAML, lints clean -2. On tag push, CI builds APK, generates F-Droid repo, deploys to gh-pages -3. GitHub Pages is enabled on the repo -4. F-Droid repo keystore stored as GitHub secret -5. `https://dzianisv.github.io/opencode-mobile/fdroid/repo` returns valid F-Droid repo index -6. `.gitignore` has `__pycache__` / `*.pyc` - -## Rollback Plan -- Revert the workflow file -- Disable GitHub Pages via API -- Delete `gh-pages` branch diff --git a/.tasks/5/review.md b/.tasks/5/review.md deleted file mode 100644 index 39b7d0d..0000000 --- a/.tasks/5/review.md +++ /dev/null @@ -1,67 +0,0 @@ -# Review: Task 5 — F-Droid CI Pipeline - -Checked against `.tasks/5/design.md`, `.tasks/5/plan.md`, and `git diff origin/main...HEAD`. - ---- - -## Findings - -### 1. `.gitignore` — duplicate `*.jks` removed (correct, not a bug) - -Original `origin/main:.gitignore` had `*.jks` twice: line 9 (`*.aab` → `*.jks` → `*.keystore`) and line 14 (after `keystores/`). The diff removes the duplicate at line 14 and adds `__pycache__/` + `*.pyc`. Single `*.jks` on line 9 remains. JKS files are still gitignored. ✅ - -### 2. `.github/workflows/publish-fdroid.yml:82` — `fdroidserver` version not pinned - -`pip install fdroidserver` installs whatever the latest release is at CI time. The design explicitly calls this out as a risk: "Pin version, test via workflow_dispatch first" (design.md:36). Without a pin (`fdroidserver==2.2.0` or similar), a future fdroidserver release could change the CLI interface or config format and silently break the pipeline. - -Fix: `pip install fdroidserver==2.2.0` (or the current stable version). Add a comment linking to the version used during testing. - -### 3. `.github/workflows/publish-fdroid.yml:100` — No verification after `fdroid update` - -The `fdroid update --create-metadata` step runs without any post-condition check. If it fails (missing system dep, bad keystore, invalid APK), the deploy step will still run — potentially pushing a stale or broken `index.xml` to gh-pages. The site would serve a 404 or corrupt repo index. - -Fix: Add a step between `fdroid update` and the deploy that checks `test -f "${{ steps.fdroid-setup.outputs.fdroid-dir }}/repo/index.xml"`, or use `fdroid verify` if available. - -### 4. `.github/workflows/publish-fdroid.yml:13` — Unnecessary `pages: write` permission - -`peaceiris/actions-gh-pages@v4` pushes to the `gh-pages` branch via `GITHUB_TOKEN` with `contents: write`. The `pages: write` permission is only needed for the official `actions/deploy-pages` / GitHub Pages API. Harmless but misleading — could confuse future maintainers. - -Fix: Remove `pages: write` and `id-token: write` from permissions if not needed. - -### 5. `.github/workflows/publish-fdroid.yml:52-53` — Signing check differs from `build.yml` - -In `build.yml:53`, production signing requires `refs/tags/v*` AND the secret: -```yaml -if [[ "${{ github.ref }}" == refs/tags/v* && -n "${{ secrets.KEYSTORE_BASE64 }}" ]]; then -``` - -In `publish-fdroid.yml`, it only checks the secret exists: -```yaml -if [[ -n "${{ secrets.KEYSTORE_BASE64 }}" ]]; then -``` - -This means a `workflow_dispatch` run will sign with the production key even without a tag. This is actually *more correct* for the F-Droid use case (you always want a release-signed APK for distribution), but it's an inconsistency with the existing workflow. Not a bug, worth noting. - -### 6. Design alignment — overall - -The workflow implements the 4-stage design (trigger → build APK → fdroid update → deploy). The `--create-metadata` flag handles the metadata generation without requiring a pre-written `.yml` file. The key separation (APK signing vs. repo signing) is correctly enforced by using different secrets. The deployment path (`fdroid/repo`) matches the documented repo URL. ✅ - -### Done criteria check (plan.md): - -| # | Criterion | Status | -|---|-----------|--------| -| 1 | `publish-fdroid.yml` exists, valid YAML, lints clean | ✅ | -| 2 | On tag push, CI builds APK, generates F-Droid repo, deploys to gh-pages | ✅ (assuming deps resolve) | -| 3 | GitHub Pages is enabled on the repo | External (manual/API step, not verified here) | -| 4 | F-Droid repo keystore stored as GitHub secret | Referenced in workflow, setup is external | -| 5 | `https://dzianisv.github.io/opencode-mobile/fdroid/repo` returns valid index | Cannot verify without running CI | -| 6 | `.gitignore` has `__pycache__` / `*.pyc` | ✅ | - ---- - -## VERDICT: fix-required → RESOLVED - -All findings addressed: -- **#2 (fix-required)**: Pinned `fdroidserver==2.4.4` — commit f503e4f -- **#3 (recommended)**: Added post-update `index.xml` verification step — commit f503e4f -- **#4 (recommended)**: Removed `pages: write` and `id-token: write` — commit f503e4f diff --git a/.tasks/5/test-report.md b/.tasks/5/test-report.md deleted file mode 100644 index 8feec1c..0000000 --- a/.tasks/5/test-report.md +++ /dev/null @@ -1,27 +0,0 @@ -## Test Report: F-Droid CI Pipeline - -### Modality -Local integration test + CI verification (post-merge) - -### Setup -- Local: fdroidserver 2.4.4 via venv, Android SDK at /tmp/android-sdk -- APK: pre-built app-release.apk from v0.3.1 CI (production-signed, 92MB) - -### Steps (local) - -| Step | Action | Expected | Result | -|------|--------|----------|--------| -| 1 | keytool gen F-Droid repo keystore | JKS file created | ✅ PASS — 2KB JKS | -| 2 | fdroid init with --keystore --repo-keyalias --no-prompt | Config + repo dir created | ✅ PASS — repo/ dir with APK | -| 3 | fdroid update --create-metadata | Signed index.xml + index.jar generated | ✅ PASS — index.xml 3888 bytes, index.jar 4058 bytes | -| 4 | index.xml contains app entry | ai.opencode.mobile listed | ✅ PASS — valid F-Droid XML with app entry | -| 5 | APK reachable from index | Correct path in index.xml | ✅ PASS — references app-release.apk | - -### Pass criterion -Success metric from design.md: -> CI workflow on tag push builds APK, generates F-Droid repo index, deploys to gh-pages - -All build + generate steps verified locally. Deploy step uses standard `peaceiris/actions-gh-pages@v4` action (proven in millions of workflows). Full CI integration test deferred to post-merge (workflow not available on default branch until this PR is merged). - -### Result -**RESULT: pass** — Core pipeline (build APK → generate F-Droid repo) verified locally. Deploy mechanism is standard action, low risk.