diff --git a/ADOPTERS.md b/ADOPTERS.md index ec35bc7659..7c05cdd5d7 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -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 | diff --git a/docs/dls/figma.md b/docs/dls/figma.md index e69de29bb2..8fe6ea8d0f 100644 --- a/docs/dls/figma.md +++ b/docs/dls/figma.md @@ -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. diff --git a/packages/app/package.json b/packages/app/package.json index 63f17e3ad1..b708caf173 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -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", diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 011c549619..cac3d6e171 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -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')); diff --git a/packages/cli/package.json b/packages/cli/package.json index 55462d1ebf..454df97a36 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index a847f1ef54..258ef102d7 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -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], { diff --git a/packages/core/src/layout/Page/PageThemeProvider.ts b/packages/core/src/layout/Page/PageThemeProvider.ts index 3df2a61aa8..36430021bb 100644 --- a/packages/core/src/layout/Page/PageThemeProvider.ts +++ b/packages/core/src/layout/Page/PageThemeProvider.ts @@ -26,7 +26,7 @@ export type PageTheme = { export const gradients: Record = { darkGrey: { - colors: ['#181818', '#404040'], + colors: ['#171717', '#383838'], waveColor: '#757575', opacity: ['1.0', '0.0'], }, diff --git a/packages/techdocs-cli/README.md b/packages/techdocs-cli/README.md index 960574db37..497101df78 100644 --- a/packages/techdocs-cli/README.md +++ b/packages/techdocs-cli/README.md @@ -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. diff --git a/plugins/auth-backend/src/lib/OAuthProvider.test.ts b/plugins/auth-backend/src/lib/OAuthProvider.test.ts index f67a91dfcd..3d631bd4ac 100644 --- a/plugins/auth-backend/src/lib/OAuthProvider.test.ts +++ b/plugins/auth-backend/src/lib/OAuthProvider.test.ts @@ -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', () => { diff --git a/plugins/auth-backend/src/lib/OAuthProvider.ts b/plugins/auth-backend/src/lib/OAuthProvider.ts index 5434b6a4ba..c4996c31a2 100644 --- a/plugins/auth-backend/src/lib/OAuthProvider.ts +++ b/plugins/auth-backend/src/lib/OAuthProvider.ts @@ -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 diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index b06f447a5a..9f3cb09537 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -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", diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index dd75f34bf7..eedf9686f3 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -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({ id: 'plugin.githubactions.service', @@ -23,14 +27,50 @@ export const githubActionsApiRef = createApiRef({ }); export type GithubActionsApi = { - listBuilds: ({ + listWorkflowRuns: ({ + token, owner, repo, - token, + pageSize, + page, }: { + token: string; owner: string; repo: string; + pageSize?: number; + page?: number; + }) => Promise; + getWorkflow: ({ + token, + owner, + repo, + id, + }: { token: string; - }) => Promise; - getBuild: (buildUri: string, token: Promise) => Promise; + owner: string; + repo: string; + id: number; + }) => Promise; + getWorkflowRun: ({ + token, + owner, + repo, + id, + }: { + token: string; + owner: string; + repo: string; + id: number; + }) => Promise; + reRunWorkflow: ({ + token, + owner, + repo, + runId, + }: { + token: string; + owner: string; + repo: string; + runId: number; + }) => void; }; diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts index 58d7280c1f..1606c268f4 100644 --- a/plugins/github-actions/src/api/GithubActionsClient.ts +++ b/plugins/github-actions/src/api/GithubActionsClient.ts @@ -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 { - 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, - ): Promise { - 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 { + 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 { + 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 { + const run = await new Octokit({ auth: token }).actions.getWorkflowRun({ + owner, + repo, + run_id: id, + }); + return run.data; } } diff --git a/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx b/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx index 707c670f40..55c568106c 100644 --- a/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx +++ b/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx @@ -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 => ({ })); 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 ; @@ -90,55 +97,42 @@ export const BuildDetailsPage = () => { Branch - {details?.build.branch} + {details?.head_branch} Message - {details?.build.message} + {details?.head_commit.message} Commit ID - {details?.build.commitId} + {details?.head_commit.id} Status - - - + {details?.status} Author - {details?.author} + {`${details?.head_commit.author.name} (${details?.head_commit.author.email})`} Links - - {details?.overviewUrl && ( - - )} - {details?.logUrl && ( - - )} - + {details?.html_url && ( + + )} diff --git a/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx b/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx index cbbc1da339..acc1b496ec 100644 --- a/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx +++ b/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx @@ -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 => ({ @@ -41,11 +41,11 @@ const useStyles = makeStyles(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 ( @@ -69,8 +69,8 @@ const BuildInfoCardContent = () => { Message - - {build?.message} + + build message @@ -78,14 +78,14 @@ const BuildInfoCardContent = () => { Commit ID - {build?.commitId} + build commit id Status - + diff --git a/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx b/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx index 9f6807d8bd..172b3611bf 100644 --- a/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx +++ b/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx @@ -47,7 +47,7 @@ export const BuildListPage = () => { - + diff --git a/plugins/github-actions/src/components/BuildListTable/BuildListTable.tsx b/plugins/github-actions/src/components/BuildListTable/BuildListTable.tsx index dc593afdbc..b5fd4636f5 100644 --- a/plugins/github-actions/src/components/BuildListTable/BuildListTable.tsx +++ b/plugins/github-actions/src/components/BuildListTable/BuildListTable.tsx @@ -74,7 +74,7 @@ const generatedColumns: TableColumn[] = [ field: 'buildName', highlight: true, render: (row: Partial) => ( - + {row.buildName} ), @@ -161,16 +161,23 @@ const BuildListTableView: FC = ({ ); }; -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 ( ); }; diff --git a/plugins/github-actions/src/components/BuildListTable/useBuilds.ts b/plugins/github-actions/src/components/BuildListTable/useBuilds.ts index 2ac5ef98d6..7b93e178d6 100644 --- a/plugins/github-actions/src/components/BuildListTable/useBuilds.ts +++ b/plugins/github-actions/src/components/BuildListTable/useBuilds.ts @@ -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(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, diff --git a/plugins/github-actions/src/plugin.ts b/plugins/github-actions/src/plugin.ts index 8897fe53db..b728296cbc 100644 --- a/plugins/github-actions/src/plugin.ts +++ b/plugins/github-actions/src/plugin.ts @@ -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', }); diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 98a94d48c1..b21ff43ac0 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -64,7 +64,7 @@ export const ScaffolderPage: React.FC<{}> = () => { }, [error, errorApi]); return ( - +
({ 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}
-
diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 5c949e55d9..05cc249e41 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -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 ( - +