Skip to content

Commit 72bb011

Browse files
committed
feat(jenkins): remove deprecated 'jenkins' NPM package
Remove the deprecated 'jenkins' package and replace it with a built-in, light-weight client. Signed-off-by: Bryan Ramos <[email protected]>
1 parent 05bc849 commit 72bb011

File tree

26 files changed

+1486
-85
lines changed

26 files changed

+1486
-85
lines changed

workspaces/jenkins/plugins/jenkins-backend/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
"@types/express": "^4.17.6",
5353
"express": "^4.17.1",
5454
"express-promise-router": "^4.1.0",
55-
"jenkins": "^1.0.0",
5655
"node-fetch": "^2.6.7",
5756
"yn": "^4.0.0"
5857
},
@@ -61,7 +60,6 @@
6160
"@backstage/cli": "backstage:^",
6261
"@backstage/plugin-auth-backend": "backstage:^",
6362
"@backstage/plugin-auth-backend-module-guest-provider": "backstage:^",
64-
"@types/jenkins": "^1.0.0",
6563
"@types/node-fetch": "^2.5.12",
6664
"@types/supertest": "^6.0.0"
6765
},

workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsApi.test.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,30 @@
1515
*/
1616

1717
import { JenkinsApiImpl } from './jenkinsApi';
18-
import jenkins from 'jenkins';
18+
import {
19+
Jenkins,
20+
type JenkinsBuild,
21+
} from '@backstage-community/plugin-jenkins-common';
1922
import { JenkinsInfo } from './jenkinsInfoProvider';
20-
import { JenkinsBuild, JenkinsProject } from '../types';
23+
import { JenkinsProject } from '../types';
2124
import { AuthorizeResult } from '@backstage/plugin-permission-common';
2225
import fetch, { Response } from 'node-fetch';
2326
import { mockServices } from '@backstage/backend-test-utils';
2427

25-
jest.mock('jenkins');
28+
jest.mock('@backstage-community/plugin-jenkins-common');
2629
jest.mock('node-fetch');
2730
const mockedJenkinsClient = {
2831
job: {
2932
get: jest.fn(),
3033
build: jest.fn(),
34+
getBuilds: jest.fn(),
3135
},
3236
build: {
3337
get: jest.fn(),
38+
getConsoleText: jest.fn(),
3439
},
3540
};
36-
const mockedJenkins = jenkins as jest.Mocked<any>;
41+
const mockedJenkins = Jenkins as jest.Mocked<any>;
3742
mockedJenkins.mockReturnValue(mockedJenkinsClient);
3843

3944
const resourceRef = 'component:default/example-component';
@@ -777,9 +782,9 @@ describe('JenkinsApi', () => {
777782
json: async () => {},
778783
} as Response);
779784
await jenkinsApi.getJobBuilds(jenkinsInfo, jobs);
780-
expect(mockFetch).toHaveBeenCalledWith(
781-
'https://jenkins.example.com/job/example-jobName/job/foo/api/json?tree=name,description,url,fullName,displayName,fullDisplayName,inQueue,builds[*]',
782-
{ headers: { headerName: 'headerValue' }, method: 'get' },
785+
expect(mockedJenkinsClient.job.getBuilds).toHaveBeenCalledWith(
786+
['example-jobName', 'foo'],
787+
'name,description,url,fullName,displayName,fullDisplayName,inQueue,builds[*]',
783788
);
784789
});
785790

@@ -790,21 +795,18 @@ describe('JenkinsApi', () => {
790795
} as Response);
791796
const fullJobName = ['test', 'folder', 'depth', 'foo'];
792797
await jenkinsApi.getJobBuilds(jenkinsInfo, fullJobName);
793-
expect(mockFetch).toHaveBeenCalledWith(
794-
'https://jenkins.example.com/job/test/job/folder/job/depth/job/foo/api/json?tree=name,description,url,fullName,displayName,fullDisplayName,inQueue,builds[*]',
795-
{ headers: { headerName: 'headerValue' }, method: 'get' },
798+
expect(mockedJenkinsClient.job.getBuilds).toHaveBeenCalledWith(
799+
['test', 'folder', 'depth', 'foo'],
800+
'name,description,url,fullName,displayName,fullDisplayName,inQueue,builds[*]',
796801
);
797802
});
798803
});
799804
describe('getBuildConsoleText', () => {
800805
it('should return the console text for a build', async () => {
801806
const mockedConsoleText = 'Build Ran';
802-
mockFetch.mockResolvedValueOnce({
803-
status: 200,
804-
text: async () => {
805-
return mockedConsoleText;
806-
},
807-
} as unknown as Response);
807+
mockedJenkinsClient.build.getConsoleText.mockResolvedValueOnce(
808+
mockedConsoleText,
809+
);
808810

809811
const consoleText = await jenkinsApi.getBuildConsoleText(
810812
jenkinsInfo,
@@ -813,9 +815,9 @@ describe('JenkinsApi', () => {
813815
);
814816

815817
expect(consoleText).toBe('Build Ran');
816-
expect(mockFetch).toHaveBeenCalledWith(
817-
'https://jenkins.example.com/job/example-jobName/job/foo/19/consoleText',
818-
{ headers: { headerName: 'headerValue' }, method: 'get' },
818+
expect(mockedJenkinsClient.build.getConsoleText).toHaveBeenCalledWith(
819+
['example-jobName', 'foo'],
820+
19,
819821
);
820822
});
821823
});

workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsApi.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
*/
1616

1717
import type { JenkinsInfo } from './jenkinsInfoProvider';
18-
import Jenkins from 'jenkins';
18+
import {
19+
Jenkins,
20+
type JenkinsBuild,
21+
} from '@backstage-community/plugin-jenkins-common';
1922
import type {
2023
BackstageBuild,
2124
BackstageProject,
22-
JenkinsBuild,
2325
JenkinsProject,
2426
ScmDetails,
2527
} from '../types';
@@ -227,7 +229,7 @@ export class JenkinsApiImpl {
227229

228230
private static async getClient(jenkinsInfo: JenkinsInfo) {
229231
// The typings for the jenkins library are out of date so just cast to any
230-
return new (Jenkins as any)({
232+
return new Jenkins({
231233
baseUrl: jenkinsInfo.baseUrl,
232234
headers: jenkinsInfo.headers,
233235
promisify: true,
@@ -379,18 +381,10 @@ export class JenkinsApiImpl {
379381
}
380382

381383
async getJobBuilds(jenkinsInfo: JenkinsInfo, jobs: string[]) {
382-
const response = await fetch(
383-
`${jenkinsInfo.baseUrl}/job/${jobs.join(
384-
'/job/',
385-
)}/api/json?tree=${JenkinsApiImpl.jobBuildsTreeSpec.replace(/\s/g, '')}`,
386-
{
387-
method: 'get',
388-
headers: jenkinsInfo.headers as HeaderInit,
389-
},
390-
);
384+
const client = await JenkinsApiImpl.getClient(jenkinsInfo);
385+
const tree = JenkinsApiImpl.jobBuildsTreeSpec.replace(/\s/g, '');
391386

392-
const jobBuilds = await response.json();
393-
return jobBuilds;
387+
return await client.job.getBuilds(jobs, tree);
394388
}
395389

396390
/**
@@ -402,14 +396,8 @@ export class JenkinsApiImpl {
402396
jobs: string[],
403397
buildNumber: number,
404398
) {
405-
const buildUrl = this.getBuildUrl(jenkinsInfo, jobs, buildNumber);
406-
407-
const response = await fetch(`${buildUrl}/consoleText`, {
408-
method: 'get',
409-
headers: jenkinsInfo.headers as HeaderInit,
410-
});
399+
const client = await JenkinsApiImpl.getClient(jenkinsInfo);
411400

412-
const consoleText = await response.text();
413-
return consoleText;
401+
return await client.build.getConsoleText(jobs, buildNumber);
414402
}
415403
}

workspaces/jenkins/plugins/jenkins-backend/src/types.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17+
import type {
18+
JenkinsBuild,
19+
CommonBuild,
20+
} from '@backstage-community/plugin-jenkins-common';
21+
1722
export interface ScmDetails {
1823
url?: string;
1924
displayName?: string;
2025
author?: string;
2126
}
2227

23-
interface CommonBuild {
24-
// standard Jenkins
25-
timestamp: number;
26-
building: boolean;
27-
duration: number;
28-
result?: string;
29-
fullDisplayName: string;
30-
displayName: string;
31-
url: string;
32-
number: number;
33-
}
34-
35-
export interface JenkinsBuild extends CommonBuild {
36-
// read by us from jenkins but not passed to frontend
37-
actions: any;
38-
}
39-
4028
/**
4129
* A build as presented by this plugin to the backstage jenkins plugin
4230
*/

workspaces/jenkins/plugins/jenkins-common/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
},
4040
"dependencies": {
4141
"@backstage/plugin-catalog-common": "backstage:^",
42-
"@backstage/plugin-permission-common": "backstage:^"
42+
"@backstage/plugin-permission-common": "backstage:^",
43+
"form-data": "^4.0.4",
44+
"node-fetch": "^2.6.7"
4345
},
4446
"devDependencies": {
4547
"@backstage/cli": "backstage:^"

workspaces/jenkins/plugins/jenkins-common/report.api.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,128 @@
55
```ts
66
import { ResourcePermission } from '@backstage/plugin-permission-common';
77

8+
// @public (undocumented)
9+
export interface CommonBuild {
10+
// (undocumented)
11+
building: boolean;
12+
// (undocumented)
13+
displayName: string;
14+
// (undocumented)
15+
duration: number;
16+
// (undocumented)
17+
fullDisplayName: string;
18+
// (undocumented)
19+
number: number;
20+
// (undocumented)
21+
result?: string;
22+
// (undocumented)
23+
timestamp: number;
24+
// (undocumented)
25+
url: string;
26+
}
27+
28+
// @public (undocumented)
29+
export interface CrumbData {
30+
// (undocumented)
31+
cookies?: string[];
32+
// (undocumented)
33+
headerName: string;
34+
// (undocumented)
35+
headerValue: string;
36+
}
37+
38+
// @public (undocumented)
39+
export interface CrumbDataHeaderValues {
40+
// (undocumented)
41+
crumb: string;
42+
// (undocumented)
43+
crumbRequestField: string;
44+
}
45+
46+
// @public (undocumented)
47+
export type HeaderValue = string | string[] | undefined;
48+
49+
// @public (undocumented)
50+
export class Jenkins {
51+
constructor(opts: JenkinsClientOptions);
52+
// (undocumented)
53+
build: {
54+
get: (
55+
name: string | string[],
56+
buildNumber: string | number,
57+
) => Promise<JenkinsBuild>;
58+
getConsoleText: (
59+
name: string | string[],
60+
buildNumber: string | number,
61+
) => Promise<string>;
62+
};
63+
// (undocumented)
64+
job: {
65+
get: (input: string | string[] | JobGetOptions) => Promise<any>;
66+
getBuilds: (name: string | string[], tree?: string) => Promise<unknown>;
67+
build: (
68+
name: string | string[],
69+
opts?: JobBuildOptions | undefined,
70+
) => Promise<unknown>;
71+
copy: (name: string | string[], from: string) => Promise<void>;
72+
create: (name: string | string[], xml: string) => Promise<void>;
73+
destroy: (name: string | string[]) => Promise<void>;
74+
enable: (name: string | string[]) => Promise<void>;
75+
disable: (name: string | string[]) => Promise<void>;
76+
};
77+
// (undocumented)
78+
readonly opts: JenkinsClientOptions;
79+
}
80+
81+
// @public (undocumented)
82+
export interface JenkinsBuild extends CommonBuild {
83+
// (undocumented)
84+
actions: any;
85+
}
86+
87+
// @public (undocumented)
88+
export interface JenkinsClientOptions {
89+
// (undocumented)
90+
baseUrl: string;
91+
// (undocumented)
92+
crumbIssuer?: boolean | ((client: any) => Promise<CrumbData>) | undefined;
93+
// (undocumented)
94+
headers?: Record<string, HeaderValue>;
95+
// (undocumented)
96+
promisify?: boolean;
97+
}
98+
899
// @public
9100
export const jenkinsExecutePermission: ResourcePermission<'catalog-entity'>;
10101

102+
// @public (undocumented)
103+
export type JenkinsParams =
104+
| Record<string, unknown>
105+
| URLSearchParams
106+
| undefined;
107+
11108
// @public
12109
export const jenkinsPermissions: ResourcePermission<'catalog-entity'>[];
13110

111+
// @public (undocumented)
112+
export interface JobBuildOptions {
113+
// (undocumented)
114+
delay?: string;
115+
// (undocumented)
116+
parameters?: JenkinsParams;
117+
// (undocumented)
118+
token?: string;
119+
}
120+
121+
// @public (undocumented)
122+
export interface JobGetOptions {
123+
// (undocumented)
124+
depth?: number;
125+
// (undocumented)
126+
name: string | string[];
127+
// (undocumented)
128+
tree?: string;
129+
}
130+
14131
// (No @packageDocumentation comment for this package)
15132
```

0 commit comments

Comments
 (0)