Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions apps/usage-collector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ Then point a client at it without a release:
LOOPX_USAGE_PING_ENDPOINT=https://<worker-host>/v0/ping loopx usage-ping
```

Clients send by default only after `DEFAULT_ENDPOINT` in
`loopx/usage_ping.py` is set to the deployed URL and released.
The project collector is deployed at
`https://loopx-usage-collector.huangrt01.workers.dev`; its public statistics are
available at [`/v0/stats`](https://loopx-usage-collector.huangrt01.workers.dev/v0/stats).
This source version uses its `/v0/ping` endpoint by default after explicit
machine opt-in. Older builds with an empty `DEFAULT_ENDPOINT` can use the
environment override above. A deployment alone never enables a machine.

## Limits

Expand Down
13 changes: 8 additions & 5 deletions docs/reference/usage-ping.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ check it.
until the next day.
- `loopx usage-ping` itself never triggers a send.

The endpoint is `LOOPX_USAGE_PING_ENDPOINT` if set, otherwise the release
default. It must be `https://` (plain `http://` is accepted only for
loopback addresses, for local testing). Until the project collector is
deployed the release default is empty, so enabling records consent but sends
nothing; `loopx usage-ping` shows `sending: false` in that case.
The default endpoint in this source version is
`https://loopx-usage-collector.huangrt01.workers.dev/v0/ping`.
`LOOPX_USAGE_PING_ENDPOINT` overrides it with another `https://` collector
(plain `http://` is accepted only for loopback addresses, for local testing).
Endpoint availability does not grant consent: machines remain off until
`enable`. Older builds with an empty default can use this environment variable
without waiting for a release. A network that cannot reach `workers.dev` will
not send successfully; the command still completes normally.

## Switches

Expand Down
11 changes: 7 additions & 4 deletions docs/reference/usage-ping.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ LoopX 不读取也不发送项目名、goal / todo 内容、路径、主机名
执行,超时 3 秒,不会拖慢命令或让命令失败;失败当天不重试。
- `loopx usage-ping` 本身永远不会触发发送。

地址优先取 `LOOPX_USAGE_PING_ENDPOINT`,否则用发布版默认值。必须是
`https://`(明文 `http://` 仅允许回环地址,用于本地测试)。项目 collector
部署之前,发布版默认值为空,因此开启只会记录授权、不会发送;此时
`loopx usage-ping` 显示 `sending: false`。
当前源码版本的默认地址是
`https://loopx-usage-collector.huangrt01.workers.dev/v0/ping`。
`LOOPX_USAGE_PING_ENDPOINT` 可覆盖为另一个 `https://` collector
(明文 `http://` 仅允许回环地址,用于本地测试)。地址可用不代表授权:
机器仍需明确 `enable` 才会发送。默认地址为空的旧版本可通过此环境变量
直接使用服务,无需等待发布。无法访问 `workers.dev` 的网络会发送失败,
但不会影响命令正常完成。

## 开关

Expand Down
5 changes: 2 additions & 3 deletions loopx/usage_ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
PAYLOAD_SCHEMA = "loopx_usage_ping_v0"
STATE_SCHEMA = "loopx_usage_ping_state_v0"
STATE_FILENAME = "usage-ping.json"
# Empty until the project collector (apps/usage-collector) is deployed; while it
# is empty an enabled ping records consent but sends nothing.
DEFAULT_ENDPOINT = ""
# Project collector; configuring an endpoint never grants machine consent.
DEFAULT_ENDPOINT = "https://loopx-usage-collector.huangrt01.workers.dev/v0/ping"
ENDPOINT_ENV = "LOOPX_USAGE_PING_ENDPOINT"
SWITCH_ENV = "LOOPX_USAGE_PING"
REQUEST_TIMEOUT_SECONDS = 3.0
Expand Down
18 changes: 11 additions & 7 deletions tests/test_usage_ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ def __call__(self, argv, **kwargs):
self.calls.append(argv)


def test_undecided_machine_never_schedules_or_creates_state(tmp_path):
@pytest.mark.parametrize("env", [{}, ENDPOINT])
def test_undecided_machine_never_schedules_or_creates_state(tmp_path, env):
spawn = _Spawn()
assert usage_ping.maybe_schedule(["status"], env=ENDPOINT, runtime_root=tmp_path, spawn=spawn) is False
assert usage_ping.maybe_schedule(["status"], env=env, runtime_root=tmp_path, spawn=spawn) is False
assert spawn.calls == []
assert not usage_ping.state_path(tmp_path).exists()
assert usage_ping.status(usage_ping.state_path(tmp_path), env=ENDPOINT)["consent"] == "undecided"
assert usage_ping.status(usage_ping.state_path(tmp_path), env=env)["consent"] == "undecided"


def test_enable_generates_random_id_and_disable_forgets_it(tmp_path):
Expand Down Expand Up @@ -85,7 +86,8 @@ def test_endpoint_must_be_https_or_loopback():
assert usage_ping.resolve_endpoint(ENDPOINT) == ENDPOINT["LOOPX_USAGE_PING_ENDPOINT"]


def test_without_a_configured_collector_consent_is_recorded_but_nothing_is_scheduled(tmp_path):
def test_without_a_configured_collector_consent_is_recorded_but_nothing_is_scheduled(tmp_path, monkeypatch):
monkeypatch.setattr(usage_ping, "DEFAULT_ENDPOINT", "")
usage_ping.enable(usage_ping.state_path(tmp_path), now=DAY1)
spawn = _Spawn()
assert usage_ping.maybe_schedule(["status"], env={}, runtime_root=tmp_path, spawn=spawn) is False
Expand Down Expand Up @@ -161,13 +163,15 @@ def log_message(self, *args):
assert received[0]["body"] == usage_ping.build_payload(usage_ping.load_state(path))


def test_cli_enable_status_disable(tmp_path, monkeypatch, capsys):
def test_cli_opt_in_reports_deployed_default_and_disable(tmp_path, monkeypatch, capsys):
monkeypatch.setattr(usage_ping, "DEFAULT_RUNTIME_ROOT", tmp_path)
monkeypatch.delenv("LOOPX_USAGE_PING_ENDPOINT", raising=False)
for key in ("LOOPX_USAGE_PING_ENDPOINT", "CI", "DO_NOT_TRACK", "LOOPX_USAGE_PING"):
monkeypatch.delenv(key, raising=False)
assert cli_main(["usage-ping", "enable", "--format", "json"]) == 0
enabled = json.loads(capsys.readouterr().out)
assert enabled["consent"] == "enabled"
assert enabled["sending"] is False # no collector configured in this build
assert enabled["sending"] is True
assert enabled["endpoint"] == usage_ping.DEFAULT_ENDPOINT
assert enabled["next_payload"]["install_id"]
assert cli_main(["usage-ping", "disable"]) == 0
assert "LoopX usage ping: disabled" in capsys.readouterr().out
Expand Down
Loading