@blogic-cz/agent-tools 1.7.4 → 1.8.0
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 +2 -1
- package/dist/db-tool/service.d.ts.map +1 -1
- package/dist/gh-tool/branch.d.ts +2 -3
- package/dist/gh-tool/branch.d.ts.map +1 -1
- package/dist/gh-tool/environment.d.ts +1 -1
- package/dist/gh-tool/environment.d.ts.map +1 -1
- package/dist/gh-tool/gist.d.ts +8 -28
- package/dist/gh-tool/gist.d.ts.map +1 -1
- package/dist/gh-tool/issue/commands.d.ts +9 -9
- package/dist/gh-tool/issue/commands.d.ts.map +1 -1
- package/dist/gh-tool/issue/core.d.ts +103 -9
- package/dist/gh-tool/issue/core.d.ts.map +1 -1
- package/dist/gh-tool/issue/triage.d.ts +6 -6
- package/dist/gh-tool/issue/triage.d.ts.map +1 -1
- package/dist/gh-tool/pr/commands.d.ts +53 -46
- package/dist/gh-tool/pr/commands.d.ts.map +1 -1
- package/dist/gh-tool/pr/core.d.ts +179 -74
- package/dist/gh-tool/pr/core.d.ts.map +1 -1
- package/dist/gh-tool/pr/review.d.ts +16 -16
- package/dist/gh-tool/pr/review.d.ts.map +1 -1
- package/dist/gh-tool/pr/snapshot.d.ts +9 -0
- package/dist/gh-tool/pr/snapshot.d.ts.map +1 -0
- package/dist/gh-tool/pr/stack-read.d.ts +1 -6
- package/dist/gh-tool/pr/stack-read.d.ts.map +1 -1
- package/dist/gh-tool/pr/stack.d.ts +3 -3
- package/dist/gh-tool/pr/stack.d.ts.map +1 -1
- package/dist/gh-tool/{api.d.ts → read/api.d.ts} +9 -3
- package/dist/gh-tool/read/api.d.ts.map +1 -0
- package/dist/gh-tool/read/cache.d.ts +44 -0
- package/dist/gh-tool/read/cache.d.ts.map +1 -0
- package/dist/gh-tool/read/catalog.d.ts +170 -0
- package/dist/gh-tool/read/catalog.d.ts.map +1 -0
- package/dist/gh-tool/read/classes.d.ts +94 -0
- package/dist/gh-tool/read/classes.d.ts.map +1 -0
- package/dist/gh-tool/read/commands.d.ts +3 -0
- package/dist/gh-tool/read/commands.d.ts.map +1 -0
- package/dist/gh-tool/read/gated.d.ts +14 -0
- package/dist/gh-tool/read/gated.d.ts.map +1 -0
- package/dist/gh-tool/read/index.d.ts +5 -0
- package/dist/gh-tool/read/index.d.ts.map +1 -0
- package/dist/gh-tool/read/issues.d.ts +68 -0
- package/dist/gh-tool/read/issues.d.ts.map +1 -0
- package/dist/gh-tool/read/keys.d.ts +61 -0
- package/dist/gh-tool/read/keys.d.ts.map +1 -0
- package/dist/gh-tool/read/porcelain.d.ts +68 -0
- package/dist/gh-tool/read/porcelain.d.ts.map +1 -0
- package/dist/gh-tool/read/pulls.d.ts +137 -0
- package/dist/gh-tool/read/pulls.d.ts.map +1 -0
- package/dist/gh-tool/read/reads.d.ts +209 -0
- package/dist/gh-tool/read/reads.d.ts.map +1 -0
- package/dist/gh-tool/read/rest.d.ts +49 -0
- package/dist/gh-tool/read/rest.d.ts.map +1 -0
- package/dist/gh-tool/read/review-guards.d.ts +20 -0
- package/dist/gh-tool/read/review-guards.d.ts.map +1 -0
- package/dist/gh-tool/read/review-model.d.ts +190 -0
- package/dist/gh-tool/read/review-model.d.ts.map +1 -0
- package/dist/gh-tool/read/reviews.d.ts +9 -0
- package/dist/gh-tool/read/reviews.d.ts.map +1 -0
- package/dist/gh-tool/read/stacks.d.ts +7 -0
- package/dist/gh-tool/read/stacks.d.ts.map +1 -0
- package/dist/gh-tool/read/store.d.ts +70 -0
- package/dist/gh-tool/read/store.d.ts.map +1 -0
- package/dist/gh-tool/{service.d.ts → read/transport.d.ts} +5 -3
- package/dist/gh-tool/read/transport.d.ts.map +1 -0
- package/dist/gh-tool/read/waits.d.ts +22 -0
- package/dist/gh-tool/read/waits.d.ts.map +1 -0
- package/dist/gh-tool/read/writes.d.ts +16 -0
- package/dist/gh-tool/read/writes.d.ts.map +1 -0
- package/dist/gh-tool/release.d.ts +10 -26
- package/dist/gh-tool/release.d.ts.map +1 -1
- package/dist/gh-tool/repo.d.ts +4 -4
- package/dist/gh-tool/repo.d.ts.map +1 -1
- package/dist/gh-tool/types.d.ts +1 -1
- package/dist/gh-tool/types.d.ts.map +1 -1
- package/dist/gh-tool/workflow.d.ts +43 -21
- package/dist/gh-tool/workflow.d.ts.map +1 -1
- package/dist/observability-tool/shared.d.ts.map +1 -1
- package/dist/shared/content-security.d.ts +2 -0
- package/dist/shared/content-security.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/api-tool/proxy-entry.ts +3 -1
- package/src/db-tool/service.ts +4 -1
- package/src/gh-tool/branch.ts +5 -8
- package/src/gh-tool/environment.ts +2 -1
- package/src/gh-tool/gist.ts +8 -35
- package/src/gh-tool/issue/commands.ts +1 -1
- package/src/gh-tool/issue/core.ts +55 -122
- package/src/gh-tool/issue/triage.ts +4 -26
- package/src/gh-tool/main.ts +5 -2
- package/src/gh-tool/pr/commands.ts +19 -23
- package/src/gh-tool/pr/core.ts +542 -582
- package/src/gh-tool/pr/review.ts +106 -454
- package/src/gh-tool/pr/snapshot.ts +23 -0
- package/src/gh-tool/pr/stack-read.ts +1 -73
- package/src/gh-tool/pr/stack.ts +37 -36
- package/src/gh-tool/{api.ts → read/api.ts} +40 -7
- package/src/gh-tool/read/cache.ts +192 -0
- package/src/gh-tool/read/catalog.ts +310 -0
- package/src/gh-tool/read/classes.ts +30 -0
- package/src/gh-tool/read/commands.ts +25 -0
- package/src/gh-tool/read/gated.ts +56 -0
- package/src/gh-tool/read/index.ts +4 -0
- package/src/gh-tool/read/issues.ts +107 -0
- package/src/gh-tool/read/keys.ts +102 -0
- package/src/gh-tool/read/porcelain.ts +58 -0
- package/src/gh-tool/read/pulls.ts +266 -0
- package/src/gh-tool/read/reads.ts +306 -0
- package/src/gh-tool/read/rest.ts +111 -0
- package/src/gh-tool/read/review-guards.ts +61 -0
- package/src/gh-tool/read/review-model.ts +286 -0
- package/src/gh-tool/read/reviews.ts +114 -0
- package/src/gh-tool/read/stacks.ts +73 -0
- package/src/gh-tool/read/store.ts +504 -0
- package/src/gh-tool/{service.ts → read/transport.ts} +26 -6
- package/src/gh-tool/read/waits.ts +52 -0
- package/src/gh-tool/read/writes.ts +37 -0
- package/src/gh-tool/release.ts +15 -168
- package/src/gh-tool/repo.ts +5 -78
- package/src/gh-tool/types.ts +1 -1
- package/src/gh-tool/workflow.ts +123 -214
- package/src/observability-tool/shared.ts +2 -1
- package/src/shared/content-security.ts +10 -1
- package/src/shared/credential-patterns.ts +1 -1
- package/dist/gh-tool/api.d.ts.map +0 -1
- package/dist/gh-tool/pr/check-status-cache.d.ts +0 -6
- package/dist/gh-tool/pr/check-status-cache.d.ts.map +0 -1
- package/dist/gh-tool/service.d.ts.map +0 -1
- package/src/gh-tool/pr/check-status-cache.ts +0 -142
package/src/gh-tool/workflow.ts
CHANGED
|
@@ -1,50 +1,20 @@
|
|
|
1
1
|
import { Command, Flag, Param } from "effect/cli";
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
3
|
|
|
4
4
|
import { formatOption, logFormatted, logText } from "#shared";
|
|
5
5
|
import { CI_CHECK_WATCH_TIMEOUT_MS } from "#gh/config";
|
|
6
6
|
import { GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
7
|
-
import { GitHubService } from "./
|
|
7
|
+
import { GitHubService } from "./read/transport";
|
|
8
|
+
import { reads, resolveRepo, waits, writes } from "./read";
|
|
9
|
+
import { rawJobLog } from "./read/reads";
|
|
10
|
+
import type { JobRef } from "./read/reads";
|
|
11
|
+
import { repoSlug } from "./read/keys";
|
|
8
12
|
import type { CheckRunAnnotation, JobAnnotations, LogDiagnosis } from "./types";
|
|
9
13
|
|
|
10
14
|
// ---------------------------------------------------------------------------
|
|
11
15
|
// Types
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
|
|
14
|
-
type WorkflowRun = {
|
|
15
|
-
databaseId: number;
|
|
16
|
-
displayTitle: string;
|
|
17
|
-
status: string;
|
|
18
|
-
conclusion: string | null;
|
|
19
|
-
headBranch: string;
|
|
20
|
-
createdAt: string;
|
|
21
|
-
event: string;
|
|
22
|
-
url: string;
|
|
23
|
-
workflowName: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type WorkflowJob = {
|
|
27
|
-
databaseId: number;
|
|
28
|
-
name: string;
|
|
29
|
-
status: string;
|
|
30
|
-
conclusion: string | null;
|
|
31
|
-
startedAt: string;
|
|
32
|
-
completedAt: string | null;
|
|
33
|
-
url: string;
|
|
34
|
-
steps: Array<{
|
|
35
|
-
name: string;
|
|
36
|
-
status: string;
|
|
37
|
-
conclusion: string | null;
|
|
38
|
-
number: number;
|
|
39
|
-
startedAt: string | null;
|
|
40
|
-
completedAt: string | null;
|
|
41
|
-
}>;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
type WorkflowRunDetail = WorkflowRun & {
|
|
45
|
-
jobs: WorkflowJob[];
|
|
46
|
-
};
|
|
47
|
-
|
|
48
18
|
export type LogEntry = {
|
|
49
19
|
step: string;
|
|
50
20
|
message: string;
|
|
@@ -62,6 +32,7 @@ const resolveRepoArg = Effect.fn("workflow.resolveRepoArg")(function* (
|
|
|
62
32
|
if (target === null) {
|
|
63
33
|
return null;
|
|
64
34
|
}
|
|
35
|
+
if (/^[^/\s]+\/[^/\s]+$/.test(target)) return target;
|
|
65
36
|
|
|
66
37
|
const gh = yield* GitHubService;
|
|
67
38
|
const info = yield* gh.withRepoTarget(target, gh.getRepoInfo());
|
|
@@ -78,66 +49,26 @@ const listRuns = Effect.fn("workflow.listRuns")(function* (opts: {
|
|
|
78
49
|
limit: number;
|
|
79
50
|
repo: string | null;
|
|
80
51
|
}) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
if (opts.repo !== null) {
|
|
93
|
-
args.push("--repo", opts.repo);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (opts.workflow !== null) {
|
|
97
|
-
args.push("--workflow", opts.workflow);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (opts.branch !== null) {
|
|
101
|
-
args.push("--branch", opts.branch);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (opts.status !== null) {
|
|
105
|
-
args.push("--status", opts.status);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return yield* gh.runGhJson<WorkflowRun[]>(args);
|
|
52
|
+
const repo = yield* resolveRepo(opts.repo);
|
|
53
|
+
const runs = yield* reads.runList(repo, {
|
|
54
|
+
workflow: opts.workflow,
|
|
55
|
+
branch: opts.branch,
|
|
56
|
+
status: opts.status,
|
|
57
|
+
event: null,
|
|
58
|
+
limit: opts.limit,
|
|
59
|
+
});
|
|
60
|
+
return runs.map(({ headSha: _headSha, ...run }) => run);
|
|
109
61
|
});
|
|
110
62
|
|
|
111
63
|
const viewRun = Effect.fn("workflow.viewRun")(function* (runId: number, repo: string | null) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
"view",
|
|
117
|
-
String(runId),
|
|
118
|
-
"--json",
|
|
119
|
-
"databaseId,displayTitle,status,conclusion,headBranch,createdAt,event,url,workflowName,jobs",
|
|
120
|
-
];
|
|
121
|
-
if (repo !== null) {
|
|
122
|
-
args.push("--repo", repo);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return yield* gh.runGhJson<WorkflowRunDetail>(args);
|
|
64
|
+
const target = yield* resolveRepo(repo);
|
|
65
|
+
const detail = yield* reads.runView(target, runId);
|
|
66
|
+
const { attempt: _attempt, headSha: _headSha, ...snapshot } = detail;
|
|
67
|
+
return snapshot;
|
|
126
68
|
});
|
|
127
69
|
|
|
128
70
|
const listJobs = Effect.fn("workflow.listJobs")(function* (runId: number, repo: string | null) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const args = ["run", "view", String(runId), "--json", "jobs"];
|
|
132
|
-
if (repo !== null) {
|
|
133
|
-
args.push("--repo", repo);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const run = yield* gh.runGhJson<{
|
|
137
|
-
jobs: WorkflowJob[];
|
|
138
|
-
}>(args);
|
|
139
|
-
|
|
140
|
-
return run.jobs;
|
|
71
|
+
return yield* reads.runJobs(yield* resolveRepo(repo), runId);
|
|
141
72
|
});
|
|
142
73
|
|
|
143
74
|
const fetchLogs = Effect.fn("workflow.fetchLogs")(function* (
|
|
@@ -146,36 +77,19 @@ const fetchLogs = Effect.fn("workflow.fetchLogs")(function* (
|
|
|
146
77
|
jobId: number | null = null,
|
|
147
78
|
repo: string | null = null,
|
|
148
79
|
) {
|
|
149
|
-
const
|
|
150
|
-
const args = ["run", "view", String(runId)];
|
|
151
|
-
|
|
152
|
-
if (repo !== null) {
|
|
153
|
-
args.push("--repo", repo);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (jobId !== null) {
|
|
157
|
-
args.push(failedOnly ? "--log-failed" : "--log", "--job", String(jobId));
|
|
158
|
-
} else if (failedOnly) {
|
|
159
|
-
args.push("--log-failed");
|
|
160
|
-
} else {
|
|
161
|
-
args.push("--log");
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const result = yield* gh.runGh(args);
|
|
80
|
+
const log = yield* reads.runLog(yield* resolveRepo(repo), runId, failedOnly, jobId);
|
|
165
81
|
return {
|
|
166
82
|
runId,
|
|
167
83
|
failedOnly,
|
|
168
|
-
log
|
|
84
|
+
log,
|
|
169
85
|
};
|
|
170
86
|
});
|
|
171
87
|
|
|
172
|
-
const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
|
|
88
|
+
export const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
|
|
173
89
|
runId: number,
|
|
174
90
|
failedOnly: boolean,
|
|
175
91
|
repo: string | null,
|
|
176
92
|
) {
|
|
177
|
-
const gh = yield* GitHubService;
|
|
178
|
-
|
|
179
93
|
const args = ["run", "rerun", String(runId)];
|
|
180
94
|
if (failedOnly) {
|
|
181
95
|
args.push("--failed");
|
|
@@ -184,7 +98,8 @@ const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
|
|
|
184
98
|
args.push("--repo", repo);
|
|
185
99
|
}
|
|
186
100
|
|
|
187
|
-
yield*
|
|
101
|
+
const target = yield* resolveRepo(repo);
|
|
102
|
+
yield* writes.run(args, [`repo:${repoSlug(target)}`]);
|
|
188
103
|
|
|
189
104
|
return {
|
|
190
105
|
rerun: true as const,
|
|
@@ -196,15 +111,17 @@ const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
|
|
|
196
111
|
};
|
|
197
112
|
});
|
|
198
113
|
|
|
199
|
-
const cancelRun = Effect.fn("workflow.cancelRun")(function* (
|
|
200
|
-
|
|
201
|
-
|
|
114
|
+
export const cancelRun = Effect.fn("workflow.cancelRun")(function* (
|
|
115
|
+
runId: number,
|
|
116
|
+
repo: string | null,
|
|
117
|
+
) {
|
|
202
118
|
const args = ["run", "cancel", String(runId)];
|
|
203
119
|
if (repo !== null) {
|
|
204
120
|
args.push("--repo", repo);
|
|
205
121
|
}
|
|
206
122
|
|
|
207
|
-
yield*
|
|
123
|
+
const target = yield* resolveRepo(repo);
|
|
124
|
+
yield* writes.run(args, [`repo:${repoSlug(target)}`]);
|
|
208
125
|
|
|
209
126
|
return {
|
|
210
127
|
cancelled: true as const,
|
|
@@ -219,7 +136,6 @@ export const dispatchWorkflow = Effect.fn("workflow.dispatchWorkflow")(function*
|
|
|
219
136
|
fields: ReadonlyArray<string>;
|
|
220
137
|
repo: string | null;
|
|
221
138
|
}) {
|
|
222
|
-
const gh = yield* GitHubService;
|
|
223
139
|
const args = ["workflow", "run", opts.workflow, "--ref", opts.ref];
|
|
224
140
|
|
|
225
141
|
if (opts.repo !== null) {
|
|
@@ -230,7 +146,8 @@ export const dispatchWorkflow = Effect.fn("workflow.dispatchWorkflow")(function*
|
|
|
230
146
|
args.push("-f", field);
|
|
231
147
|
}
|
|
232
148
|
|
|
233
|
-
yield*
|
|
149
|
+
const target = yield* resolveRepo(opts.repo);
|
|
150
|
+
yield* writes.run(args, [`repo:${repoSlug(target)}`]);
|
|
234
151
|
|
|
235
152
|
return {
|
|
236
153
|
dispatched: true as const,
|
|
@@ -249,36 +166,38 @@ export type DispatchedRun = {
|
|
|
249
166
|
url: string;
|
|
250
167
|
};
|
|
251
168
|
|
|
252
|
-
const
|
|
253
|
-
const DISPATCH_DISCOVERY_INTERVAL_MS =
|
|
169
|
+
const DISPATCH_DISCOVERY_BUDGET_MS = 16_000;
|
|
170
|
+
const DISPATCH_DISCOVERY_INTERVAL_MS = 5000;
|
|
254
171
|
|
|
255
172
|
export const listDispatchedRuns = Effect.fn("workflow.listDispatchedRuns")(function* (opts: {
|
|
256
173
|
workflow: string;
|
|
257
174
|
ref: string;
|
|
258
175
|
repo: string | null;
|
|
259
176
|
}) {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
177
|
+
const repo = yield* resolveRepo(opts.repo);
|
|
178
|
+
return yield* reads
|
|
179
|
+
.runList(
|
|
180
|
+
repo,
|
|
181
|
+
{
|
|
182
|
+
workflow: opts.workflow,
|
|
183
|
+
branch: opts.ref,
|
|
184
|
+
status: null,
|
|
185
|
+
event: "workflow_dispatch",
|
|
186
|
+
limit: 20,
|
|
187
|
+
},
|
|
188
|
+
"origin",
|
|
189
|
+
)
|
|
190
|
+
.pipe(
|
|
191
|
+
Effect.map((runs) =>
|
|
192
|
+
runs.map((run) => ({
|
|
193
|
+
conclusion: run.conclusion,
|
|
194
|
+
databaseId: run.databaseId,
|
|
195
|
+
headSha: run.headSha,
|
|
196
|
+
status: run.status,
|
|
197
|
+
url: run.url,
|
|
198
|
+
})),
|
|
199
|
+
),
|
|
200
|
+
)
|
|
282
201
|
.pipe(Effect.catchTag("GitHubCommandError", () => Effect.succeed<DispatchedRun[]>([])));
|
|
283
202
|
});
|
|
284
203
|
|
|
@@ -291,13 +210,13 @@ export const discoverDispatchedRun = Effect.fn("workflow.discoverDispatchedRun")
|
|
|
291
210
|
repo: string | null;
|
|
292
211
|
knownRunIds: ReadonlySet<number>;
|
|
293
212
|
}) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return null;
|
|
213
|
+
const outcome = yield* waits.until(
|
|
214
|
+
listDispatchedRuns(opts),
|
|
215
|
+
(runs) => runs.some((run) => !opts.knownRunIds.has(run.databaseId)),
|
|
216
|
+
{ intervalMs: DISPATCH_DISCOVERY_INTERVAL_MS, timeoutMs: DISPATCH_DISCOVERY_BUDGET_MS },
|
|
217
|
+
);
|
|
218
|
+
const runs = outcome.kind === "done" ? outcome.value : outcome.last;
|
|
219
|
+
return runs.find((run) => !opts.knownRunIds.has(run.databaseId)) ?? null;
|
|
301
220
|
});
|
|
302
221
|
|
|
303
222
|
// `gh run watch` has no native timeout (observed hanging 36 min). Block for the caller's --timeout,
|
|
@@ -338,37 +257,29 @@ const watchRun = Effect.fn("workflow.watchRun")(function* (
|
|
|
338
257
|
timeoutSeconds: number,
|
|
339
258
|
frames: boolean,
|
|
340
259
|
) {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
return Effect.fail(error);
|
|
359
|
-
}),
|
|
360
|
-
Effect.timeoutOrElse({
|
|
361
|
-
duration: timeoutSeconds * 1000,
|
|
362
|
-
orElse: () => Effect.succeed(null),
|
|
363
|
-
}),
|
|
260
|
+
const target = yield* resolveRepo(repo);
|
|
261
|
+
const progress: string[] = [];
|
|
262
|
+
let previous = "";
|
|
263
|
+
const outcome = yield* waits.until(
|
|
264
|
+
reads.runView(target, runId),
|
|
265
|
+
(run) => run.status === "completed",
|
|
266
|
+
{
|
|
267
|
+
intervalMs: 5000,
|
|
268
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
269
|
+
onValue: (run) =>
|
|
270
|
+
Effect.sync(() => {
|
|
271
|
+
const frame = `${run.status} ${run.conclusion ?? ""}\n${run.jobs.map((job) => `${job.name}: ${job.status} ${job.conclusion ?? ""}`).join("\n")}`;
|
|
272
|
+
if (frames && frame !== previous) progress.push(frame);
|
|
273
|
+
previous = frame;
|
|
274
|
+
}),
|
|
275
|
+
},
|
|
364
276
|
);
|
|
365
|
-
|
|
366
|
-
const finalState = yield* viewRun(runId, repo);
|
|
277
|
+
const finalState = outcome.kind === "done" ? outcome.value : outcome.last;
|
|
367
278
|
|
|
368
279
|
return buildWatchResult(
|
|
369
280
|
runId,
|
|
370
281
|
finalState,
|
|
371
|
-
|
|
282
|
+
outcome.kind === "timedOut" ? null : progress.join("\n"),
|
|
372
283
|
frames,
|
|
373
284
|
timeoutSeconds,
|
|
374
285
|
);
|
|
@@ -405,18 +316,14 @@ const fetchAnnotations = Effect.fn("workflow.fetchAnnotations")(function* (opts:
|
|
|
405
316
|
|
|
406
317
|
let targetJobs = jobs;
|
|
407
318
|
if (opts.job !== null) {
|
|
408
|
-
const
|
|
409
|
-
targetJobs = jobs.filter((j) => j.databaseId ===
|
|
319
|
+
const selected = yield* resolveJob(opts.runId, opts.job, opts.repo);
|
|
320
|
+
targetJobs = jobs.filter((j) => j.databaseId === selected.databaseId);
|
|
410
321
|
}
|
|
411
322
|
|
|
412
323
|
const results: JobAnnotations[] = [];
|
|
413
324
|
for (const job of targetJobs) {
|
|
414
|
-
const annotations = yield*
|
|
415
|
-
.
|
|
416
|
-
"api",
|
|
417
|
-
`repos/${owner}/${repoName}/check-runs/${job.databaseId}/annotations`,
|
|
418
|
-
"--paginate",
|
|
419
|
-
])
|
|
325
|
+
const annotations = yield* reads
|
|
326
|
+
.annotations({ owner, name: repoName }, { id: job.databaseId, status: job.status })
|
|
420
327
|
.pipe(
|
|
421
328
|
Effect.catchTag("GitHubCommandError", () => Effect.succeed([] as CheckRunAnnotation[])),
|
|
422
329
|
);
|
|
@@ -425,7 +332,7 @@ const fetchAnnotations = Effect.fn("workflow.fetchAnnotations")(function* (opts:
|
|
|
425
332
|
results.push({
|
|
426
333
|
jobId: job.databaseId,
|
|
427
334
|
jobName: job.name,
|
|
428
|
-
annotations,
|
|
335
|
+
annotations: [...annotations],
|
|
429
336
|
});
|
|
430
337
|
}
|
|
431
338
|
}
|
|
@@ -457,8 +364,8 @@ export function cleanLogLine(line: string): string {
|
|
|
457
364
|
type StepTiming = {
|
|
458
365
|
name: string;
|
|
459
366
|
conclusion: string | null;
|
|
460
|
-
startedAt
|
|
461
|
-
completedAt
|
|
367
|
+
startedAt?: string | null;
|
|
368
|
+
completedAt?: string | null;
|
|
462
369
|
};
|
|
463
370
|
|
|
464
371
|
type StepWindow = { name: string; failed: boolean; start: number; end: number };
|
|
@@ -466,18 +373,7 @@ type StepWindow = { name: string; failed: boolean; start: number; end: number };
|
|
|
466
373
|
const LINE_SECOND_RE = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/;
|
|
467
374
|
|
|
468
375
|
// Older gh versions reject --allow-escape-sequences; they print escapes without it.
|
|
469
|
-
export const readJobLogApi =
|
|
470
|
-
const gh = yield* GitHubService;
|
|
471
|
-
return yield* gh
|
|
472
|
-
.runGh(["api", path, "--allow-escape-sequences"])
|
|
473
|
-
.pipe(
|
|
474
|
-
Effect.catchTag("GitHubCommandError", (error) =>
|
|
475
|
-
`${error.stderr}${error.message}`.includes("unknown flag: --allow-escape-sequences")
|
|
476
|
-
? gh.runGh(["api", path])
|
|
477
|
-
: Effect.fail(error),
|
|
478
|
-
),
|
|
479
|
-
);
|
|
480
|
-
});
|
|
376
|
+
export const readJobLogApi = rawJobLog;
|
|
481
377
|
|
|
482
378
|
// Step times have one-second precision and several steps can share a second, so a
|
|
483
379
|
// boundary line goes to the failed step: a few extra lines beat a lost final error.
|
|
@@ -621,7 +517,7 @@ export const diagnoseLogEntries = (entries: readonly LogEntry[]): LogDiagnosis =
|
|
|
621
517
|
};
|
|
622
518
|
};
|
|
623
519
|
|
|
624
|
-
const
|
|
520
|
+
const resolveJob = Effect.fn("workflow.resolveJob")(function* (
|
|
625
521
|
runId: number,
|
|
626
522
|
jobName: string,
|
|
627
523
|
repo: string | null,
|
|
@@ -630,13 +526,13 @@ const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (
|
|
|
630
526
|
|
|
631
527
|
// Exact match first
|
|
632
528
|
const exact = jobs.find((j) => j.name === jobName);
|
|
633
|
-
if (exact) return exact
|
|
529
|
+
if (exact) return exact;
|
|
634
530
|
|
|
635
531
|
// Case-insensitive partial match
|
|
636
532
|
const lower = jobName.toLowerCase();
|
|
637
533
|
const partial = jobs.filter((j) => j.name.toLowerCase().includes(lower));
|
|
638
534
|
|
|
639
|
-
if (partial.length === 1) return partial[0]
|
|
535
|
+
if (partial.length === 1) return partial[0];
|
|
640
536
|
|
|
641
537
|
if (partial.length > 1) {
|
|
642
538
|
return yield* new GitHubCommandError({
|
|
@@ -658,17 +554,18 @@ const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (
|
|
|
658
554
|
});
|
|
659
555
|
});
|
|
660
556
|
|
|
661
|
-
export const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
557
|
+
export const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (
|
|
558
|
+
opts: {
|
|
559
|
+
runId: number;
|
|
560
|
+
job: string;
|
|
561
|
+
failedStepNames?: readonly string[] | null;
|
|
562
|
+
steps?: ReadonlyArray<StepTiming>;
|
|
563
|
+
failedStepsOnly: boolean;
|
|
564
|
+
diagnose?: boolean;
|
|
565
|
+
format: string;
|
|
566
|
+
repo: string | null;
|
|
567
|
+
} & ({ jobRef: JobRef; jobId?: never } | { jobId?: number | null; jobRef?: never }),
|
|
568
|
+
) {
|
|
672
569
|
const gh = yield* GitHubService;
|
|
673
570
|
|
|
674
571
|
let owner: string;
|
|
@@ -691,11 +588,23 @@ export const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (opts:
|
|
|
691
588
|
repoName = info.name;
|
|
692
589
|
}
|
|
693
590
|
|
|
694
|
-
const
|
|
591
|
+
const job =
|
|
592
|
+
opts.jobRef === undefined && (opts.jobId === null || opts.jobId === undefined)
|
|
593
|
+
? yield* resolveJob(opts.runId, opts.job, opts.repo)
|
|
594
|
+
: null;
|
|
595
|
+
const jobId = opts.jobRef?.id ?? opts.jobId ?? job?.databaseId;
|
|
596
|
+
if (jobId === undefined)
|
|
597
|
+
return yield* new GitHubCommandError({
|
|
598
|
+
message: "Job identity unavailable",
|
|
599
|
+
command: "workflow job-logs",
|
|
600
|
+
exitCode: 1,
|
|
601
|
+
stderr: "",
|
|
602
|
+
});
|
|
695
603
|
|
|
696
604
|
// `gh run view --log` can omit lines, so the job log endpoint is the primary source.
|
|
697
|
-
const apiLogs =
|
|
698
|
-
|
|
605
|
+
const apiLogs = reads.jobLog(
|
|
606
|
+
{ owner, name: repoName },
|
|
607
|
+
opts.jobRef ?? { id: jobId, status: job?.status ?? "unknown" },
|
|
699
608
|
);
|
|
700
609
|
const cliLogs = fetchLogs(opts.runId, opts.failedStepsOnly, jobId, opts.repo).pipe(
|
|
701
610
|
Effect.map((r) => r.log),
|
|
@@ -5,7 +5,7 @@ import { ConfigService, getToolConfig } from "#config";
|
|
|
5
5
|
import type { ObservabilityConfig } from "#config";
|
|
6
6
|
|
|
7
7
|
import { ObservabilityToolError } from "./errors";
|
|
8
|
-
import { unsafeOutboundTextReason } from "#shared/content-security";
|
|
8
|
+
import { registerResolvedSecret, unsafeOutboundTextReason } from "#shared/content-security";
|
|
9
9
|
import type {
|
|
10
10
|
DsQueryOpts,
|
|
11
11
|
DsQueryResponse,
|
|
@@ -58,6 +58,7 @@ function resolveToken(tokenEnvVar?: string): string | undefined {
|
|
|
58
58
|
throw new Error(`${tokenEnvVar} environment variable is not set`);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
registerResolvedSecret(token);
|
|
61
62
|
return token;
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -8,11 +8,20 @@ const isSensitiveEnvironmentName = (name: string) =>
|
|
|
8
8
|
name !== "CODEX_SESSION_ID" &&
|
|
9
9
|
SENSITIVE_ENV_NAME.test(name);
|
|
10
10
|
|
|
11
|
+
const MIN_SECRET_LENGTH = 8;
|
|
12
|
+
const resolvedSecrets = new Set<string>();
|
|
13
|
+
|
|
14
|
+
/** Redact a credential a tool resolved outside a sensitively named environment variable. */
|
|
15
|
+
export const registerResolvedSecret = (value: string) => {
|
|
16
|
+
if (value.length >= MIN_SECRET_LENGTH) resolvedSecrets.add(value);
|
|
17
|
+
};
|
|
18
|
+
|
|
11
19
|
const sensitiveEnvironmentValues = (environment: Record<string, string | undefined>) =>
|
|
12
20
|
Object.entries(environment)
|
|
13
21
|
.flatMap(([name, value]) =>
|
|
14
|
-
isSensitiveEnvironmentName(name) && value && value.length >=
|
|
22
|
+
isSensitiveEnvironmentName(name) && value && value.length >= MIN_SECRET_LENGTH ? [value] : [],
|
|
15
23
|
)
|
|
24
|
+
.concat([...resolvedSecrets])
|
|
16
25
|
.toSorted((left, right) => right.length - left.length);
|
|
17
26
|
|
|
18
27
|
export function unsafeOutboundTextReason(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Patterns used by the credential guard and shared output redaction. */
|
|
2
2
|
export const SECRET_PATTERNS = [
|
|
3
3
|
{ name: "AWS Access Key", pattern: /(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/ },
|
|
4
|
-
{ name: "GitHub Token", pattern: /gh[
|
|
4
|
+
{ name: "GitHub Token", pattern: /gh[opsur]_[A-Za-z0-9]{36}/ },
|
|
5
5
|
{ name: "GitHub PAT", pattern: /github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}/ },
|
|
6
6
|
{ name: "OpenAI Key", pattern: /sk-[A-Za-z0-9]{48}/ },
|
|
7
7
|
{
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/gh-tool/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIvC,OAAO,EAAE,eAAe,EAA2C,MAAM,UAAU,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAW/C,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAYF,eAAO,MAAM,kBAAkB,qDA6B7B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAuHF,eAAO,MAAM,SAAS,GAAI,CAAC,QACnB,gBAAgB,KACrB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,cAAc,CAyCjD,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Context } from "effect";
|
|
2
|
-
import type { CheckResult } from "#gh/types";
|
|
3
|
-
export declare const CheckStatusCacheDirectory: Context.Reference<string | undefined>;
|
|
4
|
-
export declare const readCheckStatusCache: (identity: string, repo: string, pr: number, headSha: string, now: number, watchPending?: boolean, directory?: string) => Promise<CheckResult[] | null>;
|
|
5
|
-
export declare const writeCheckStatusCache: (identity: string, repo: string, pr: number, headSha: string, checks: CheckResult[], fetchedAt: number, directory?: string) => Promise<void>;
|
|
6
|
-
//# sourceMappingURL=check-status-cache.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"check-status-cache.d.ts","sourceRoot":"","sources":["../../../src/gh-tool/pr/check-status-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM7C,eAAO,MAAM,yBAAyB,uCAGrC,CAAC;AAqBF,eAAO,MAAM,oBAAoB,aACrB,MAAM,QACV,MAAM,MACR,MAAM,WACD,MAAM,OACV,MAAM,iDAGV,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CA+C9B,CAAC;AAEF,eAAO,MAAM,qBAAqB,aACtB,MAAM,QACV,MAAM,MACR,MAAM,WACD,MAAM,UACP,WAAW,EAAE,aACV,MAAM,yBAEhB,OAAO,CAAC,IAAI,CAwCd,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/gh-tool/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,aAAa,EAA4C,MAAM,SAAS,CAAC;AA4DlF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,kBAAkB,GAAG,eAAe,GAAG,mBAAmB,CAAC;;oBAK7D,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;yBAC/C,CACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;yBACf,CAAC,CAAC,EACrB,IAAI,EAAE,gBAAgB,KACnB,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;4BACzB,MAAM,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,SAAS,EAAE,KAAK,CAAC;0BAC1D,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;6BACnC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,CAAC;;AAjBpD,qBAAa,aAAc,SAAQ,kBAmBF;IAC/B,MAAM,CAAC,QAAQ,CAAC,KAAK,6FAuTnB;CACH"}
|