Skip to content

feat(profile): add profile= to ActivityWatchClient and --profile CLI - #118

Open
TimeToBuildBob wants to merge 5 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/profile
Open

feat(profile): add profile= to ActivityWatchClient and --profile CLI#118
TimeToBuildBob wants to merge 5 commits into
ActivityWatch:masterfrom
TimeToBuildBob:feat/profile

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Part of ActivityWatch/activitywatch#1399 (isolated profiles per edition/mode).

Python aw-client half of the profile work. ActivityWatchClient(profile=...) and --profile NAME pick the instance; testing=True / --testing stay as the alias for profile="testing". Existing behaviour is unchanged for no-flag and --testing (port 5600 / 5666, persistqueue -testing suffix, [server] / [server-testing]).

  • Resolve + validate the profile (aw_client/profile.py) — same rule as aw-qt, aw-server, and aw-server-rust.
  • Export AW_PROFILE before loading config so aw-core#149 isolates the queue dir. If no constructor flag is set, AW_PROFILE from the launcher is used (watchers inherit it).
  • Default profile unsets AW_PROFILE instead of setting it to "default". aw-core suffixes any non-empty value, so AW_PROFILE=default would resolve to activitywatch-default and orphan an existing install.
  • Persistqueue filename keeps the existing -testing suffix; named profiles get the same shape. MockClient without a profile attr still uses the testing bool.
  • Config sections [server-<profile>] / [client-<profile>], falling back to [server] / [client]. Unprovisioned named profiles log a warning that port 5600 may collide with the default instance (same as aw-server#167).
  • Local rust API-key lookup follows the #1399 testing-root rule (identical to aw-core#152 / aw-server-rust#652):
    1. If activitywatch-testing/ exists → activitywatch-testing/aw-server-rust/config.toml
    2. Else if legacy testing artifacts exist in the bare activitywatch/ root → config-testing.toml there
    3. Else (fresh) → new-root config.toml
      Isolated roots (including named profiles) use bare config.toml. Suffixed config-<profile>.toml is no longer read. Lookup does not create directories. Testing also tries the other layout second so a python client that already created activitywatch-testing/ still finds a rust server that wrote the key on the shared root.

Testing-root note

Erik, 2026-08-29 on #1399: testing moves to activitywatch-testing/ going forward, with legacy fallback. This PR implements that for rust API-key lookup. Do not merge until aw-core#152 has a PyPI release and aw-server-rust#652 is in (maintainer order: #152 + release → #652 → #167 → #118 → #241).

Not in this PR

Verification

  • pytest tests/test_profile.py tests/test_profile_config.py tests/test_auth.py tests/test_requestqueue.py — 50 passed
  • mypy aw_client tests/test_profile.py tests/test_profile_config.py tests/test_auth.py — clean
  • ruff check on changed files — clean (pre-existing unused time in cli.py left alone)

--testing stays as an alias for --profile testing. The client exports
AW_PROFILE before loading config so aw-core dirs isolate the queue
file; persistqueue keeps the -testing suffix and named profiles get
the same shape. Local rust API-key lookup reads config-<profile>.toml
for named profiles so a research client does not inherit the default
instance's key.

The default profile unsets AW_PROFILE rather than setting it to
"default", because aw-core suffixes any non-empty value.

Part of ActivityWatch/activitywatch#1399.
@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds named profile selection to the Python client and CLI while preserving default and testing compatibility.

  • Resolves, validates, and exports profiles before loading configuration.
  • Selects profile-specific configuration, queue paths, and local Rust API keys.
  • Preserves explicit CLI port overrides and the original persistent queue path across disconnects.
  • Adds focused profile, authentication, CLI, and queue-path tests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the previously reported areas.

No blocking failure remains.

Important Files Changed

Filename Overview
aw_client/cli.py Adds --profile and correctly distinguishes an omitted port from an explicit --port 5600.
aw_client/client.py Resolves profile-aware configuration and authentication while preserving the queue’s original path during lifecycle recreation.
aw_client/config.py Adds profile-aware Rust API-key discovery with new and legacy testing-root fallback order.
aw_client/profile.py Centralizes profile validation, environment resolution, testing compatibility, and filename suffix generation.
tests/test_profile_config.py Covers constructor selection, queue isolation, CLI port behavior, and profile-specific authentication lookup.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[CLI flags or AW_PROFILE] --> B[Resolve and validate profile]
    B --> C[Export AW_PROFILE]
    C --> D[Load profile configuration]
    D --> E[Select host and port]
    D --> F[Select persistent queue path]
    E --> G[Discover matching local API key]
    E --> H[ActivityWatchClient]
    F --> H
    G --> H
Loading

Reviews (4): Last reviewed commit: "fix(profile): apply #1399 testing-root r..." | Re-trigger Greptile

Comment thread aw_client/cli.py Outdated
Do not pull tests/test_auth.py into make test — those tests assume
XDG_CONFIG_HOME, which platformdirs ignores on macOS/Windows. Patch
get_config_dir in the named-profile test instead.
Click defaulted --port to 5600, so an explicit --port 5600 was
indistinguishable from "unset" and got discarded. Default is now None
so profile config wins only when the flag is omitted.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI is all green now (including Windows). Addressing the two Greptile P1 findings:

Port override discarded (cli.py) — false positive on the reviewed commit. The --port option has default=None, not default=5600, so an explicit --port 5600 passes through as-is. The sentinel check Greptile saw was removed before the final push; test_explicit_port_5600_is_not_discarded covers this case and passes.

Queue profile changes on disconnect (client.py) — valid design note. AW_PROFILE is process-global; two differently-profiled clients in the same process would have this issue. That's a pre-existing constraint of the env-var approach used across aw-qt, aw-server-rust, and the aw-core dirs, not something introduced here. In practice, each process has one profile (the CLI, watchers, and test suites all operate that way). Documenting as a known limitation for library users who mix profiles in one process.

Ready for review when maintainers have bandwidth.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Correction to my previous note: the review exposed a real, cheap-to-fix reconnect invariant, so I fixed it in 9072c70. ActivityWatchClient.disconnect() now reopens the exact original persistqueue path instead of recomputing its root from mutable process-global AW_PROFILE; the regression test switches from a research client to a default client before reconnecting and proves the research path is preserved. Local verification: 43 queue/profile tests passed, mypy clean, and scoped ruff clean. CI is running on the new head.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Isolated profile roots use bare config.toml. Legacy testing keeps
config-testing.toml on the shared root. Named profiles no longer
read config-<profile>.toml. Warn when an unprovisioned named
profile falls back to [server]/5600, matching aw-server#167.

Part of ActivityWatch/activitywatch#1399.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Pushed 8f45af7 for the #1399 testing-root rule on rust API-key lookup.

Isolated profile roots now read bare config.toml (named profiles no longer look at config-<profile>.toml). Testing follows the same 3-rule as aw-core#152 / aw-server-rust#652: new root if activitywatch-testing/ exists, else legacy config-testing.toml on the shared root if those artifacts are present, else the new root. Lookup does not create directories. If python already created an empty activitywatch-testing/, the lookup still falls back to the legacy file so a --testing client finds the key rust actually wrote.

Unprovisioned named profiles now warn on the silent [server]/5600 fallback, matching aw-server#167.

50 profile/queue/auth tests passed locally; mypy + ruff clean. Do not merge until #152 has a PyPI release (maintainer order unchanged).

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

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.

1 participant