feat(profile): named instance profiles via --profile flag - #652
Conversation
Greptile SummaryThe PR adds named server and sync profiles, including CLI/environment resolution and isolated configuration, database, and log roots while preserving legacy testing paths.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[CLI --profile] --> D[Resolve profile]
B[AW_PROFILE] --> D
C[--testing alias or build default] --> D
D --> E[Validate and initialize OnceLock]
E --> F[Select profile config root]
E --> G[Select profile database root]
E --> H[Select profile log root]
E --> I[Expose profile through server info]
E --> J[Propagate profile to aw-sync]
F --> K{Testing profile?}
G --> K
H --> K
K -->|New root exists| L[Use isolated activitywatch-testing root]
K -->|Legacy artifacts exist| M[Use shared root with testing suffixes]
K -->|Fresh setup| L
Reviews (6): Last reviewed commit: "fix(aw-sync): keep Android filesDir conf..." | Re-trigger Greptile |
|
Fixed two CI failures: Clippy ( let profile = if opts.testing { "testing" } else { "default" };
aw_server::logging::setup_logger("aw-sync", profile, verbose)?;Format ( Push: e7719a2 |
|
@greptileai review |
|
@greptileai review |
|
@TimeToBuildBob fix broken CI |
|
Confirmed. The four red jobs all fail at the same integration assertion: |
|
Fixed in |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #652 +/- ##
==========================================
+ Coverage 70.81% 79.26% +8.44%
==========================================
Files 51 67 +16
Lines 2916 5768 +2852
==========================================
+ Hits 2065 4572 +2507
- Misses 851 1196 +345 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
|
@TimeToBuildBob This is not the approach we've discussed |
|
You're right, and I see exactly what's wrong. This PR still uses the old filename-suffix approach — I'll revise this PR to match: when profile is non-default, the app name passed to the One question before I push: should the Rust side pick up the |
|
@TimeToBuildBob Probably env fallback? What does aw-core do? |
|
aw-core reads For aw-server-rust that means: |
|
@TimeToBuildBob fix it |
|
Revised in three commits (
Result: |
|
@greptileai review |
|
Testing-root fallback is in Rule (identical to python):
Bare filenames inside isolated profile roots. Local: |
|
@greptileai review |
Replace the two-valued `testing: bool` with a named profile string so that more than two parallel ActivityWatch instances can coexist on one machine. - `dirs.rs`: `db_path(profile)` → `sqlite.db` / `sqlite-<profile>.db`; add `validate_profile()` (lowercase alnum + `-_`, max 32 chars, starts with alnum); tests for suffix rule and validation - `config.rs`: replace `static mut TESTING: bool` with `OnceLock<String> PROFILE`; `set_profile()` is idempotent for same value, panics on conflict; `get_profile()` / `is_testing()` derived from it; config file is `config.toml` / `config-<profile>.toml` - `logging.rs`: `setup_logger(module, profile, verbose)` — logfile suffix is `<module>-<profile>_<ts>.log` for non-default profiles - `main.rs`: add `--profile NAME`; `--testing` remains as alias for `--profile testing`; debug builds still default to "testing"; profile is validated before use - `android/mod.rs`: update two call-sites to pass `"default"` Backwards compatibility: - `--testing` still works (alias for `--profile testing`) - `default` profile maps to existing unsuffixed paths (sqlite.db, config.toml) — no migration required - `testing` profile maps to existing -testing suffix paths Part of ActivityWatch/activitywatch#1399.
setup_logger signature changed to accept profile: &str instead of testing: bool. Convert opts.testing bool to "testing"/"default" profile string at the call site. Also run cargo fmt to fix long assert! lines in dirs.rs tests.
…::set result Previously the function checked PROFILE.get() then PROFILE.set() in two separate steps. Two threads with different profile values could both see get()==None before either set, causing the loser's set() error to be discarded and the loser to silently proceed under the wrong profile. Fix: use the atomic OnceLock::set return value directly. If Ok(()), we won the race. If Err(_), the lock was already set by a concurrent caller; check the existing value and panic only if it differs.
…ct panics Greptile P1: bare PROFILE.set() in set_testing silently discarded conflicts even when the existing profile differed, allowing a losing caller to proceed under the wrong instance. Delegating to set_profile() reuses its idempotent same-value check and conflict panic, matching the documented semantics.
…rver info
--profile only suffixed the DB filename, so a non-default instance still
shared config.toml, the cache dir and the log dir with prod — the one thing
profile isolation is for.
Move the profile into the platformdirs appname instead
("activitywatch-<profile>"), which isolates config/data/cache/logs and
everything nested under them in one place, with no per-module path changes.
default and testing deliberately keep the bare "activitywatch" root: their
legacy per-file suffixes (sqlite-testing.db, config-testing.toml, port 5666)
already separate them, and moving their root would orphan existing installs.
Also add Info.profile so clients (webui badge) can tell concurrent instances
apart; it deserializes with a "default" fallback so a new client still parses
an older server's /api/0/info.
aw-qt exports AW_PROFILE for the modules it spawns (ActivityWatch/aw-qt#128), so a profile set on the launcher reaches aw-server-rust without every module growing its own flag. --profile still wins when given.
aw-sync hard-coded activitywatch/aw-sync, so a research instance would
read prod's sync config. Use aw_server::dirs::appname() and the same
config-{profile}.toml filename rule as the server.
aw-sync now resolves --profile / AW_PROFILE / --testing and calls
set_profile so appname() is the named profile, not always default.
Adopt the activitywatch#1399 rule so rust matches aw-core#152: 1. activitywatch-testing/ exists → use it 2. else legacy testing files in activitywatch/ → stay on the shared root (sqlite-testing.db, config-testing.toml) 3. else fresh setup → create and use activitywatch-testing/ Isolated profile roots use bare sqlite.db / config.toml / log names. set_profile now runs before setup_logger so named profiles log into their own cache dir.
Master's ActivityWatch#666 reads the embedded server's config from filesDir for the API key. After rebasing onto that, get_server_config_path must not switch Android onto desktop XDG + appname_for (those helpers are cfg-gated off Android). Desktop still uses the isolated activitywatch-<profile> root.
4e555f2 to
aae79f8
Compare
|
Rebased onto current Conflict was in
Follow-up Local verify: |
|
@greptileai review |
|
Merged — thanks. The final head uses per-profile directory roots, |
… absence Since ActivityWatch/aw-server-rust#652, --testing servers log to ~/.cache/activitywatch-testing/ while released builds use ~/.cache/activitywatch/. Use shopt nullglob to gracefully handle both paths and avoid failing when no log files are present. Git-Session-Id: 8fb4
… absence (#962) The 'aw-server-rust master' matrix leg has failed on every PR since the aw-server-rust nightly picked up ActivityWatch/aw-server-rust#652 (named instance profiles, merged 2026-08-31). That change resolves the cache root via appname(): a --testing server on a machine with no legacy testing data now logs under ~/.cache/activitywatch-testing/, not ~/.cache/activitywatch/. GitHub runners are fresh, so the nightly leg writes to the new root while the pinned v0.12.3b18 legs (pre-#652 binaries) still use the old one - exactly the observed pass/fail split. Glob both roots so the diagnostics come back, and guard with nullglob so these always()-gated log steps can no longer fail the job when no logs exist. A diagnostic step should never gate a merge. Git-Session-Id: f31d
… absence (#962) The 'aw-server-rust master' matrix leg has failed on every PR since the aw-server-rust nightly picked up ActivityWatch/aw-server-rust#652 (named instance profiles, merged 2026-08-31). That change resolves the cache root via appname(): a --testing server on a machine with no legacy testing data now logs under ~/.cache/activitywatch-testing/, not ~/.cache/activitywatch/. GitHub runners are fresh, so the nightly leg writes to the new root while the pinned v0.12.3b18 legs (pre-#652 binaries) still use the old one - exactly the observed pass/fail split. Glob both roots so the diagnostics come back, and guard with nullglob so these always()-gated log steps can no longer fail the job when no logs exist. A diagnostic step should never gate a merge. Git-Session-Id: f31d
Part of ActivityWatch/activitywatch#1399.
What
Named
--profileon aw-server-rust, with--testingas an alias for--profile testing. Replacesstatic mut TESTINGwithOnceLock<String> PROFILE.Testing-root fallback (activitywatch#1399 / aw-core#152)
Identical contract to python. Resolution rule:
activitywatch-testing/already exists → use it.activitywatch/root (sqlite-testing.db,config-testing.toml, pythonpeewee-sqlite-testing*.db, …) → stay in legacy mode (old paths, old filenames).activitywatch-testing/.Inside isolated profile roots (including new-style testing), filenames are bare:
sqlite.db,config.toml, unsuffixed logs. Suffixed names remain only in legacy mode so existingsqlite-testing.dbfiles are not orphaned.set_profile()now runs beforesetup_logger(), so named profiles log into their own cache dir rather than the shared one.AW_PROFILEis the env fallback when--profileis absent (matches aw-qt exporting the env to children). CLI flag wins.Developer-mode
is_testing()is stillprofile == "testing"only. A research instance is production-mode (no Rocket debug / permissive CORS).Tests
Fallback rule covered against fake XDG roots: fresh / legacy artifacts / new-root-wins / config-testing.toml marker / named profiles stay isolated.