diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index f306c03ff8..4ab0decfbe 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -23,6 +23,7 @@ "dependencies": { "@backstage/catalog-model": "^0.1.1-alpha.16", "@backstage/core": "^0.1.1-alpha.16", + "@backstage/plugin-github-actions": "^0.1.1-alpha.16", "@backstage/plugin-scaffolder": "^0.1.1-alpha.16", "@backstage/plugin-sentry": "^0.1.1-alpha.16", "@backstage/theme": "^0.1.1-alpha.16", diff --git a/plugins/catalog/src/components/EntityPage/EntityPage.tsx b/plugins/catalog/src/components/EntityPage/EntityPage.tsx index 1c56924589..ce128777bd 100644 --- a/plugins/catalog/src/components/EntityPage/EntityPage.tsx +++ b/plugins/catalog/src/components/EntityPage/EntityPage.tsx @@ -28,6 +28,7 @@ import { useApi, } from '@backstage/core'; import { SentryIssuesWidget } from '@backstage/plugin-sentry'; +import { Widget as GithubActionsWidget } from '@backstage/plugin-github-actions'; import { Grid, Box } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import React, { FC, useEffect, useState } from 'react'; @@ -192,6 +193,13 @@ export const EntityPage: FC<{}> = () => { statsFor="24h" /> + {entity.metadata?.annotations?.[ + 'backstage.io/github-actions-id' + ] && ( + + + + )} diff --git a/plugins/catalog/src/components/useEntityCompoundName.ts b/plugins/catalog/src/components/useEntityCompoundName.ts new file mode 100644 index 0000000000..2be23cb6d9 --- /dev/null +++ b/plugins/catalog/src/components/useEntityCompoundName.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useParams } from 'react-router'; + +/** + * Grabs entity kind and name + optional namespace from location + */ +export const useEntityCompoundName = () => { + const params = useParams(); + const { kind, optionalNamespaceAndName = '' } = params; + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + return { kind, name, namespace }; +}; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 701394df34..36fd69971d 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -18,3 +18,4 @@ export { plugin } from './plugin'; export * from './api/CatalogClient'; export * from './api/types'; export * from './routes'; +export { useEntityCompoundName } from './components/useEntityCompoundName'; diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index bb54fae703..acdfb90cf3 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -33,12 +33,14 @@ export type GithubActionsApi = { repo, pageSize, page, + branch, }: { token: string; owner: string; repo: string; pageSize?: number; page?: number; + branch?: string; }) => Promise; getWorkflow: ({ token, diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts index a96f5341fd..72f65075e6 100644 --- a/plugins/github-actions/src/api/GithubActionsClient.ts +++ b/plugins/github-actions/src/api/GithubActionsClient.ts @@ -46,12 +46,14 @@ export class GithubActionsClient implements GithubActionsApi { repo, pageSize = 100, page = 0, + branch, }: { token: string; owner: string; repo: string; pageSize?: number; page?: number; + branch?: string; }): Promise { const workflowRuns = await new Octokit({ auth: token, @@ -60,6 +62,7 @@ export class GithubActionsClient implements GithubActionsApi { repo, per_page: pageSize, page, + ...(branch ? { branch } : {}), }); return workflowRuns.data; } diff --git a/plugins/github-actions/src/components/Widget/Widget.tsx b/plugins/github-actions/src/components/Widget/Widget.tsx new file mode 100644 index 0000000000..031364c8ce --- /dev/null +++ b/plugins/github-actions/src/components/Widget/Widget.tsx @@ -0,0 +1,110 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect } from 'react'; +import { useWorkflowRuns } from '../useWorkflowRuns'; +import { WorkflowRun } from '../WorkflowRunsTable'; +import { Entity } from '@backstage/catalog-model'; +import { WorkflowRunStatus } from '../WorkflowRunStatus'; +import { + Link, + Theme, + makeStyles, + LinearProgress, + Typography, +} from '@material-ui/core'; +import { + InfoCard, + StructuredMetadataTable, + errorApiRef, + useApi, +} from '@backstage/core'; +import ExternalLinkIcon from '@material-ui/icons/Launch'; + +const useStyles = makeStyles({ + externalLinkIcon: { + fontSize: 'inherit', + verticalAlign: 'bottom', + }, +}); + +const WidgetContent = ({ + error, + loading, + lastRun, + branch, +}: { + error?: Error; + loading?: boolean; + lastRun: WorkflowRun; + branch: string; +}) => { + const classes = useStyles(); + if (error) return Couldn't fetch latest {branch} run; + if (loading) return ; + return ( + + + + ), + message: lastRun.message, + url: ( + + See more on GitHub{' '} + + + ), + }} + /> + ); +}; + +export const Widget = ({ + entity, + branch = 'master', +}: { + entity: Entity; + branch: string; +}) => { + const errorApi = useApi(errorApiRef); + const [owner, repo] = ( + entity?.metadata.annotations?.['backstage.io/github-actions-id'] ?? '/' + ).split('/'); + const [{ runs, loading, error }] = useWorkflowRuns({ + owner, + repo, + branch, + }); + const lastRun = runs?.[0] ?? ({} as WorkflowRun); + useEffect(() => { + if (error) { + errorApi.post(error); + } + }, [error, errorApi]); + + return ( + + + + ); +}; diff --git a/plugins/github-actions/src/components/Widget/index.ts b/plugins/github-actions/src/components/Widget/index.ts new file mode 100644 index 0000000000..2b34671ab5 --- /dev/null +++ b/plugins/github-actions/src/components/Widget/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { Widget } from './Widget'; diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx new file mode 100644 index 0000000000..8c445ca9f6 --- /dev/null +++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -0,0 +1,236 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { useEntityCompoundName } from '@backstage/plugin-catalog'; +import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; +import { useWorkflowRunJobs } from './useWorkflowRunJobs'; +import { useProjectName } from '../useProjectName'; +import { + makeStyles, + Box, + TableRow, + TableCell, + ListItemText, + ExpansionPanel, + ExpansionPanelSummary, + Typography, + ExpansionPanelDetails, + TableContainer, + Table, + Paper, + TableBody, + LinearProgress, + CircularProgress, + Theme, + Link, +} from '@material-ui/core'; +import { Jobs, Job, Step } from '../../api'; +import moment from 'moment'; +import { WorkflowRunStatus } from '../WorkflowRunStatus'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ExternalLinkIcon from '@material-ui/icons/Launch'; + +const useStyles = makeStyles(theme => ({ + root: { + maxWidth: 720, + margin: theme.spacing(2), + }, + title: { + padding: theme.spacing(1, 0, 2, 0), + }, + table: { + padding: theme.spacing(1), + }, + expansionPanelDetails: { + padding: 0, + }, + button: { + order: -1, + marginRight: 0, + marginLeft: '-20px', + }, + externalLinkIcon: { + fontSize: 'inherit', + verticalAlign: 'bottom', + }, +})); + +const JobsList = ({ jobs }: { jobs?: Jobs }) => { + const classes = useStyles(); + return ( + + {jobs && + jobs.total_count > 0 && + jobs.jobs.map((job: Job) => ( + + ))} + + ); +}; + +const getElapsedTime = (start: string, end: string) => { + const diff = moment(moment(end || moment()).diff(moment(start))); + const timeElapsed = diff.format('m [minutes] s [seconds]'); + return timeElapsed; +}; + +const StepView = ({ step }: { step: Step }) => { + return ( + + + + + + + + + ); +}; + +const JobListItem = ({ job, className }: { job: Job; className: string }) => { + const classes = useStyles(); + return ( + + } + aria-controls={`panel-${name}-content`} + id={`panel-${name}-header`} + IconButtonProps={{ + className: classes.button, + }} + > + + {job.name} ({getElapsedTime(job.started_at, job.completed_at)}) + + + + + + {job.steps.map((step: Step) => ( + + ))} +
+
+
+
+ ); +}; + +export const WorkflowRunDetails = () => { + let entityCompoundName = useEntityCompoundName(); + if (!entityCompoundName.name) { + // TODO(shmidt-i): remove when is fully integrated + // into the entity view + entityCompoundName = { + kind: 'Component', + name: 'backstage', + namespace: 'default', + }; + } + const projectName = useProjectName(entityCompoundName); + + const [owner, repo] = projectName.value ? projectName.value.split('/') : []; + const details = useWorkflowRunsDetails(repo, owner); + const jobs = useWorkflowRunJobs(details.value?.jobs_url); + + const error = projectName.error || (projectName.value && details.error); + const classes = useStyles(); + if (error) { + return ( + + Failed to load build, {error.message} + + ); + } else if (projectName.loading || details.loading) { + return ; + } + return ( +
+ + + + + + Branch + + {details.value?.head_branch} + + + + Message + + {details.value?.head_commit.message} + + + + Commit ID + + {details.value?.head_commit.id} + + + + Status + + + + + + + + Author + + {`${details.value?.head_commit.author.name} (${details.value?.head_commit.author.email})`} + + + + Links + + + {details.value?.html_url && ( + + Workflow runs on GitHub{' '} + + + )} + + + + + Jobs + {jobs.loading ? ( + + ) : ( + + )} + + + +
+
+
+ ); +}; diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/index.ts b/plugins/github-actions/src/components/WorkflowRunDetails/index.ts new file mode 100644 index 0000000000..2886a26740 --- /dev/null +++ b/plugins/github-actions/src/components/WorkflowRunDetails/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { WorkflowRunDetails } from './WorkflowRunDetails'; diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/useWorkflowRunJobs.ts b/plugins/github-actions/src/components/WorkflowRunDetails/useWorkflowRunJobs.ts similarity index 100% rename from plugins/github-actions/src/components/WorkflowRunDetailsPage/useWorkflowRunJobs.ts rename to plugins/github-actions/src/components/WorkflowRunDetails/useWorkflowRunJobs.ts diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/useWorkflowRunsDetails.ts b/plugins/github-actions/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts similarity index 85% rename from plugins/github-actions/src/components/WorkflowRunDetailsPage/useWorkflowRunsDetails.ts rename to plugins/github-actions/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts index 623f55eea6..b38284d52f 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/useWorkflowRunsDetails.ts +++ b/plugins/github-actions/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts @@ -24,12 +24,14 @@ export const useWorkflowRunsDetails = (repo: string, owner: string) => { const { id } = useParams(); const details = useAsync(async () => { const token = await auth.getAccessToken(['repo']); - return api.getWorkflowRun({ - token, - owner, - repo, - id: parseInt(id, 10), - }); + return repo && owner + ? api.getWorkflowRun({ + token, + owner, + repo, + id: parseInt(id, 10), + }) + : Promise.reject('No repo/owner provided'); }, [repo, owner, id]); return details; }; diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx b/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx index 283fd60ec6..ec9a3f484d 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx +++ b/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx @@ -14,28 +14,7 @@ * limitations under the License. */ -import { - Button, - LinearProgress, - makeStyles, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, - Theme, - Typography, - Box, - ExpansionPanelDetails, - ExpansionPanel, - ExpansionPanelSummary, - ListItemText, - CircularProgress, - Grid, - Breadcrumbs, -} from '@material-ui/core'; -import moment from 'moment'; +import { Typography, Grid, Breadcrumbs } from '@material-ui/core'; import React from 'react'; import { @@ -48,133 +27,13 @@ import { SupportButton, pageTheme, } from '@backstage/core'; -import { Job, Step, Jobs } from '../../api/types'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; -import { useWorkflowRunJobs } from './useWorkflowRunJobs'; -import { WorkflowRunStatusIcon } from '../WorkflowRunStatusIcon/WorkflowRunStatusIcon'; -import { useProjectName } from '../useProjectName'; -import GitHubIcon from '@material-ui/icons/GitHub'; -const useStyles = makeStyles(theme => ({ - root: { - maxWidth: 720, - margin: theme.spacing(2), - }, - title: { - padding: theme.spacing(1, 0, 2, 0), - }, - table: { - padding: theme.spacing(1), - }, - expansionPanelDetails: { - padding: 0, - }, - button: { - order: -1, - marginRight: 0, - marginLeft: '-20px', - }, -})); - -const JobsList = ({ jobs }: { jobs?: Jobs }) => { - const classes = useStyles(); - return ( - - {jobs && - jobs.total_count > 0 && - jobs.jobs.map((job: Job) => ( - - ))} - - ); -}; - -const getElapsedTime = (start: string, end: string) => { - const diff = moment(moment(end || moment()).diff(moment(start))); - const timeElapsed = diff.format('m [minutes] s [seconds]'); - return timeElapsed; -}; - -const StepView = ({ step }: { step: Step }) => { - return ( - - - - - - - {step.status} - - - ); -}; - -const JobListItem = ({ job, className }: { job: Job; className: string }) => { - const classes = useStyles(); - return ( - - } - aria-controls={`panel-${name}-content`} - id={`panel-${name}-header`} - IconButtonProps={{ - className: classes.button, - }} - > - - {job.name} ({getElapsedTime(job.started_at, job.completed_at)}) - - - - - - {job.steps.map((step: Step) => ( - - ))} -
-
-
-
- ); -}; +import { WorkflowRunDetails } from '../WorkflowRunDetails'; /** * A component for Jobs visualization. Jobs are a property of a Workflow Run. */ export const WorkflowRunDetailsPage = () => { - const [owner, repo] = ( - useProjectName({ - kind: 'Component', - name: 'backstage', - }) ?? '/' - ).split('/'); - const details = useWorkflowRunsDetails(repo, owner); - const jobs = useWorkflowRunJobs(details.value?.jobs_url); - - const classes = useStyles(); - - if (details.loading) { - return ; - } else if (details.error) { - return ( - - Failed to load build, {details.error.message} - - ); - } - return (
{ -
- - - - - - Branch - - {details.value?.head_branch} - - - - Message - - - {details.value?.head_commit.message} - - - - - Commit ID - - {details.value?.head_commit.id} - - - - Status - - - {' '} - {details.value?.status.toUpperCase()} - - - - - Author - - {`${details.value?.head_commit.author.name} (${details.value?.head_commit.author.email})`} - - - - Links - - - {details.value?.html_url && ( - - - - )} - - - - - Jobs - {jobs.loading ? ( - - ) : ( - - )} - - - -
-
-
+
diff --git a/plugins/github-actions/src/components/WorkflowRunStatusIcon/WorkflowRunStatusIcon.tsx b/plugins/github-actions/src/components/WorkflowRunStatus/WorkflowRunStatus.tsx similarity index 71% rename from plugins/github-actions/src/components/WorkflowRunStatusIcon/WorkflowRunStatusIcon.tsx rename to plugins/github-actions/src/components/WorkflowRunStatus/WorkflowRunStatus.tsx index 645224ad24..089c319802 100644 --- a/plugins/github-actions/src/components/WorkflowRunStatusIcon/WorkflowRunStatusIcon.tsx +++ b/plugins/github-actions/src/components/WorkflowRunStatus/WorkflowRunStatus.tsx @@ -17,7 +17,7 @@ import { StatusPending, StatusRunning, StatusOK } from '@backstage/core'; import React from 'react'; -export const WorkflowRunStatusIcon = ({ +export const WorkflowRunStatus = ({ status, }: { status: string | undefined; @@ -25,12 +25,28 @@ export const WorkflowRunStatusIcon = ({ if (status === undefined) return null; switch (status.toLowerCase()) { case 'queued': - return ; + return ( + <> + Queued + + ); case 'in_progress': - return ; + return ( + <> + In progress + + ); case 'completed': - return ; + return ( + <> + Completed + + ); default: - return ; + return ( + <> + Pending + + ); } }; diff --git a/plugins/github-actions/src/components/WorkflowRunStatusIcon/index.ts b/plugins/github-actions/src/components/WorkflowRunStatus/index.ts similarity index 90% rename from plugins/github-actions/src/components/WorkflowRunStatusIcon/index.ts rename to plugins/github-actions/src/components/WorkflowRunStatus/index.ts index 05fff3f46d..8ebca32cbd 100644 --- a/plugins/github-actions/src/components/WorkflowRunStatusIcon/index.ts +++ b/plugins/github-actions/src/components/WorkflowRunStatus/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { WorkflowRunStatusIcon } from './WorkflowRunStatusIcon'; +export { WorkflowRunStatus } from './WorkflowRunStatus'; diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 313a51e28a..a2a6ead9b5 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -17,16 +17,20 @@ import React, { FC } from 'react'; import { Link, Typography, Box, IconButton, Tooltip } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import GitHubIcon from '@material-ui/icons/GitHub'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink, generatePath } from 'react-router-dom'; import { Table, TableColumn } from '@backstage/core'; -import { useWorkflowRuns } from './useWorkflowRuns'; -import { WorkflowRunStatusIcon } from '../WorkflowRunStatusIcon'; +import { useWorkflowRuns } from '../useWorkflowRuns'; +import { WorkflowRunStatus } from '../WorkflowRunStatus'; import SyncIcon from '@material-ui/icons/Sync'; +import { buildRouteRef } from '../../plugin'; +import { useEntityCompoundName } from '@backstage/plugin-catalog'; +import { useProjectName } from '../useProjectName'; export type WorkflowRun = { id: string; message: string; url?: string; + githubUrl?: string; source: { branchName: string; commit: { @@ -52,7 +56,7 @@ const generatedColumns: TableColumn[] = [ render: (row: Partial) => ( {row.message} @@ -69,13 +73,11 @@ const generatedColumns: TableColumn[] = [ }, { title: 'Status', + width: '150px', + render: (row: Partial) => ( - - - - {row.status} - + ), }, @@ -104,7 +106,7 @@ type Props = { onChangePageSize: (pageSize: number) => void; }; -const WorkflowRunsTableView: FC = ({ +export const WorkflowRunsTableView: FC = ({ projectName, loading, pageSize, @@ -145,10 +147,28 @@ const WorkflowRunsTableView: FC = ({ }; export const WorkflowRunsTable = () => { - const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns(); + let entityCompoundName = useEntityCompoundName(); + + if (!entityCompoundName.name) { + // TODO(shmidt-i): remove when is fully integrated + // into the entity view + entityCompoundName = { + kind: 'Component', + name: 'backstage', + namespace: 'default', + }; + } + + const { value: projectName, loading } = useProjectName(entityCompoundName); + const [owner, repo] = (projectName ?? '/').split('/'); + const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ + owner, + repo, + }); return ( { const catalogApi = useApi(catalogApiRef); - const { value } = useAsync(async () => { + const { value, loading, error } = useAsync(async () => { const entity = await catalogApi.getEntityByName(name); return ( entity?.metadata.annotations?.['backstage.io/github-actions-id'] ?? '' ); }); - return value; + return { value, loading, error }; }; diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/useWorkflowRuns.ts b/plugins/github-actions/src/components/useWorkflowRuns.ts similarity index 81% rename from plugins/github-actions/src/components/WorkflowRunsTable/useWorkflowRuns.ts rename to plugins/github-actions/src/components/useWorkflowRuns.ts index 4f72f88de0..f935d57810 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/useWorkflowRuns.ts +++ b/plugins/github-actions/src/components/useWorkflowRuns.ts @@ -15,13 +15,20 @@ */ import { useState } from 'react'; import { useAsyncRetry } from 'react-use'; -import { WorkflowRun } from './WorkflowRunsTable'; -import { githubActionsApiRef } from '../../api/GithubActionsApi'; +import { WorkflowRun } from './WorkflowRunsTable/WorkflowRunsTable'; +import { githubActionsApiRef } from '../api/GithubActionsApi'; import { useApi, githubAuthApiRef, errorApiRef } from '@backstage/core'; import { ActionsListWorkflowRunsForRepoResponseData } from '@octokit/types'; -import { useProjectName } from '../useProjectName'; -export function useWorkflowRuns() { +export function useWorkflowRuns({ + owner, + repo, + branch, +}: { + owner: string; + repo: string; + branch?: string; +}) { const api = useApi(githubActionsApiRef); const auth = useApi(githubAuthApiRef); @@ -31,19 +38,21 @@ export function useWorkflowRuns() { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(5); - const projectName = useProjectName({ - kind: 'Component', - name: 'backstage', - }); - const { loading, value: runs, retry } = useAsyncRetry< + const { loading, value: runs, retry, error } = useAsyncRetry< WorkflowRun[] >(async () => { const token = await auth.getAccessToken(['repo']); - const [owner, repo] = (projectName ?? '/').split('/'); return ( api // GitHub API pagination count starts from 1 - .listWorkflowRuns({ token, owner, repo, pageSize, page: page + 1 }) + .listWorkflowRuns({ + token, + owner, + repo, + pageSize, + page: page + 1, + branch, + }) .then( ( workflowRunsData: ActionsListWorkflowRunsForRepoResponseData, @@ -77,11 +86,12 @@ export function useWorkflowRuns() { }, status: run.status, url: run.url, + githubUrl: run.html_url, })); }, ) ); - }, [page, pageSize, projectName]); + }, [page, pageSize, repo, owner]); return [ { @@ -89,8 +99,9 @@ export function useWorkflowRuns() { pageSize, loading, runs, - projectName: projectName ?? '', + projectName: `${owner}/${repo}`, total, + error, }, { runs, diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index d67bc6a864..4a69c363cd 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -16,3 +16,4 @@ export { plugin } from './plugin'; export * from './api'; +export { Widget } from './components/Widget'; diff --git a/yarn.lock b/yarn.lock index 4541fdaa69..69207fe1d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20295,11 +20295,6 @@ whatwg-fetch@^2.0.0, whatwg-fetch@^2.0.4: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz#8e134f701f0a4ab5fda82626f113e2b647fd16dc" - integrity sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w== - whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"