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
+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) {