-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommands.js
More file actions
124 lines (114 loc) · 4.55 KB
/
Copy pathcommands.js
File metadata and controls
124 lines (114 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// @ts-check
import { requireGithubRuntime } from './runtime.js'
import { runCaptureTick } from './tick.js'
/**
* @import { CommandRunContext } from './types.js'
*/
/** `owner/repo` - exactly one slash, non-empty halves, no whitespace. */
const REPO_SLUG = /^[^/\s]+\/[^/\s]+$/
/**
* `hyp github` - usage banner.
*
* @param {string[]} _argv
* @param {CommandRunContext} ctx
* @returns {Promise<number>}
*/
export async function runGithub(_argv, ctx) {
ctx.stdout.write(
'hyp github <subcommand>\n' +
' backfill [owner/repo ...] pull full history into github_events (cold-start)\n' +
' sync run one poll tick now (off the daemon)\n' +
"\nthen run 'hyp graph project' to project github_events into the node/edge graph\n",
)
return 0
}
/**
* `hyp github backfill [owner/repo ...]` - the deliberate cold-start pull of
* full history (polling is forward-only, so a freshly-configured repo has years
* of history a poller would never see - LLP 0360). With no positional repos it
* backfills the whole configured selection. Fills `github_events` only;
* projection is a separate `hyp graph project`.
*
* @param {string[]} argv
* @param {CommandRunContext} ctx
* @returns {Promise<number>}
*/
export async function runGithubBackfill(argv, ctx) {
const parsed = parseRepoArgv(argv)
if (!parsed.ok) {
ctx.stderr.write(`hyp github backfill: ${parsed.error}\n`)
return 2
}
try {
const runtime = requireGithubRuntime()
const only = parsed.repos.length > 0 ? parsed.repos : undefined
const result = await runCaptureTick(runtime, { mode: 'backfill', only })
ctx.stdout.write(`github backfill: ${result.events} event(s) across ${result.repos} repo(s)\n`)
if (result.pending) ctx.stdout.write('github backfill: bounded work remains and will resume on the next GitHub capture tick\n')
reportErrors(ctx, result.errors)
// An empty selection means "these repositories are not in the inventory"
// only when the inventory actually resolved. When resolution itself failed
// the selection is unknown rather than empty, and `reportErrors` has just
// named the real cause: claiming the repositories are absent sends the
// reader to their config instead of the failure they were shown.
if (only && result.repos === 0 && result.errors.length === 0) {
ctx.stderr.write(`hyp github backfill: none of [${only.join(', ')}] are in the active repository inventory\n`)
return 1
}
// The same ambiguity on the success tail: an unresolved inventory captured
// nothing at all, so the next step is retrying capture, not projecting a
// table this run never touched. `reportErrors` has already said why.
if (result.repos === 0 && result.errors.length > 0) return 1
ctx.stdout.write("run 'hyp graph project' to project github_events into the graph\n")
return result.errors.length > 0 ? 1 : 0
} catch (err) {
ctx.stderr.write(`hyp github backfill: ${errMessage(err)}\n`)
return 1
}
}
/**
* `hyp github sync` - run one poll tick now, off the daemon (the manual
* analogue of the ongoing source; for tests and demos - LLP 0360).
*
* @param {string[]} _argv
* @param {CommandRunContext} ctx
* @returns {Promise<number>}
*/
export async function runGithubSync(_argv, ctx) {
try {
const runtime = requireGithubRuntime()
const result = await runCaptureTick(runtime, { mode: 'poll' })
ctx.stdout.write(`github sync: ${result.events} event(s) across ${result.repos} repo(s)\n`)
if (result.pending) ctx.stdout.write('github sync: bounded work remains and will resume on the next GitHub capture tick\n')
reportErrors(ctx, result.errors)
return result.errors.length > 0 ? 1 : 0
} catch (err) {
ctx.stderr.write(`hyp github sync: ${errMessage(err)}\n`)
return 1
}
}
/**
* @param {string[]} argv
* @returns {{ ok: true, repos: string[] } | { ok: false, error: string }}
*/
function parseRepoArgv(argv) {
/** @type {string[]} */
const repos = []
for (const token of argv) {
if (token.startsWith('--')) return { ok: false, error: `unknown flag ${token}` }
if (!REPO_SLUG.test(token)) return { ok: false, error: `expected "owner/repo", got ${token}` }
repos.push(token)
}
return { ok: true, repos }
}
/**
* @param {CommandRunContext} ctx
* @param {Array<{ repo: string, error: string }>} errors
*/
function reportErrors(ctx, errors) {
for (const e of errors) ctx.stderr.write(` ! ${e.repo}: ${e.error}\n`)
}
/** @param {unknown} err @returns {string} */
function errMessage(err) {
return err instanceof Error ? err.message : String(err)
}