feat: use annotation to get project name

This commit is contained in:
Nikita Nek Dudnik
2020-07-23 21:05:55 +02:00
parent 9bdb010432
commit 25e41d2274
12 changed files with 95 additions and 423 deletions
@@ -3,6 +3,8 @@ kind: Component
metadata:
name: shuffle-api
description: Shuffle API
annotations:
backstage.io/github-actions-id: 'spotify/backstage'
spec:
type: service
lifecycle: production
@@ -23,7 +23,7 @@ import {
} from '@octokit/types';
export class GithubActionsClient implements GithubActionsApi {
reRunWorkflow({
async reRunWorkflow({
token,
owner,
repo,
+25 -203
View File
@@ -14,211 +14,33 @@
* limitations under the License.
*/
export enum BuildStatus {
Null,
Success,
Failure,
Pending,
Running,
}
export type Build = {
commitId: string;
message: string;
branch: string;
status: BuildStatus;
uri: string;
};
export type BuildDetails = {
build: Build;
author: string;
logUrl: string;
overviewUrl: string;
};
export interface Author {
export type Step = {
name: string;
email: string;
}
export interface Committer {
name: string;
email: string;
}
export interface HeadCommit {
id: string;
tree_id: string;
message: string;
timestamp: Date;
author: Author;
committer: Committer;
}
export interface Owner {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}
export interface Repository {
id: number;
node_id: string;
name: string;
full_name: string;
private: boolean;
owner: Owner;
html_url: string;
description?: any;
fork: boolean;
url: string;
forks_url: string;
keys_url: string;
collaborators_url: string;
teams_url: string;
hooks_url: string;
issue_events_url: string;
events_url: string;
assignees_url: string;
branches_url: string;
tags_url: string;
blobs_url: string;
git_tags_url: string;
git_refs_url: string;
trees_url: string;
statuses_url: string;
languages_url: string;
stargazers_url: string;
contributors_url: string;
subscribers_url: string;
subscription_url: string;
commits_url: string;
git_commits_url: string;
comments_url: string;
issue_comment_url: string;
contents_url: string;
compare_url: string;
merges_url: string;
archive_url: string;
downloads_url: string;
issues_url: string;
pulls_url: string;
milestones_url: string;
notifications_url: string;
labels_url: string;
releases_url: string;
deployments_url: string;
}
export interface Owner2 {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}
export interface HeadRepository {
id: number;
node_id: string;
name: string;
full_name: string;
private: boolean;
owner: Owner2;
html_url: string;
description?: any;
fork: boolean;
url: string;
forks_url: string;
keys_url: string;
collaborators_url: string;
teams_url: string;
hooks_url: string;
issue_events_url: string;
events_url: string;
assignees_url: string;
branches_url: string;
tags_url: string;
blobs_url: string;
git_tags_url: string;
git_refs_url: string;
trees_url: string;
statuses_url: string;
languages_url: string;
stargazers_url: string;
contributors_url: string;
subscribers_url: string;
subscription_url: string;
commits_url: string;
git_commits_url: string;
comments_url: string;
issue_comment_url: string;
contents_url: string;
compare_url: string;
merges_url: string;
archive_url: string;
downloads_url: string;
issues_url: string;
pulls_url: string;
milestones_url: string;
notifications_url: string;
labels_url: string;
releases_url: string;
deployments_url: string;
}
export interface WorkflowRun {
id: number;
node_id: string;
head_branch: string;
head_sha: string;
run_number: number;
event: string;
status: string;
conclusion: string;
workflow_id: number;
url: string;
number: number; // starts from 1
started_at: string;
completed_at: string;
};
export type Job = {
html_url: string;
pull_requests: any[];
created_at: Date;
updated_at: Date;
jobs_url: string;
logs_url: string;
check_suite_url: string;
artifacts_url: string;
cancel_url: string;
rerun_url: string;
workflow_url: string;
head_commit: HeadCommit;
repository: Repository;
head_repository: HeadRepository;
status: string;
conclusion: string;
started_at: string;
completed_at: string;
name: string;
steps: Step[];
};
export type Jobs = {
total_count: number;
jobs: Job[];
};
export enum BuildStatus {
'success',
'failure',
'pending',
'running',
}
@@ -33,14 +33,13 @@ import {
ListItemText,
CircularProgress,
Grid,
Breadcrumbs,
} from '@material-ui/core';
import moment from 'moment';
import React from 'react';
import {
Link,
useApi,
configApiRef,
Page,
Header,
HeaderLabel,
@@ -49,11 +48,12 @@ import {
SupportButton,
pageTheme,
} from '@backstage/core';
import { Job, Step, Jobs } from '../types';
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';
const useStyles = makeStyles<Theme>(theme => ({
root: {
@@ -153,9 +153,12 @@ const JobListItem = ({ job, className }: { job: Job; className: string }) => {
* A component for Jobs visualization. Jobs are a property of a Workflow Run.
*/
export const WorkflowRunDetailsPage = () => {
const configApi = useApi(configApiRef);
const repo = configApi.getString('github-actions.repo');
const owner = configApi.getString('github-actions.owner');
const [owner, repo] = (
useProjectName({
kind: 'Component',
name: 'backstage-site',
}) ?? '/'
).split('/');
const details = useWorkflowRunsDetails(repo, owner);
const jobs = useWorkflowRunJobs(details.value?.jobs_url);
@@ -175,7 +178,7 @@ export const WorkflowRunDetailsPage = () => {
<Page theme={pageTheme.tool}>
<Header
title="GitHub Actions"
subtitle="See recent worflow runs and their status"
subtitle="See recent workflow runs and their status"
>
<HeaderLabel label="Owner" value="Spotify" />
<HeaderLabel label="Lifecycle" value="Alpha" />
@@ -187,16 +190,13 @@ export const WorkflowRunDetailsPage = () => {
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>
<div className={classes.root}>
<Typography className={classes.title} variant="h3">
<Link to="/github-actions">
<Typography component="span" variant="h3" color="primary">
&lt; Back
</Typography>
</Link>
</Typography>
<TableContainer component={Paper} className={classes.table}>
<Table>
<TableBody>
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { useAsync } from 'react-use';
import { Jobs } from '../types';
import { Jobs } from '../../api/types';
export const useWorkflowRunJobs = (jobsUrl?: string) => {
const jobs = useAsync<Jobs>(async () => {
@@ -23,13 +23,13 @@ export const useWorkflowRunsDetails = (repo: string, owner: string) => {
const auth = useApi(githubAuthApiRef);
const { id } = useParams();
const details = useAsync(async () => {
const token = await auth.getAccessToken(['repo', 'user']);
const token = await auth.getAccessToken(['repo']);
return api.getWorkflowRun({
token,
owner,
repo,
id: parseInt(id, 10),
});
}, [id]);
}, [repo, owner, id]);
return details;
};
@@ -22,8 +22,6 @@ import {
Content,
ContentHeader,
SupportButton,
useApi,
configApiRef,
} from '@backstage/core';
import { Grid } from '@material-ui/core';
import React from 'react';
@@ -31,14 +29,11 @@ import React from 'react';
import { WorkflowRunsTable } from '../WorkflowRunsTable';
export const WorkflowRunsPage = () => {
const configApi = useApi(configApiRef);
const repo = configApi.getString('github-actions.repo');
const owner = configApi.getString('github-actions.owner');
return (
<Page theme={pageTheme.tool}>
<Header
title="GitHub Actions"
subtitle="See recent worflow runs and their status"
subtitle="See recent workflow runs and their status"
>
<HeaderLabel label="Owner" value="Spotify" />
<HeaderLabel label="Lifecycle" value="Alpha" />
@@ -52,7 +47,7 @@ export const WorkflowRunsPage = () => {
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<WorkflowRunsTable repo={repo} owner={owner} />
<WorkflowRunsTable />
</Grid>
</Grid>
</Content>
@@ -14,13 +14,14 @@
* limitations under the License.
*/
import React, { FC } from 'react';
import { Link, Typography, Box, IconButton } from '@material-ui/core';
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 { Table, TableColumn } from '@backstage/core';
import { useWorkflowRuns } from './useWorkflowRuns';
import { WorkflowRunStatusIcon } from '../WorkflowRunStatusIcon';
import SyncIcon from '@material-ui/icons/Sync';
export type WorkflowRun = {
id: string;
@@ -81,9 +82,11 @@ const generatedColumns: TableColumn[] = [
{
title: 'Actions',
render: (row: Partial<WorkflowRun>) => (
<IconButton onClick={row.onReRunClick}>
<RetryIcon />
</IconButton>
<Tooltip title="Rerun workflow">
<IconButton onClick={row.onReRunClick}>
<RetryIcon />
</IconButton>
</Tooltip>
),
width: '10%',
},
@@ -120,8 +123,8 @@ const WorkflowRunsTableView: FC<Props> = ({
page={page}
actions={[
{
icon: () => <RetryIcon />,
tooltip: 'Refresh Data',
icon: () => <SyncIcon />,
tooltip: 'Reload workflow runs',
isFreeAction: true,
onClick: () => retry(),
},
@@ -141,17 +144,8 @@ const WorkflowRunsTableView: FC<Props> = ({
);
};
export const WorkflowRunsTable = ({
repo,
owner,
}: {
repo: string;
owner: string;
}) => {
const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({
repo,
owner,
});
export const WorkflowRunsTable = () => {
const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns();
return (
<WorkflowRunsTableView
{...tableProps}
@@ -19,16 +19,9 @@ import { WorkflowRun } from './WorkflowRunsTable';
import { githubActionsApiRef } from '../../api/GithubActionsApi';
import { useApi, githubAuthApiRef, errorApiRef } from '@backstage/core';
import { ActionsListWorkflowRunsForRepoResponseData } from '@octokit/types';
import { catalogApiRef } from '@backstage/plugin-catalog';
import { useProjectName } from '../useProjectName';
export function useWorkflowRuns({
repo,
owner,
}: {
repo: string;
owner: string;
}) {
const catalogApi = useApi(catalogApiRef);
export function useWorkflowRuns() {
const api = useApi(githubActionsApiRef);
const auth = useApi(githubAuthApiRef);
@@ -38,17 +31,15 @@ export function useWorkflowRuns({
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(5);
const reRunWorkflow = async () => {};
const projectName = useProjectName({
kind: 'Component',
name: 'backstage-site',
});
const { loading, value: runs, retry } = useAsyncRetry<
WorkflowRun[]
>(async () => {
const token = await auth.getAccessToken(['repo', 'user']);
const entity = await catalogApi.getEntityByName({
kind: 'Component',
name: 'backstage-site',
});
console.log(entity);
const token = await auth.getAccessToken(['repo']);
const [owner, repo] = (projectName ?? '/').split('/');
return (
api
// GitHub API pagination count starts from 1
@@ -90,23 +81,21 @@ export function useWorkflowRuns({
},
)
);
}, [page, pageSize]);
}, [page, pageSize, projectName]);
const projectName = `${owner}/${repo}`;
return [
{
page,
pageSize,
loading,
runs,
projectName,
projectName: projectName ?? '',
total,
},
{
runs,
setPage,
setPageSize,
reRunWorkflow,
retry,
},
] as const;
@@ -13,33 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type Step = {
name: string;
status: string;
conclusion: string;
number: number; // starts from 1
started_at: string;
completed_at: string;
};
export type Job = {
html_url: string;
status: string;
conclusion: string;
started_at: string;
completed_at: string;
name: string;
steps: Step[];
};
import { useAsync } from 'react-use';
import { catalogApiRef, EntityCompoundName } from '@backstage/plugin-catalog';
import { useApi } from '@backstage/core';
export type Jobs = {
total_count: number;
jobs: Job[];
};
export const useProjectName = (name: EntityCompoundName) => {
const catalogApi = useApi(catalogApiRef);
export enum BuildStatus {
'success',
'failure',
'pending',
'running',
}
const { value } = useAsync<string>(async () => {
const entity = await catalogApi.getEntityByName(name);
return (
entity?.metadata.annotations?.['backstage.io/github-actions-id'] ?? ''
);
});
return value;
};
@@ -13,7 +13,7 @@ spec:
type: website
path: '.'
schema:
required:
required:
- component_id
- description
properties:
@@ -22,7 +22,18 @@ spec:
type: string
description: Unique name of the component
description:
title: Description
title: Description
type: string
description: Description of the component
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-site
description: backstage.io
annotations:
backstage.io/github-actions-id: 'spotify/backstage'
spec:
type: website
lifecycle: production
owner: guest
-127
View File
@@ -939,133 +939,6 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@backstage/catalog-model@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.1.1-alpha.15.tgz#d1bb2eb53ff12af6868f49a24693fb2c0c209c78"
integrity sha512-nHfdeVpeyUpSyL4sjXRgCZ53ODP66o9Koc7oAXM2Gih07Z+thefIXDpY2bjWRcz+VLkVJJyVxRj/5VhrN2bukg==
dependencies:
"@backstage/config" "^0.1.1-alpha.13"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.28.2"
json-schema "^0.2.5"
lodash "^4.17.15"
uuid "^8.0.0"
yup "^0.29.1"
"@backstage/core-api@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/core-api/-/core-api-0.1.1-alpha.15.tgz#c5d70339ee1b0646841c95f69665be9094b54331"
integrity sha512-U4MWV2MZS+0tEUod0HaugYEbsBB2VLG2t41UXm2kYYhLc3JfCr99DRR2KXJSyl2To6fc9//l7IMg4efpfhpWYw==
dependencies:
"@backstage/config" "^0.1.1-alpha.13"
"@backstage/theme" "^0.1.1-alpha.15"
"@material-ui/core" "^4.9.1"
"@material-ui/icons" "^4.9.1"
"@types/react" "^16.9"
prop-types "^15.7.2"
react "^16.12.0"
react-router-dom "6.0.0-beta.0"
react-use "^14.2.0"
zen-observable "^0.8.15"
"@backstage/core@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/core/-/core-0.1.1-alpha.15.tgz#bb998f84643d6e6c88dfe6e8c3b1855cc35d7ed9"
integrity sha512-DaKwwj+mS5fa1Wq4R9+jbWMJdPj0JXv+Rzyy1iFKq9Ng9z1Rwum8h3R+q/DlPwSdSNlWaTdikZyw1C43Z+3FYw==
dependencies:
"@backstage/config" "^0.1.1-alpha.13"
"@backstage/core-api" "^0.1.1-alpha.15"
"@backstage/theme" "^0.1.1-alpha.15"
"@material-ui/core" "^4.9.1"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@types/react" "^16.9"
"@types/react-sparklines" "^1.7.0"
classnames "^2.2.6"
clsx "^1.1.0"
lodash "^4.17.15"
material-table "1.62.x"
prop-types "^15.7.2"
rc-progress "^3.0.0"
react "^16.12.0"
react-dom "^16.12.0"
react-helmet "6.1.0"
react-hook-form "^5.7.2"
react-router "6.0.0-beta.0"
react-router-dom "6.0.0-beta.0"
react-sparklines "^1.7.0"
react-syntax-highlighter "^12.2.1"
react-use "^14.2.0"
"@backstage/plugin-catalog@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/plugin-catalog/-/plugin-catalog-0.1.1-alpha.15.tgz#438b2bf035ea2acf21cddda9c33363ffc6629b55"
integrity sha512-4wGs4AROaF+nq7DsQzpgdtifwjQnTfWRO541zSNh117qJe7yIHXzY+XVAzQ5ct421hYXn7ofxwKUsGI4hYgVRg==
dependencies:
"@backstage/catalog-model" "^0.1.1-alpha.15"
"@backstage/core" "^0.1.1-alpha.15"
"@backstage/plugin-scaffolder" "^0.1.1-alpha.15"
"@backstage/plugin-sentry" "^0.1.1-alpha.15"
"@backstage/theme" "^0.1.1-alpha.15"
"@material-ui/core" "^4.9.1"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
moment "^2.26.0"
react "^16.13.1"
react-dom "^16.13.1"
react-router "6.0.0-beta.0"
react-router-dom "6.0.0-beta.0"
react-use "^14.2.0"
swr "^0.2.2"
"@backstage/plugin-scaffolder@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/plugin-scaffolder/-/plugin-scaffolder-0.1.1-alpha.15.tgz#935d21a5c815c530bd4fed89b9c1d4cb983a57bb"
integrity sha512-+HZ/aPlMcacuT7JYN2hYlZqWA8jYoAEoh7laBripm1nIxhxCLi4BuPX4X0W73a3cpCv+MYefvUq0g7yYDJvQJw==
dependencies:
"@backstage/catalog-model" "^0.1.1-alpha.15"
"@backstage/core" "^0.1.1-alpha.15"
"@backstage/plugin-catalog" "^0.1.1-alpha.15"
"@backstage/theme" "^0.1.1-alpha.15"
"@material-ui/core" "^4.9.1"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@rjsf/core" "^2.1.0"
"@rjsf/material-ui" "^2.1.0"
classnames "^2.2.6"
moment "^2.26.0"
react "^16.13.1"
react-dom "^16.13.1"
react-lazylog "^4.5.2"
react-router "6.0.0-beta.0"
react-router-dom "6.0.0-beta.0"
react-use "^14.2.0"
swr "^0.2.2"
"@backstage/plugin-sentry@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/plugin-sentry/-/plugin-sentry-0.1.1-alpha.15.tgz#8a507f7143766582dc5bed7a9ee09ea256cbdd0d"
integrity sha512-wBOSqOvF6qedZ9gAYR3aQ8rqbgCUQz5WA5Mw7KZVa0FeWdjLIKYsDl0oI6EE3EQZV9iZef6kzY7W/8gjJV3fHQ==
dependencies:
"@backstage/core" "^0.1.1-alpha.15"
"@backstage/theme" "^0.1.1-alpha.15"
"@material-ui/core" "^4.9.1"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@types/react" "^16.9"
react "^16.13.1"
react-dom "^16.13.1"
react-sparklines "^1.7.0"
react-use "^14.2.0"
timeago.js "^4.0.2"
"@backstage/theme@^0.1.1-alpha.15":
version "0.1.1-alpha.15"
resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.1.1-alpha.15.tgz#b30ec50ef0aba672f3b6f6ed3fb0209ec2990008"
integrity sha512-cyE3jj3Oynd8NHYSVm7H1Dhz573oe7V6vOAetdoqt7fX7I28onoSrUzv8x9eVM8uX4FiMck8PISPVKPGddCDZQ==
dependencies:
"@material-ui/core" "^4.9.1"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"