@andrian.yablonskyy/thub-common 1.1.2 → 1.1.4

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-common",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Shared job-spec schema, state enums and a thin API client used by the Agent and Client",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -13,23 +13,14 @@
13
13
 
14
14
  'use strict';
15
15
 
16
- // A Docker image reference: [host[:port]/]path[:tag][@sha256:digest], e.g.
17
- // `alpine`, `alpine:3.20`, `library/ubuntu:24.04`, `registry.lab:5000/emu:1`.
18
- const DOCKER_IMAGE_PATTERN =
19
- '^[A-Za-z0-9][A-Za-z0-9.-]*(:[0-9]+)?(/[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*)*(:[A-Za-z0-9_][A-Za-z0-9_.-]{0,127})?(@sha256:[a-f0-9]{64})?$',
20
-
21
- // A git ref to check out — a branch, a tag or a commit (hex) — as git
22
- // allows them: no leading "-" (never taken for an option) or "/", no "..",
23
- // no spaces or the characters git forbids, not ending in "/", ".lock", ".".
24
- GIT_REF_PATTERN = '^(?![-/])(?!.*\\.\\.)(?!.*//)(?!.*(/|\\.lock|\\.)$)[A-Za-z0-9._/+@-]+$',
25
-
26
- // An environment variable name a job may set (`--env NAME=value`).
27
- ENV_NAME_PATTERN = '^[A-Za-z_][A-Za-z0-9_]*$',
16
+ // An environment variable name a job may set (`--env NAME=value`).
17
+ const ENV_NAME_PATTERN = '^[A-Za-z_][A-Za-z0-9_]*$',
28
18
 
29
19
  // Matches the job spec shape documented in README.md §4.3. A task is:
30
- // optionally files to download and/or a git checkout, optionally (SW only)
31
- // a Docker image to run as the DUT, and — always — the shell command that
32
- // is its entry point, run on the Client in the checkout / work directory.
20
+ // optionally files to download, and — always — the shell command that is
21
+ // its entry point, run on the Client in the job's work directory. Anything
22
+ // else it needs (a git checkout, a Docker container) the command does
23
+ // itself, with credentials passed in `env`.
33
24
  jobSpecSchema = {
34
25
  $id: 'https://thub.example.com/schemas/job-spec.json',
35
26
  type: 'object',
@@ -65,7 +56,7 @@ const DOCKER_IMAGE_PATTERN =
65
56
  // Client runs with `sh -c`, `args` as "$@" (`--arg`, repeatable).
66
57
  command: { type: 'string', minLength: 1, maxLength: 4096 },
67
58
  args: { type: 'array', items: { type: 'string' }, default: [] },
68
- // Passed to the command as THUB_SUITE (`--suite`).
59
+ // Passed to the command as JOB_SUITE (`--suite`).
69
60
  suite: { type: 'string', default: 'default' },
70
61
  // Files the Client downloads into the task's work directory before
71
62
  // running the command (`--download-file`, repeatable).
@@ -80,30 +71,8 @@ const DOCKER_IMAGE_PATTERN =
80
71
  },
81
72
  default: []
82
73
  },
83
- // A Docker image an SW Client runs as the job's DUT container, next to
84
- // its command — `--docker-image`.
85
- image: { type: 'string', maxLength: 255, pattern: DOCKER_IMAGE_PATTERN },
86
- // A repository the Client clones before running the command — at `ref`
87
- // (branch, tag or commit; default: the default branch), `depth` commits
88
- // deep (0 = full history). `--git-repo <url> [ref] [--depth <n>]`.
89
- git: {
90
- type: 'object',
91
- additionalProperties: false,
92
- required: ['url'],
93
- properties: {
94
- url: { type: 'string', minLength: 1, maxLength: 2048 },
95
- ref: { type: 'string', maxLength: 255, pattern: GIT_REF_PATTERN },
96
- depth: { type: 'integer', minimum: 0, maximum: 100000, default: 1 },
97
- // Extra git options (`--git-options`), shell-quoted, inserted
98
- // between `git` and its subcommand on the Client — e.g.
99
- // -c core.sshCommand="ssh -i ~/.ssh/lab_key -p 2222". Stored with
100
- // the job and visible like the rest of it: reference key files on
101
- // the Client rather than putting secrets here.
102
- options: { type: 'string', minLength: 1, maxLength: 1024 }
103
- }
104
- },
105
- // Environment variables (`--env NAME=value`) the Client sets for every
106
- // command it runs for the job — git and the job's command. No name
74
+ // Environment variables (`--env NAME=value`) the Client sets for the
75
+ // job's command — tokens for a git clone or docker login, say. No name
107
76
  // means anything special. Values are secrets as far as the Coordinator
108
77
  // is concerned: masked in the Agent API and dropped from its database
109
78
  // once the job ends.
@@ -130,4 +99,4 @@ const DOCKER_IMAGE_PATTERN =
130
99
  }
131
100
  };
132
101
 
133
- module.exports = { jobSpecSchema, DOCKER_IMAGE_PATTERN, ENV_NAME_PATTERN };
102
+ module.exports = { jobSpecSchema, ENV_NAME_PATTERN };
package/src/split-args.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @file packages/shared/src/split-args.js
3
3
  * @description Splits a command-line options string into arguments the way a POSIX shell would quote them —
4
- * without running a shell (for `thub run --git-options`)
4
+ * without running a shell (kept for callers of thub-common; TestHub itself no longer uses it)
5
5
  *
6
6
  * @author Andrian Yablonskyy
7
7
  * @copyright Copyright (c) 2026 Andrian Yablonskyy. All rights reserved.
@@ -15,8 +15,7 @@
15
15
 
16
16
  const Ajv = require('ajv'),
17
17
  addFormats = require('ajv-formats'),
18
- { jobSpecSchema } = require('./job-spec.schema'),
19
- { splitArgs } = require('./split-args');
18
+ { jobSpecSchema } = require('./job-spec.schema');
20
19
 
21
20
  const ajv = new Ajv({ useDefaults: true, allErrors: true, strict: false });
22
21
  addFormats(ajv);
@@ -27,7 +26,7 @@ const validateFn = ajv.compile(jobSpecSchema);
27
26
  * Returns { valid, spec, errors }.
28
27
  */
29
28
  function validateJobSpec(spec){
30
- const legacy = legacyShapeError(spec);
29
+ const legacy = legacyShapeError(spec) || removedFieldsError(spec);
31
30
  if (legacy){
32
31
  return { valid: false, spec: JSON.parse(JSON.stringify(spec ?? {})), errors: [legacy] };
33
32
  }
@@ -44,42 +43,31 @@ function validateJobSpec(spec){
44
43
  function legacyShapeError(spec){
45
44
  if (spec && typeof spec === 'object' && ('firmware' in spec || 'tests' in spec) && !('command' in spec)){
46
45
  return '/ this job spec is from an older Agent (firmware/tests fields) — update the Agent (thub self-update) ' +
47
- 'and use --command, --download-file, --docker-image and --git-repo';
46
+ 'and use --command and --download-file';
48
47
  }
49
48
  return null;
50
49
  }
51
50
 
52
- // Git transports a Client may fetch sources over. Never `ext::` (runs a
53
- // command), `file://` or a local path: the Client also sets
54
- // GIT_ALLOW_PROTOCOL to the same list (downloader.js).
55
- const GIT_URL = /^(?:(?:https?|ssh|git):\/\/[^\s]+|[A-Za-z0-9._-]+@[A-Za-z0-9.-]+:[^\s]+)$/,
56
-
57
- // Job env names the Client sets itself for a job's commands (besides
58
- // THUB_* and JOB_* — the job's own parameters): git's safety settings.
59
- RESERVED_ENV = ['GIT_TERMINAL_PROMPT', 'GIT_ALLOW_PROTOCOL'];
51
+ // `git` (--git-repo/--depth/--git-options) and `image` (--docker-image) are
52
+ // gone: the Client no longer clones or runs containers itself. Said plainly
53
+ // rather than as unknown properties.
54
+ function removedFieldsError(spec){
55
+ const removed = ['git', 'image'].filter((k) => spec && typeof spec === 'object' && spec[k] !== undefined);
56
+ if (!removed.length){
57
+ return null;
58
+ }
59
+ return `/ ${removed.join(', ')}: --git-repo, --git-options, --depth and --docker-image are no longer supported — ` +
60
+ 'clone the repository or run docker in --command, passing credentials with --env (update the Agent: thub self-update)';
61
+ }
60
62
 
61
63
  // Rules across fields, spelled out here rather than as schema if/then,
62
64
  // whose errors ("must match a schema in then") say little.
63
65
  function crossFieldErrors(spec){
64
- const errors = [];
65
- if (spec.image && spec.target?.type === 'hw'){
66
- errors.push('/image a Docker image only works for SW jobs (target.type "sw")');
67
- }
68
- if (spec.git && !GIT_URL.test(spec.git.url || '')){
69
- errors.push('/git/url must be an https://, http://, ssh:// or git:// URL, or user@host:path');
70
- }
71
- const envNames = Object.keys(spec.env && typeof spec.env === 'object' ? spec.env : {}),
72
- reserved = envNames.filter((n) => /^(THUB|JOB)_/.test(n) || RESERVED_ENV.includes(n));
66
+ const errors = [],
67
+ envNames = Object.keys(spec.env && typeof spec.env === 'object' ? spec.env : {}),
68
+ reserved = envNames.filter((n) => /^(THUB|JOB)_/.test(n));
73
69
  if (reserved.length){
74
- errors.push(`/env ${reserved.join(', ')}: set by the Client itself (THUB_*, JOB_*, ${RESERVED_ENV.join(', ')}) — use other names`);
75
- }
76
- if (spec.git?.options){
77
- try {
78
- splitArgs(spec.git.options);
79
- }
80
- catch (err){
81
- errors.push(`/git/options can't be split into arguments: ${err.message}`);
82
- }
70
+ errors.push(`/env ${reserved.join(', ')}: set by the Client itself (THUB_*, JOB_*) — use other names`);
83
71
  }
84
72
  return errors;
85
73
  }