feat(profile): named instance profiles via --profile flag - #652
feat(profile): named instance profiles via --profile flag#652TimeToBuildBob wants to merge 9 commits into
Conversation
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.
Greptile SummaryAdds named instance profiles to the server and sync binaries while retaining
Confidence Score: 5/5The PR appears safe to merge. The previously reported conflict paths are fixed: differing profile initialization now panics after an atomic losing set, while identical initialization remains idempotent and the compatibility setter uses the same enforcement. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI[--profile] --> Resolve[Resolve active profile]
Env[AW_PROFILE] --> Resolve
Testing[--testing alias] --> Resolve
Resolve --> Validate[Validate profile name]
Validate --> Global[Initialize process profile]
Global --> Paths[Select profile directories and filenames]
Paths --> Logs[Initialize profile log]
Paths --> Config[Load profile configuration]
Paths --> Database[Open profile datastore]
Reviews (5): Last reviewed commit: "feat(profile): testing-root fallback wit..." | Re-trigger Greptile |
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.
|
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 |
…::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.
|
@greptileai review |
…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.
|
@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% 78.45% +7.63%
==========================================
Files 51 67 +16
Lines 2916 5611 +2695
==========================================
+ Hits 2065 4402 +2337
- Misses 851 1209 +358 ☔ 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. |
…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.
|
@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 |
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.
|
Testing-root fallback is in Rule (identical to python):
Bare filenames inside isolated profile roots. Local: |
|
@greptileai review |
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.