Add links to GitHub and include PR title on build info
This commit is contained in:
committed by
Fredrik Adelöw
parent
ea15090bb3
commit
c85e952300
@@ -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('/');
|
||||
|
||||
@@ -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 }) => (
|
||||
<Box display="flex" alignItems="center">
|
||||
{build?.fullDisplayName}
|
||||
<IconLink href={build?.url} target="_blank">
|
||||
<LaunchIcon />
|
||||
{build?.buildName}
|
||||
<IconLink href={build?.url} target="_blank" title="View on Jenkins">
|
||||
<LaunchIcon /> {/* TODO use Jenkins logo*/}
|
||||
</IconLink>
|
||||
<IconLink href={build?.source.url} target="_blank" title="View on GitHub">
|
||||
<GitHubIcon />
|
||||
</IconLink>
|
||||
</Box>
|
||||
);
|
||||
@@ -111,10 +115,9 @@ const BuildWithStepsView = () => {
|
||||
startPolling();
|
||||
return () => stopPolling();
|
||||
}, [buildPath, startPolling, stopPolling]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PluginHeader title="Build info" />
|
||||
<PluginHeader title={value?.source.displayName || 'Build details'} />
|
||||
|
||||
<Grid container spacing={3} direction="column">
|
||||
<Grid item>
|
||||
|
||||
@@ -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<CITableBuildInfo>) => (
|
||||
<>
|
||||
<p>{row.source?.branchName}</p>
|
||||
<p>{row.source?.commit.hash}</p>
|
||||
<p>
|
||||
<Link href={row.source?.url || ''} target="_blank">
|
||||
{row.source?.branchName}
|
||||
</Link>
|
||||
</p>
|
||||
<p>{row.source?.commit?.hash}</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
render: (row: Partial<CITableBuildInfo>) => {
|
||||
render: (row: Partial<CITableBuildInfo>) => {
|
||||
return (
|
||||
<Box display="flex" alignItems="center">
|
||||
<JenkinsRunStatus status={row.status} />
|
||||
</Box>
|
||||
)},
|
||||
<Box display="flex" alignItems="center">
|
||||
<JenkinsRunStatus status={row.status} />
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user