Skip to content

Build master @ bc50cd5bunreleased — 31 commits past v1.282.0Last reviewed Last changed Grade fresh (aging >90d, stale >180d)Source / History

Control actions table ​

A project's page in BoB Control offers bob commands as buttons, in three groups: Start work, Check and Ship. Every button comes from one declared table, scripts/dashboard/control-actions.json (#3920). The page runs nothing the table does not declare, and adding an action changes no page code.

Filescripts/dashboard/control-actions.json
Schemabob-control-actions/1, validated by schemas/control-actions.schema.json (make check-schemas)
Read bythe dashboard server (resolveActions in scripts/dashboard/control-project-page.js) and the run route (scripts/dashboard/control-action-run.js)
Guarded bymake check-control-actions (scripts/checks/check-control-actions.js)
Tested bytests/test-control-actions.sh

The document ​

json
{
  "schema": "bob-control-actions/1",
  "actions": [
    { "id": "plan-remaining", "group": "Check", "label": "What is left in the plan",
      "command": "bob plan remaining <N>", "mutates": false,
      "placeholders": { "N": { "label": "Flight plan", "pattern": "^[1-9][0-9]*$", "prefill": "flightplan" } } }
  ]
}
FieldRequiredMeaning
schemayesbob-control-actions/1.
descriptionnoA note on the file itself.
actions[]yesOne row per button, at least one. The page lists them in the order written, within their group.
actions[].idyesThe key a run request names: lowercase, digits and -, up to 41 characters. A request never carries the command. Unique in the table.
actions[].groupyesStart work, Check or Ship.
actions[].labelyesWhat the action does, in plain words, up to 60 characters.
actions[].commandyesThe command template: bob <noun> and what follows, on one line. Its noun and verb must be a row of scripts/bob/commands.json.
actions[].mutatesyestrue when the command changes something. See Commands that change something.
actions[].confirmnotrue makes every run ask first, its dry run too.
actions[].descriptionnoThe one line shown beside the action. Without it, the line is the command's tagline from commands.json.
actions[].placeholdersnoOne entry per <NAME> in the command. See Placeholders.

No other field is allowed, at either level.

The actions the page ships with ​

make check-control-actions requires each of these, under its group (#3920):

GroupCommands
Start workbob plan build, bob warp start, bob do "<question>", bob decompose <N>
Checkbob status, bob warp status, bob plan remaining <N>, bob groom --all, bob tour
Shipbob warp finalize, bob deploy (confirm), bob warp stop (confirm)

How to add an action ​

  1. Make sure the command is routed: it must be a row of scripts/bob/commands.json (bob <noun> --help answers for it).
  2. Add one row to actions[] in scripts/dashboard/control-actions.json, in its group and in the position it should appear. Give it an id, a group, a label and the command. Set mutates to whether it changes anything.
  3. For each <NAME> in the command, add a placeholders entry. Give it a pattern whenever the value has a known shape.
  4. Set confirm: true when even a dry run should ask first.
  5. Run make check-schemas check-control-actions. Both must pass.
  6. Run make test-control-actions. The page shows the new row on its next load; the server reads the table each time.

No page code changes. A row the server cannot resolve (its command is not routed) is left off the page, and the check fails on it first.

Commands that change something ​

mutates: true decides which button the row gets and when it runs:

The rowButtonWhat happens
mutates: falseRunRuns at once, in the project's terminal.
mutates: true, and the command has --dry-runDry runRuns with --dry-run. Run for real… then appears, and asks first.
mutates: true, no --dry-runRun…Shows the full command and runs it only after Run it.
confirm: trueDry run…Asks before the dry run too.
Any row with a placeholderFill in…Opens the form first; the rules above apply to the run it sends.

Whether a command has --dry-run is read from its flags in commands.json, not declared here. On the server, a mutating action runs without confirmed: true only as its dry run. Its real run, and every run of a confirm row, needs confirmed: true; without it the route answers 409 with the command it would have run.

Placeholders ​

A placeholder is a <NAME> in the command, or "<NAME>" when the value is one quoted argument. Each has an entry under placeholders:

FieldMeaning
labelThe field's label in the form.
patternA regular expression the value must match. Without it, any one line of up to 300 characters.
prefillWhat the page fills in from the project's state. flightplan is the open flight plan's number: the warp run's plan, else the first open plan.

The form has one field per placeholder, the command's own flags from commands.json with their descriptions, and a live preview of the command that will run. The server checks each value against its pattern, accepts only the command's own flags, and shell-quotes every value. Anything else is 400.

The run route ​

A button sends POST /control/project/run with { project, action, values?, flags?, dry_run?, confirmed?, machine? }, never the command. machine names the fleet machine the caller expects the project's terminal on:

RuleAnswer
A body with any other field400
An action the table does not declare404
A value that fails its pattern, or a flag that is not the command's own400
A real run of a mutating action, or any run of a confirm row, without confirmed: true409, with the command it would have run
A caller that is neither this machine with its token nor a machine machines.json declares, or a request arriving on an address other than loopback or the tailnet403
Run200. The command is typed into the project's terminal, and one line is added to $BOB_HOME/control-runs.jsonl (bob control runs prints it)

The command bar uses the same route with command_id in place of action (The command bar).

make check-control-actions ​

FindingMeans
unknown-commandAn action's command is not a row of scripts/bob/commands.json.
duplicate-idTwo actions share an id.
missing-actionOne of the actions the page ships with is not in the table, or not under its group.
unknown-declaredscripts/dashboard/control-commands.json (the command bar's reads and machine lists) names a command that is not routed.

Exit 0 is clean, 1 is a finding, and 2 means a file could not be read.

Other values Control reads ​

ValueDefined in
The stuck threshold: a session quiet for more than 60 minutes is stuck on the Now pageFleet Feed → the stuck threshold (STUCK_AFTER_MINUTES in scripts/dashboard/control-running.js)
An ask's unblocks field, and the order of the asksbob asks → What an ask unblocks (scripts/asks/unblocks.js)