feat: IoC, DI, indirection, routes, tabs, catalog, app, github-actions

This commit is contained in:
Ivan Shmidt
2020-08-21 23:04:51 +02:00
parent 9b8024352a
commit d3db3a7849
20 changed files with 494 additions and 178 deletions
+34
View File
@@ -0,0 +1,34 @@
/*
* 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 { Entity } from '@backstage/catalog-model';
import { Routes, Route } from 'react-router';
import { rootRouteRef, buildRouteRef } from './plugin';
import { WorkflowRunDetails } from './components/WorkflowRunDetails';
import { WorkflowRunsTable } from './components/WorkflowRunsTable';
export const GitHubActionsPlugin = ({ entity }: { entity: Entity }) => (
<Routes>
<Route
path={`/${rootRouteRef.path}`}
element={<WorkflowRunsTable entity={entity} />}
/>
<Route
path={`/${buildRouteRef.path}`}
element={<WorkflowRunDetails entity={entity} />}
/>
</Routes>
);
@@ -14,7 +14,6 @@
* 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';
@@ -35,13 +34,16 @@ import {
LinearProgress,
CircularProgress,
Theme,
Link,
Breadcrumbs,
Link as MaterialLink,
} 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';
import { Entity } from '@backstage/catalog-model';
import { Link } from '@backstage/core';
const useStyles = makeStyles<Theme>(theme => ({
root: {
@@ -140,18 +142,8 @@ const JobListItem = ({ job, className }: { job: Job; className: string }) => {
);
};
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);
export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => {
const projectName = useProjectName(entity);
const [owner, repo] = projectName.value ? projectName.value.split('/') : [];
const details = useWorkflowRunsDetails(repo, owner);
@@ -170,6 +162,10 @@ export const WorkflowRunDetails = () => {
}
return (
<div className={classes.root}>
<Breadcrumbs aria-label="breadcrumb">
<Link to="..">Workflow runs</Link>
<Typography>Workflow run details</Typography>
</Breadcrumbs>
<TableContainer component={Paper} className={classes.table}>
<Table>
<TableBody>
@@ -211,10 +207,10 @@ export const WorkflowRunDetails = () => {
</TableCell>
<TableCell>
{details.value?.html_url && (
<Link target="_blank" href={details.value.html_url}>
<MaterialLink target="_blank" href={details.value.html_url}>
Workflow runs on GitHub{' '}
<ExternalLinkIcon className={classes.externalLinkIcon} />
</Link>
</MaterialLink>
)}
</TableCell>
</TableRow>
@@ -1,65 +0,0 @@
/*
* 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 { Typography, Grid, Breadcrumbs } from '@material-ui/core';
import React from 'react';
import {
Link,
Page,
Header,
HeaderLabel,
Content,
ContentHeader,
SupportButton,
pageTheme,
} from '@backstage/core';
import { WorkflowRunDetails } from '../WorkflowRunDetails';
/**
* A component for Jobs visualization. Jobs are a property of a Workflow Run.
*/
export const WorkflowRunDetailsPage = () => {
return (
<Page theme={pageTheme.tool}>
<Header
title="GitHub Actions"
subtitle="See recent workflow runs and their status"
>
<HeaderLabel label="Owner" value="Spotify" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Workflow run details">
<SupportButton>
This plugin allows you to view and interact with your builds within
the GitHub Actions environment.
</SupportButton>
</ContentHeader>
<Breadcrumbs aria-label="breadcrumb">
<Link to="/github-actions">Workflow runs</Link>
<Typography>Workflow run details</Typography>
</Breadcrumbs>
<Grid container spacing={3} direction="column">
<Grid item>
<WorkflowRunDetails />
</Grid>
</Grid>
</Content>
</Page>
);
};
@@ -1,17 +0,0 @@
/*
* 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 { WorkflowRunDetailsPage } from './WorkflowRunDetailsPage';
@@ -1,56 +0,0 @@
/*
* 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 {
Header,
HeaderLabel,
pageTheme,
Page,
Content,
ContentHeader,
SupportButton,
} from '@backstage/core';
import { Grid } from '@material-ui/core';
import React from 'react';
import { WorkflowRunsTable } from '../WorkflowRunsTable';
export const WorkflowRunsPage = () => {
return (
<Page theme={pageTheme.tool}>
<Header
title="GitHub Actions"
subtitle="See recent workflow runs and their status"
>
<HeaderLabel label="Owner" value="Spotify" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Workflow runs">
<SupportButton>
This plugin allows you to view and interact with your builds within
the GitHub Actions environment.
</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<WorkflowRunsTable />
</Grid>
</Grid>
</Content>
</Page>
);
};
@@ -1,17 +0,0 @@
/*
* 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 { WorkflowRunsPage } from './WorkflowRunsPage';
@@ -25,6 +25,7 @@ import SyncIcon from '@material-ui/icons/Sync';
import { buildRouteRef } from '../../plugin';
import { useEntityCompoundName } from '@backstage/plugin-catalog';
import { useProjectName } from '../useProjectName';
import { Entity } from '@backstage/catalog-model';
export type WorkflowRun = {
id: string;
@@ -134,6 +135,7 @@ export const WorkflowRunsTableView: FC<Props> = ({
data={runs ?? []}
onChangePage={onChangePage}
onChangeRowsPerPage={onChangePageSize}
style={{ width: '100%' }}
title={
<Box display="flex" alignItems="center">
<GitHubIcon />
@@ -146,25 +148,14 @@ export const WorkflowRunsTableView: FC<Props> = ({
);
};
export const WorkflowRunsTable = () => {
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);
export const WorkflowRunsTable = ({ entity }: { entity: Entity }) => {
const { value: projectName, loading } = useProjectName(entity);
const [owner, repo] = (projectName ?? '/').split('/');
const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({
owner,
repo,
});
return (
<WorkflowRunsTableView
{...tableProps}
@@ -15,15 +15,13 @@
*/
import { useAsync } from 'react-use';
import { catalogApiRef, EntityCompoundName } from '@backstage/plugin-catalog';
import { useApi } from '@backstage/core';
import { Entity } from '@backstage/catalog-model';
export const useProjectName = (name: EntityCompoundName) => {
const catalogApi = useApi(catalogApiRef);
export const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug';
export const useProjectName = (entity: Entity) => {
const { value, loading, error } = useAsync(async () => {
const entity = await catalogApi.getEntityByName(name);
return entity?.metadata.annotations?.['github.com/project-slug'] ?? '';
return entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? '';
});
return { value, loading, error };
};
+2
View File
@@ -17,3 +17,5 @@
export { plugin } from './plugin';
export * from './api';
export { Widget } from './components/Widget';
export { GitHubActionsPlugin } from './Router';
export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName';
+4 -13
View File
@@ -15,28 +15,19 @@
*/
import { createPlugin, createRouteRef } from '@backstage/core';
import { WorkflowRunDetailsPage } from './components/WorkflowRunDetailsPage';
import { WorkflowRunsPage } from './components/WorkflowRunsPage';
// TODO(freben): This is just a demo route for now
export const rootRouteRef = createRouteRef({
path: '/github-actions',
path: '',
title: 'GitHub Actions',
});
export const projectRouteRef = createRouteRef({
path: '/github-actions/:kind/:optionalNamespaceAndName/',
title: 'GitHub Actions for project',
});
export const buildRouteRef = createRouteRef({
path: '/github-actions/workflow-run/:id',
path: ':id',
title: 'GitHub Actions Workflow Run',
});
export const plugin = createPlugin({
id: 'github-actions',
register({ router }) {
router.addRoute(rootRouteRef, WorkflowRunsPage);
router.addRoute(projectRouteRef, WorkflowRunsPage);
router.addRoute(buildRouteRef, WorkflowRunDetailsPage);
},
register() {},
});