@backstage-community/plugin-github-actions 0.6.16

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.
@@ -0,0 +1,1238 @@
1
+ import { createApiRef, createRouteRef, createSubRouteRef, createPlugin, createApiFactory, configApiRef, createRoutableExtension, createComponentExtension, useApi, useRouteRefParams, errorApiRef, useRouteRef } from '@backstage/core-plugin-api';
2
+ import { readGithubIntegrationConfigs } from '@backstage/integration';
3
+ import { Octokit } from '@octokit/rest';
4
+ import { scmAuthApiRef } from '@backstage/integration-react';
5
+ import React, { useState, useEffect } from 'react';
6
+ import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
7
+ import { Link as Link$1, Routes, Route } from 'react-router-dom';
8
+ import Accordion from '@material-ui/core/Accordion';
9
+ import AccordionDetails from '@material-ui/core/AccordionDetails';
10
+ import AccordionSummary from '@material-ui/core/AccordionSummary';
11
+ import Box from '@material-ui/core/Box';
12
+ import CircularProgress from '@material-ui/core/CircularProgress';
13
+ import LinearProgress from '@material-ui/core/LinearProgress';
14
+ import ListItemText from '@material-ui/core/ListItemText';
15
+ import Paper from '@material-ui/core/Paper';
16
+ import Table from '@material-ui/core/Table';
17
+ import TableBody from '@material-ui/core/TableBody';
18
+ import TableCell from '@material-ui/core/TableCell';
19
+ import TableContainer from '@material-ui/core/TableContainer';
20
+ import TableRow from '@material-ui/core/TableRow';
21
+ import Typography from '@material-ui/core/Typography';
22
+ import { makeStyles, createStyles } from '@material-ui/core/styles';
23
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
24
+ import ExternalLinkIcon from '@material-ui/icons/Launch';
25
+ import { DateTime } from 'luxon';
26
+ import { StatusPending, StatusOK, StatusError, StatusWarning, StatusAborted, StatusRunning, LogViewer, Breadcrumbs, Link, EmptyState, InfoCard, MarkdownContent, Table as Table$1, StructuredMetadataTable, ErrorPanel } from '@backstage/core-components';
27
+ import useAsync from 'react-use/esm/useAsync';
28
+ import Fade from '@material-ui/core/Fade';
29
+ import Modal from '@material-ui/core/Modal';
30
+ import Tooltip from '@material-ui/core/Tooltip';
31
+ import Zoom from '@material-ui/core/Zoom';
32
+ import DescriptionIcon from '@material-ui/icons/Description';
33
+ import { ANNOTATION_SOURCE_LOCATION, ANNOTATION_LOCATION } from '@backstage/catalog-model';
34
+ import gitUrlParse from 'git-url-parse';
35
+ import IconButton from '@material-ui/core/IconButton';
36
+ import Button from '@material-ui/core/Button';
37
+ import Chip from '@material-ui/core/Chip';
38
+ import ButtonGroup from '@material-ui/core/ButtonGroup';
39
+ import Grid from '@material-ui/core/Grid';
40
+ import TablePagination from '@material-ui/core/TablePagination';
41
+ import Select from '@material-ui/core/Select';
42
+ import MenuItem from '@material-ui/core/MenuItem';
43
+ import TextField from '@material-ui/core/TextField';
44
+ import GitHubIcon from '@material-ui/icons/GitHub';
45
+ import RetryIcon from '@material-ui/icons/Replay';
46
+ import SyncIcon from '@material-ui/icons/Sync';
47
+ import useAsyncRetry from 'react-use/esm/useAsyncRetry';
48
+ import Alert from '@material-ui/lab/Alert';
49
+
50
+ const githubActionsApiRef = createApiRef({
51
+ id: "plugin.githubactions.service"
52
+ });
53
+
54
+ var __defProp = Object.defineProperty;
55
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
56
+ var __publicField = (obj, key, value) => {
57
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
58
+ return value;
59
+ };
60
+ class GithubActionsClient {
61
+ constructor(options) {
62
+ __publicField(this, "configApi");
63
+ __publicField(this, "scmAuthApi");
64
+ this.configApi = options.configApi;
65
+ this.scmAuthApi = options.scmAuthApi;
66
+ }
67
+ async getOctokit(hostname = "github.com") {
68
+ var _a;
69
+ const { token } = await this.scmAuthApi.getCredentials({
70
+ url: `https://${hostname}/`,
71
+ additionalScope: {
72
+ customScopes: {
73
+ github: ["repo"]
74
+ }
75
+ }
76
+ });
77
+ const configs = readGithubIntegrationConfigs(
78
+ (_a = this.configApi.getOptionalConfigArray("integrations.github")) != null ? _a : []
79
+ );
80
+ const githubIntegrationConfig = configs.find((v) => v.host === hostname);
81
+ const baseUrl = githubIntegrationConfig == null ? void 0 : githubIntegrationConfig.apiBaseUrl;
82
+ return new Octokit({ auth: token, baseUrl });
83
+ }
84
+ async reRunWorkflow(options) {
85
+ const { hostname, owner, repo, runId } = options;
86
+ const octokit = await this.getOctokit(hostname);
87
+ return octokit.actions.reRunWorkflow({
88
+ owner,
89
+ repo,
90
+ run_id: runId
91
+ });
92
+ }
93
+ async listWorkflowRuns(options) {
94
+ const { hostname, owner, repo, pageSize = 100, page = 0, branch } = options;
95
+ const octokit = await this.getOctokit(hostname);
96
+ const workflowRuns = await octokit.actions.listWorkflowRunsForRepo({
97
+ owner,
98
+ repo,
99
+ per_page: pageSize,
100
+ page,
101
+ ...branch ? { branch } : {}
102
+ });
103
+ return workflowRuns.data;
104
+ }
105
+ async getWorkflow(options) {
106
+ const { hostname, owner, repo, id } = options;
107
+ const octokit = await this.getOctokit(hostname);
108
+ const workflow = await octokit.actions.getWorkflow({
109
+ owner,
110
+ repo,
111
+ workflow_id: id
112
+ });
113
+ return workflow.data;
114
+ }
115
+ async getWorkflowRun(options) {
116
+ const { hostname, owner, repo, id } = options;
117
+ const octokit = await this.getOctokit(hostname);
118
+ const run = await octokit.actions.getWorkflowRun({
119
+ owner,
120
+ repo,
121
+ run_id: id
122
+ });
123
+ return run.data;
124
+ }
125
+ async listJobsForWorkflowRun(options) {
126
+ const { hostname, owner, repo, id, pageSize = 100, page = 0 } = options;
127
+ const octokit = await this.getOctokit(hostname);
128
+ const jobs = await octokit.actions.listJobsForWorkflowRun({
129
+ owner,
130
+ repo,
131
+ run_id: id,
132
+ per_page: pageSize,
133
+ page
134
+ });
135
+ return jobs.data;
136
+ }
137
+ async downloadJobLogsForWorkflowRun(options) {
138
+ const { hostname, owner, repo, runId } = options;
139
+ const octokit = await this.getOctokit(hostname);
140
+ const workflow = await octokit.actions.downloadJobLogsForWorkflowRun({
141
+ owner,
142
+ repo,
143
+ job_id: runId
144
+ });
145
+ return workflow.data;
146
+ }
147
+ async listBranches(options) {
148
+ const { hostname, owner, repo, page = 0 } = options;
149
+ const octokit = await this.getOctokit(hostname);
150
+ const response = await octokit.rest.repos.listBranches({
151
+ owner,
152
+ repo,
153
+ per_page: 100,
154
+ page
155
+ });
156
+ return response.data;
157
+ }
158
+ async getDefaultBranch(options) {
159
+ const { hostname, owner, repo } = options;
160
+ const octokit = await this.getOctokit(hostname);
161
+ const response = await octokit.rest.repos.get({
162
+ owner,
163
+ repo
164
+ });
165
+ return response.data.default_branch;
166
+ }
167
+ }
168
+
169
+ var BuildStatus = /* @__PURE__ */ ((BuildStatus2) => {
170
+ BuildStatus2[BuildStatus2["success"] = 0] = "success";
171
+ BuildStatus2[BuildStatus2["failure"] = 1] = "failure";
172
+ BuildStatus2[BuildStatus2["pending"] = 2] = "pending";
173
+ BuildStatus2[BuildStatus2["running"] = 3] = "running";
174
+ return BuildStatus2;
175
+ })(BuildStatus || {});
176
+
177
+ const rootRouteRef = createRouteRef({
178
+ id: "github-actions"
179
+ });
180
+ const buildRouteRef = createSubRouteRef({
181
+ id: "github-actions/build",
182
+ path: "/:id",
183
+ parent: rootRouteRef
184
+ });
185
+
186
+ const githubActionsPlugin = createPlugin({
187
+ id: "github-actions",
188
+ apis: [
189
+ createApiFactory({
190
+ api: githubActionsApiRef,
191
+ deps: { configApi: configApiRef, scmAuthApi: scmAuthApiRef },
192
+ factory: ({ configApi, scmAuthApi }) => new GithubActionsClient({ configApi, scmAuthApi })
193
+ })
194
+ ],
195
+ routes: {
196
+ entityContent: rootRouteRef
197
+ }
198
+ });
199
+ const EntityGithubActionsContent = githubActionsPlugin.provide(
200
+ createRoutableExtension({
201
+ name: "EntityGithubActionsContent",
202
+ component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.Router),
203
+ mountPoint: rootRouteRef
204
+ })
205
+ );
206
+ const EntityLatestGithubActionRunCard = githubActionsPlugin.provide(
207
+ createComponentExtension({
208
+ name: "EntityLatestGithubActionRunCard",
209
+ component: {
210
+ lazy: () => import('./esm/index-CRpdBPJi.esm.js').then((m) => m.LatestWorkflowRunCard)
211
+ }
212
+ })
213
+ );
214
+ const EntityLatestGithubActionsForBranchCard = githubActionsPlugin.provide(
215
+ createComponentExtension({
216
+ name: "EntityLatestGithubActionsForBranchCard",
217
+ component: {
218
+ lazy: () => import('./esm/index-CRpdBPJi.esm.js').then(
219
+ (m) => m.LatestWorkflowsForBranchCard
220
+ )
221
+ }
222
+ })
223
+ );
224
+ const EntityRecentGithubActionsRunsCard = githubActionsPlugin.provide(
225
+ createComponentExtension({
226
+ name: "EntityRecentGithubActionsRunsCard",
227
+ component: {
228
+ lazy: () => import('./esm/index-CRpdBPJi.esm.js').then((m) => m.RecentWorkflowRunsCard)
229
+ }
230
+ })
231
+ );
232
+
233
+ const GITHUB_ACTIONS_ANNOTATION = "github.com/project-slug";
234
+ const getProjectNameFromEntity = (entity) => {
235
+ var _a, _b;
236
+ return (_b = (_a = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _a[GITHUB_ACTIONS_ANNOTATION]) != null ? _b : "";
237
+ };
238
+
239
+ const WorkflowRunStatus = (props) => {
240
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(WorkflowIcon, { ...props }), getStatusDescription(props));
241
+ };
242
+ function WorkflowIcon({
243
+ status,
244
+ conclusion
245
+ }) {
246
+ if (status === void 0)
247
+ return null;
248
+ switch (status.toLocaleLowerCase("en-US")) {
249
+ case "queued":
250
+ return /* @__PURE__ */ React.createElement(StatusPending, null);
251
+ case "in_progress":
252
+ return /* @__PURE__ */ React.createElement(StatusRunning, null);
253
+ case "completed":
254
+ switch (conclusion == null ? void 0 : conclusion.toLocaleLowerCase("en-US")) {
255
+ case "skipped":
256
+ case "cancelled":
257
+ return /* @__PURE__ */ React.createElement(StatusAborted, null);
258
+ case "timed_out":
259
+ return /* @__PURE__ */ React.createElement(StatusWarning, null);
260
+ case "failure":
261
+ return /* @__PURE__ */ React.createElement(StatusError, null);
262
+ default:
263
+ return /* @__PURE__ */ React.createElement(StatusOK, null);
264
+ }
265
+ default:
266
+ return /* @__PURE__ */ React.createElement(StatusPending, null);
267
+ }
268
+ }
269
+ function getStatusDescription({
270
+ status,
271
+ conclusion
272
+ }) {
273
+ if (status === void 0)
274
+ return "";
275
+ switch (status.toLocaleLowerCase("en-US")) {
276
+ case "queued":
277
+ return "Queued";
278
+ case "in_progress":
279
+ return "In progress";
280
+ case "completed":
281
+ switch (conclusion == null ? void 0 : conclusion.toLocaleLowerCase("en-US")) {
282
+ case "skipped":
283
+ case "cancelled":
284
+ return "Aborted";
285
+ case "timed_out":
286
+ return "Timed out";
287
+ case "failure":
288
+ return "Error";
289
+ default:
290
+ return "Completed";
291
+ }
292
+ default:
293
+ return "Pending";
294
+ }
295
+ }
296
+
297
+ const useWorkflowRunJobs = ({
298
+ hostname,
299
+ owner,
300
+ repo
301
+ }) => {
302
+ const api = useApi(githubActionsApiRef);
303
+ const { id } = useRouteRefParams(buildRouteRef);
304
+ return useAsync(async () => {
305
+ if (!repo || !owner) {
306
+ throw new Error("No repo/owner provided");
307
+ }
308
+ const jobs = await api.listJobsForWorkflowRun({
309
+ hostname,
310
+ owner,
311
+ repo,
312
+ id: parseInt(id, 10)
313
+ });
314
+ return {
315
+ total_count: jobs.total_count,
316
+ jobs: jobs.jobs.map((job) => {
317
+ var _a, _b, _c, _d;
318
+ return {
319
+ html_url: (_a = job.html_url) != null ? _a : void 0,
320
+ status: job.status,
321
+ conclusion: (_b = job.conclusion) != null ? _b : void 0,
322
+ started_at: job.started_at,
323
+ completed_at: (_c = job.completed_at) != null ? _c : void 0,
324
+ id: job.id,
325
+ name: job.name,
326
+ steps: (_d = job.steps) == null ? void 0 : _d.map((step) => {
327
+ var _a2, _b2, _c2;
328
+ return {
329
+ name: step.name,
330
+ status: step.status,
331
+ conclusion: (_a2 = step.conclusion) != null ? _a2 : void 0,
332
+ number: step.number,
333
+ started_at: (_b2 = step.started_at) != null ? _b2 : void 0,
334
+ completed_at: (_c2 = step.completed_at) != null ? _c2 : void 0
335
+ };
336
+ })
337
+ };
338
+ })
339
+ };
340
+ }, [repo, owner, id]);
341
+ };
342
+
343
+ const useWorkflowRunsDetails = ({
344
+ hostname,
345
+ owner,
346
+ repo
347
+ }) => {
348
+ const api = useApi(githubActionsApiRef);
349
+ const { id } = useRouteRefParams(buildRouteRef);
350
+ const details = useAsync(async () => {
351
+ return repo && owner ? api.getWorkflowRun({
352
+ hostname,
353
+ owner,
354
+ repo,
355
+ id: parseInt(id, 10)
356
+ }) : Promise.reject(new Error("No repo/owner provided"));
357
+ }, [repo, owner, id]);
358
+ return details;
359
+ };
360
+
361
+ const useDownloadWorkflowRunLogs = ({
362
+ hostname,
363
+ owner,
364
+ repo,
365
+ id
366
+ }) => {
367
+ const api = useApi(githubActionsApiRef);
368
+ const details = useAsync(async () => {
369
+ return repo && owner ? api.downloadJobLogsForWorkflowRun({
370
+ hostname,
371
+ owner,
372
+ repo,
373
+ runId: id
374
+ }) : Promise.reject("No repo/owner provided");
375
+ }, [repo, owner, id]);
376
+ return details;
377
+ };
378
+
379
+ const getHostnameFromEntity = (entity) => {
380
+ var _a, _b, _c;
381
+ const location = (_c = (_a = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _a[ANNOTATION_SOURCE_LOCATION]) != null ? _c : (_b = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _b[ANNOTATION_LOCATION];
382
+ return (location == null ? void 0 : location.startsWith("url:")) ? gitUrlParse(location.slice(4)).resource : void 0;
383
+ };
384
+
385
+ const useStyles$3 = makeStyles((theme) => ({
386
+ button: {
387
+ order: -1,
388
+ marginRight: 0,
389
+ marginLeft: "-20px"
390
+ },
391
+ modal: {
392
+ display: "flex",
393
+ alignItems: "center",
394
+ width: "85%",
395
+ height: "85%",
396
+ justifyContent: "center",
397
+ margin: "auto"
398
+ },
399
+ normalLogContainer: {
400
+ height: "75vh",
401
+ width: "100%"
402
+ },
403
+ modalLogContainer: {
404
+ height: "100%",
405
+ width: "100%"
406
+ },
407
+ log: {
408
+ background: theme.palette.background.default
409
+ }
410
+ }));
411
+ const WorkflowRunLogs = ({
412
+ entity,
413
+ runId,
414
+ inProgress
415
+ }) => {
416
+ const classes = useStyles$3();
417
+ const projectName = getProjectNameFromEntity(entity);
418
+ const hostname = getHostnameFromEntity(entity);
419
+ const [owner, repo] = projectName && projectName.split("/") || [];
420
+ const jobLogs = useDownloadWorkflowRunLogs({
421
+ hostname,
422
+ owner,
423
+ repo,
424
+ id: runId
425
+ });
426
+ const logText = jobLogs.value ? String(jobLogs.value) : void 0;
427
+ const [open, setOpen] = React.useState(false);
428
+ const handleOpen = () => {
429
+ setOpen(true);
430
+ };
431
+ const handleClose = () => {
432
+ setOpen(false);
433
+ };
434
+ return /* @__PURE__ */ React.createElement(Accordion, { TransitionProps: { unmountOnExit: true }, disabled: inProgress }, /* @__PURE__ */ React.createElement(
435
+ AccordionSummary,
436
+ {
437
+ expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
438
+ IconButtonProps: {
439
+ className: classes.button
440
+ }
441
+ },
442
+ /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, jobLogs.loading ? /* @__PURE__ */ React.createElement(CircularProgress, null) : "Job Log"),
443
+ /* @__PURE__ */ React.createElement(Tooltip, { title: "Open Log", TransitionComponent: Zoom, arrow: true }, /* @__PURE__ */ React.createElement(
444
+ DescriptionIcon,
445
+ {
446
+ onClick: (event) => {
447
+ event.stopPropagation();
448
+ handleOpen();
449
+ },
450
+ style: { marginLeft: "auto" }
451
+ }
452
+ )),
453
+ /* @__PURE__ */ React.createElement(
454
+ Modal,
455
+ {
456
+ className: classes.modal,
457
+ onClick: (event) => event.stopPropagation(),
458
+ open,
459
+ onClose: handleClose
460
+ },
461
+ /* @__PURE__ */ React.createElement(Fade, { in: open }, /* @__PURE__ */ React.createElement("div", { className: classes.modalLogContainer }, /* @__PURE__ */ React.createElement(
462
+ LogViewer,
463
+ {
464
+ text: logText != null ? logText : "No Values Found",
465
+ classes: { root: classes.log }
466
+ }
467
+ )))
468
+ )
469
+ ), logText && /* @__PURE__ */ React.createElement("div", { className: classes.normalLogContainer }, /* @__PURE__ */ React.createElement(LogViewer, { text: logText, classes: { root: classes.log } })));
470
+ };
471
+
472
+ const useStyles$2 = makeStyles((theme) => ({
473
+ root: {
474
+ maxWidth: 720,
475
+ margin: theme.spacing(2)
476
+ },
477
+ title: {
478
+ padding: theme.spacing(1, 0, 2, 0)
479
+ },
480
+ table: {
481
+ padding: theme.spacing(1)
482
+ },
483
+ accordionDetails: {
484
+ padding: 0
485
+ },
486
+ button: {
487
+ order: -1,
488
+ marginRight: 0,
489
+ marginLeft: "-20px"
490
+ },
491
+ externalLinkIcon: {
492
+ fontSize: "inherit",
493
+ verticalAlign: "bottom"
494
+ }
495
+ }));
496
+ const getElapsedTime = (start, end) => {
497
+ if (!start || !end) {
498
+ return "";
499
+ }
500
+ const startDate = DateTime.fromISO(start);
501
+ const endDate = end ? DateTime.fromISO(end) : DateTime.now();
502
+ const diff = endDate.diff(startDate);
503
+ const timeElapsed = diff.toFormat(`m 'minutes' s 'seconds'`);
504
+ return timeElapsed;
505
+ };
506
+ const StepView = ({ step }) => {
507
+ var _a;
508
+ return /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
509
+ ListItemText,
510
+ {
511
+ primary: step.name,
512
+ secondary: getElapsedTime(step.started_at, step.completed_at)
513
+ }
514
+ )), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
515
+ WorkflowRunStatus,
516
+ {
517
+ status: step.status.toLocaleUpperCase("en-US"),
518
+ conclusion: (_a = step.conclusion) == null ? void 0 : _a.toLocaleUpperCase("en-US")
519
+ }
520
+ )));
521
+ };
522
+ const JobListItem = ({
523
+ job,
524
+ className,
525
+ entity
526
+ }) => {
527
+ var _a;
528
+ const classes = useStyles$2();
529
+ return /* @__PURE__ */ React.createElement(Accordion, { TransitionProps: { unmountOnExit: true }, className }, /* @__PURE__ */ React.createElement(
530
+ AccordionSummary,
531
+ {
532
+ expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
533
+ IconButtonProps: {
534
+ className: classes.button
535
+ }
536
+ },
537
+ /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, job.name, " (", getElapsedTime(job.started_at, job.completed_at), ")")
538
+ ), /* @__PURE__ */ React.createElement(AccordionDetails, { className: classes.accordionDetails }, /* @__PURE__ */ React.createElement(TableContainer, null, /* @__PURE__ */ React.createElement(Table, null, (_a = job.steps) == null ? void 0 : _a.map((step) => /* @__PURE__ */ React.createElement(StepView, { key: step.number, step }))))), job.status === "queued" || job.status === "in_progress" ? /* @__PURE__ */ React.createElement(WorkflowRunLogs, { runId: job.id, inProgress: true, entity }) : /* @__PURE__ */ React.createElement(WorkflowRunLogs, { runId: job.id, inProgress: false, entity }));
539
+ };
540
+ const JobsList = ({ jobs, entity }) => {
541
+ const classes = useStyles$2();
542
+ return /* @__PURE__ */ React.createElement(Box, null, jobs && jobs.total_count > 0 && jobs.jobs.map((job) => /* @__PURE__ */ React.createElement(
543
+ JobListItem,
544
+ {
545
+ key: job.id,
546
+ job,
547
+ className: job.status !== "success" ? classes.failed : classes.success,
548
+ entity
549
+ }
550
+ )));
551
+ };
552
+ const WorkflowRunDetails = ({ entity }) => {
553
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
554
+ const projectName = getProjectNameFromEntity(entity);
555
+ const hostname = getHostnameFromEntity(entity);
556
+ const [owner, repo] = projectName && projectName.split("/") || [];
557
+ const details = useWorkflowRunsDetails({ hostname, owner, repo });
558
+ const jobs = useWorkflowRunJobs({ hostname, owner, repo });
559
+ const classes = useStyles$2();
560
+ if (details.error && details.error.message) {
561
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "h6", color: "error" }, "Failed to load build, ", details.error.message);
562
+ } else if (details.loading) {
563
+ return /* @__PURE__ */ React.createElement(LinearProgress, null);
564
+ }
565
+ return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(Box, { mb: 3 }, /* @__PURE__ */ React.createElement(Breadcrumbs, { "aria-label": "breadcrumb" }, /* @__PURE__ */ React.createElement(Link, { to: ".." }, "Workflow runs"), /* @__PURE__ */ React.createElement(Typography, null, "Workflow run details"))), /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, className: classes.table }, /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableBody, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Branch")), /* @__PURE__ */ React.createElement(TableCell, null, (_a = details.value) == null ? void 0 : _a.head_branch)), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Message")), /* @__PURE__ */ React.createElement(TableCell, null, (_c = (_b = details.value) == null ? void 0 : _b.head_commit) == null ? void 0 : _c.message)), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Commit ID")), /* @__PURE__ */ React.createElement(TableCell, null, (_e = (_d = details.value) == null ? void 0 : _d.head_commit) == null ? void 0 : _e.id)), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Workflow")), /* @__PURE__ */ React.createElement(TableCell, null, (_f = details.value) == null ? void 0 : _f.name)), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Status")), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
566
+ WorkflowRunStatus,
567
+ {
568
+ status: ((_g = details.value) == null ? void 0 : _g.status) || void 0,
569
+ conclusion: ((_h = details.value) == null ? void 0 : _h.conclusion) || void 0
570
+ }
571
+ ))), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Author")), /* @__PURE__ */ React.createElement(TableCell, null, `${(_k = (_j = (_i = details.value) == null ? void 0 : _i.head_commit) == null ? void 0 : _j.author) == null ? void 0 : _k.name} (${(_n = (_m = (_l = details.value) == null ? void 0 : _l.head_commit) == null ? void 0 : _m.author) == null ? void 0 : _n.email})`)), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Links")), /* @__PURE__ */ React.createElement(TableCell, null, ((_o = details.value) == null ? void 0 : _o.html_url) && /* @__PURE__ */ React.createElement(Link, { to: details.value.html_url }, "Workflow runs on GitHub", " ", /* @__PURE__ */ React.createElement(ExternalLinkIcon, { className: classes.externalLinkIcon })))), /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, { colSpan: 2 }, /* @__PURE__ */ React.createElement(Typography, { noWrap: true }, "Jobs"), jobs.loading ? /* @__PURE__ */ React.createElement(CircularProgress, null) : /* @__PURE__ */ React.createElement(JobsList, { jobs: jobs.value, entity })))))));
572
+ };
573
+
574
+ function useWorkflowRuns({
575
+ hostname,
576
+ owner,
577
+ repo,
578
+ branch,
579
+ initialPageSize = 6
580
+ }) {
581
+ const api = useApi(githubActionsApiRef);
582
+ const errorApi = useApi(errorApiRef);
583
+ const [total, setTotal] = useState(0);
584
+ const [page, setPage] = useState(0);
585
+ const [pageSize, setPageSize] = useState(initialPageSize);
586
+ const [branches, setBranches] = useState([]);
587
+ const [defaultBranch, setDefaultBranch] = useState("");
588
+ const {
589
+ loading,
590
+ value: runs,
591
+ retry,
592
+ error
593
+ } = useAsyncRetry(async () => {
594
+ const fetchedDefaultBranch = await api.getDefaultBranch({
595
+ hostname,
596
+ owner,
597
+ repo
598
+ });
599
+ setDefaultBranch(fetchedDefaultBranch);
600
+ let selectedBranch = branch;
601
+ if (branch === "default") {
602
+ selectedBranch = fetchedDefaultBranch;
603
+ }
604
+ const fetchBranches = async () => {
605
+ let next = true;
606
+ let iteratePage = 0;
607
+ const branchSet2 = [];
608
+ while (next) {
609
+ const branchesData = await api.listBranches({
610
+ hostname,
611
+ owner,
612
+ repo,
613
+ page: iteratePage
614
+ });
615
+ if (branchesData.length === 0) {
616
+ next = false;
617
+ }
618
+ iteratePage++;
619
+ branchSet2.push(...branchesData);
620
+ }
621
+ return branchSet2;
622
+ };
623
+ const branchSet = await fetchBranches();
624
+ setBranches(branchSet);
625
+ const workflowRunsData = await api.listWorkflowRuns({
626
+ hostname,
627
+ owner,
628
+ repo,
629
+ pageSize,
630
+ page: page + 1,
631
+ branch: selectedBranch
632
+ });
633
+ setTotal(workflowRunsData.total_count);
634
+ return workflowRunsData.workflow_runs.map((run) => {
635
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
636
+ return {
637
+ workflowName: (_a = run.name) != null ? _a : void 0,
638
+ message: (_b = run.head_commit) == null ? void 0 : _b.message,
639
+ id: `${run.id}`,
640
+ onReRunClick: async () => {
641
+ try {
642
+ await api.reRunWorkflow({
643
+ hostname,
644
+ owner,
645
+ repo,
646
+ runId: run.id
647
+ });
648
+ } catch (e) {
649
+ errorApi.post(
650
+ new Error(`Failed to rerun the workflow: ${e.message}`)
651
+ );
652
+ }
653
+ },
654
+ source: {
655
+ branchName: (_c = run.head_branch) != null ? _c : void 0,
656
+ commit: {
657
+ hash: (_d = run.head_commit) == null ? void 0 : _d.id,
658
+ url: (_g = (_e = run.head_repository) == null ? void 0 : _e.branches_url) == null ? void 0 : _g.replace(
659
+ "{/branch}",
660
+ (_f = run.head_branch) != null ? _f : ""
661
+ )
662
+ }
663
+ },
664
+ status: (_h = run.status) != null ? _h : void 0,
665
+ conclusion: (_i = run.conclusion) != null ? _i : void 0,
666
+ url: run.url,
667
+ githubUrl: run.html_url
668
+ };
669
+ });
670
+ }, [page, pageSize, repo, owner]);
671
+ return [
672
+ {
673
+ page,
674
+ pageSize,
675
+ loading,
676
+ runs,
677
+ branches,
678
+ defaultBranch,
679
+ projectName: `${owner}/${repo}`,
680
+ total,
681
+ error
682
+ },
683
+ {
684
+ runs,
685
+ setPage,
686
+ setPageSize,
687
+ retry
688
+ }
689
+ ];
690
+ }
691
+
692
+ const useStyles$1 = makeStyles(
693
+ (theme) => createStyles({
694
+ card: {
695
+ border: `1px solid ${theme.palette.divider}`,
696
+ boxShadow: theme.shadows[2],
697
+ borderRadius: "4px",
698
+ overflow: "visible",
699
+ position: "relative",
700
+ margin: theme.spacing(4, 1, 1),
701
+ flex: "1",
702
+ minWidth: "0px"
703
+ },
704
+ externalLinkIcon: {
705
+ fontSize: "inherit",
706
+ verticalAlign: "middle"
707
+ },
708
+ bottomline: {
709
+ display: "flex",
710
+ justifyContent: "space-between",
711
+ alignItems: "center",
712
+ marginTop: "-5px"
713
+ },
714
+ pagination: {
715
+ width: "100%"
716
+ }
717
+ })
718
+ );
719
+ const statusColors = {
720
+ skipped: "warning",
721
+ canceled: "info",
722
+ timed_out: "error",
723
+ failure: "error",
724
+ success: "success"
725
+ };
726
+ const matchesSearchTerm = (run, searchTerm) => {
727
+ var _a, _b, _c, _d;
728
+ const lowerCaseSearchTerm = searchTerm.toLocaleLowerCase();
729
+ return ((_a = run.workflowName) == null ? void 0 : _a.toLocaleLowerCase().includes(lowerCaseSearchTerm)) || ((_b = run.source.branchName) == null ? void 0 : _b.toLocaleLowerCase().includes(lowerCaseSearchTerm)) || ((_c = run.status) == null ? void 0 : _c.toLocaleLowerCase().includes(lowerCaseSearchTerm)) || ((_d = run.id) == null ? void 0 : _d.toLocaleLowerCase().includes(lowerCaseSearchTerm));
730
+ };
731
+ const WorkflowRunsCardView = ({
732
+ runs,
733
+ searchTerm,
734
+ loading,
735
+ onChangePageSize,
736
+ onChangePage,
737
+ page,
738
+ total,
739
+ pageSize
740
+ }) => {
741
+ const classes = useStyles$1();
742
+ const routeLink = useRouteRef(buildRouteRef);
743
+ const filteredRuns = runs == null ? void 0 : runs.filter((run) => matchesSearchTerm(run, searchTerm));
744
+ return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, filteredRuns && (runs == null ? void 0 : runs.length) !== 0 ? filteredRuns.map((run) => {
745
+ var _a, _b;
746
+ return /* @__PURE__ */ React.createElement(Grid, { key: run.id, item: true, container: true, xs: 12, sm: 4, md: 4, xl: 4 }, /* @__PURE__ */ React.createElement(Box, { className: classes.card }, /* @__PURE__ */ React.createElement(
747
+ Box,
748
+ {
749
+ display: "flex",
750
+ flexDirection: "column",
751
+ m: 3,
752
+ alignItems: "center",
753
+ justifyContent: "center"
754
+ },
755
+ /* @__PURE__ */ React.createElement(Box, { pt: 2, sx: { width: "100%" }, textAlign: "center" }, /* @__PURE__ */ React.createElement(
756
+ Tooltip,
757
+ {
758
+ title: (_a = run.status) != null ? _a : "No Status",
759
+ placement: "top-start"
760
+ },
761
+ /* @__PURE__ */ React.createElement(
762
+ Alert,
763
+ {
764
+ variant: "outlined",
765
+ severity: statusColors[run.conclusion],
766
+ style: { alignItems: "center" }
767
+ },
768
+ /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, /* @__PURE__ */ React.createElement(Link, { to: routeLink({ id: run.id }) }, /* @__PURE__ */ React.createElement(Typography, { color: "primary", variant: "h6" }, run.workflowName)))
769
+ )
770
+ ), /* @__PURE__ */ React.createElement(Tooltip, { title: (_b = run.message) != null ? _b : "No run message" }, /* @__PURE__ */ React.createElement(
771
+ Typography,
772
+ {
773
+ variant: "body2",
774
+ component: "span",
775
+ style: { fontSize: "smaller" }
776
+ },
777
+ /* @__PURE__ */ React.createElement(
778
+ MarkdownContent,
779
+ {
780
+ content: `Commit ID : ${run.source.commit.hash}`
781
+ }
782
+ )
783
+ )), run.source.branchName && /* @__PURE__ */ React.createElement(
784
+ MarkdownContent,
785
+ {
786
+ content: `Branch : ${run.source.branchName}`
787
+ }
788
+ ), /* @__PURE__ */ React.createElement(
789
+ Chip,
790
+ {
791
+ key: run.id,
792
+ size: "small",
793
+ label: `Workflow ID : ${run.id}`
794
+ }
795
+ ), /* @__PURE__ */ React.createElement(
796
+ Chip,
797
+ {
798
+ size: "small",
799
+ label: /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(
800
+ WorkflowRunStatus,
801
+ {
802
+ status: run.status,
803
+ conclusion: run.conclusion
804
+ }
805
+ ))
806
+ }
807
+ ), /* @__PURE__ */ React.createElement("div", { className: classes.bottomline }, run.githubUrl && /* @__PURE__ */ React.createElement(Link, { to: run.githubUrl }, "Workflow runs on GitHub", " ", /* @__PURE__ */ React.createElement(
808
+ ExternalLinkIcon,
809
+ {
810
+ className: classes.externalLinkIcon
811
+ }
812
+ )), /* @__PURE__ */ React.createElement(ButtonGroup, null, /* @__PURE__ */ React.createElement(Tooltip, { title: "Rerun workflow" }, /* @__PURE__ */ React.createElement(
813
+ IconButton,
814
+ {
815
+ onClick: run.onReRunClick,
816
+ style: { fontSize: "12px" }
817
+ },
818
+ /* @__PURE__ */ React.createElement(RetryIcon, null)
819
+ )))))
820
+ )));
821
+ }) : /* @__PURE__ */ React.createElement(Box, { p: 2 }, loading ? /* @__PURE__ */ React.createElement(CircularProgress, null) : "No matching runs found."), /* @__PURE__ */ React.createElement("div", { className: classes.pagination }, /* @__PURE__ */ React.createElement(
822
+ TablePagination,
823
+ {
824
+ component: "div",
825
+ count: total,
826
+ page,
827
+ rowsPerPage: pageSize,
828
+ onPageChange: (_, newPage) => onChangePage(newPage),
829
+ onRowsPerPageChange: (event) => onChangePageSize(parseInt(event.target.value, 6)),
830
+ labelRowsPerPage: "Workflows per page",
831
+ rowsPerPageOptions: [6, 12, 18, { label: "All", value: -1 }]
832
+ }
833
+ )));
834
+ };
835
+ const WorkflowRunsCardSearch = ({
836
+ searchTerm,
837
+ handleSearch,
838
+ retry
839
+ }) => {
840
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }), /* @__PURE__ */ React.createElement(
841
+ TextField,
842
+ {
843
+ type: "search",
844
+ label: "Search",
845
+ value: searchTerm,
846
+ onChange: handleSearch,
847
+ "data-testid": "search-control",
848
+ style: { marginRight: "20px" }
849
+ }
850
+ ), /* @__PURE__ */ React.createElement(ButtonGroup, null, /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload workflow runs" }, /* @__PURE__ */ React.createElement(IconButton, { onClick: retry }, /* @__PURE__ */ React.createElement(SyncIcon, null)))));
851
+ };
852
+ const WorkflowRunsCard = ({ entity }) => {
853
+ const projectName = getProjectNameFromEntity(entity);
854
+ const hostname = getHostnameFromEntity(entity);
855
+ const [owner, repo] = (projectName != null ? projectName : "/").split("/");
856
+ const [branch, setBranch] = useState("default");
857
+ const [runs, setRuns] = useState([]);
858
+ const [searchTerm, setSearchTerm] = useState("");
859
+ const handleSearch = (event) => {
860
+ setSearchTerm(event.target.value);
861
+ };
862
+ const [
863
+ { runs: runsData, branches, defaultBranch, ...cardProps },
864
+ { retry, setPage, setPageSize }
865
+ ] = useWorkflowRuns({
866
+ hostname,
867
+ owner,
868
+ repo,
869
+ branch: branch === "all" ? void 0 : branch
870
+ });
871
+ const githubHost = hostname || "github.com";
872
+ const hasNoRuns = !cardProps.loading && !runsData;
873
+ const handleMenuChange = (event) => {
874
+ const selectedValue = event.target.value;
875
+ setBranch(selectedValue);
876
+ setPage(0);
877
+ retry();
878
+ };
879
+ useEffect(() => {
880
+ setRuns(runsData);
881
+ }, [runsData, branch]);
882
+ useEffect(() => {
883
+ setBranch(defaultBranch);
884
+ }, [defaultBranch]);
885
+ return /* @__PURE__ */ React.createElement(Grid, { item: true }, hasNoRuns ? /* @__PURE__ */ React.createElement(
886
+ EmptyState,
887
+ {
888
+ missing: "data",
889
+ title: "No Workflow Data",
890
+ description: "This component has GitHub Actions enabled, but no data was found. Have you created any Workflows? Click the button below to create a new Workflow.",
891
+ action: /* @__PURE__ */ React.createElement(
892
+ Button,
893
+ {
894
+ variant: "contained",
895
+ color: "primary",
896
+ href: `https://${githubHost}/${projectName}/actions/new`
897
+ },
898
+ "Create new Workflow"
899
+ )
900
+ }
901
+ ) : /* @__PURE__ */ React.createElement(
902
+ InfoCard,
903
+ {
904
+ title: /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(GitHubIcon, null), /* @__PURE__ */ React.createElement(Box, { mr: 1 }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, projectName), /* @__PURE__ */ React.createElement(
905
+ Select,
906
+ {
907
+ value: branch,
908
+ key: branch,
909
+ label: "Branch",
910
+ onChange: handleMenuChange,
911
+ "data-testid": "menu-control",
912
+ style: {
913
+ marginLeft: "30px",
914
+ marginRight: "20px",
915
+ width: "230px"
916
+ }
917
+ },
918
+ branches.map((branchItem) => /* @__PURE__ */ React.createElement(MenuItem, { key: branchItem.name, value: branchItem.name }, branchItem.name === defaultBranch ? /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "span" }, branchItem.name, " ", /* @__PURE__ */ React.createElement(
919
+ Typography,
920
+ {
921
+ variant: "body2",
922
+ component: "span",
923
+ style: { color: "lightgray", fontSize: "x-small" }
924
+ },
925
+ "(default)"
926
+ )) : branchItem.name)),
927
+ /* @__PURE__ */ React.createElement(
928
+ MenuItem,
929
+ {
930
+ value: "all",
931
+ key: "all",
932
+ style: { color: "lightGrey", fontSize: "small" }
933
+ },
934
+ "select all branches"
935
+ )
936
+ ), /* @__PURE__ */ React.createElement(
937
+ WorkflowRunsCardSearch,
938
+ {
939
+ searchTerm,
940
+ handleSearch,
941
+ retry
942
+ }
943
+ ))
944
+ },
945
+ /* @__PURE__ */ React.createElement(
946
+ WorkflowRunsCardView,
947
+ {
948
+ runs,
949
+ loading: cardProps.loading,
950
+ onChangePageSize: setPageSize,
951
+ onChangePage: setPage,
952
+ page: cardProps.page,
953
+ total: cardProps.total,
954
+ pageSize: cardProps.pageSize,
955
+ searchTerm,
956
+ projectName
957
+ }
958
+ )
959
+ ));
960
+ };
961
+
962
+ const generatedColumns = [
963
+ {
964
+ title: "ID",
965
+ field: "id",
966
+ type: "numeric",
967
+ width: "150px"
968
+ },
969
+ {
970
+ title: "Message",
971
+ field: "message",
972
+ highlight: true,
973
+ render: (row) => {
974
+ const LinkWrapper = () => {
975
+ const routeLink = useRouteRef(buildRouteRef);
976
+ return /* @__PURE__ */ React.createElement(Link, { component: Link$1, to: routeLink({ id: row.id }) }, row.message);
977
+ };
978
+ return /* @__PURE__ */ React.createElement(LinkWrapper, null);
979
+ }
980
+ },
981
+ {
982
+ title: "Source",
983
+ render: (row) => {
984
+ var _a, _b;
985
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "body2", noWrap: true }, /* @__PURE__ */ React.createElement(Typography, { paragraph: true, variant: "body2" }, (_a = row.source) == null ? void 0 : _a.branchName), /* @__PURE__ */ React.createElement(Typography, { paragraph: true, variant: "body2" }, (_b = row.source) == null ? void 0 : _b.commit.hash));
986
+ }
987
+ },
988
+ {
989
+ title: "Workflow",
990
+ field: "workflowName"
991
+ },
992
+ {
993
+ title: "Status",
994
+ customSort: (d1, d2) => {
995
+ return getStatusDescription(d1).localeCompare(getStatusDescription(d2));
996
+ },
997
+ render: (row) => /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(WorkflowRunStatus, { status: row.status, conclusion: row.conclusion }))
998
+ },
999
+ {
1000
+ title: "Actions",
1001
+ render: (row) => /* @__PURE__ */ React.createElement(Tooltip, { title: "Rerun workflow" }, /* @__PURE__ */ React.createElement(IconButton, { onClick: row.onReRunClick }, /* @__PURE__ */ React.createElement(RetryIcon, null))),
1002
+ width: "10%"
1003
+ }
1004
+ ];
1005
+ const WorkflowRunsTableView = ({
1006
+ projectName,
1007
+ loading,
1008
+ pageSize,
1009
+ page,
1010
+ retry,
1011
+ runs,
1012
+ onChangePage,
1013
+ onChangePageSize,
1014
+ total
1015
+ }) => {
1016
+ return /* @__PURE__ */ React.createElement(
1017
+ Table$1,
1018
+ {
1019
+ isLoading: loading,
1020
+ options: { paging: true, pageSize, padding: "dense" },
1021
+ totalCount: total,
1022
+ page,
1023
+ actions: [
1024
+ {
1025
+ icon: () => /* @__PURE__ */ React.createElement(SyncIcon, null),
1026
+ tooltip: "Reload workflow runs",
1027
+ isFreeAction: true,
1028
+ onClick: () => retry()
1029
+ }
1030
+ ],
1031
+ data: runs != null ? runs : [],
1032
+ onPageChange: onChangePage,
1033
+ onRowsPerPageChange: onChangePageSize,
1034
+ style: { width: "100%" },
1035
+ title: /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(GitHubIcon, null), /* @__PURE__ */ React.createElement(Box, { mr: 1 }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, projectName)),
1036
+ columns: generatedColumns
1037
+ }
1038
+ );
1039
+ };
1040
+ const WorkflowRunsTable = ({
1041
+ entity,
1042
+ branch
1043
+ }) => {
1044
+ const projectName = getProjectNameFromEntity(entity);
1045
+ const hostname = getHostnameFromEntity(entity);
1046
+ const [owner, repo] = (projectName != null ? projectName : "/").split("/");
1047
+ const [{ runs, ...tableProps }, { retry, setPage, setPageSize }] = useWorkflowRuns({
1048
+ hostname,
1049
+ owner,
1050
+ repo,
1051
+ branch
1052
+ });
1053
+ const githubHost = hostname || "github.com";
1054
+ const hasNoRuns = !tableProps.loading && !runs;
1055
+ return hasNoRuns ? /* @__PURE__ */ React.createElement(
1056
+ EmptyState,
1057
+ {
1058
+ missing: "data",
1059
+ title: "No Workflow Data",
1060
+ description: "This component has GitHub Actions enabled, but no data was found. Have you created any Workflows? Click the button below to create a new Workflow.",
1061
+ action: /* @__PURE__ */ React.createElement(
1062
+ Button,
1063
+ {
1064
+ variant: "contained",
1065
+ color: "primary",
1066
+ href: `https://${githubHost}/${projectName}/actions/new`
1067
+ },
1068
+ "Create new Workflow"
1069
+ )
1070
+ }
1071
+ ) : /* @__PURE__ */ React.createElement(
1072
+ WorkflowRunsTableView,
1073
+ {
1074
+ ...tableProps,
1075
+ runs,
1076
+ loading: tableProps.loading,
1077
+ retry,
1078
+ onChangePageSize: setPageSize,
1079
+ onChangePage: setPage
1080
+ }
1081
+ );
1082
+ };
1083
+
1084
+ const isGithubActionsAvailable = (entity) => {
1085
+ var _a;
1086
+ return Boolean((_a = entity.metadata.annotations) == null ? void 0 : _a[GITHUB_ACTIONS_ANNOTATION]);
1087
+ };
1088
+ const Router = (props) => {
1089
+ const { view = "table" } = props;
1090
+ const { entity } = useEntity();
1091
+ if (!isGithubActionsAvailable(entity)) {
1092
+ return /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { annotation: GITHUB_ACTIONS_ANNOTATION });
1093
+ }
1094
+ const workflowRunsComponent = view === "cards" ? /* @__PURE__ */ React.createElement(WorkflowRunsCard, { entity }) : /* @__PURE__ */ React.createElement(WorkflowRunsTable, { entity });
1095
+ return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: "/", element: workflowRunsComponent }), /* @__PURE__ */ React.createElement(
1096
+ Route,
1097
+ {
1098
+ path: `${buildRouteRef.path}`,
1099
+ element: /* @__PURE__ */ React.createElement(WorkflowRunDetails, { entity })
1100
+ }
1101
+ ));
1102
+ };
1103
+
1104
+ var Router$1 = /*#__PURE__*/Object.freeze({
1105
+ __proto__: null,
1106
+ Router: Router,
1107
+ isGithubActionsAvailable: isGithubActionsAvailable
1108
+ });
1109
+
1110
+ const useStyles = makeStyles({
1111
+ externalLinkIcon: {
1112
+ fontSize: "inherit",
1113
+ verticalAlign: "bottom"
1114
+ }
1115
+ });
1116
+ const WidgetContent = (props) => {
1117
+ var _a;
1118
+ const { error, loading, lastRun, branch } = props;
1119
+ const classes = useStyles();
1120
+ if (error)
1121
+ return /* @__PURE__ */ React.createElement(Typography, null, "Couldn't fetch latest ", branch, " run");
1122
+ if (loading)
1123
+ return /* @__PURE__ */ React.createElement(LinearProgress, null);
1124
+ return /* @__PURE__ */ React.createElement(
1125
+ StructuredMetadataTable,
1126
+ {
1127
+ metadata: {
1128
+ status: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1129
+ WorkflowRunStatus,
1130
+ {
1131
+ status: lastRun.status,
1132
+ conclusion: lastRun.conclusion
1133
+ }
1134
+ )),
1135
+ message: lastRun.message,
1136
+ url: /* @__PURE__ */ React.createElement(Link, { to: (_a = lastRun.githubUrl) != null ? _a : "" }, "See more on GitHub", " ", /* @__PURE__ */ React.createElement(ExternalLinkIcon, { className: classes.externalLinkIcon }))
1137
+ }
1138
+ }
1139
+ );
1140
+ };
1141
+ const LatestWorkflowRunCard = (props) => {
1142
+ var _a, _b, _c;
1143
+ const { branch = "master", variant } = props;
1144
+ const { entity } = useEntity();
1145
+ const errorApi = useApi(errorApiRef);
1146
+ const hostname = getHostnameFromEntity(entity);
1147
+ const [owner, repo] = ((_b = (_a = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _a[GITHUB_ACTIONS_ANNOTATION]) != null ? _b : "/").split("/");
1148
+ const [{ runs, loading, error }] = useWorkflowRuns({
1149
+ hostname,
1150
+ owner,
1151
+ repo,
1152
+ branch
1153
+ });
1154
+ const lastRun = (_c = runs == null ? void 0 : runs[0]) != null ? _c : {};
1155
+ useEffect(() => {
1156
+ if (error) {
1157
+ errorApi.post(error);
1158
+ }
1159
+ }, [error, errorApi]);
1160
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: `Last ${branch} build`, variant }, /* @__PURE__ */ React.createElement(
1161
+ WidgetContent,
1162
+ {
1163
+ error,
1164
+ loading,
1165
+ branch,
1166
+ lastRun
1167
+ }
1168
+ ));
1169
+ };
1170
+ const LatestWorkflowsForBranchCard = (props) => {
1171
+ const { branch = "master", variant } = props;
1172
+ const { entity } = useEntity();
1173
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: `Last ${branch} build`, variant }, /* @__PURE__ */ React.createElement(WorkflowRunsTable, { branch, entity }));
1174
+ };
1175
+
1176
+ const firstLine = (message) => message.split("\n")[0];
1177
+ const RecentWorkflowRunsCard = (props) => {
1178
+ var _a, _b;
1179
+ const { branch, dense = false, limit = 5, variant } = props;
1180
+ const { entity } = useEntity();
1181
+ const errorApi = useApi(errorApiRef);
1182
+ const hostname = getHostnameFromEntity(entity);
1183
+ const [owner, repo] = ((_b = (_a = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _a[GITHUB_ACTIONS_ANNOTATION]) != null ? _b : "/").split("/");
1184
+ const [{ runs = [], loading, error }] = useWorkflowRuns({
1185
+ hostname,
1186
+ owner,
1187
+ repo,
1188
+ branch,
1189
+ initialPageSize: limit
1190
+ });
1191
+ useEffect(() => {
1192
+ if (error) {
1193
+ errorApi.post(error);
1194
+ }
1195
+ }, [error, errorApi]);
1196
+ const githubHost = hostname || "github.com";
1197
+ const routeLink = useRouteRef(buildRouteRef);
1198
+ if (error) {
1199
+ return /* @__PURE__ */ React.createElement(ErrorPanel, { title: error.message, error });
1200
+ }
1201
+ return /* @__PURE__ */ React.createElement(
1202
+ InfoCard,
1203
+ {
1204
+ title: "Recent Workflow Runs",
1205
+ subheader: branch ? `Branch: ${branch}` : "All Branches",
1206
+ noPadding: true,
1207
+ variant
1208
+ },
1209
+ !runs.length ? /* @__PURE__ */ React.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "This component has GitHub Actions enabled, but no workflows were found."), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement(Link, { to: `https://${githubHost}/${owner}/${repo}/actions/new` }, "Create a new workflow"))) : /* @__PURE__ */ React.createElement(
1210
+ Table$1,
1211
+ {
1212
+ isLoading: loading,
1213
+ options: {
1214
+ search: false,
1215
+ paging: false,
1216
+ padding: dense ? "dense" : "default",
1217
+ toolbar: false
1218
+ },
1219
+ columns: [
1220
+ {
1221
+ title: "Commit Message",
1222
+ field: "message",
1223
+ render: (data) => {
1224
+ var _a2;
1225
+ return /* @__PURE__ */ React.createElement(Link, { component: Link$1, to: routeLink({ id: data.id }) }, firstLine((_a2 = data.message) != null ? _a2 : ""));
1226
+ }
1227
+ },
1228
+ { title: "Branch", field: "source.branchName" },
1229
+ { title: "Status", field: "status", render: WorkflowRunStatus }
1230
+ ],
1231
+ data: runs
1232
+ }
1233
+ )
1234
+ );
1235
+ };
1236
+
1237
+ export { BuildStatus, EntityGithubActionsContent, EntityLatestGithubActionRunCard, EntityLatestGithubActionsForBranchCard, EntityRecentGithubActionsRunsCard, GITHUB_ACTIONS_ANNOTATION, GithubActionsClient, LatestWorkflowRunCard, LatestWorkflowsForBranchCard, RecentWorkflowRunsCard, Router, githubActionsApiRef, githubActionsPlugin, isGithubActionsAvailable, isGithubActionsAvailable as isPluginApplicableToEntity, githubActionsPlugin as plugin };
1238
+ //# sourceMappingURL=index.esm.js.map