Consistently use job-full-name not name or slug
Signed-off-by: Andrew Shirley <andrew.shirley@sainsburys.co.uk> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -71,10 +71,10 @@ kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
annotations:
|
||||
'jenkins.io/job-slug': teamA/artistLookup-build
|
||||
'jenkins.io/job-full-name': teamA/artistLookup-build
|
||||
```
|
||||
|
||||
The old annotation name of `jenkins.io/github-folder` is equivalent to `jenkins.io/job-slug`
|
||||
The old annotation name of `jenkins.io/github-folder` is equivalent to `jenkins.io/job-full-name`
|
||||
|
||||
#### Example - Multiple global instances
|
||||
|
||||
@@ -103,7 +103,7 @@ kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
annotations:
|
||||
'jenkins.io/job-slug': departmentFoo:teamA/artistLookup-build
|
||||
'jenkins.io/job-full-name': departmentFoo:teamA/artistLookup-build
|
||||
```
|
||||
|
||||
If the `departmentFoo:` part is omitted, the default instance will be assumed.
|
||||
@@ -124,7 +124,7 @@ jenkins:
|
||||
|
||||
### Custom JenkinsInfoProvider
|
||||
|
||||
An example of a bespoke JenkinsInfoProvider which uses an organisation specific annotation to look up the Jenkins info (including jobName):
|
||||
An example of a bespoke JenkinsInfoProvider which uses an organisation specific annotation to look up the Jenkins info (including jobFullName):
|
||||
|
||||
```typescript
|
||||
class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
@@ -132,7 +132,7 @@ class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
|
||||
async getInstance(opt: {
|
||||
entityRef: EntityName;
|
||||
jobName?: string;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo> {
|
||||
const PAAS_ANNOTATION = 'acme.example.com/paas-project-name';
|
||||
|
||||
@@ -157,7 +157,7 @@ class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
const { team, dept } = this.lookupPaasInfo(paasProjectName);
|
||||
|
||||
const baseUrl = `https://jenkins-${dept}.example.com/`;
|
||||
const jobName = `${team}/${paasProjectName}`;
|
||||
const jobFullName = `${team}/${paasProjectName}`;
|
||||
const username = 'backstage-bot';
|
||||
const apiKey = this.getJenkinsApiKey(paasProjectName);
|
||||
const creds = btoa(`${username}:${apiKey}`);
|
||||
@@ -167,7 +167,7 @@ class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
headers: {
|
||||
Authorization: `Basic ${creds}`,
|
||||
},
|
||||
jobName,
|
||||
jobFullName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -205,3 +205,5 @@ The domain model for Jenkins is not particularly clear but for the purposes of t
|
||||
|
||||
Jenkins contains a tree of *job*s which have children of either; other *job*s (making it a _folder_) or *build*s (making it a _project_).
|
||||
Concepts like _pipeline_ and *view*s are meaningless (pipelines are just jobs for our purposes, views are (as the name suggests) just views of subsets of jobs)
|
||||
|
||||
A _job full name_ is a slash separated list of the names of the job, and the folders which contain it. For example `teamA/artistLookupService/develop`, and the same way that a filesystem path has folders and file names.
|
||||
|
||||
@@ -23,10 +23,10 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
// (undocumented)
|
||||
getInstance(opt: {
|
||||
entityRef: EntityName;
|
||||
jobName?: string;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo>;
|
||||
// (undocumented)
|
||||
static readonly NEW_JENKINS_ANNOTATION = "jenkins.io/job-slug";
|
||||
static readonly NEW_JENKINS_ANNOTATION = "jenkins.io/job-full-name";
|
||||
// (undocumented)
|
||||
static readonly OLD_JENKINS_ANNOTATION = "jenkins.io/github-folder";
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export interface JenkinsInfo {
|
||||
// (undocumented)
|
||||
headers?: Record<string, string | string[]>;
|
||||
// (undocumented)
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -46,7 +46,7 @@ export interface JenkinsInfoProvider {
|
||||
// (undocumented)
|
||||
getInstance(options: {
|
||||
entityRef: EntityName;
|
||||
jobName?: string;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo>;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ const mockedJenkinsClient = {
|
||||
const mockedJenkins = jenkins as jest.Mocked<any>;
|
||||
mockedJenkins.mockReturnValue(mockedJenkinsClient);
|
||||
|
||||
const jobName = 'example-jobName/foo';
|
||||
const jobFullName = 'example-jobName/foo';
|
||||
const buildNumber = 19;
|
||||
const jenkinsInfo: JenkinsInfo = {
|
||||
baseUrl: 'https://jenkins.example.com',
|
||||
headers: { headerName: 'headerValue' },
|
||||
jobName: 'example-jobName',
|
||||
jobFullName: 'example-jobName',
|
||||
};
|
||||
|
||||
describe('JenkinsApi', () => {
|
||||
@@ -74,7 +74,7 @@ describe('JenkinsApi', () => {
|
||||
promisify: true,
|
||||
});
|
||||
expect(mockedJenkinsClient.job.get).toBeCalledWith({
|
||||
name: jenkinsInfo.jobName,
|
||||
name: jenkinsInfo.jobFullName,
|
||||
tree: expect.anything(),
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
@@ -117,7 +117,7 @@ describe('JenkinsApi', () => {
|
||||
promisify: true,
|
||||
});
|
||||
expect(mockedJenkinsClient.job.get).toBeCalledWith({
|
||||
name: `${jenkinsInfo.jobName}/testBranchName`,
|
||||
name: `${jenkinsInfo.jobFullName}/testBranchName`,
|
||||
tree: expect.anything(),
|
||||
});
|
||||
expect(result).toHaveLength(1);
|
||||
@@ -368,7 +368,7 @@ describe('JenkinsApi', () => {
|
||||
mockedJenkinsClient.job.get.mockResolvedValueOnce(project);
|
||||
mockedJenkinsClient.build.get.mockResolvedValueOnce(build);
|
||||
|
||||
await jenkinsApi.getBuild(jenkinsInfo, jobName, buildNumber);
|
||||
await jenkinsApi.getBuild(jenkinsInfo, jobFullName, buildNumber);
|
||||
|
||||
expect(mockedJenkins).toHaveBeenCalledWith({
|
||||
baseUrl: jenkinsInfo.baseUrl,
|
||||
@@ -376,19 +376,22 @@ describe('JenkinsApi', () => {
|
||||
promisify: true,
|
||||
});
|
||||
expect(mockedJenkinsClient.job.get).toBeCalledWith({
|
||||
name: jobName,
|
||||
name: jobFullName,
|
||||
depth: 1,
|
||||
});
|
||||
expect(mockedJenkinsClient.build.get).toBeCalledWith(jobName, buildNumber);
|
||||
expect(mockedJenkinsClient.build.get).toBeCalledWith(
|
||||
jobFullName,
|
||||
buildNumber,
|
||||
);
|
||||
});
|
||||
it('buildProject', async () => {
|
||||
await jenkinsApi.buildProject(jenkinsInfo, jobName);
|
||||
await jenkinsApi.buildProject(jenkinsInfo, jobFullName);
|
||||
|
||||
expect(mockedJenkins).toHaveBeenCalledWith({
|
||||
baseUrl: jenkinsInfo.baseUrl,
|
||||
headers: jenkinsInfo.headers,
|
||||
promisify: true,
|
||||
});
|
||||
expect(mockedJenkinsClient.job.build).toBeCalledWith(jobName);
|
||||
expect(mockedJenkinsClient.job.build).toBeCalledWith(jobFullName);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,18 +67,18 @@ export class JenkinsApiImpl {
|
||||
|
||||
if (branch) {
|
||||
// we have been asked to filter to a single branch.
|
||||
// Assume jenkinsInfo.jobName is a folder which contains one job per branch.
|
||||
// Assume jenkinsInfo.jobFullName is a folder which contains one job per branch.
|
||||
// TODO: extract a strategy interface for this
|
||||
const job = await client.job.get({
|
||||
name: `${jenkinsInfo.jobName}/${branch}`,
|
||||
name: `${jenkinsInfo.jobFullName}/${branch}`,
|
||||
tree: JenkinsApiImpl.jobTreeSpec.replace(/\s/g, ''),
|
||||
});
|
||||
projects.push(this.augmentProject(job));
|
||||
} else {
|
||||
// We aren't filtering
|
||||
// Assume jenkinsInfo.jobName is a folder which contains one job per branch.
|
||||
// Assume jenkinsInfo.jobFullName is a folder which contains one job per branch.
|
||||
const folder = await client.job.get({
|
||||
name: jenkinsInfo.jobName,
|
||||
name: jenkinsInfo.jobFullName,
|
||||
// Filter only be the information we need, instead of loading all fields.
|
||||
// Limit to only show the latest build for each job and only load 50 jobs
|
||||
// at all.
|
||||
@@ -107,17 +107,17 @@ export class JenkinsApiImpl {
|
||||
*/
|
||||
async getBuild(
|
||||
jenkinsInfo: JenkinsInfo,
|
||||
jobName: string,
|
||||
jobFullName: string,
|
||||
buildNumber: number,
|
||||
) {
|
||||
const client = await JenkinsApiImpl.getClient(jenkinsInfo);
|
||||
|
||||
const project = await client.job.get({
|
||||
name: jobName,
|
||||
name: jobFullName,
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
const build = await client.build.get(jobName, buildNumber);
|
||||
const build = await client.build.get(jobFullName, buildNumber);
|
||||
const jobScmInfo = JenkinsApiImpl.extractScmDetailsFromJob(project);
|
||||
|
||||
return this.augmentBuild(build, jobScmInfo);
|
||||
@@ -127,7 +127,7 @@ export class JenkinsApiImpl {
|
||||
* Trigger a build of a project
|
||||
* @see ../../../jenkins/src/api/JenkinsApi.ts#retry
|
||||
*/
|
||||
async buildProject(jenkinsInfo: JenkinsInfo, jobName: string) {
|
||||
async buildProject(jenkinsInfo: JenkinsInfo, jobFullName: string) {
|
||||
const client = await JenkinsApiImpl.getClient(jenkinsInfo);
|
||||
|
||||
// looks like the current SDK only supports triggering a new build
|
||||
@@ -135,7 +135,7 @@ export class JenkinsApiImpl {
|
||||
|
||||
// Note Jenkins itself has concepts of rebuild and replay on a job.
|
||||
// The latter should be possible to trigger with a POST to /replay/rebuild
|
||||
await client.job.build(jobName);
|
||||
await client.job.build(jobFullName);
|
||||
}
|
||||
|
||||
// private helper methods
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -75,7 +75,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
Authorization:
|
||||
'Basic YmFja3N0YWdlIC0gYm90OjEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNlZGYwMTI=',
|
||||
},
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -106,7 +106,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -143,7 +143,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,7 +170,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'other:teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'other:teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -180,7 +180,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins-other.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,7 +196,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'default:teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'default:teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -206,7 +206,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,7 +232,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -253,7 +253,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
{
|
||||
metadata: {
|
||||
annotations: {
|
||||
'jenkins.io/job-slug': 'other:teamA/artistLookup-build',
|
||||
'jenkins.io/job-full-name': 'other:teamA/artistLookup-build',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -263,7 +263,7 @@ describe('DefaultJenkinsInfoProvider', () => {
|
||||
expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef);
|
||||
expect(info).toMatchObject({
|
||||
baseUrl: 'https://jenkins-other.example.com',
|
||||
jobName: 'teamA/artistLookup-build',
|
||||
jobFullName: 'teamA/artistLookup-build',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,14 +30,14 @@ export interface JenkinsInfoProvider {
|
||||
/**
|
||||
* A specific job to get. This is only passed in when we know about a job name we are interested in.
|
||||
*/
|
||||
jobName?: string;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo>;
|
||||
}
|
||||
|
||||
export interface JenkinsInfo {
|
||||
baseUrl: string;
|
||||
headers?: Record<string, string | string[]>;
|
||||
jobName: string; // TODO: make this an array
|
||||
jobFullName: string; // TODO: make this an array
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ export interface JenkinsInfo {
|
||||
*/
|
||||
export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
static readonly OLD_JENKINS_ANNOTATION = 'jenkins.io/github-folder';
|
||||
static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-slug';
|
||||
static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-full-name';
|
||||
|
||||
private constructor(
|
||||
private readonly config: {
|
||||
@@ -71,7 +71,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
|
||||
async getInstance(opt: {
|
||||
entityRef: EntityName;
|
||||
jobName?: string;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo> {
|
||||
// load entity
|
||||
const entity = await this.catalog.getEntityByName(opt.entityRef);
|
||||
@@ -81,7 +81,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
);
|
||||
}
|
||||
|
||||
// lookup `[jenkinsName#]jobName` from entity annotation
|
||||
// lookup `[jenkinsName#]jobFullName` from entity annotation
|
||||
const jenkinsAndJobName = DefaultJenkinsInfoProvider.getEntityAnnotationValue(
|
||||
entity,
|
||||
);
|
||||
@@ -93,16 +93,16 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
);
|
||||
}
|
||||
|
||||
let jobName;
|
||||
let jobFullName;
|
||||
let jenkinsName: string | undefined;
|
||||
const splitIndex = jenkinsAndJobName.indexOf(':');
|
||||
if (splitIndex === -1) {
|
||||
// no jenkinsName specified, use default
|
||||
jobName = jenkinsAndJobName;
|
||||
jobFullName = jenkinsAndJobName;
|
||||
} else {
|
||||
// There is a jenkinsName specified
|
||||
jenkinsName = jenkinsAndJobName.substring(0, splitIndex);
|
||||
jobName = jenkinsAndJobName.substring(
|
||||
jobFullName = jenkinsAndJobName.substring(
|
||||
splitIndex + 1,
|
||||
jenkinsAndJobName.length,
|
||||
);
|
||||
@@ -124,7 +124,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
headers: {
|
||||
Authorization: `Basic ${creds}`,
|
||||
},
|
||||
jobName,
|
||||
jobFullName,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -73,9 +73,15 @@ export async function createRouter(
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/v1/entity/:namespace/:kind/:name/job/:jobName/:buildNumber',
|
||||
'/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber',
|
||||
async (request, response) => {
|
||||
const { namespace, kind, name, jobName, buildNumber } = request.params;
|
||||
const {
|
||||
namespace,
|
||||
kind,
|
||||
name,
|
||||
jobFullName,
|
||||
buildNumber,
|
||||
} = request.params;
|
||||
|
||||
const jenkinsInfo = await jenkinsInfoProvider.getInstance({
|
||||
entityRef: {
|
||||
@@ -83,12 +89,12 @@ export async function createRouter(
|
||||
namespace,
|
||||
name,
|
||||
},
|
||||
jobName,
|
||||
jobFullName,
|
||||
});
|
||||
|
||||
const build = await jenkinsApi.getBuild(
|
||||
jenkinsInfo,
|
||||
jobName,
|
||||
jobFullName,
|
||||
parseInt(buildNumber, 10),
|
||||
);
|
||||
|
||||
@@ -99,9 +105,9 @@ export async function createRouter(
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/v1/entity/:namespace/:kind/:name/job/:jobName/:buildNumber::rebuild',
|
||||
'/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber::rebuild',
|
||||
async (request, response) => {
|
||||
const { namespace, kind, name, jobName } = request.params;
|
||||
const { namespace, kind, name, jobFullName } = request.params;
|
||||
|
||||
const jenkinsInfo = await jenkinsInfoProvider.getInstance({
|
||||
entityRef: {
|
||||
@@ -109,10 +115,10 @@ export async function createRouter(
|
||||
namespace,
|
||||
name,
|
||||
},
|
||||
jobName,
|
||||
jobFullName,
|
||||
});
|
||||
|
||||
await jenkinsApi.buildProject(jenkinsInfo, jobName);
|
||||
await jenkinsApi.buildProject(jenkinsInfo, jobFullName);
|
||||
response.json({});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function startStandaloneServer(
|
||||
logger,
|
||||
jenkinsInfoProvider: {
|
||||
async getInstance(_: { entityRef: EntityRef }): Promise<JenkinsInfo> {
|
||||
return { baseUrl: 'https://example.com/', jobName: 'build-foo' };
|
||||
return { baseUrl: 'https://example.com/', jobFullName: 'build-foo' };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,20 +91,20 @@ export interface JenkinsApi {
|
||||
*
|
||||
* This takes an entity to support selecting between multiple jenkins instances.
|
||||
*
|
||||
* TODO: abstract jobName (so we could support differentiating between the same named job on multiple instances).
|
||||
* TODO: abstract jobFullName (so we could support differentiating between the same named job on multiple instances).
|
||||
* @param options.entity
|
||||
* @param options.jobName
|
||||
* @param options.jobFullName
|
||||
* @param options.buildNumber
|
||||
*/
|
||||
getBuild(options: {
|
||||
entity: EntityName;
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
buildNumber: string;
|
||||
}): Promise<Build>;
|
||||
|
||||
retry(options: {
|
||||
entity: EntityName;
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
buildNumber: string;
|
||||
}): Promise<void>;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export class JenkinsClient implements JenkinsApi {
|
||||
onRestartClick: async () => {
|
||||
await this.retry({
|
||||
entity,
|
||||
jobName: p.fullName,
|
||||
jobFullName: p.fullName,
|
||||
buildNumber: String(p.lastBuild.number),
|
||||
});
|
||||
},
|
||||
@@ -164,11 +164,11 @@ export class JenkinsClient implements JenkinsApi {
|
||||
|
||||
async getBuild({
|
||||
entity,
|
||||
jobName,
|
||||
jobFullName,
|
||||
buildNumber,
|
||||
}: {
|
||||
entity: EntityName;
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
buildNumber: string;
|
||||
}): Promise<Build> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl(
|
||||
@@ -176,7 +176,7 @@ export class JenkinsClient implements JenkinsApi {
|
||||
)}/v1/entity/${encodeURIComponent(entity.namespace)}/${encodeURIComponent(
|
||||
entity.kind,
|
||||
)}/${encodeURIComponent(entity.name)}/job/${encodeURIComponent(
|
||||
jobName,
|
||||
jobFullName,
|
||||
)}/${encodeURIComponent(buildNumber)}`;
|
||||
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
@@ -192,11 +192,11 @@ export class JenkinsClient implements JenkinsApi {
|
||||
|
||||
async retry({
|
||||
entity,
|
||||
jobName,
|
||||
jobFullName,
|
||||
buildNumber,
|
||||
}: {
|
||||
entity: EntityName;
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
buildNumber: string;
|
||||
}): Promise<void> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl(
|
||||
@@ -204,7 +204,7 @@ export class JenkinsClient implements JenkinsApi {
|
||||
)}/v1/entity/${encodeURIComponent(entity.namespace)}/${encodeURIComponent(
|
||||
entity.kind,
|
||||
)}/${encodeURIComponent(entity.name)}/job/${encodeURIComponent(
|
||||
jobName,
|
||||
jobFullName,
|
||||
)}/${encodeURIComponent(buildNumber)}:rebuild`;
|
||||
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
|
||||
@@ -49,10 +49,10 @@ const useStyles = makeStyles(theme => ({
|
||||
|
||||
const BuildWithStepsView = () => {
|
||||
// TODO: Add a test that react-router decodes this (even though `generatePath` doesn't encode it for you!)
|
||||
const { jobName, buildNumber } = useRouteRefParams(buildRouteRef);
|
||||
const { jobFullName, buildNumber } = useRouteRefParams(buildRouteRef);
|
||||
const classes = useStyles();
|
||||
|
||||
const [{ value }] = useBuildWithSteps({ jobName, buildNumber });
|
||||
const [{ value }] = useBuildWithSteps({ jobFullName, buildNumber });
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
|
||||
@@ -100,7 +100,7 @@ const generatedColumns: TableColumn[] = [
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={generatePath(buildRouteRef.path, {
|
||||
jobName: encodeURIComponent(row.fullName),
|
||||
jobFullName: encodeURIComponent(row.fullName),
|
||||
buildNumber: String(row.lastBuild?.number),
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -25,14 +25,14 @@ const INTERVAL_AMOUNT = 1500;
|
||||
|
||||
/**
|
||||
* Hook to expose a specific build.
|
||||
* @param jobName the full name of the project (job with builds, not a folder). e.g. "department-A/team-1/project-foo/master"
|
||||
* @param jobFullName the full name of the project (job with builds, not a folder). e.g. "department-A/team-1/project-foo/master"
|
||||
* @param buildNumber the number of the build. e.g. "13"
|
||||
*/
|
||||
export function useBuildWithSteps({
|
||||
jobName,
|
||||
jobFullName,
|
||||
buildNumber,
|
||||
}: {
|
||||
jobName: string;
|
||||
jobFullName: string;
|
||||
buildNumber: string;
|
||||
}) {
|
||||
const api = useApi(jenkinsApiRef);
|
||||
@@ -42,12 +42,12 @@ export function useBuildWithSteps({
|
||||
const getBuildWithSteps = useCallback(async () => {
|
||||
try {
|
||||
const entityName = await getEntityName(entity);
|
||||
return api.getBuild({ entity: entityName, jobName, buildNumber });
|
||||
return api.getBuild({ entity: entityName, jobFullName, buildNumber });
|
||||
} catch (e) {
|
||||
errorApi.post(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}, [buildNumber, jobName, entity, api, errorApi]);
|
||||
}, [buildNumber, jobFullName, entity, api, errorApi]);
|
||||
|
||||
const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [
|
||||
getBuildWithSteps,
|
||||
|
||||
@@ -45,9 +45,9 @@ export function useBuilds({ branch }: { branch?: string } = {}) {
|
||||
errorType: ErrorType;
|
||||
}>();
|
||||
|
||||
const restartBuild = async (jobName: string, buildNumber: string) => {
|
||||
const restartBuild = async (jobFullName: string, buildNumber: string) => {
|
||||
try {
|
||||
await api.retry({ entity: entityName, jobName, buildNumber });
|
||||
await api.retry({ entity: entityName, jobFullName, buildNumber });
|
||||
} catch (e) {
|
||||
errorApi.post(e);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ export const rootRouteRef = createRouteRef({
|
||||
});
|
||||
|
||||
export const buildRouteRef = createRouteRef({
|
||||
path: 'build/:jobName/:buildNumber',
|
||||
params: ['jobName', 'buildNumber'],
|
||||
path: 'build/:jobFullName/:buildNumber',
|
||||
params: ['jobFullName', 'buildNumber'],
|
||||
title: 'Jenkins build',
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user