@andrian.yablonskyy/thub-agent 1.1.1 → 1.1.2
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/README.md +3 -10
- package/package.json +2 -2
- package/src/cli.js +12 -13
- package/src/config.js +4 -12
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npx -y @andrian.yablonskyy/thub-agent run --type sw --download-file "$IMAGE_URL"
|
|
|
14
14
|
|
|
15
15
|
## Configuration
|
|
16
16
|
|
|
17
|
-
Read from flags (`--url`, `--key`), then environment (`THUB_URL`, `THUB_KEY`, `
|
|
17
|
+
Read from flags (`--url`, `--key`), then environment (`THUB_URL`, `THUB_KEY`, `THUB_USER`; the old names `--token`/`THUB_TOKEN` still work), then `~/.config/thub/agent.json`, then a bundled default. `url`/`token` are required by the time a command actually talks to the Coordinator; `group`/`user` are optional everywhere.
|
|
18
18
|
|
|
19
19
|
`npm install -g` creates `~/.config/thub/agent.json` for you (blank `url`/`token`, so nothing works until you set them) if it doesn't already exist — a re-install never overwrites it. Fill it in with `thub config set`:
|
|
20
20
|
|
|
@@ -24,7 +24,6 @@ thub config set key thk_... # your access key: from an admin (Users), or y
|
|
|
24
24
|
thub whoami # jane (user) <jane@example.com>
|
|
25
25
|
thub key show # its last characters, created, last used
|
|
26
26
|
thub key rotate # a new key; the old one stops at once (saved here if it came from here)
|
|
27
|
-
thub config set group 548ae4ae-... # optional default --group
|
|
28
27
|
thub config set user "Your Name" # optional default --user
|
|
29
28
|
```
|
|
30
29
|
|
|
@@ -36,7 +35,7 @@ thub status <jobId> Show status; follow log if running, verdict, test
|
|
|
36
35
|
thub cancel <jobId> Cancel a job
|
|
37
36
|
thub resources List resources and their status
|
|
38
37
|
thub jobs [--mine] [--state <s>] List recent jobs (a cli token: only its own; a ci token: all, or its own with --mine)
|
|
39
|
-
thub config set <key> <value> Save coordinator URL /
|
|
38
|
+
thub config set <key> <value> Save coordinator URL / key / default user locally
|
|
40
39
|
thub check-update Compare this Agent with the latest published version
|
|
41
40
|
thub self-update [--to <x.y.z>] Update this Agent with npm i -g
|
|
42
41
|
thub --version
|
|
@@ -50,7 +49,6 @@ Key options for `thub run`. **On the Client** names the environment variable the
|
|
|
50
49
|
|---|---|---|
|
|
51
50
|
| `--type hw\|sw` | Required resource type. | `JOB_TYPE` |
|
|
52
51
|
| `--board <name>` / `--label <l>` | Required labels (repeatable). | `JOB_BOARD`; `JOB_LABEL`, `JOB_LABEL_<n>` |
|
|
53
|
-
| `--group <groupId>` | Restrict scheduling to resources that are members of this group. Falls back to `THUB_GROUP` / `thub config set group <id>`. | `JOB_GROUP` |
|
|
54
52
|
| `--client <name\|id>` | Run on this specific Client (resource name or id) only; the job waits in that Client's queue even if other matching resources are idle. | `JOB_CLIENT` |
|
|
55
53
|
| `--user <name>` | Free-text job owner — a label, not an identity. Falls back to `THUB_USER` / `thub config set user <name>`. | `JOB_USER` |
|
|
56
54
|
| `--command <string>` | **Required.** The task's entry point: a shell command the Client runs (`sh -c`) in the task's work directory — the `--git-repo` checkout, else an empty directory — after preparing its inputs. Its exit code is the verdict. On HW it flashes the board itself (the Client doesn't); it gets `THUB_DUT_STLINK`/`_UART`/`_USB`/`_HOST`/`_CONTAINER`, `THUB_DOWNLOAD_<n>`, `THUB_DOWNLOADS_DIR`, `THUB_GIT_COMMIT`, `THUB_SUITE`, `THUB_META_*`. | `JOB_COMMAND` |
|
|
@@ -122,12 +120,7 @@ thub run --type sw --download-file https://does-not-exist.invalid/app.bin \
|
|
|
122
120
|
--command ./ci/test.sh --dry-run --wait
|
|
123
121
|
```
|
|
124
122
|
|
|
125
|
-
**Run
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
thub run --type sw --git-repo "$TESTS_REPO" --command ./ci/test.sh \
|
|
129
|
-
--group 548ae4ae-ac5b-401f-acaa-24bbe790e62d --wait
|
|
130
|
-
```
|
|
123
|
+
**Run in a resource group:** there's no `--group` option. An admin or maintainer gives your user (or a CI token) one group on the dashboard, and every job it submits runs only on that group's resources; `thub whoami` shows it.
|
|
131
124
|
|
|
132
125
|
**Label a job with its owner** — purely informational, shows up on the dashboard, in `thub jobs`, and on the Client's own console:
|
|
133
126
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrian.yablonskyy/thub-agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "TestHub Agent CLI — the single entry point CI/CD and developers use to submit and follow test jobs",
|
|
5
5
|
"bin": {
|
|
6
6
|
"thub": "./src/cli.js"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"lint:fix": "eslint . --fix"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@andrian.yablonskyy/thub-common": "^1.1.
|
|
15
|
+
"@andrian.yablonskyy/thub-common": "^1.1.2",
|
|
16
16
|
"commander": "^13.1.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
package/src/cli.js
CHANGED
|
@@ -21,7 +21,7 @@ const { Command, Option } = require('commander'),
|
|
|
21
21
|
splitArgs,
|
|
22
22
|
parseEnvList
|
|
23
23
|
} = require('@andrian.yablonskyy/thub-common'),
|
|
24
|
-
{ resolveConnection,
|
|
24
|
+
{ resolveConnection, resolveUser, writeConfigFile, readConfigFile, CONFIG_PATH } = require('./config'),
|
|
25
25
|
{ parseDurationSec } = require('./duration'),
|
|
26
26
|
{ followJob } = require('./streaming'),
|
|
27
27
|
{ applyRequestedUpdate, installAgent, version } = require('./self-update');
|
|
@@ -140,11 +140,6 @@ program
|
|
|
140
140
|
.requiredOption('--type <hw|sw>', 'Required resource type')
|
|
141
141
|
.option('--board <name>', 'Shorthand for --label board:<name>')
|
|
142
142
|
.option('--label <label>', 'Required label the resource must have (repeatable)', collectRepeatable, [])
|
|
143
|
-
.option(
|
|
144
|
-
'--group <groupId>',
|
|
145
|
-
'Restrict scheduling to resources that are members of this group (§13.1). ' +
|
|
146
|
-
'Overrides THUB_GROUP / config file; leave unset for an unconstrained run.'
|
|
147
|
-
)
|
|
148
143
|
.option(
|
|
149
144
|
'--client <nameOrId>',
|
|
150
145
|
'Run on this specific Client (resource name or id) only; the job waits in that Client\'s queue ' +
|
|
@@ -218,11 +213,11 @@ program
|
|
|
218
213
|
c = client(),
|
|
219
214
|
labels = [...(opts.board ? [`board:${opts.board}`] : []), ...opts.label],
|
|
220
215
|
meta = Object.fromEntries(opts.meta.map((kv) => kv.split(/=(.*)/s).slice(0, 2))),
|
|
221
|
-
group = resolveGroup({ group: opts.group }),
|
|
222
216
|
user = resolveUser({ user: opts.user }),
|
|
223
217
|
|
|
224
218
|
spec = {
|
|
225
|
-
|
|
219
|
+
// No group: the Coordinator uses this key's, set on the dashboard (§13.1).
|
|
220
|
+
target: { type: opts.type, labels, ...(opts.client ? { client: opts.client } : {}) },
|
|
226
221
|
...task,
|
|
227
222
|
timeoutSec: parseDurationSec(opts.timeout),
|
|
228
223
|
...(opts.priority !== undefined ? { priority: opts.priority } : {}),
|
|
@@ -375,13 +370,15 @@ function formatBytes(bytes){
|
|
|
375
370
|
const config = program.command('config').description('Manage local Agent configuration');
|
|
376
371
|
config
|
|
377
372
|
.command('set')
|
|
378
|
-
.argument('<name>', 'url | key |
|
|
373
|
+
.argument('<name>', 'url | key | user')
|
|
379
374
|
.argument('<value>')
|
|
380
375
|
.action((name, value) => {
|
|
381
376
|
// `token` is the old name of `key`.
|
|
382
377
|
const setting = name === 'token' ? 'key' : name;
|
|
383
|
-
if (!['url', 'key', '
|
|
384
|
-
console.error(
|
|
378
|
+
if (!['url', 'key', 'user'].includes(setting)){
|
|
379
|
+
console.error(setting === 'group'
|
|
380
|
+
? 'Error: a job\'s group is set on the dashboard now (Users / CI tokens), not in the Agent'
|
|
381
|
+
: 'Error: the setting must be "url", "key", or "user"');
|
|
385
382
|
process.exit(EXIT_CODES.USAGE);
|
|
386
383
|
}
|
|
387
384
|
const { token: _old, ...current } = readConfigFile();
|
|
@@ -391,8 +388,10 @@ config
|
|
|
391
388
|
|
|
392
389
|
// Who this access key belongs to (§10.3).
|
|
393
390
|
function describeMe(me){
|
|
394
|
-
const role = me.user?.role ? me.user.role.charAt(0).toUpperCase() + me.user.role.slice(1) : ''
|
|
395
|
-
|
|
391
|
+
const role = me.user?.role ? me.user.role.charAt(0).toUpperCase() + me.user.role.slice(1) : '',
|
|
392
|
+
who = me.user ? `${me.user.username} (${role})${me.user.email ? ` <${me.user.email}>` : ''}` : `CI token "${me.name}"`;
|
|
393
|
+
// Older Coordinators don't say: then nothing about groups.
|
|
394
|
+
return me.group === undefined ? who : `${who}\nJobs run in: ${me.group ? `group ${me.group}` : 'any resource (no group)'}`;
|
|
396
395
|
}
|
|
397
396
|
|
|
398
397
|
program
|
package/src/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file packages/agent/src/config.js
|
|
3
|
-
* @description Agent config resolution: flags > env > config file > bundled default (url/token/
|
|
3
|
+
* @description Agent config resolution: flags > env > config file > bundled default (url/token/user)
|
|
4
4
|
*
|
|
5
5
|
* @author Andrian Yablonskyy
|
|
6
6
|
* @copyright Copyright (c) 2026 Andrian Yablonskyy. All rights reserved.
|
|
@@ -65,16 +65,8 @@ function resolveConnection(flags = {}){
|
|
|
65
65
|
return { url, token, keySource };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// Optional default for `--
|
|
69
|
-
//
|
|
70
|
-
// by group, same as if `groups` had never been used at all.
|
|
71
|
-
function resolveGroup(flags = {}){
|
|
72
|
-
const file = { ...readJsonFile(PACKAGE_DEFAULT_CONFIG_PATH), ...readConfigFile() };
|
|
73
|
-
return flags.group || process.env.THUB_GROUP || file.group || undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Optional default for `--user` (§4.3/§7.1) — same flags > env > file
|
|
77
|
-
// precedence as group. Purely a label (who submitted this job), not an
|
|
68
|
+
// Optional default for `--user` (§4.3/§7.1) — flags > env > file, like
|
|
69
|
+
// url/token. Purely a label (who submitted this job), not an
|
|
78
70
|
// identity: unset is fine, and nothing on the Coordinator side enforces
|
|
79
71
|
// or authenticates it.
|
|
80
72
|
function resolveUser(flags = {}){
|
|
@@ -82,4 +74,4 @@ function resolveUser(flags = {}){
|
|
|
82
74
|
return flags.user || process.env.THUB_USER || file.user || undefined;
|
|
83
75
|
}
|
|
84
76
|
|
|
85
|
-
module.exports = { CONFIG_PATH, readConfigFile, writeConfigFile, resolveConnection,
|
|
77
|
+
module.exports = { CONFIG_PATH, readConfigFile, writeConfigFile, resolveConnection, resolveUser };
|