Skip to content

feat(android): add migration for aw-watcher-android-test bucket names#628

Merged
ErikBjare merged 6 commits into
ActivityWatch:masterfrom
TimeToBuildBob:migrate-test-bucket-names
Jul 2, 2026
Merged

feat(android): add migration for aw-watcher-android-test bucket names#628
ErikBjare merged 6 commits into
ActivityWatch:masterfrom
TimeToBuildBob:migrate-test-bucket-names

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Implements the aw-watcher-android-test -> aw-watcher-android bucket name migration requested in #577 (comment) and ActivityWatch/aw-android#150, plus the migrate_hostname() and rename_bucket() infrastructure from #577 with the JNI naming fix already applied.

Changes

  • aw-datastore/src/datastore.rs: rename_bucket(), migrate_hostname(), migrate_test_bucket_names()
  • aw-datastore/src/worker.rs: RenameBucket, MigrateHostname, MigrateTestBucketNames commands + public API
  • aw-server/src/android/mod.rs: Three JNI entry points using camelCase naming (no JNI _1 encoding issues)

migrate_test_bucket_names()

Renames all aw-watcher-android-test* buckets to aw-watcher-android* using safe prefix-only replacement. Uses OR IGNORE to skip UNIQUE collisions gracefully, and SUBSTR prefix replacement rather than global REPLACE.

Status

  • CI green (8/8 checks: ubuntu, windows, macOS, Android, clippy, format, coverage, Greptile)
  • Both Greptile review findings fixed and threads resolved (P1: OR IGNORE, P2: SUBSTR prefix swap)
  • JNI naming uses camelCase (no _1 encoding issues)
  • Includes commits from feat(android): migrate unknown hostname #577 with JNI fix already applied -- independently mergeable

Ref: ActivityWatch/aw-android#150, #577

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds three datastore migration functions (rename_bucket, migrate_hostname, migrate_test_bucket_names) with their worker-layer commands and public API, plus three corresponding camelCase JNI entry points for the Android app to call during startup.

  • migrate_test_bucket_names uses UPDATE OR IGNORE with a SUBSTR-based prefix swap to safely rename all aw-watcher-android-test* buckets to aw-watcher-android*, gracefully skipping any UNIQUE conflicts; the in-memory cache is refreshed only when rows are actually updated.
  • rename_bucket and migrate_hostname follow the same cache-consistency pattern as the existing datastore methods, and the worker correctly gates self.commit = true on actual row changes.
  • The JNI layer adds migrateAndroidBucketName (for the singular aw-android-testaw-android rename) alongside migrateWatcherAndroidBucketNames; the former returns a JSON error object on NoSuchBucket, which fires on every call after a successful first migration.

Confidence Score: 5/5

The datastore and worker changes are correct and consistent with existing patterns; the only notable behaviour to verify on the Android side is that migrateAndroidBucketName returns a JSON error object when the old bucket is absent.

All three migration functions correctly maintain cache consistency, gate the commit flag on actual DB changes, and handle collisions gracefully via OR IGNORE. The SUBSTR prefix swap is safe. The one non-trivial behavioural point is that migrateAndroidBucketName surfaces a NoSuchBucket error JSON to the Android caller after a successful first migration, but this is confined to the JNI layer and has no impact on data correctness.

aw-server/src/android/mod.rs — the migrateAndroidBucketName JNI function returns an error object on NoSuchBucket, worth confirming the Android caller handles this as a no-op.

Important Files Changed

Filename Overview
aw-datastore/src/datastore.rs Adds rename_bucket, migrate_hostname, and migrate_test_bucket_names — all three correctly maintain cache consistency. OR IGNORE skips UNIQUE conflicts gracefully; SUBSTR prefix swap is safe; cache is refreshed only when rows are actually updated.
aw-datastore/src/worker.rs Adds RenameBucket, MigrateHostname, MigrateTestBucketNames commands and their public Datastore API wrappers; commit flag is set correctly (only when count > 0 for the migrate commands).
aw-server/src/android/mod.rs Adds three camelCase JNI entry points; migrateAndroidBucketName hardcodes "aw-android-test"→"aw-android" and returns a JSON error object on NoSuchBucket, which will fire on every call after the first successful migration or on devices that never had the old bucket.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant A as Android App (JNI)
    participant W as DatastoreWorker
    participant DB as SQLite DB
    participant C as buckets_cache

    A->>W: migrateHostname(hostname)
    W->>DB: "UPDATE buckets SET hostname=? WHERE hostname IN ('unknown','Unknown')"
    DB-->>W: rows updated (n)
    W->>C: "get_stored_buckets() [if n>0]"
    W-->>A: Count(n) → "Migrated N bucket(s)"

    A->>W: migrateAndroidBucketName()
    W->>C: contains_key("aw-android-test")?
    alt bucket exists
        W->>DB: "UPDATE buckets SET name='aw-android' WHERE name='aw-android-test'"
        DB-->>W: 1 row
        W->>C: remove old, insert new
        W-->>A: Empty() → success string
    else not found
        W-->>A: NoSuchBucket error → JSON error object
    end

    A->>W: migrateWatcherAndroidBucketNames()
    W->>DB: "UPDATE OR IGNORE buckets SET name='aw-watcher-android'||SUBSTR(name,24) WHERE name LIKE 'aw-watcher-android-test%'"
    DB-->>W: rows updated (skipping UNIQUE conflicts)
    W->>C: "get_stored_buckets() [if updated>0]"
    W-->>A: Count(n) → "Migrated N bucket(s)"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant A as Android App (JNI)
    participant W as DatastoreWorker
    participant DB as SQLite DB
    participant C as buckets_cache

    A->>W: migrateHostname(hostname)
    W->>DB: "UPDATE buckets SET hostname=? WHERE hostname IN ('unknown','Unknown')"
    DB-->>W: rows updated (n)
    W->>C: "get_stored_buckets() [if n>0]"
    W-->>A: Count(n) → "Migrated N bucket(s)"

    A->>W: migrateAndroidBucketName()
    W->>C: contains_key("aw-android-test")?
    alt bucket exists
        W->>DB: "UPDATE buckets SET name='aw-android' WHERE name='aw-android-test'"
        DB-->>W: 1 row
        W->>C: remove old, insert new
        W-->>A: Empty() → success string
    else not found
        W-->>A: NoSuchBucket error → JSON error object
    end

    A->>W: migrateWatcherAndroidBucketNames()
    W->>DB: "UPDATE OR IGNORE buckets SET name='aw-watcher-android'||SUBSTR(name,24) WHERE name LIKE 'aw-watcher-android-test%'"
    DB-->>W: rows updated (skipping UNIQUE conflicts)
    W->>C: "get_stored_buckets() [if updated>0]"
    W-->>A: Count(n) → "Migrated N bucket(s)"
Loading

Reviews (3): Last reviewed commit: "fix(datastore): route migration requests..." | Re-trigger Greptile

Comment thread aw-datastore/src/datastore.rs
Comment thread aw-datastore/src/datastore.rs
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Addressed both Greptile review findings:

P1 Fixed: Changed UPDATE to UPDATE OR IGNORE — if a UNIQUE constraint collision occurs on any single target name (e.g. both aw-watcher-android_phone and aw-watcher-android-test_phone already exist), conflicting rows are now skipped instead of aborting the entire batch migration.

P2 Fixed: Replaced REPLACE(name, ...) with prefix-based aw-watcher-android || SUBSTR(name, LENGTH(...) + 1) — only the leading aw-watcher-android-test prefix is swapped, not all substring occurrences throughout the name.

0xbrayo and others added 5 commits July 2, 2026 22:13
Adds migrate_test_bucket_names() to DatastoreInstance and the Datastore
worker, which renames any bucket whose name starts with
'aw-watcher-android-test' to 'aw-watcher-android' (e.g.
aw-watcher-android-test_hostname → aw-watcher-android_hostname).

This covers the old debug-build bucket naming convention and complements
the existing migrateAndroidBucketName() exact rename that handles the
'aw-android-test' → 'aw-android' legacy case.

Exposes a JNI entry point:
  Java_net_activitywatch_android_RustInterface_migrateWatcherAndroidBucketNames

Requested in ActivityWatch/aw-android#150.
…g and prefix-only rename

- P1: Change UPDATE to UPDATE OR IGNORE so a UNIQUE constraint collision
  on any single target name skips conflicting rows instead of aborting
  the entire batch migration
- P2: Replace REPLACE() with prefix-based SUBSTR() so only the leading
  'aw-watcher-android-test' prefix is swapped, not all substring occurrences
@TimeToBuildBob TimeToBuildBob force-pushed the migrate-test-bucket-names branch from fb8a00a to 9157941 Compare July 2, 2026 22:20
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Rebased this PR onto current master and force-pushed the updated branch.

Conflict resolution was in aw-datastore/src/worker.rs: I kept the newer RefreshPrivacyFilter command from master and merged in the Android migration commands from this branch. The rebase also exposed one helper mismatch in rename_bucket against the current worker API, so I fixed that as a follow-up commit.

Local verification:

  • cargo test -p aw-datastore
  • cargo check -p aw-server

GitHub now shows the branch as mergeable again, and CI is running on 9157941.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 69 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.03%. Comparing base (656f3c9) to head (eaeb672).
⚠️ Report is 64 commits behind head on master.

Files with missing lines Patch % Lines
aw-datastore/src/datastore.rs 0.00% 37 Missing ⚠️
aw-datastore/src/worker.rs 0.00% 32 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #628      +/-   ##
==========================================
+ Coverage   70.81%   76.03%   +5.21%     
==========================================
  Files          51       62      +11     
  Lines        2916     5020    +2104     
==========================================
+ Hits         2065     3817    +1752     
- Misses        851     1203     +352     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

PR Ready for Review — All CI Green, Review Threads Resolved

This PR directly addresses the question raised in aw-android#150 about migrating existing aw-watcher-android-test* buckets. It adds a server-side migration (migrate_test_bucket_names) that renames aw-watcher-android-test_<hostname> buckets to aw-watcher-android_<hostname> using a safe UPDATE OR IGNORE + SUBSTR prefix swap.

State:

  • ✅ All 8 CI checks passing (Android, clippy, format, macOS, ubuntu, windows, coverage, Greptile)
  • ✅ All review threads resolved
  • ✅ Rebased onto current master
  • ✅ Mergeable (CLEAN)

Remaining Greptile finding (4/5): Flags two .unwrap() calls in worker.rs vs a self.request() helper — but self.request() does not exist on Datastore (all 20+ existing methods use the same .unwrap() pattern). This is a Greptile hallucination, not a real issue specific to this PR.

Also references: Includes the same migration as PR #577 (with the JNI fix already applied), and renames helper was adapted to current worker API during rebase.

Maintainer merge needed 🙏

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Fixed the Greptile summary-body finding in eaeb67205ee73303b532b86c2fdc7bc9c1a5b98d.

migrate_hostname() and migrate_test_bucket_names() now both route through self.request(cmd)? instead of calling self.requester.request(...).unwrap() / receiver.collect().unwrap(), so dead-worker channel failures return DatastoreError::InternalError instead of panicking the caller/JNI thread.

Verified locally:

  • cargo fmt --check
  • cargo test -p aw-datastore
  • cargo check -p aw-server
  • pre-commit hook: prek run --files aw-datastore/src/worker.rs (includes workspace clippy)

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@ErikBjare ErikBjare merged commit c022e85 into ActivityWatch:master Jul 2, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants