diff --git a/.changeset/tough-drinks-scream.md b/.changeset/tough-drinks-scream.md index fc174f3158..3d2f154016 100644 --- a/.changeset/tough-drinks-scream.md +++ b/.changeset/tough-drinks-scream.md @@ -2,8 +2,11 @@ '@backstage/plugin-cloudbuild': minor --- -Changed build list view to automatically filter repositories based on component-info's metadata.name. -Added `google.com/cloudbuild-repo-name` annotation which allows you to specify a different repository to filter on. -Added `google.com/cloudbuild-trigger-name` annotation which allows you to filter based on a trigger name instead of a repo name. +Changed build list view to automatically filter builds based on repository name matching component-info's metadata.name. +Added optional `google.com/cloudbuild-repo-name` annotation which allows you to specify a different repository to filter on. +Added optional `google.com/cloudbuild-trigger-name` annotation which allows you to filter based on a trigger name instead of a repo name. Updated the ReadMe with information about the filtering and some other minor verbiage updates. Changed `substitutions.BRANCH_NAME` to `substitutions.REF_NAME` so that the Ref field is populated properly. +Added optional `google.com/cloudbuild-location` annotation which allows you to specify the Cloud Build location of your builds. Default is global scope. +Changed build list view to show builds in a specific location if the location annotation is used. +Updated ReadMe with information about the use of the location filtering. diff --git a/plugins/cloudbuild/README.md b/plugins/cloudbuild/README.md index cde383b8c4..2677d02b13 100644 --- a/plugins/cloudbuild/README.md +++ b/plugins/cloudbuild/README.md @@ -64,7 +64,7 @@ const cicdContent = ( ### Add annotation(s) to your component-info.yaml file -Any component, that you would like the Cloud Build Plugin to populate for, should include the following `cloudbuild-project-slug` annotation. This annotation sets the GCP project name to be used for pulling the cloud build details from. +Any component, that you would like the Cloud Build Plugin to populate for, should include the following `cloudbuild-project-slug` annotation. This annotation sets the GCP project name to be used for pulling the Cloud Build details from. ```diff // component-info.yaml @@ -82,6 +82,8 @@ spec: By default, the cloud build results list is filtered by repository name equal to the name you have set in your component-info.yaml file. This is `metadata.name`. So if your metadata.name is `backstage` then it will only show builds matching the backstage repo name. +Additionally, build results are pulled from the `global` region by default. + #### Change Filtering If you need the page to be filtered on a different repository name, then you can use the following annotation: @@ -119,3 +121,20 @@ spec: ``` `Note:` The `cloudbuild-repo-name` annotation takes precedence over the `cloudbuild-trigger-name` annotation. So if you happen to use both annotations, cloudbuild-repo-name will be used. It is recommended to use one or the other if required. + +If you need to pull Cloud Build results from a location or region other than the global scope, then use the following annotation: + +```diff +// component-info.yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage + description: Backstage application. + annotations: + google.com/cloudbuild-project-slug: your-project-name ++ google.com/cloudbuild-location: us-central1 +spec: + type: website + lifecycle: development +``` diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index 5a8f2ed1db..2ce42d5e34 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -58,18 +58,22 @@ export const CLOUDBUILD_ANNOTATION = 'google.com/cloudbuild-project-slug'; export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + location: string; cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; + location: string; id: string; }) => Promise; getWorkflowRun: (options: { projectId: string; + location: string; id: string; }) => Promise; reRunWorkflow: (options: { projectId: string; + location: string; runId: string; }) => Promise; }; @@ -85,20 +89,27 @@ export class CloudbuildClient implements CloudbuildApi { // (undocumented) getWorkflow(options: { projectId: string; + location: string; id: string; }): Promise; // (undocumented) getWorkflowRun(options: { projectId: string; + location: string; id: string; }): Promise; // (undocumented) listWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }): Promise; // (undocumented) - reRunWorkflow(options: { projectId: string; runId: string }): Promise; + reRunWorkflow(options: { + projectId: string; + location: string; + runId: string; + }): Promise; } // @public (undocumented) diff --git a/plugins/cloudbuild/src/api/CloudbuildApi.ts b/plugins/cloudbuild/src/api/CloudbuildApi.ts index cdcfbab88d..9e2d63fc01 100644 --- a/plugins/cloudbuild/src/api/CloudbuildApi.ts +++ b/plugins/cloudbuild/src/api/CloudbuildApi.ts @@ -29,18 +29,22 @@ export const cloudbuildApiRef = createApiRef({ export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + location: string; cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; + location: string; id: string; }) => Promise; getWorkflowRun: (options: { projectId: string; + location: string; id: string; }) => Promise; reRunWorkflow: (options: { projectId: string; + location: string; runId: string; }) => Promise; }; diff --git a/plugins/cloudbuild/src/api/CloudbuildClient.ts b/plugins/cloudbuild/src/api/CloudbuildClient.ts index b41506f852..3717e3d1db 100644 --- a/plugins/cloudbuild/src/api/CloudbuildClient.ts +++ b/plugins/cloudbuild/src/api/CloudbuildClient.ts @@ -27,11 +27,14 @@ export class CloudbuildClient implements CloudbuildApi { async reRunWorkflow(options: { projectId: string; + location: string; runId: string; }): Promise { await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.runId)}:retry`, { method: 'POST', @@ -45,11 +48,14 @@ export class CloudbuildClient implements CloudbuildApi { async listWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }): Promise { const workflowRuns = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds?filter=${encodeURIComponent(options.cloudBuildFilter)}`, { headers: new Headers({ @@ -67,11 +73,14 @@ export class CloudbuildClient implements CloudbuildApi { async getWorkflow(options: { projectId: string; + location: string; id: string; }): Promise { const workflow = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.id)}`, { headers: new Headers({ @@ -88,11 +97,14 @@ export class CloudbuildClient implements CloudbuildApi { async getWorkflowRun(options: { projectId: string; + location: string; id: string; }): Promise { const workflow = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.id)}`, { headers: new Headers({ diff --git a/plugins/cloudbuild/src/components/Cards/Cards.tsx b/plugins/cloudbuild/src/components/Cards/Cards.tsx index cd15bae6b9..e1d9199b7e 100644 --- a/plugins/cloudbuild/src/components/Cards/Cards.tsx +++ b/plugins/cloudbuild/src/components/Cards/Cards.tsx @@ -22,6 +22,7 @@ import { WorkflowRunStatus } from '../WorkflowRunStatus'; import { Theme, makeStyles, LinearProgress } from '@material-ui/core'; import ExternalLinkIcon from '@material-ui/icons/Launch'; import { CLOUDBUILD_ANNOTATION } from '../useProjectName'; +import { getLocation } from '../useLocation'; import { getCloudbuildFilter } from '../useCloudBuildFilter'; import { @@ -80,10 +81,12 @@ export const LatestWorkflowRunCard = (props: { branch: string }) => { const { entity } = useEntity(); const errorApi = useApi(errorApiRef); const projectId = entity?.metadata.annotations?.[CLOUDBUILD_ANNOTATION] || ''; + const location = getLocation(entity); const cloudBuildFilter = getCloudbuildFilter(entity); const [{ runs, loading, error }] = useWorkflowRuns({ projectId, + location, cloudBuildFilter, }); const lastRun = runs?.[0] ?? ({} as WorkflowRun); diff --git a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index ee282848bc..5be2401ae4 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -35,6 +35,7 @@ import { useProjectName } from '../useProjectName'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; import { Breadcrumbs, Link, WarningPanel } from '@backstage/core-components'; +import { getLocation } from '../useLocation'; const useStyles = makeStyles(theme => ({ root: { @@ -64,8 +65,9 @@ const useStyles = makeStyles(theme => ({ export const WorkflowRunDetails = (props: { entity: Entity }) => { const { value: projectName, loading, error } = useProjectName(props.entity); const [projectId] = (projectName ?? '/').split('/'); + const location = getLocation(props.entity); - const details = useWorkflowRunsDetails(projectId); + const details = useWorkflowRunsDetails(projectId, location); const classes = useStyles(); if (error) { diff --git a/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts b/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts index 549ab1eaf2..abc146fb6c 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts +++ b/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts @@ -18,13 +18,14 @@ import { cloudbuildApiRef } from '../../api'; import { useApi, useRouteRefParams } from '@backstage/core-plugin-api'; import { buildRouteRef } from '../../routes'; -export const useWorkflowRunsDetails = (projectId: string) => { +export const useWorkflowRunsDetails = (projectId: string, location: string) => { const api = useApi(cloudbuildApiRef); const { id } = useRouteRefParams(buildRouteRef); const details = useAsync(async () => { return projectId ? api.getWorkflowRun({ projectId, + location, id: id, }) : Promise.reject('No projectId provided'); diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index dbefffc617..65b5ca149f 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -26,6 +26,7 @@ import { buildRouteRef } from '../../routes'; import { DateTime } from 'luxon'; import { Table, TableColumn, Link } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; +import { getLocation } from '../useLocation'; import { getCloudbuildFilter } from '../useCloudBuildFilter'; const generatedColumns: TableColumn[] = [ @@ -163,10 +164,11 @@ export const WorkflowRunsTableView = ({ export const WorkflowRunsTable = (props: { entity: Entity }) => { const { value: projectName, loading } = useProjectName(props.entity); const [projectId] = (projectName ?? '/').split('/'); + const location = getLocation(props.entity); const cloudBuildFilter = getCloudbuildFilter(props.entity); - const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ projectId, + location, cloudBuildFilter, }); diff --git a/plugins/cloudbuild/src/components/useLocation.ts b/plugins/cloudbuild/src/components/useLocation.ts new file mode 100644 index 0000000000..54a3c8f776 --- /dev/null +++ b/plugins/cloudbuild/src/components/useLocation.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The Backstage Authors + * + * 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 { Entity } from '@backstage/catalog-model'; + +const CLOUDBUILD_LOCATION_ANNOTATION = 'google.com/cloudbuild-location'; + +/** @public */ + +export const getLocation = (entity: Entity) => { + const locationAnnotation = + entity?.metadata.annotations?.[CLOUDBUILD_LOCATION_ANNOTATION] ?? ''; + if (locationAnnotation) { + return locationAnnotation; + } + return 'global'; +}; diff --git a/plugins/cloudbuild/src/components/useWorkflowRuns.ts b/plugins/cloudbuild/src/components/useWorkflowRuns.ts index 86168018d4..7e3e148dd1 100644 --- a/plugins/cloudbuild/src/components/useWorkflowRuns.ts +++ b/plugins/cloudbuild/src/components/useWorkflowRuns.ts @@ -35,9 +35,10 @@ export type WorkflowRun = { export function useWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }) { - const { projectId, cloudBuildFilter } = options; + const { projectId, location, cloudBuildFilter } = options; const api = useApi(cloudbuildApiRef); const errorApi = useApi(errorApiRef); @@ -54,6 +55,7 @@ export function useWorkflowRuns(options: { return api .listWorkflowRuns({ projectId, + location, cloudBuildFilter, }) .then( @@ -69,6 +71,7 @@ export function useWorkflowRuns(options: { try { await api.reRunWorkflow({ projectId, + location, runId: run.id, }); } catch (e) {