@vzn/vx-history 0.0.650 → 0.0.651

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 CHANGED
@@ -47,6 +47,8 @@ for its tasks' numbers, by task definition, in the refs the run reads,
47
47
  as a remote cache does: its branch's, the branch it merges into, then
48
48
  the default branch's (`baseBranch`, else git's or the CI's). A run
49
49
  that reads no ref (a detached HEAD with no default branch) asks nothing.
50
+ A ref over the 512 characters a server takes is said and not read;
51
+ the run reads the others.
50
52
  The run starts on its local history and re-ranks the tasks not yet
51
53
  started when they land; it never waits for them. At its end it sends the
52
54
  timings of the tasks it ran, once: under DTE that is the orchestrator,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx-history",
3
- "version": "0.0.650",
3
+ "version": "0.0.651",
4
4
  "license": "MIT",
5
5
  "description": "History-based scheduling for vx — a `schedule` plugin that orders ready tasks by their expected remaining critical path, learned from the local run history.",
6
6
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "access": "public"
38
38
  },
39
39
  "peerDependencies": {
40
- "@vzn/vx": "^0.0.650"
40
+ "@vzn/vx": "^0.0.651"
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
@@ -181,7 +181,8 @@ export class HistoryServer {
181
181
  u.searchParams.set('audience', new URL(this.url).origin)
182
182
  const res = await this.askGithub(u, signal)
183
183
  if (!res.ok) throw new Error(`GitHub's OIDC endpoint: ${(await refusal(res)).message}`)
184
- const jwt = ((await res.json()) as { value?: unknown }).value
184
+ // quiet: a body that is not JSON holds no token, said below
185
+ const jwt = ((await res.json().catch(() => null)) as { value?: unknown } | null)?.value
185
186
  if (typeof jwt !== 'string' || jwt === '') throw new Error("GitHub's OIDC endpoint: no token")
186
187
  return { jwt, exp: expiryOf(jwt) }
187
188
  })()
package/src/index.ts CHANGED
@@ -109,6 +109,9 @@ export interface VxHistoryOptions {
109
109
  // hint needs less and reads a 2.5× smaller slice of the run history.
110
110
  const DEFAULT_WINDOW = 20
111
111
 
112
+ /** The longest ref a history server takes in a query (its PROTOCOL.md). */
113
+ const REF_MAX = 512
114
+
112
115
  // Each option is read in ONE place that a run's hooks and `vx history`
113
116
  // share: two copies of each let either drift unseen (item 805).
114
117
  const windowOf = (options: VxHistoryOptions): number =>
@@ -277,6 +280,11 @@ function namedOf(issuer: unknown, repository: unknown): Named {
277
280
  return { issuer: issuer.replace(/\/$/, ''), repository: repository.toLowerCase() }
278
281
  }
279
282
 
283
+ /** `url` as a refusal may print it: a `user:pass@` would reach the CI log. */
284
+ function shown(url: string): string {
285
+ return url.replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/?#]*@/i, '$1***@')
286
+ }
287
+
280
288
  /**
281
289
  * https, or http to this machine: every request carries the CI token, and
282
290
  * http:// to another host sent it in clear.
@@ -299,12 +307,12 @@ function serverUrlOf(
299
307
  const from = option === undefined ? 'VX_HISTORY_URL' : 'url'
300
308
  if (!secure(url))
301
309
  throw new UserError(
302
- `vxHistory(): ${from} must be https (http only to 127.0.0.1, ::1 or localhost), got ${url}`,
310
+ `vxHistory(): ${from} must be https (http only to 127.0.0.1, ::1 or localhost), got ${shown(url)}`,
303
311
  )
304
312
  // Each route is the server's /v1/…: one that names /v1 asked /v1/v1/…, and every answer was a 404.
305
313
  if (url.endsWith('/v1'))
306
314
  throw new UserError(
307
- `vxHistory(): ${from} is the server's base, without /v1: ${url.slice(0, -3)}`,
315
+ `vxHistory(): ${from} is the server's base, without /v1: ${shown(url.slice(0, -3))}`,
308
316
  )
309
317
  return url
310
318
  }
@@ -440,14 +448,22 @@ export function vxHistory(options: VxHistoryOptions = {}): VxPlugin {
440
448
  // branch it merges into, the default branch (`baseBranch`). `main`
441
449
  // was assumed, so a `master` or `baseBranch: 'trunk'` repository
442
450
  // read no trunk's numbers; a run with none asks nothing.
443
- const refs = ctx.refs
451
+ // A ref the server cannot name was a 400 for the whole query, and
452
+ // the run read no ref's numbers, its base branch's too.
453
+ const refs = [...new Set(ctx.refs)].filter((ref) => {
454
+ if (ref.length <= REF_MAX) return true
455
+ ctx.warn(
456
+ `[vx] vx-history: ${ref} is ${ref.length} characters, over the ${REF_MAX} a history server takes: the run does not read it`,
457
+ )
458
+ return false
459
+ })
444
460
  if (remote !== undefined && refs.length > 0) {
445
461
  const server = remote
446
462
  let late = false
447
463
  stopImport = () => (late = true)
448
464
  const weights = (async () => {
449
465
  const hashes = [...new Set(ids.flatMap((id) => defs.get(id) ?? []))]
450
- const got = await server.query([...new Set(refs)], hashes)
466
+ const got = await server.query(refs, hashes)
451
467
  const timings = new Map<string, number>()
452
468
  const needs = new Map<string, TaskResources>()
453
469
  for (const id of ids) {
@@ -800,7 +816,7 @@ async function historyCmd(
800
816
  // A plugin verb owns its help; completions offer `--help` for every verb.
801
817
  if (a === '--help' || a === '-h') {
802
818
  ui.say('info', 'Usage: vx history [--format pretty|json]', 'out')
803
- process.stdout.write('\n')
819
+ ui.print('out', ui.gap())
804
820
  ui.say(
805
821
  'info',
806
822
  'What vx-history learned per task (p50, peak RSS, CPU parallelism) and the reservation it packs.',
@@ -876,7 +892,7 @@ async function historyCmd(
876
892
  }
877
893
  })
878
894
  if (format === 'json') {
879
- process.stdout.write(`${JSON.stringify({ window, budgets, tasks: rows })}\n`)
895
+ ui.data('out', `${JSON.stringify({ window, budgets, tasks: rows })}\n`)
880
896
  return 0
881
897
  }
882
898
  // The table's module loads with the verb, off every run's path.