Update plugin to support different Build locations. Update changeset and ReadMe.

Signed-off-by: PurseChicken <chris@tschantz.name>
This commit is contained in:
PurseChicken
2023-11-27 11:36:48 -08:00
parent 568121143a
commit 8745a6fa8a
11 changed files with 99 additions and 9 deletions
+6 -3
View File
@@ -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.
+20 -1
View File
@@ -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
```
+12 -1
View File
@@ -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<ActionsListWorkflowRunsForRepoResponseData>;
getWorkflow: (options: {
projectId: string;
location: string;
id: string;
}) => Promise<ActionsGetWorkflowResponseData>;
getWorkflowRun: (options: {
projectId: string;
location: string;
id: string;
}) => Promise<ActionsGetWorkflowResponseData>;
reRunWorkflow: (options: {
projectId: string;
location: string;
runId: string;
}) => Promise<any>;
};
@@ -85,20 +89,27 @@ export class CloudbuildClient implements CloudbuildApi {
// (undocumented)
getWorkflow(options: {
projectId: string;
location: string;
id: string;
}): Promise<ActionsGetWorkflowResponseData>;
// (undocumented)
getWorkflowRun(options: {
projectId: string;
location: string;
id: string;
}): Promise<ActionsGetWorkflowResponseData>;
// (undocumented)
listWorkflowRuns(options: {
projectId: string;
location: string;
cloudBuildFilter: string;
}): Promise<ActionsListWorkflowRunsForRepoResponseData>;
// (undocumented)
reRunWorkflow(options: { projectId: string; runId: string }): Promise<void>;
reRunWorkflow(options: {
projectId: string;
location: string;
runId: string;
}): Promise<void>;
}
// @public (undocumented)
@@ -29,18 +29,22 @@ export const cloudbuildApiRef = createApiRef<CloudbuildApi>({
export type CloudbuildApi = {
listWorkflowRuns: (options: {
projectId: string;
location: string;
cloudBuildFilter: string;
}) => Promise<ActionsListWorkflowRunsForRepoResponseData>;
getWorkflow: (options: {
projectId: string;
location: string;
id: string;
}) => Promise<ActionsGetWorkflowResponseData>;
getWorkflowRun: (options: {
projectId: string;
location: string;
id: string;
}) => Promise<ActionsGetWorkflowResponseData>;
reRunWorkflow: (options: {
projectId: string;
location: string;
runId: string;
}) => Promise<any>;
};
@@ -27,11 +27,14 @@ export class CloudbuildClient implements CloudbuildApi {
async reRunWorkflow(options: {
projectId: string;
location: string;
runId: string;
}): Promise<void> {
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<ActionsListWorkflowRunsForRepoResponseData> {
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<ActionsGetWorkflowResponseData> {
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<ActionsGetWorkflowResponseData> {
const workflow = await fetch(
`https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent(
options.projectId,
)}/locations/${encodeURIComponent(
options.location,
)}/builds/${encodeURIComponent(options.id)}`,
{
headers: new Headers({
@@ -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);
@@ -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>(theme => ({
root: {
@@ -64,8 +65,9 @@ const useStyles = makeStyles<Theme>(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) {
@@ -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');
@@ -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,
});
@@ -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';
};
@@ -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) {