Skip to content

Commit acbfa60

Browse files
committed
fix(cicd-statistics-module-github): Align authentication with other github frontends
1 parent 9b77dac commit acbfa60

File tree

9 files changed

+222
-166
lines changed

9 files changed

+222
-166
lines changed

docs/compatibility/new-frontend-system.md

Lines changed: 87 additions & 85 deletions
Large diffs are not rendered by default.

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ This is an extension module to the `cicd-statistics` plugin, providing a `CicdSt
1111

1212
```tsx
1313
// packages/app/src/apis.ts
14-
import { githubAuthApiRef } from '@backstage/core-plugin-api';
14+
import { configApiRef } from '@backstage/core-plugin-api';
15+
import { scmAuthApiRef } from '@backstage/integration-react';
1516

1617
import { cicdStatisticsApiRef } from '@backstage-community/plugin-cicd-statistics';
1718
import { CicdStatisticsApiGithub } from '@backstage-community/plugin-cicd-statistics-module-github';
@@ -20,11 +21,11 @@ export const apis: AnyApiFactory[] = [
2021
createApiFactory({
2122
api: cicdStatisticsApiRef,
2223
deps: {
23-
githubAuthApi: githubAuthApiRef,
24+
scmAuthApi: scmAuthApiRef,
2425
configApi: configApiRef,
2526
},
26-
factory: ({ githubAuthApi, configApi }) => {
27-
return new CicdStatisticsApiGithub(githubAuthApi, configApi);
27+
factory: ({ scmAuthApi, configApi }) => {
28+
return new CicdStatisticsApiGithub({ scmAuthApi, configApi });
2829
},
2930
}),
3031
];

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/dev/index.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
2929
import { cicdStatisticsApiRef } from '@backstage-community/plugin-cicd-statistics';
3030
import { catalogApiRef } from '@backstage/plugin-catalog-react';
3131
import { CicdStatisticsApiGithub } from '../src/api';
32-
import { configApiRef, githubAuthApiRef } from '@backstage/core-plugin-api';
32+
import { configApiRef } from '@backstage/core-plugin-api';
33+
import { scmAuthApiRef } from '@backstage/integration-react';
3334

3435
createDevApp()
3536
// We need the catalog plugin to get the example entities and make the front entity page functional
@@ -84,26 +85,34 @@ createDevApp()
8485
}),
8586
)
8687
.registerApi({
87-
api: githubAuthApiRef,
88+
api: scmAuthApiRef,
8889
deps: {},
8990
factory: () => {
9091
return {
91-
getAccessToken: async () => {
92+
getCredentials: async () => {
9293
// This nonsense is only here to make been able to locally test this plugin without having to setup a backend app
93-
return 'THIS_IS_ONLY_FOR_TESTING_LOCALLY_PLEASE_DO_NOT_NEVER_USE_THIS_IN_PRODUCTION';
94+
return {
95+
token:
96+
'THIS_IS_ONLY_FOR_TESTING_LOCALLY_PLEASE_DO_NOT_NEVER_USE_THIS_IN_PRODUCTION',
97+
headers: {},
98+
};
9499
},
95-
} as typeof githubAuthApiRef.T;
100+
} as typeof scmAuthApiRef.T;
96101
},
97102
})
98103
.registerApi({
99104
api: cicdStatisticsApiRef,
100105
deps: {
101106
catalogApiMock: catalogApiRef,
102-
githubAuthApi: githubAuthApiRef,
107+
scmAuthApi: scmAuthApiRef,
103108
configApi: configApiRef,
104109
},
105-
factory: ({ githubAuthApi, configApi }) => {
106-
return new CicdStatisticsApiGithub(githubAuthApi, configApi, {});
110+
factory: ({ scmAuthApi, configApi }) => {
111+
return new CicdStatisticsApiGithub({
112+
scmAuthApi,
113+
configApi,
114+
cicdDefaults: {},
115+
});
107116
},
108117
})
109118
.render();

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"@backstage/core-plugin-api": "backstage:^",
5959
"@backstage/frontend-plugin-api": "backstage:^",
6060
"@backstage/integration": "backstage:^",
61+
"@backstage/integration-react": "backstage:^",
6162
"@octokit/rest": "^21.1.1",
6263
"p-limit": "^3.1.0"
6364
},

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/src/alpha.ts

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { cicdStatisticsApiRef } from '@backstage-community/plugin-cicd-statistics';
17-
import {
18-
ApiBlueprint,
19-
configApiRef,
20-
createFrontendModule,
21-
githubAuthApiRef,
22-
} from '@backstage/frontend-plugin-api';
23-
import { CicdStatisticsApiGithub } from './api';
16+
import { createFrontendModule } from '@backstage/frontend-plugin-api';
17+
import { cicdStatisticsGithubApi } from './alpha/index';
2418

2519
/**
2620
* @alpha
2721
*/
28-
export const cicdStatisticsGithubExtension = ApiBlueprint.make({
29-
name: 'cicd-statistics-github-api',
30-
params: defineParams =>
31-
defineParams({
32-
api: cicdStatisticsApiRef,
33-
deps: {
34-
githubAuthApi: githubAuthApiRef,
35-
configApi: configApiRef,
36-
},
37-
factory: ({ githubAuthApi, configApi }) => {
38-
return new CicdStatisticsApiGithub(githubAuthApi, configApi);
39-
},
40-
}),
41-
});
42-
43-
/**
44-
* @alpha
45-
*/
46-
const cicdStatisticsExtensionOverrides = createFrontendModule({
22+
export default createFrontendModule({
4723
pluginId: 'cicd-statistics',
48-
extensions: [cicdStatisticsGithubExtension],
24+
extensions: [cicdStatisticsGithubApi],
4925
});
50-
51-
export default cicdStatisticsExtensionOverrides;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2024 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { configApiRef, ApiBlueprint } from '@backstage/frontend-plugin-api';
17+
import { scmAuthApiRef } from '@backstage/integration-react';
18+
import { cicdStatisticsApiRef } from '@backstage-community/plugin-cicd-statistics';
19+
import { CicdStatisticsApiGithub } from '../api';
20+
21+
/**
22+
* @alpha
23+
*/
24+
export const cicdStatisticsGithubApi = ApiBlueprint.make({
25+
name: 'cicd-statistics/cicd-statistics-github-api',
26+
params: defineParams =>
27+
defineParams({
28+
api: cicdStatisticsApiRef,
29+
deps: { configApi: configApiRef, scmAuthApi: scmAuthApiRef },
30+
factory: ({ configApi, scmAuthApi }) =>
31+
new CicdStatisticsApiGithub({ configApi, scmAuthApi }),
32+
}),
33+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2024 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export * from './apis';

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/src/api/github.ts

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ import {
2323
FetchBuildsOptions,
2424
Stage,
2525
} from '@backstage-community/plugin-cicd-statistics';
26-
import { ConfigApi, OAuthApi } from '@backstage/core-plugin-api';
26+
import { ConfigApi } from '@backstage/core-plugin-api';
2727
import limiterFactory from 'p-limit';
2828
import { Entity, getEntitySourceLocation } from '@backstage/catalog-model';
2929
import { jobToStages, workflowToBuild } from './utils';
3030

3131
import { readGithubIntegrationConfigs } from '@backstage/integration';
32+
import { ScmAuthApi } from '@backstage/integration-react';
3233
import { Octokit } from '@octokit/rest';
3334

3435
/** @public */
@@ -38,16 +39,16 @@ export const getProjectNameFromEntity = (entity: Entity) =>
3839
entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? '';
3940

4041
/**
41-
* This type represents a initialized github client with octokit
42+
* This type represents an initialized github client with octokit
4243
*
4344
* @public
4445
*/
4546
export type GithubClient = {
46-
/* the actual API of octokit */
47-
api: InstanceType<typeof Octokit>;
48-
/* the owner the repository, retrieved from the entity source location */
47+
/* the octokit instance for making GitHub API calls */
48+
octokit: InstanceType<typeof Octokit>;
49+
/* the owner of the repository, retrieved from the entity source location */
4950
owner: string;
50-
/* the repository name, retrieved from the entity source location */
51+
/* the repository name, retrieved from the entity source location */
5152
repo: string;
5253
};
5354

@@ -57,36 +58,46 @@ export type GithubClient = {
5758
* @public
5859
*/
5960
export class CicdStatisticsApiGithub implements CicdStatisticsApi {
60-
readonly #githubAuthApi: OAuthApi;
61-
readonly #cicdDefaults: Partial<CicdDefaults>;
62-
readonly configApi: ConfigApi;
63-
64-
constructor(
65-
githubAuthApi: OAuthApi,
66-
configApi: ConfigApi,
67-
cicdDefaults: Partial<CicdDefaults> = {},
68-
) {
69-
this.#githubAuthApi = githubAuthApi;
70-
this.#cicdDefaults = cicdDefaults;
71-
this.configApi = configApi;
61+
private readonly scmAuthApi: ScmAuthApi;
62+
private readonly configApi: ConfigApi;
63+
private readonly cicdDefaults: Partial<CicdDefaults>;
64+
65+
constructor(options: {
66+
scmAuthApi: ScmAuthApi;
67+
configApi: ConfigApi;
68+
cicdDefaults?: Partial<CicdDefaults>;
69+
}) {
70+
this.scmAuthApi = options.scmAuthApi;
71+
this.configApi = options.configApi;
72+
this.cicdDefaults = options.cicdDefaults ?? {};
7273
}
7374

74-
public async createGithubApi(
75-
entity: Entity,
76-
scopes: string[],
77-
): Promise<GithubClient> {
78-
const entityInfo = getEntitySourceLocation(entity);
79-
const [owner, repo] = getProjectNameFromEntity(entity).split('/');
80-
const url = new URL(entityInfo.target);
81-
const oauthToken = await this.#githubAuthApi.getAccessToken(scopes);
82-
75+
private async getOctokit(hostname: string = 'github.com'): Promise<Octokit> {
76+
const { token } = await this.scmAuthApi.getCredentials({
77+
url: `https://${hostname}/`,
78+
additionalScope: {
79+
customScopes: {
80+
github: ['repo'],
81+
},
82+
},
83+
});
8384
const configs = readGithubIntegrationConfigs(
8485
this.configApi.getOptionalConfigArray('integrations.github') ?? [],
8586
);
86-
const githubIntegrationConfig = configs.find(v => v.host === url.hostname);
87+
const githubIntegrationConfig = configs.find(v => v.host === hostname);
8788
const baseUrl = githubIntegrationConfig?.apiBaseUrl;
89+
return new Octokit({ auth: token, baseUrl });
90+
}
91+
92+
public async createGithubClient(entity: Entity): Promise<GithubClient> {
93+
const entityInfo = getEntitySourceLocation(entity);
94+
const [owner, repo] = getProjectNameFromEntity(entity).split('/');
95+
const url = new URL(entityInfo.target);
96+
const hostname = url.hostname;
97+
98+
const octokit = await this.getOctokit(hostname);
8899
return {
89-
api: new Octokit({ auth: oauthToken, baseUrl }),
100+
octokit,
90101
owner,
91102
repo,
92103
};
@@ -142,18 +153,16 @@ export class CicdStatisticsApiGithub implements CicdStatisticsApi {
142153
filterStatus = ['all'],
143154
filterType = 'all',
144155
} = options;
145-
const { api, owner, repo } = await this.createGithubApi(entity, [
146-
'read_api',
147-
]);
156+
const { octokit, owner, repo } = await this.createGithubClient(entity);
148157
updateProgress(0, 0, 0);
149158

150159
const branch =
151160
filterType === 'master'
152-
? await CicdStatisticsApiGithub.getDefaultBranch(api, owner, repo)
161+
? await CicdStatisticsApiGithub.getDefaultBranch(octokit, owner, repo)
153162
: undefined;
154163

155-
const workflowsRuns = await api.paginate(
156-
api.actions.listWorkflowRunsForRepo,
164+
const workflowsRuns = await octokit.paginate(
165+
octokit.actions.listWorkflowRunsForRepo,
157166
{
158167
owner,
159168
repo,
@@ -168,10 +177,15 @@ export class CicdStatisticsApiGithub implements CicdStatisticsApi {
168177
const builds = workflowsRuns.map(async build => ({
169178
...build,
170179
duration: await limiter(() =>
171-
CicdStatisticsApiGithub.getDurationOfBuild(api, owner, repo, build),
180+
CicdStatisticsApiGithub.getDurationOfBuild(octokit, owner, repo, build),
172181
),
173182
stages: await limiter(() =>
174-
CicdStatisticsApiGithub.updateBuildWithStages(api, owner, repo, build),
183+
CicdStatisticsApiGithub.updateBuildWithStages(
184+
octokit,
185+
owner,
186+
repo,
187+
build,
188+
),
175189
),
176190
}));
177191
const promisedBuilds = (await Promise.all(builds)).filter(b =>
@@ -194,7 +208,7 @@ export class CicdStatisticsApiGithub implements CicdStatisticsApi {
194208
'enqueued',
195209
'scheduled',
196210
] as const,
197-
defaults: this.#cicdDefaults,
211+
defaults: this.cicdDefaults,
198212
};
199213
}
200214
}

workspaces/cicd-statistics/plugins/cicd-statistics-module-github/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
export { CicdStatisticsApiGithub } from './api';
17+
/**
18+
* A Backstage plugin that provides GitHub CI/CD statistics
19+
*
20+
* @packageDocumentation
21+
*/
22+
23+
export { CicdStatisticsApiGithub, GITHUB_ACTIONS_ANNOTATION } from './api';
1824
export type { GithubClient } from './api';

0 commit comments

Comments
 (0)