diff --git a/plugins/jenkins/src/api/index.ts b/plugins/jenkins/src/api/index.ts index 0218a14cac..38f6f719c1 100644 --- a/plugins/jenkins/src/api/index.ts +++ b/plugins/jenkins/src/api/index.ts @@ -49,22 +49,53 @@ export class JenkinsApi { return lastBuild; } + extractScmDetailsFromJob(jobDetails: any): any { + const scmInfo = jobDetails.actions + .filter( + (action: any) => + action._class === 'jenkins.scm.api.metadata.ObjectMetadataAction', + ) + .map((action: any) => { + return { + url: action?.objectUrl, + // https://javadoc.jenkins.io/plugin/scm-api/jenkins/scm/api/metadata/ObjectMetadataAction.html + // branch name for regular builds, pull request title on pull requests + displayName: action?.objectDisplayName, + }; + }) + .pop(); + + return scmInfo; + } + + async getJob(jobName: string) { + return this.jenkins.job.get({ + name: jobName, + depth: 1, + }); + } + async getFolder(folderName: string) { const folder = await this.jenkins.job.get(folderName); const results = []; for (const jobSummary of folder.jobs) { - const jobDetails = await this.jenkins.job.get( - `${folderName}/${jobSummary.name}`, - ); + const jobDetails = await this.jenkins.job.get({ + name: `${folderName}/${jobSummary.name}`, + depth: 1, + }); + + const jobScmInfo = this.extractScmDetailsFromJob(jobDetails); if (jobDetails.jobs) { // skipping folders inside folders for now } else { for (const buildDetails of jobDetails.builds) { - const build = await this.jenkins.build.get( - `${folderName}/${jobSummary.name}`, - buildDetails.number, - ); - const ciTable = this.mapJenkinsBuildToCITable(build); + const build = await this.jenkins.build.get({ + name: `${folderName}/${jobSummary.name}`, + number: buildDetails.number, + depth: 1, + }); + + const ciTable = this.mapJenkinsBuildToCITable(build, jobScmInfo); results.push(ciTable); } } @@ -72,22 +103,32 @@ export class JenkinsApi { return results; } - private mapJenkinsBuildToCITable(jenkinsResult: any): CITableBuildInfo { - const source = jenkinsResult.actions - .filter( - (action: any) => action._class === 'hudson.plugins.git.util.BuildData', - ) - .map((action: any) => { - const [first]: any = Object.values(action.buildsByBranchName); - const branch = first.revision.branch[0]; - return { - branchName: branch.name, - commit: { - hash: branch.SHA1.substring(0, 8), - }, - }; - }) - .pop(); + mapJenkinsBuildToCITable( + jenkinsResult: any, + jobScmInfo?: any, + ): CITableBuildInfo { + const source = + jenkinsResult.actions + .filter( + (action: any) => + action._class === 'hudson.plugins.git.util.BuildData', + ) + .map((action: any) => { + const [first]: any = Object.values(action.buildsByBranchName); + const branch = first.revision.branch[0]; + return { + branchName: branch.name, + commit: { + hash: branch.SHA1.substring(0, 8), + }, + }; + }) + .pop() || {}; + + if (jobScmInfo) { + source.url = jobScmInfo?.url; + source.displayName = jobScmInfo?.displayName; + } const path = new URL(jenkinsResult.url).pathname; @@ -112,7 +153,7 @@ export class JenkinsApi { return buildResult; } - private extractJobDetailsFromBuildName(buildName: string) { + extractJobDetailsFromBuildName(buildName: string) { const trimmedBuild = buildName.replace(/\/job/g, '').replace(/\/$/, ''); const split = trimmedBuild.split('/'); diff --git a/plugins/jenkins/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx b/plugins/jenkins/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx index c8d726d186..ab4fc3f34f 100644 --- a/plugins/jenkins/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx +++ b/plugins/jenkins/src/pages/BuildWithStepsPage/BuildWithStepsPage.tsx @@ -22,14 +22,18 @@ import { PluginHeader } from '../../components/PluginHeader'; import { ActionOutput } from './lib/ActionOutput/ActionOutput'; import { Layout } from '../../components/Layout'; import LaunchIcon from '@material-ui/icons/Launch'; +import GitHubIcon from '@material-ui/icons/GitHub'; import { useBuildWithSteps } from '../../state/useBuildWithSteps'; const IconLink = IconButton as typeof Link; const BuildName: FC<{ build?: any }> = ({ build }) => ( - {build?.fullDisplayName} - - + {build?.buildName} + + {/* TODO use Jenkins logo*/} + + + ); @@ -111,10 +115,9 @@ const BuildWithStepsView = () => { startPolling(); return () => stopPolling(); }, [buildPath, startPolling, stopPolling]); - return ( <> - + diff --git a/plugins/jenkins/src/pages/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/pages/BuildsPage/lib/CITable/CITable.tsx index 85a6056427..90d8db616e 100644 --- a/plugins/jenkins/src/pages/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/pages/BuildsPage/lib/CITable/CITable.tsx @@ -18,11 +18,8 @@ import { Link, Typography, Box, IconButton } from '@material-ui/core'; import RetryIcon from '@material-ui/icons/Replay'; import GitHubIcon from '@material-ui/icons/GitHub'; import { Link as RouterLink } from 'react-router-dom'; -import { - Table, - TableColumn, -} from '@backstage/core'; -import {JenkinsRunStatus} from "../Status"; +import { Table, TableColumn } from '@backstage/core'; +import { JenkinsRunStatus } from '../Status'; export type CITableBuildInfo = { id: string; @@ -30,9 +27,10 @@ export type CITableBuildInfo = { buildUrl?: string; source: { branchName: string; + url: string; + displayName: string; commit: { hash: string; - url: string; }; }; status: string; @@ -41,7 +39,7 @@ export type CITableBuildInfo = { passed: number; skipped: number; failed: number; - testUrl: string; // fixme better name + testUrl: string; }; onRestartClick: () => void; }; @@ -61,19 +59,24 @@ const generatedColumns: TableColumn[] = [ title: 'Source', render: (row: Partial) => ( <> -

{row.source?.branchName}

-

{row.source?.commit.hash}

+

+ + {row.source?.branchName} + +

+

{row.source?.commit?.hash}

), }, { title: 'Status', - render: (row: Partial) => { + render: (row: Partial) => { return ( - - - - )}, + + + + ); + }, }, { title: 'Actions', diff --git a/plugins/jenkins/src/state/useBuildWithSteps.ts b/plugins/jenkins/src/state/useBuildWithSteps.ts index a681438685..617b2b1f4f 100644 --- a/plugins/jenkins/src/state/useBuildWithSteps.ts +++ b/plugins/jenkins/src/state/useBuildWithSteps.ts @@ -27,7 +27,12 @@ export function useBuildWithSteps(buildName: string) { const getBuildWithSteps = useCallback(async () => { try { const build = await api.getBuild(buildName); - return Promise.resolve(build); + + const { jobName } = api.extractJobDetailsFromBuildName(buildName); + const job = await api.getJob(jobName); + const jobInfo = api.extractScmDetailsFromJob(job); + + return Promise.resolve(api.mapJenkinsBuildToCITable(build, jobInfo)); } catch (e) { errorApi.post(e); return Promise.reject(e);