@andrian.yablonskyy/thub-coordinator 1.1.10 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrian.yablonskyy/thub-coordinator",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "TestHub Coordinator — job queue, resource registry, scheduler, heartbeat monitor, log/artifact store and web dashboard",
5
5
  "main": "src/server.js",
6
6
  "engines": {
@@ -56,7 +56,7 @@
56
56
  }
57
57
 
58
58
  .resource-capabilities {
59
- font-size: 0.75rem;
59
+ font-size: 0.875rem;
60
60
  }
61
61
 
62
62
  /* An overlay inside a modal (e.g. "Import to config"'s review note on the
@@ -80,7 +80,7 @@
80
80
  }
81
81
 
82
82
  /* The client details modal has up to 7 tabs (Details, the config tabs,
83
- Connected USB devices) — tighter padding keeps them on one row, ⚠ icons
83
+ USB devices) — tighter padding keeps them on one row, ⚠ icons
84
84
  included, at the modal-lg width. */
85
85
  .thub-resource-card .nav-tabs .nav-link {
86
86
  padding: 0.45rem 0.65rem;
@@ -126,7 +126,7 @@
126
126
  pointer-events: auto;
127
127
  }
128
128
 
129
- /* Connected USB devices: the lsusb output takes the rest of the modal's
129
+ /* USB devices: the lsusb output takes the rest of the modal's
130
130
  height — what the tallest other tab sets, at least 12rem — and scrolls in
131
131
  it. Its 0 basis keeps it from making the modal taller. */
132
132
  .thub-resource-card .tab-content .tab-pane.thub-usb-pane {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file packages/coordinator/public/js/usb-import.js
3
- * @description Dashboard: "Import to config" on the resource card's Connected USB devices tab — updates the Config
3
+ * @description Dashboard: "Import to config" on the resource card's USB devices tab — updates the Config
4
4
  * sub-tabs' device lists from the last `lsusb -tvv`, opens the first changed one, and asks to verify
5
5
  *
6
6
  * @author Andrian Yablonskyy
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file packages/coordinator/public/js/usb-scan.js
3
- * @description Dashboard: the resource card's "Connected USB devices" tab — Refresh asks the Client for `lsusb`,
3
+ * @description Dashboard: the resource card's "USB devices" tab — Refresh asks the Client for `lsusb`,
4
4
  * then polls until its answer arrives and shows it in place
5
5
  *
6
6
  * @author Andrian Yablonskyy
@@ -1,4 +1,4 @@
1
- -- "Connected USB devices" (resource card tab, README §10): the Client runs
1
+ -- "USB devices" (resource card tab, README §10): the Client runs
2
2
  -- `lsusb` only when an admin presses Refresh — a scan-usb command with its
3
3
  -- next heartbeat — and posts the output back. `usb_scan` is the last result
4
4
  -- (JSON {output, error, at}); `usb_scan_requested_at`/`_request_id` the
@@ -625,7 +625,7 @@ function createRegistryService(db, { bus, events }){
625
625
  return db.prepare('SELECT * FROM resources WHERE remove_requested_at IS NOT NULL').all().map(rowToResource);
626
626
  }
627
627
 
628
- // "Connected USB devices" tab: Refresh asks the Client to run `lsusb` on
628
+ // "USB devices" tab: Refresh asks the Client to run `lsusb` on
629
629
  // its next heartbeat (a scan-usb command, delivered by the caller's bus
630
630
  // via commands). Only while it's connected — it would otherwise run
631
631
  // whenever it next reconnects.
package/src/web/routes.js CHANGED
@@ -616,7 +616,7 @@ function createWebRouter({ services, config }){
616
616
  res.redirect(returnTo(req, '/runners'));
617
617
  });
618
618
 
619
- // "Connected USB devices" tab (public/js/usb-scan.js): Refresh queues a
619
+ // "USB devices" tab (public/js/usb-scan.js): Refresh queues a
620
620
  // scan-usb for the Client's next heartbeat; the tab then polls the GET
621
621
  // until the answer is in. JSON both ways — the modal stays open.
622
622
  function usbScanView(req, r){
@@ -340,3 +340,21 @@ test('Labels tab: in effect at once, sent to the Client as its next config revis
340
340
  const old = register(services, 'lab-old-30', { configFile: null });
341
341
  assert.throws(() => services.registry.setClientLabels(old, ['x'], { by: 'x' }), /too old/);
342
342
  });
343
+
344
+ test('runner card: capabilities have their own tab, next to Details', async (t) => {
345
+ const { base, services, admin } = await start(t),
346
+ { resourceId } = services.registry.registerAuto({
347
+ clientId: 'hw8', name: 'lab-hw-08', type: 'hw', labels: [],
348
+ capabilities: { hw: { stlinks: [{ index: 1, path: '/dev/thub/dut1-stlink', present: false }], uarts: [], usbs: [] } }
349
+ }),
350
+ html = await (await fetch(`${base}/runners`, { headers: { cookie: admin } })).text(),
351
+ card = html.slice(html.indexOf(`id="resource-card-${resourceId}"`)),
352
+ details = card.slice(card.indexOf(`id="rc-${resourceId}-details"`), card.indexOf(`id="rc-${resourceId}-caps"`)),
353
+ caps = card.slice(card.indexOf(`id="rc-${resourceId}-caps"`)),
354
+ // The tab right after Details.
355
+ tabs = [...card.slice(0, card.indexOf('</ul>')).matchAll(/<button class="nav-link[^"]*" id="rc-[^"]*-(\w+)-tab"/g)].map((m) => m[1]);
356
+ assert.deepEqual(tabs.slice(0, 2), ['details', 'caps']);
357
+ assert.doesNotMatch(details, />Capabilities</); // no longer a Details row
358
+ assert.match(caps, new RegExp(`^id="rc-${resourceId}-caps"[^>]*data-live="rc-caps-${resourceId}"`)); // updates live
359
+ assert.match(caps, /\/dev\/thub\/dut1-stlink<\/code><span class="badge text-bg-danger[^"]*"[^>]*>missing/);
360
+ });
@@ -40,14 +40,14 @@
40
40
  lsusb -t # USB tree
41
41
  udevadm info -a -n /dev/ttyUSB0 | grep -m1 'ATTRS{devpath}'
42
42
  st-info --probe # ST-Link serials
43
- | Or open the runner card → #[strong Connected USB devices] → #[strong Refresh], then #[strong Import to config].
43
+ | Or open the runner card → #[strong USB devices] → #[strong Refresh], then #[strong Import to config].
44
44
  li
45
45
  | Put the devpaths into #[code hw-devices] and restart. On every start the Client writes
46
46
  | #[code /etc/udev/rules.d/99-thub-&lt;instance&gt;.rules], so the devices appear as #[code /dev/thub/dut&lt;N&gt;-stlink|uart|usb]:
47
47
  +code('HW machine').
48
48
  sudo systemctl restart thub-client@client
49
49
  ls -l /dev/thub/
50
- li Check the runner card's #[strong Capabilities]: each configured device must be present (a missing one is flagged).
50
+ li Check the runner card's #[strong Capabilities] tab: each configured device must be present (a missing one is flagged).
51
51
  li Your job's #[code --command] flashes the board. The Client never flashes by itself; it passes the ST-Link serials and the downloaded files in the environment (#[a(href="#env") Environment variables]).
52
52
  +code('A typical HW job').
53
53
  thub run --type hw --label board:nucleo-f401re \
@@ -103,7 +103,7 @@
103
103
  SW Clients have no settings of their own: they run whatever DUT image a job brings (#[code --docker-image]).
104
104
  Admins can also edit an HW Client's devices from its runner card (#[strong ST-Link | UART | DUT USB]
105
105
  tabs). The tabs show the config the Client runs, as it reported it at its last start, or a saved
106
- change it hasn't applied yet. #[strong Connected USB devices → Import to config] fills them from the host's #[code lsusb].
106
+ change it hasn't applied yet. #[strong USB devices → Import to config] fills them from the host's #[code lsusb].
107
107
  The card's #[strong Export] and #[strong Import] icons download a Client's config file (never its
108
108
  #[code joinKey]) and apply one to it. An older HW Client takes only the file's #[code hw-devices].
109
109
  There's no ST-Link serial field: the job's #[code --command] picks the probe
@@ -121,4 +121,4 @@
121
121
  td Files written to #[code /work] stay owned by the Client user, so the workspace can be cleaned up.
122
122
  +note('info').
123
123
  Docker access is effectively root access on that host. The Client host must have Docker and the service user must be in the
124
- #[code docker] group. The runner card's #[strong Capabilities] show whether a Client can use Docker, and why not.
124
+ #[code docker] group. The runner card's #[strong Capabilities] tab shows whether a Client can use Docker, and why not.
@@ -298,8 +298,9 @@ mixin resourceCard(r, groupsById, returnTo)
298
298
  //- data-config-root: where public/js/client-config.js tracks unsaved
299
299
  //- changes (⚠ on the config tabs).
300
300
  .modal-body.pt-2(data-config-root)
301
- //- Details: what the Client is and reports. Then its config tabs
302
- //- (client-config.pug; read-only for viewers), then its USB devices.
301
+ //- Details: what the Client is and reports; Capabilities: what it can
302
+ //- drive. Then its config tabs (client-config.pug; read-only for
303
+ //- viewers), then its USB devices.
303
304
  ul.nav.nav-tabs.mb-3(role="tablist")
304
305
  li.nav-item(role="presentation")
305
306
  button.nav-link.active(
@@ -311,6 +312,16 @@ mixin resourceCard(r, groupsById, returnTo)
311
312
  aria-controls=`rc-${r.id}-details`
312
313
  aria-selected="true"
313
314
  ) Details
315
+ li.nav-item(role="presentation")
316
+ button.nav-link(
317
+ id=`rc-${r.id}-caps-tab`
318
+ type="button"
319
+ role="tab"
320
+ data-bs-toggle="tab"
321
+ data-bs-target=`#rc-${r.id}-caps`
322
+ aria-controls=`rc-${r.id}-caps`
323
+ aria-selected="false"
324
+ ) Capabilities
314
325
  +configTabItems(r)
315
326
  li.nav-item(role="presentation")
316
327
  button.nav-link(
@@ -321,16 +332,21 @@ mixin resourceCard(r, groupsById, returnTo)
321
332
  data-bs-target=`#rc-${r.id}-usb`
322
333
  aria-controls=`rc-${r.id}-usb`
323
334
  aria-selected="false"
324
- ) Connected USB devices
335
+ ) USB devices
325
336
 
326
337
  .tab-content
327
338
  .tab-pane.fade.show.active(id=`rc-${r.id}-details` role="tabpanel" aria-labelledby=`rc-${r.id}-details-tab` tabindex="0" data-live=`rc-details-${r.id}`)
328
339
  +resourceDetails(r, groupsById, returnTo, local, external)
340
+ //- Live too: a Client that restarts reports them again.
341
+ .tab-pane.fade(id=`rc-${r.id}-caps` role="tabpanel" aria-labelledby=`rc-${r.id}-caps-tab` tabindex="0" data-live=`rc-caps-${r.id}`)
342
+ p.small.text-body-secondary.mb-2 What the Client reported it can drive at its last start, checked against its host.
343
+ .resource-capabilities
344
+ +capabilitiesList(r.capabilities)
329
345
  +configPanes(r, returnTo, groupsById)
330
346
  .tab-pane.fade.thub-usb-pane(id=`rc-${r.id}-usb` role="tabpanel" aria-labelledby=`rc-${r.id}-usb-tab` tabindex="0")
331
347
  +usbScanPane(r)
332
348
 
333
- //- "Connected USB devices" (resource card tab): the Client's `lsusb`, run
349
+ //- "USB devices" (resource card tab): the Client's `lsusb`, run
334
350
  //- only on Refresh (admins; public/js/usb-scan.js fetches and fills it in).
335
351
  mixin usbScanPane(r)
336
352
  - const offline = r.status === 'OUT_OF_SERVICE' || !r.last_heartbeat_at
@@ -496,9 +512,6 @@ mixin resourceDetails(r, groupsById, returnTo, local, external)
496
512
  data-bs-toggle="tooltip"
497
513
  data-bs-title="The address the Coordinator last saw this Client connect from"
498
514
  ) external
499
- dt.col-sm-4 Capabilities
500
- dd.col-sm-8.resource-capabilities
501
- +capabilitiesList(r.capabilities)
502
515
  dt.col-sm-4 Labels
503
516
  dd.col-sm-8
504
517
  if r.labels.length