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
71 changes: 70 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,55 @@ itself is `"schema": 1` and stays there until something breaks compatibility,
independently of the version below.

Since 0.5.0 that is enforced rather than asserted: `VERSION` at the repository
root is the source, `scripts/version.mjs` writes it into all eleven places, and
root is the source, `scripts/version.mjs` writes it into all thirteen places, and
a CI job fails when they disagree.

## [Unreleased]

### Added

- **A pull request's build no longer becomes the nightly's `latest`.**
`ci-upload` always tagged an upload with its app, and `ci-enqueue` did
whenever the spec named one, so the next nightly asking for `latest` tested
an unmerged branch. An upload can now carry `x-artifact-channel: pr`, which
`"sha256": "latest"` never resolves to; both scripts send it when
`GITHUB_EVENT_NAME` is `pull_request` (or with `--channel pr`). The pull
request's own job still names its build by hash. Builds uploaded before this
count as merged builds, and the same bytes republished from main come off the
channel. The `/api/v1` artifact document does not show the channel yet;
`GET /api/artifacts` does.
- **`FLEET_PEER_TOKENS`: the brain switcher can read a peer with tokens on.**
The peers proxy sent no token, so a peer running `FLEET_AUTH=tokens`
answered 401 through it. Each peer's URL now maps to a `read` token that
peer issued, sent only to that peer.

### Changed

- **`ci-enqueue` speaks `/api/v1` and exits like `fleetr run`.** It polled the
deprecated `GET /jobs/:id` and exited 0 on `done`, which includes a job that
skipped and tested nothing, and it sent no token, so it failed against a
collector with tokens on. It now enqueues with `POST /api/v1/jobs`, waits
with `/wait`, and exits 0 passed, 1 failed, 2 invalid spec, 3 still running,
4 skipped, 5 refused or unreachable. A workflow that read exit 1 as "timed
out" now sees 3.
- **`ci-enqueue` and `ci-upload` send `FLEET_RUNNER_TOKEN` (or
`FLEET_TOKEN`) as a bearer token.** `ci-upload` read only
`FLEET_DASH_TOKEN`, even when the collector needed an `agent` token there.
`FLEET_DASH_TOKEN` is still sent as `x-fleet-token`. `ci-upload` exits 5
rather than 1 when the upload is refused or the collector is unreachable.
- **The example jobs are checked the way `/api/v1` checks a spec.**
`check-examples.ts` used the loose `job.schema.json`, so an example could
pass the check and be refused by the API it documents. It now runs the API's
strict schema and each workload's params manifest, imported rather than
copied. All 37 pass.
- **Explore's `CHECK_NAMES` lists only checks something raises.** `regression`,
`agent_stuck` and `leash` were accepted by `POST /findings` and raised by
nothing; they are refused now. A refused action is on the mission's steps,
not a finding. Stored findings are not touched, and reading does not
validate the name.
- **The desktop app is versioned with everything else.** `scripts/version.mjs`
now writes `desktop/src-tauri/Cargo.toml` and `tauri.conf.json` (they said
0.5.0), so the CI version check covers them. Both say 0.8.0.
- **The explore docs' three diagrams are drawn, not generated.** The night,
one mission's sequence and the code map were mermaid blocks; they are now
figures in `docs/figures/` rendered to PNG like the rest
Expand All @@ -32,6 +74,33 @@ a CI job fails when they disagree.

### Fixed

- **The older job routes skipped two auth checks.** With `FLEET_AUTH=tokens`,
`POST /api/jobs/:id/cancel` and `/retry` let any `run` token stop or re-run
anyone's job, and `POST /jobs` and `POST /api/jobs` accepted the `shell`
workload from a token without `admin`. Only `/api/v1` asked. Both paths now
call the same two functions (`collector/src/api/policy.ts`). With auth off
nothing changes.
- **`fleet-cli`'s `bin` pointed at a file the build does not produce.** It
named `dist/cli.mjs`; the build writes `dist/bin/fleet.mjs`, and `dist/` was
left out of the package by `.gitignore`, so installing from a tarball gave no
command. Both `fleet` and `fleetr` now point at the bundle, and `files`
includes `dist/`.
- **A mistyped timer interval became a near-zero one.** `FLEET_SWEEP_MS`,
`FLEET_SCHEDULER_TICK_MS` and `FLEET_ALERT_TICK_MS` went through a bare
`Number()`, so `15s` was NaN and `15` (seconds, meant) a sweep every 15 ms.
Anything but whole milliseconds of at least 1000 now falls back to the
default, with a startup warning.
- **Explore's live-view frames were refused with `FLEET_AUTH_PROTOCOL=token`.**
They sent only `FLEET_DASH_TOKEN`; they now also send `FLEET_TOKEN` as a
bearer token.
- **A schedule template's empty `job_id` was replaced without a word.** A
non-empty one was already refused; an empty or `null` one was stored and
overwritten at every firing. It is dropped now and the reply carries a
`warnings` entry, and a stored template that still has one is named in the
log when it fires.
- Two comments said `fleet up` turns mDNS discovery on. It passes
`collector.discovery`, which is off by default.

- **`fleet skill` failed on an installed 0.8.0.** The 0.8.0 archives carry
`skills/`, but `install.sh` and `install.ps1` copy a release's directories
by name and did not name it, so `fleet skill show` and `fleet skill install`
Expand Down
5 changes: 2 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,8 @@ the others.
One version covers every component, so a release is one number everywhere.

1. On a branch from `develop`, run `node scripts/version.mjs set X.Y.Z`. It
writes `VERSION` and every component's own version except the desktop
app's (`desktop/src-tauri/Cargo.toml` and `tauri.conf.json` are not among
the places it writes, and still say 0.5.0). Then run
writes `VERSION` and every component's own version, the desktop app's
`Cargo.toml` and `tauri.conf.json` included. Then run
`npm --prefix collector run gen:api`, because `openapi.json` carries the
version and CI fails when it is stale. Android's `versionCode` is not
derived from it; bump that by hand if the Play Store build needs it.
Expand Down
25 changes: 23 additions & 2 deletions collector/ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,32 @@ published for an app, its nightly is **skipped rather than failed** — a job
pinned to a build that does not exist would fail in a way indistinguishable
from the app being broken.

**A pull request's build never moves `latest`.** An upload with
`x-artifact-channel: pr` is stored on the `pr` channel, and `latest` only
considers builds without it, so the nightly keeps testing what was merged.
`ci-upload` and `ci-enqueue` send the header themselves when
`GITHUB_EVENT_NAME` is `pull_request` (or `pull_request_target`); `--channel
pr|main` says it explicitly. The pull request's own job names its build by
hash, so it still tests exactly that build. Bytes republished from main come
off the channel; builds from before the channel existed count as merged.

## 2. Test a pull request — `example-workflow.yml`

Enqueue a job against real hardware and block the PR on the verdict.
`ci-enqueue` uploads the build, enqueues, polls to a verdict, and exits 0/1, so
the check fails on a red suite whether or not commit statuses are armed.
`ci-enqueue` uploads the build, enqueues through `/api/v1`, waits for the
verdict, and exits like `fleetr run`: 0 passed, 1 failed, 2 invalid spec,
3 still running at `--timeout`, 4 skipped (nothing ran), 5 refused or
unreachable. So the check fails on a red or skipped suite whether or not
commit statuses are armed.

## Tokens

With `FLEET_AUTH=tokens` on the collector, put a token in a secret and pass it
as `FLEET_RUNNER_TOKEN`: both scripts send it as `Authorization: Bearer`.
`ci-enqueue` needs `run`. The upload is the runner protocol, which is open
unless the collector also sets `FLEET_AUTH_PROTOCOL=token`; then both scripts'
uploads need `agent` as well. `FLEET_DASH_TOKEN` is still sent as
`x-fleet-token`, for a collector guarded only by that.

## Commit statuses are built and dark

Expand Down
10 changes: 10 additions & 0 deletions collector/ci/example-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
# this goes into an app repo (e.g. your-app-android). Until then this file
# documents the contract and nothing reads it.
#
# ci-enqueue exits like `fleetr run`: 0 passed, 1 failed, 2 invalid spec,
# 3 still running at --timeout, 4 skipped (nothing ran), 5 refused/unreachable.
# Any non-zero exit fails the check, so a skip is not a green check.
#
# Requirements at connect time, none of which exist today by design:
# - the runner host joined to the tailnet, FLEET_COLLECTOR_URL secret set
# - FLEET_GITHUB_STATUS=1 + FLEET_GITHUB_TOKEN on the collector, so the
Expand All @@ -23,6 +27,12 @@ jobs:
run: ./gradlew :app:assembleDebug

- name: Real-device smoke via fleet
env:
# A run token (plus agent if the collector sets FLEET_AUTH_PROTOCOL=token),
# needed only when the collector runs with FLEET_AUTH=tokens.
FLEET_RUNNER_TOKEN: ${{ secrets.FLEET_RUNNER_TOKEN }}
# GITHUB_EVENT_NAME is pull_request here, so the build is uploaded on
# the pr channel and never becomes the nightly's "latest".
run: |
cat > fleet-job.json <<EOF
{
Expand Down
4 changes: 4 additions & 0 deletions collector/ci/publish-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ jobs:
run: ./gradlew :app:assembleDebug

- name: Publish to the fleet
env:
# Only needed when the collector sets FLEET_AUTH_PROTOCOL=token; then
# the token needs the agent scope. Empty is fine otherwise.
FLEET_RUNNER_TOKEN: ${{ secrets.FLEET_RUNNER_TOKEN }}
run: |
npx --yes tsx scripts/ci-upload.ts \
--collector "${{ secrets.FLEET_COLLECTOR_URL }}" \
Expand Down
Loading
Loading