Merge branch 'master' of github.com:spotify/backstage into scaffolder/new-templates

* 'master' of github.com:spotify/backstage:
  Add AA to adopters (#1684)
  docs: Add link to Figma library (#1674)
  docs: add SDA SE to adopters (#1678)
  fix: get package name from package.json
  fix(build-imgae): get package name from env
  fix linting
  feat(scaffolder): add color bg to template cards, use correct header colors
  auth-backend: tweak error messages
  cli: pin esbuild to 0.6.3
  fix: use gitgub token from UI
  fix: code review
  Update packages/app/src/apis.ts
  feat: connect UI and API
  docs(techdocs-cli): add docs on deploying / bumping up version
This commit is contained in:
blam
2020-07-19 20:15:38 +02:00
23 changed files with 274 additions and 471 deletions
+2
View File
@@ -5,4 +5,6 @@
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
+1
View File
@@ -0,0 +1 @@
We have a [Figma component library](https://www.figma.com/@backstage) that you can use to build your own plugins for Backstage.
+1
View File
@@ -23,6 +23,7 @@
"@backstage/theme": "^0.1.1-alpha.14",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@octokit/rest": "^18.0.0",
"history": "^5.0.0",
"prop-types": "^15.7.2",
"react": "^16.12.0",
+2
View File
@@ -82,7 +82,9 @@ export const apis = (config: ConfigApi) => {
circleCIApiRef,
new CircleCIApi(`${backendUrl}/proxy/circleci/api`),
);
builder.add(githubActionsApiRef, new GithubActionsClient());
builder.add(featureFlagsApiRef, new FeatureFlags());
builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003'));
+1
View File
@@ -53,6 +53,7 @@
"css-loader": "^3.5.3",
"dashify": "^2.0.0",
"diff": "^4.0.2",
"esbuild": "0.6.3",
"eslint": "^7.1.0",
"eslint-formatter-friendly": "^7.0.0",
"eslint-plugin-import": "^2.20.2",
@@ -19,8 +19,12 @@ import { createDistWorkspace } from '../../lib/packager';
import { paths } from '../../lib/paths';
import { run } from '../../lib/run';
const PKG_PATH = 'package.json';
export default async (imageTag: string) => {
const tempDistWorkspace = await createDistWorkspace(['example-backend'], {
const pkgPath = paths.resolveTarget(PKG_PATH);
const pkg = await fs.readJson(pkgPath);
const tempDistWorkspace = await createDistWorkspace([pkg.name], {
files: [
'package.json',
'yarn.lock',
@@ -28,7 +32,6 @@ export default async (imageTag: string) => {
{ src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
],
});
console.log(`Dist workspace ready at ${tempDistWorkspace}`);
await run('docker', ['build', '.', '-t', imageTag], {
@@ -26,7 +26,7 @@ export type PageTheme = {
export const gradients: Record<string, Gradient> = {
darkGrey: {
colors: ['#181818', '#404040'],
colors: ['#171717', '#383838'],
waveColor: '#757575',
opacity: ['1.0', '0.0'],
},
+15
View File
@@ -45,3 +45,18 @@ npx techdocs serve
You should have a `localhost:3000` serving TechDocs in Backstage, as well as `localhost:8000` serving Mkdocs (which won't open up and be exposed to the user).
Happy hacking!
## Deploying a new version
Deploying the Node packages to NPM happens automatically on merge to `master` through GitHub Actions. The deployment happens through Lerna which determines which packages throughout the Backstage project have changed. In our case, the package is called `techdocs-cli` in the repository but `@techdocs/cli` in the NPM registry.
> Note: Once a package is published under a version, any subsequent changes will not override that version. You will need to bump up the version across the entire Backstage repository, which can be done through Lerna (see the command below).
In order to bump up all packages, go to the root of the Backstage repository. To see the current version see the `lerna.json` under the `version` key. To then update all the versions (locally on your machine), run the following:
```bash
git checkout -b bump-up-version
yarn lerna version --no-push --allow-branch --yes
```
Upon being merged to master, Lerna will then automatically publish these packages as configured by the Backstage core team.
@@ -51,7 +51,7 @@ describe('OAuthProvider Utils', () => {
} as unknown) as express.Request;
expect(() => {
verifyNonce(mockRequest, 'providera');
}).toThrowError('Missing nonce');
}).toThrowError('Auth response is missing cookie nonce');
});
it('should throw error if state nonce missing', () => {
@@ -63,7 +63,7 @@ describe('OAuthProvider Utils', () => {
} as unknown) as express.Request;
expect(() => {
verifyNonce(mockRequest, 'providera');
}).toThrowError('Missing nonce');
}).toThrowError('Auth response is missing state nonce');
});
it('should throw error if nonce mismatch', () => {
@@ -43,10 +43,12 @@ export const verifyNonce = (req: express.Request, providerId: string) => {
const cookieNonce = req.cookies[`${providerId}-nonce`];
const stateNonce = req.query.state;
if (!cookieNonce || !stateNonce) {
throw new Error('Missing nonce');
if (!cookieNonce) {
throw new Error('Auth response is missing cookie nonce');
}
if (!stateNonce) {
throw new Error('Auth response is missing state nonce');
}
if (cookieNonce !== stateNonce) {
throw new Error('Invalid nonce');
}
@@ -151,9 +153,8 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
}
if (!this.options.disableRefresh) {
// throw error if missing refresh token
if (!refreshToken) {
throw new Error('Missing refresh token');
throw new InputError('Missing refresh token');
}
// set new refresh token
+2
View File
@@ -28,6 +28,8 @@
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@octokit/rest": "^18.0.0",
"@octokit/types": "^5.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "6.0.0-beta.0",
@@ -15,7 +15,11 @@
*/
import { createApiRef } from '@backstage/core';
import { Build, BuildDetails } from './types';
import {
ActionsListWorkflowRunsForRepoResponseData,
ActionsGetWorkflowResponseData,
ActionsGetWorkflowRunResponseData,
} from '@octokit/types';
export const githubActionsApiRef = createApiRef<GithubActionsApi>({
id: 'plugin.githubactions.service',
@@ -23,14 +27,50 @@ export const githubActionsApiRef = createApiRef<GithubActionsApi>({
});
export type GithubActionsApi = {
listBuilds: ({
listWorkflowRuns: ({
token,
owner,
repo,
token,
pageSize,
page,
}: {
token: string;
owner: string;
repo: string;
pageSize?: number;
page?: number;
}) => Promise<ActionsListWorkflowRunsForRepoResponseData>;
getWorkflow: ({
token,
owner,
repo,
id,
}: {
token: string;
}) => Promise<Build[]>;
getBuild: (buildUri: string, token: Promise<string>) => Promise<BuildDetails>;
owner: string;
repo: string;
id: number;
}) => Promise<ActionsGetWorkflowResponseData>;
getWorkflowRun: ({
token,
owner,
repo,
id,
}: {
token: string;
owner: string;
repo: string;
id: number;
}) => Promise<ActionsGetWorkflowRunResponseData>;
reRunWorkflow: ({
token,
owner,
repo,
runId,
}: {
token: string;
owner: string;
repo: string;
runId: number;
}) => void;
};
@@ -15,117 +15,88 @@
*/
import { GithubActionsApi } from './GithubActionsApi';
import { Build, BuildDetails, BuildStatus, WorkflowRun } from './types';
const statusToBuildStatus: { [status: string]: BuildStatus } = {
success: BuildStatus.Success,
failure: BuildStatus.Failure,
pending: BuildStatus.Pending,
running: BuildStatus.Running,
in_progress: BuildStatus.Running,
completed: BuildStatus.Success,
};
const conclusionToStatus = (conslusion: string): BuildStatus =>
statusToBuildStatus[conslusion] ?? BuildStatus.Null;
import { Octokit } from '@octokit/rest';
import {
ActionsListWorkflowRunsForRepoResponseData,
ActionsGetWorkflowResponseData,
ActionsGetWorkflowRunResponseData,
} from '@octokit/types';
export class GithubActionsClient implements GithubActionsApi {
async listBuilds({
reRunWorkflow({
token,
owner,
repo,
token,
runId,
}: {
token: string;
owner: string;
repo: string;
token: string;
}): Promise<Build[]> {
const url = `https://api.github.com/repos/${owner}/${repo}/actions/runs`;
const response = await fetch(url, {
headers: new Headers({
Authorization: `Bearer ${token}`,
}),
runId: number;
}) {
new Octokit({ auth: token }).actions.reRunWorkflow({
owner,
repo,
run_id: runId,
});
if (!response.ok) {
return [
{
commitId: 'Error',
message: 'Response status is not OK',
branch: 'Error',
status: BuildStatus.Failure,
uri: 'Error',
},
];
}
const data = await response.json();
const newData: WorkflowRun[] = data.workflow_runs;
const endData: Build[] = [];
newData.forEach((element, index) => {
const transData: Build = {
commitId: '',
message: '',
branch: '',
status: BuildStatus.Null,
uri: '',
};
transData.commitId = String(element.head_commit.id);
transData.branch = element.head_branch;
transData.status = conclusionToStatus(element.conclusion);
transData.message = element.head_commit.message;
transData.uri = element.url;
endData[index] = transData;
});
return endData;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async getBuild(
buildUri: string,
token: Promise<string>,
): Promise<BuildDetails> {
const response = await fetch(buildUri, {
headers: new Headers({
Authorization: `Bearer ${await token}`,
}),
async listWorkflowRuns({
token,
owner,
repo,
pageSize = 100,
page = 0,
}: {
token: string;
owner: string;
repo: string;
pageSize?: number;
page?: number;
}): Promise<ActionsListWorkflowRunsForRepoResponseData> {
const workflowRuns = await new Octokit({
auth: token,
}).actions.listWorkflowRunsForRepo({
owner,
repo,
per_page: pageSize,
page,
});
const buildBlank: Build = {
commitId: '',
message: '',
branch: '',
status: BuildStatus.Null,
uri: '',
};
const dataBlank: BuildDetails = {
build: buildBlank,
author: '',
logUrl: '',
overviewUrl: '',
};
if (!response.ok) {
return dataBlank;
}
const data = await response.json();
const newData: WorkflowRun = data;
dataBlank.author = newData.head_commit.author.name;
dataBlank.build.branch = newData.head_branch;
dataBlank.build.commitId = newData.head_commit.id;
dataBlank.build.message = newData.head_commit.message;
dataBlank.build.status = conclusionToStatus(newData.status);
dataBlank.build.uri = newData.url;
dataBlank.logUrl = newData.logs_url;
dataBlank.overviewUrl = newData.html_url;
return dataBlank;
return workflowRuns.data;
}
async getWorkflow({
token,
owner,
repo,
id,
}: {
token: string;
owner: string;
repo: string;
id: number;
}): Promise<ActionsGetWorkflowResponseData> {
const workflow = await new Octokit({ auth: token }).actions.getWorkflow({
owner,
repo,
workflow_id: id,
});
return workflow.data;
}
async getWorkflowRun({
token,
owner,
repo,
id,
}: {
token: string;
owner: string;
repo: string;
id: number;
}): Promise<ActionsGetWorkflowRunResponseData> {
const run = await new Octokit({ auth: token }).actions.getWorkflowRun({
owner,
repo,
run_id: id,
});
return run.data;
}
}
@@ -16,7 +16,6 @@
import {
Button,
ButtonGroup,
LinearProgress,
makeStyles,
Paper,
@@ -29,9 +28,8 @@ import {
Typography,
} from '@material-ui/core';
import React from 'react';
import { useLocation } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import { useAsync } from 'react-use';
import { BuildStatusIndicator } from '../BuildStatusIndicator';
import { Link, useApi, githubAuthApiRef } from '@backstage/core';
import { githubActionsApiRef } from '../../api';
@@ -49,17 +47,26 @@ const useStyles = makeStyles<Theme>(theme => ({
}));
export const BuildDetailsPage = () => {
const repo = 'try-ssr';
const owner = 'CircleCITest3';
const api = useApi(githubActionsApiRef);
const githubApi = useApi(githubAuthApiRef);
const token = githubApi.getAccessToken('repo');
const auth = useApi(githubAuthApiRef);
const classes = useStyles();
const location = useLocation();
const status = useAsync(
() =>
api.getBuild(decodeURIComponent(location.search.split('uri=')[1]), token),
[location.search],
);
const { id } = useParams();
const status = useAsync(async () => {
const token = await auth.getAccessToken(['repo', 'user']);
return api
.getWorkflowRun({
token,
owner,
repo,
id: parseInt(id, 10),
})
.then(data => {
return data;
});
}, [location.search]);
if (status.loading) {
return <LinearProgress />;
@@ -90,55 +97,42 @@ export const BuildDetailsPage = () => {
<TableCell>
<Typography noWrap>Branch</Typography>
</TableCell>
<TableCell>{details?.build.branch}</TableCell>
<TableCell>{details?.head_branch}</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Message</Typography>
</TableCell>
<TableCell>{details?.build.message}</TableCell>
<TableCell>{details?.head_commit.message}</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Commit ID</Typography>
</TableCell>
<TableCell>{details?.build.commitId}</TableCell>
<TableCell>{details?.head_commit.id}</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Status</Typography>
</TableCell>
<TableCell>
<BuildStatusIndicator status={details?.build.status} />
</TableCell>
<TableCell>{details?.status}</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Author</Typography>
</TableCell>
<TableCell>{details?.author}</TableCell>
<TableCell>{`${details?.head_commit.author.name} (${details?.head_commit.author.email})`}</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Links</Typography>
</TableCell>
<TableCell>
<ButtonGroup
variant="text"
color="primary"
aria-label="text primary button group"
>
{details?.overviewUrl && (
<Button>
<a href={details.overviewUrl}>GitHub</a>
</Button>
)}
{details?.logUrl && (
<Button>
<a href={details.logUrl}>Logs</a>
</Button>
)}
</ButtonGroup>
{details?.html_url && (
<Button>
<a href={details.html_url}>GitHub</a>
</Button>
)}
</TableCell>
</TableRow>
</TableBody>
@@ -27,7 +27,7 @@ import {
import React from 'react';
import { useAsync } from 'react-use';
import { BuildStatusIndicator } from '../BuildStatusIndicator';
import { githubActionsApiRef } from '../../api';
import { githubActionsApiRef, BuildStatus } from '../../api';
import { Link, useApi, githubAuthApiRef } from '@backstage/core';
const useStyles = makeStyles<Theme>(theme => ({
@@ -41,11 +41,11 @@ const useStyles = makeStyles<Theme>(theme => ({
const BuildInfoCardContent = () => {
const api = useApi(githubActionsApiRef);
const githubApi = useApi(githubAuthApiRef);
const auth = useApi(githubAuthApiRef);
const status = useAsync(async () => {
const token = await githubApi.getAccessToken('repo');
return api.listBuilds({ owner: 'spotify', repo: 'backstage', token });
const token = await auth.getAccessToken(['repo', 'user']);
return api.listWorkflowRuns({ token, owner: 'spotify', repo: 'backstage' });
});
if (status.loading) {
@@ -58,8 +58,8 @@ const BuildInfoCardContent = () => {
);
}
const [build] =
status.value?.filter(({ branch }) => branch === 'master') ?? [];
// const [build] =
// status.value?.filter(({ branch }) => branch === 'master') ?? [];
return (
<Table>
@@ -69,8 +69,8 @@ const BuildInfoCardContent = () => {
<Typography noWrap>Message</Typography>
</TableCell>
<TableCell>
<Link to={`builds/${encodeURIComponent(build?.uri || '')}`}>
<Typography color="primary">{build?.message}</Typography>
<Link to="builds/123">
<Typography color="primary">build message</Typography>
</Link>
</TableCell>
</TableRow>
@@ -78,14 +78,14 @@ const BuildInfoCardContent = () => {
<TableCell>
<Typography noWrap>Commit ID</Typography>
</TableCell>
<TableCell>{build?.commitId}</TableCell>
<TableCell>build commit id</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Typography noWrap>Status</Typography>
</TableCell>
<TableCell>
<BuildStatusIndicator status={build?.status} />
<BuildStatusIndicator status={BuildStatus.Success} />
</TableCell>
</TableRow>
</TableBody>
@@ -47,7 +47,7 @@ export const BuildListPage = () => {
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<BuildListTable />
<BuildListTable repo="try-ssr" owner="CircleCITest3" />
</Grid>
</Grid>
</Content>
@@ -74,7 +74,7 @@ const generatedColumns: TableColumn[] = [
field: 'buildName',
highlight: true,
render: (row: Partial<Build>) => (
<Link component={RouterLink} to={`/build/${row.id}`}>
<Link component={RouterLink} to={`/github-actions/build/${row.id}`}>
{row.buildName}
</Link>
),
@@ -161,16 +161,23 @@ const BuildListTableView: FC<Props> = ({
);
};
const noop = () => {};
export const BuildListTable = () => {
const [tableProps] = useBuilds();
export const BuildListTable = ({
repo,
owner,
}: {
repo: string;
owner: string;
}) => {
const [tableProps, { retry, setPage, setPageSize }] = useBuilds({
repo,
owner,
});
return (
<BuildListTableView
{...tableProps}
retry={noop}
onChangePageSize={noop}
onChangePage={noop}
retry={retry}
onChangePageSize={setPageSize}
onChangePage={setPage}
/>
);
};
@@ -13,301 +13,62 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCallback, useState } from 'react';
import { useState } from 'react';
import { useAsyncRetry } from 'react-use';
import { Build } from './BuildListTable';
import { githubActionsApiRef } from '../../api/GithubActionsApi';
import { useApi, githubAuthApiRef } from '@backstage/core';
import { ActionsListWorkflowRunsForRepoResponseData } from '@octokit/types';
// TODO(shmidt-i): use real APIs
const useEntityGHSettings = () => ({ repo: 'test', owner: 'shmidt-i-test' });
const buildsMock = {
total_count: 1,
workflow_runs: [
{
id: 30433642,
node_id: 'MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ==',
head_branch: 'master',
head_sha: 'acb5820ced9479c074f688cc328bf03f341a511d',
run_number: 562,
event: 'push',
status: 'queued',
conclusion: null,
workflow_id: 159038,
url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642',
html_url: 'https://github.com/octo-org/octo-repo/actions/runs/30433642',
pull_requests: [],
created_at: '2020-01-22T19:33:08Z',
updated_at: '2020-01-22T19:33:08Z',
jobs_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs',
logs_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs',
check_suite_url:
'https://api.github.com/repos/octo-org/octo-repo/check-suites/414944374',
artifacts_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts',
cancel_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel',
rerun_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun',
workflow_url:
'https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038',
head_commit: {
id: 'acb5820ced9479c074f688cc328bf03f341a511d',
tree_id: 'd23f6eedb1e1b9610bbc754ddb5197bfe7271223',
message: 'Create linter.yml',
timestamp: '2020-01-22T19:33:05Z',
author: {
name: 'Octo Cat',
email: 'octocat@github.com',
},
committer: {
name: 'GitHub',
email: 'noreply@github.com',
},
},
repository: {
id: 1296269,
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
name: 'Hello-World',
full_name: 'octocat/Hello-World',
owner: {
login: 'octocat',
id: 1,
node_id: 'MDQ6VXNlcjE=',
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
gravatar_id: '',
url: 'https://api.github.com/users/octocat',
html_url: 'https://github.com/octocat',
followers_url: 'https://api.github.com/users/octocat/followers',
following_url:
'https://api.github.com/users/octocat/following{/other_user}',
gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
starred_url:
'https://api.github.com/users/octocat/starred{/owner}{/repo}',
subscriptions_url:
'https://api.github.com/users/octocat/subscriptions',
organizations_url: 'https://api.github.com/users/octocat/orgs',
repos_url: 'https://api.github.com/users/octocat/repos',
events_url: 'https://api.github.com/users/octocat/events{/privacy}',
received_events_url:
'https://api.github.com/users/octocat/received_events',
type: 'User',
site_admin: false,
},
private: false,
html_url: 'https://github.com/octocat/Hello-World',
description: 'This your first repo!',
fork: false,
url: 'https://api.github.com/repos/octocat/Hello-World',
archive_url:
'http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}',
assignees_url:
'http://api.github.com/repos/octocat/Hello-World/assignees{/user}',
blobs_url:
'http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}',
branches_url:
'http://api.github.com/repos/octocat/Hello-World/branches{/branch}',
collaborators_url:
'http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}',
comments_url:
'http://api.github.com/repos/octocat/Hello-World/comments{/number}',
commits_url:
'http://api.github.com/repos/octocat/Hello-World/commits{/sha}',
compare_url:
'http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}',
contents_url:
'http://api.github.com/repos/octocat/Hello-World/contents/{+path}',
contributors_url:
'http://api.github.com/repos/octocat/Hello-World/contributors',
deployments_url:
'http://api.github.com/repos/octocat/Hello-World/deployments',
downloads_url:
'http://api.github.com/repos/octocat/Hello-World/downloads',
events_url: 'http://api.github.com/repos/octocat/Hello-World/events',
forks_url: 'http://api.github.com/repos/octocat/Hello-World/forks',
git_commits_url:
'http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}',
git_refs_url:
'http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}',
git_tags_url:
'http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}',
git_url: 'git:github.com/octocat/Hello-World.git',
issue_comment_url:
'http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}',
issue_events_url:
'http://api.github.com/repos/octocat/Hello-World/issues/events{/number}',
issues_url:
'http://api.github.com/repos/octocat/Hello-World/issues{/number}',
keys_url:
'http://api.github.com/repos/octocat/Hello-World/keys{/key_id}',
labels_url:
'http://api.github.com/repos/octocat/Hello-World/labels{/name}',
languages_url:
'http://api.github.com/repos/octocat/Hello-World/languages',
merges_url: 'http://api.github.com/repos/octocat/Hello-World/merges',
milestones_url:
'http://api.github.com/repos/octocat/Hello-World/milestones{/number}',
notifications_url:
'http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}',
pulls_url:
'http://api.github.com/repos/octocat/Hello-World/pulls{/number}',
releases_url:
'http://api.github.com/repos/octocat/Hello-World/releases{/id}',
ssh_url: 'git@github.com:octocat/Hello-World.git',
stargazers_url:
'http://api.github.com/repos/octocat/Hello-World/stargazers',
statuses_url:
'http://api.github.com/repos/octocat/Hello-World/statuses/{sha}',
subscribers_url:
'http://api.github.com/repos/octocat/Hello-World/subscribers',
subscription_url:
'http://api.github.com/repos/octocat/Hello-World/subscription',
tags_url: 'http://api.github.com/repos/octocat/Hello-World/tags',
teams_url: 'http://api.github.com/repos/octocat/Hello-World/teams',
trees_url:
'http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}',
},
head_repository: {
id: 217723378,
node_id: 'MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=',
name: 'octo-repo',
full_name: 'octo-org/octo-repo',
private: true,
owner: {
login: 'octocat',
id: 1,
node_id: 'MDQ6VXNlcjE=',
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
gravatar_id: '',
url: 'https://api.github.com/users/octocat',
html_url: 'https://github.com/octocat',
followers_url: 'https://api.github.com/users/octocat/followers',
following_url:
'https://api.github.com/users/octocat/following{/other_user}',
gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
starred_url:
'https://api.github.com/users/octocat/starred{/owner}{/repo}',
subscriptions_url:
'https://api.github.com/users/octocat/subscriptions',
organizations_url: 'https://api.github.com/users/octocat/orgs',
repos_url: 'https://api.github.com/users/octocat/repos',
events_url: 'https://api.github.com/users/octocat/events{/privacy}',
received_events_url:
'https://api.github.com/users/octocat/received_events',
type: 'User',
site_admin: false,
},
html_url: 'https://github.com/octo-org/octo-repo',
description: null,
fork: false,
url: 'https://api.github.com/repos/octo-org/octo-repo',
forks_url: 'https://api.github.com/repos/octo-org/octo-repo/forks',
keys_url:
'https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}',
collaborators_url:
'https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}',
teams_url: 'https://api.github.com/repos/octo-org/octo-repo/teams',
hooks_url: 'https://api.github.com/repos/octo-org/octo-repo/hooks',
issue_events_url:
'https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}',
events_url: 'https://api.github.com/repos/octo-org/octo-repo/events',
assignees_url:
'https://api.github.com/repos/octo-org/octo-repo/assignees{/user}',
branches_url:
'https://api.github.com/repos/octo-org/octo-repo/branches{/branch}',
tags_url: 'https://api.github.com/repos/octo-org/octo-repo/tags',
blobs_url:
'https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}',
git_tags_url:
'https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}',
git_refs_url:
'https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}',
trees_url:
'https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}',
statuses_url:
'https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}',
languages_url:
'https://api.github.com/repos/octo-org/octo-repo/languages',
stargazers_url:
'https://api.github.com/repos/octo-org/octo-repo/stargazers',
contributors_url:
'https://api.github.com/repos/octo-org/octo-repo/contributors',
subscribers_url:
'https://api.github.com/repos/octo-org/octo-repo/subscribers',
subscription_url:
'https://api.github.com/repos/octo-org/octo-repo/subscription',
commits_url:
'https://api.github.com/repos/octo-org/octo-repo/commits{/sha}',
git_commits_url:
'https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}',
comments_url:
'https://api.github.com/repos/octo-org/octo-repo/comments{/number}',
issue_comment_url:
'https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}',
contents_url:
'https://api.github.com/repos/octo-org/octo-repo/contents/{+path}',
compare_url:
'https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}',
merges_url: 'https://api.github.com/repos/octo-org/octo-repo/merges',
archive_url:
'https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}',
downloads_url:
'https://api.github.com/repos/octo-org/octo-repo/downloads',
issues_url:
'https://api.github.com/repos/octo-org/octo-repo/issues{/number}',
pulls_url:
'https://api.github.com/repos/octo-org/octo-repo/pulls{/number}',
milestones_url:
'https://api.github.com/repos/octo-org/octo-repo/milestones{/number}',
notifications_url:
'https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}',
labels_url:
'https://api.github.com/repos/octo-org/octo-repo/labels{/name}',
releases_url:
'https://api.github.com/repos/octo-org/octo-repo/releases{/id}',
deployments_url:
'https://api.github.com/repos/octo-org/octo-repo/deployments',
},
},
],
};
export function useBuilds() {
const { repo, owner } = useEntityGHSettings();
export function useBuilds({ repo, owner }: { repo: string; owner: string }) {
const api = useApi(githubActionsApiRef);
const auth = useApi(githubAuthApiRef);
const [total, setTotal] = useState(0);
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(5);
const getBuilds = useCallback(async () => buildsMock, []);
const restartBuild = async () => {};
const { loading, value: builds, retry } = useAsyncRetry(
() =>
getBuilds().then((allBuilds): Build[] => {
setTotal(allBuilds.total_count);
// Transformation here
return allBuilds.workflow_runs.map(run => ({
buildName: run.head_commit.message,
id: `${run.id}`,
onRestartClick: () => {},
source: {
branchName: run.head_branch,
commit: {
hash: run.head_commit.id,
url: run.head_repository.branches_url.replace(
'{/branch}',
run.head_branch,
),
},
const { loading, value: builds, retry } = useAsyncRetry<Build[]>(async () => {
const token = await auth.getAccessToken(['repo', 'user']);
return (
api
// GitHub API pagination count starts from 1
.listWorkflowRuns({ token, owner, repo, pageSize, page: page + 1 })
.then(
(allBuilds: ActionsListWorkflowRunsForRepoResponseData): Build[] => {
setTotal(allBuilds.total_count);
// Transformation here
return allBuilds.workflow_runs.map(run => ({
buildName: run.head_commit.message,
id: `${run.id}`,
onRestartClick: () => {
api.reRunWorkflow({
token,
owner,
repo,
runId: run.id,
});
},
source: {
branchName: run.head_branch,
commit: {
hash: run.head_commit.id,
url: run.head_repository.branches_url.replace(
'{/branch}',
run.head_branch,
),
},
},
status: run.status,
buildUrl: run.url,
}));
},
status: run.status,
buildUrl: run.url,
}));
}),
[page, pageSize, getBuilds],
);
)
);
}, [page, pageSize]);
const projectName = `${owner}/${repo}`;
return [
@@ -320,7 +81,7 @@ export function useBuilds() {
total,
},
{
getBuilds,
builds,
setPage,
setPageSize,
restartBuild,
+1 -1
View File
@@ -24,7 +24,7 @@ export const rootRouteRef = createRouteRef({
title: 'GitHub Actions',
});
export const buildRouteRef = createRouteRef({
path: '/github-actions/builds',
path: '/github-actions/build/:id',
title: 'GitHub Actions Build',
});
@@ -64,7 +64,7 @@ export const ScaffolderPage: React.FC<{}> = () => {
}, [error, errorApi]);
return (
<Page theme={pageTheme.home}>
<Page theme={pageTheme.other}>
<Header
pageTitleOverride="Create a new component"
title={
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button } from '@backstage/core';
import { Button, pageTheme } from '@backstage/core';
import { Card, Chip, makeStyles, Typography } from '@material-ui/core';
import React from 'react';
import { generatePath } from 'react-router-dom';
@@ -23,8 +23,8 @@ const useStyles = makeStyles(theme => ({
header: {
color: theme.palette.common.white,
padding: theme.spacing(2, 2, 6),
backgroundImage:
'linear-gradient(-137deg, rgb(25, 230, 140) 0%, rgb(29, 127, 110) 100%)',
backgroundImage: (props: { gradientStart: string; gradientStop: string }) =>
`linear-gradient(-137deg, ${props.gradientStart} 0%, ${props.gradientStop} 100%)`,
},
content: {
padding: theme.spacing(2),
@@ -55,7 +55,9 @@ export const TemplateCard = ({
type,
name,
}: TemplateCardProps) => {
const classes = useStyles();
const theme = pageTheme[type] ?? pageTheme.other;
const [gradientStart, gradientStop] = theme.gradient.colors;
const classes = useStyles({ gradientStart, gradientStop });
const href = generatePath(templateRoute.path, { templateName: name });
return (
@@ -72,7 +74,7 @@ export const TemplateCard = ({
{description}
</Typography>
<div className={classes.footer}>
<Button color="primary" variant="contained" to={href}>
<Button color="primary" to={href}>
Choose
</Button>
</div>
@@ -22,6 +22,7 @@ import {
Lifecycle,
Page,
useApi,
pageTheme,
} from '@backstage/core';
import { catalogApiRef } from '@backstage/plugin-catalog';
import { LinearProgress } from '@material-ui/core';
@@ -135,7 +136,7 @@ export const TemplatePage = () => {
}
return (
<Page>
<Page theme={pageTheme.other}>
<Header
pageTitleOverride="Create a new component"
title={
+7 -8
View File
@@ -8077,10 +8077,10 @@ es6-shim@^0.35.5:
resolved "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab"
integrity sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg==
esbuild@^0.5.3:
version "0.5.3"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.5.3.tgz#18f5bdb618220c6f14bcb1cf5af528d02d4734c9"
integrity sha512-RVzTK62svYjnh+agJRh+NWfZX74iKwFNUX52cF7Mo4QPS6bKxP1o+8GacPUMND2QnodVp2D3nKJs8gLspSfZzA==
esbuild@0.6.3:
version "0.6.3"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.6.3.tgz#a957e22f2503c745793514388110f9b55b3a6f83"
integrity sha512-4lHgz/EvGLRQnDYzzrvW+eilaPHim5pCLz4mP0k0QIalD/n8Ji2NwQwoIa1uX+yKkbn9R/FAZvaEbodjx55rDg==
escape-goat@^2.0.0:
version "2.1.1"
@@ -16480,12 +16480,11 @@ rollup-plugin-dts@^1.4.6:
"@babel/code-frame" "^7.8.3"
rollup-plugin-esbuild@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-2.1.0.tgz#8e12337c63a5b1144e0c5e8adf2f1568ad4d7d69"
integrity sha512-XYqmwk4X0SPEExgilARbre/PplhLtE3q6wiZtfgIbwxJOVGXWec1Bkcux7TFTHGX3TQozzqEASTsRJCt7py/5Q==
version "2.3.0"
resolved "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-2.3.0.tgz#7c107d4508af80d30966a148b306cb7d5b36b258"
integrity sha512-lNbWDRaClwXauaIybuaiOBRWgxcp3GkZu0UUix9yH/OHFgVohKmzlU0NtNbvfbxAcfEooyuBRAv6YCaJl1Mbpw==
dependencies:
"@rollup/pluginutils" "^3.1.0"
esbuild "^0.5.3"
rollup-plugin-image-files@^1.4.2:
version "1.4.2"