Merge pull request #9701 from backstage/blam/deprecations/octokit

deprecations: Deprecate the `OctokitProvider` export from `scaffolder-backend`
This commit is contained in:
Ben Lambert
2022-02-22 13:38:32 +01:00
committed by GitHub
8 changed files with 158 additions and 69 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
- **DEPRECATED** - `OctokitProvider` has been deprecated and will be removed in upcoming versions
This helper doesn't make sense to be export from the `plugin-scaffolder-backend` and possibly will be moved into the `integrations` package at a later date.
All implementations have been moved over to a private implementation called `getOctokitOptions` which is then passed to the `Octokit` constructor. If you're using this API you should consider duplicating the logic that lives in `getOctokitOptions` and move away from the deprecated export.
+3 -1
View File
@@ -392,13 +392,15 @@ export function fetchContents({
// Warning: (ae-missing-release-tag) "OctokitProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
// @public @deprecated
export class OctokitProvider {
constructor(
integrations: ScmIntegrationRegistry,
githubCredentialsProvider?: GithubCredentialsProvider,
);
// Warning: (ae-forgotten-export) The symbol "OctokitIntegration" needs to be exported by the entry point index.d.ts
//
// @deprecated
getOctokit(
repoUrl: string,
options?: {
@@ -32,6 +32,9 @@ export type OctokitIntegration = {
/**
* OctokitProvider provides Octokit client based on ScmIntegrationsRegistry configuration.
* OctokitProvider supports GitHub credentials caching out of the box.
*
* @deprecated we are no longer providing a way from the scaffolder to generate octokit instances.
* Implement your own if you're using this method from an external package, or use the internal `getOctokitOptions` function instead
*/
export class OctokitProvider {
private readonly integrations: ScmIntegrationRegistry;
@@ -51,6 +54,9 @@ export class OctokitProvider {
* gets standard Octokit client based on repository URL.
*
* @param repoUrl - Repository URL
*
* @deprecated we are no longer providing a way from the scaffolder to generate octokit instances.
* Implement your own if you're using this method from an external package, or use the internal `getOctokitOptions` function instead
*/
async getOctokit(
repoUrl: string,
@@ -13,24 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { InputError } from '@backstage/errors';
import {
DefaultGithubCredentialsProvider,
GithubCredentialsProvider,
ScmIntegrations,
} from '@backstage/integration';
import { Octokit } from 'octokit';
import { createTemplateAction } from '../../createTemplateAction';
import { OctokitProvider } from './OctokitProvider';
import { parseRepoUrl } from '../publish/util';
import { getOctokitOptions } from './helpers';
export function createGithubActionsDispatchAction(options: {
integrations: ScmIntegrations;
githubCredentialsProvider?: GithubCredentialsProvider;
}) {
const { integrations, githubCredentialsProvider } = options;
const octokitProvider = new OctokitProvider(
integrations,
githubCredentialsProvider ||
DefaultGithubCredentialsProvider.fromIntegrations(integrations),
);
return createTemplateAction<{
repoUrl: string;
@@ -90,9 +87,19 @@ export function createGithubActionsDispatchAction(options: {
`Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`,
);
const { client, owner, repo } = await octokitProvider.getOctokit(
repoUrl,
{ token: providedToken },
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
if (!owner) {
throw new InputError('Invalid repository owner provided in repoUrl');
}
const client = new Octokit(
await getOctokitOptions({
integrations,
repoUrl,
credentialsProvider: githubCredentialsProvider,
token: providedToken,
}),
);
await client.rest.actions.createWorkflowDispatch({
@@ -14,14 +14,15 @@
* limitations under the License.
*/
import {
DefaultGithubCredentialsProvider,
GithubCredentialsProvider,
ScmIntegrationRegistry,
} from '@backstage/integration';
import { createTemplateAction } from '../../createTemplateAction';
import { OctokitProvider } from './OctokitProvider';
import { emitterEventNames } from '@octokit/webhooks';
import { assertError } from '@backstage/errors';
import { assertError, InputError } from '@backstage/errors';
import { Octokit } from 'octokit';
import { getOctokitOptions } from './helpers';
import { parseRepoUrl } from '../publish/util';
export function createGithubWebhookAction(options: {
integrations: ScmIntegrationRegistry;
@@ -30,11 +31,7 @@ export function createGithubWebhookAction(options: {
}) {
const { integrations, defaultWebhookSecret, githubCredentialsProvider } =
options;
const octokitProvider = new OctokitProvider(
integrations,
githubCredentialsProvider ??
DefaultGithubCredentialsProvider.fromIntegrations(integrations),
);
const eventNames = emitterEventNames.filter(event => !event.includes('.'));
return createTemplateAction<{
@@ -127,10 +124,19 @@ export function createGithubWebhookAction(options: {
} = ctx.input;
ctx.logger.info(`Creating webhook ${webhookUrl} for repo ${repoUrl}`);
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
const { client, owner, repo } = await octokitProvider.getOctokit(
repoUrl,
{ token: providedToken },
if (!owner) {
throw new InputError('Invalid repository owner provided in repoUrl');
}
const client = new Octokit(
await getOctokitOptions({
integrations,
credentialsProvider: githubCredentialsProvider,
repoUrl: repoUrl,
token: providedToken,
}),
);
try {
@@ -0,0 +1,77 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { InputError } from '@backstage/errors';
import {
DefaultGithubCredentialsProvider,
GithubCredentialsProvider,
ScmIntegrationRegistry,
} from '@backstage/integration';
import { OctokitOptions } from '@octokit/core/dist-types/types';
import { parseRepoUrl } from '../publish/util';
export async function getOctokitOptions(options: {
integrations: ScmIntegrationRegistry;
credentialsProvider?: GithubCredentialsProvider;
token?: string;
repoUrl: string;
}): Promise<OctokitOptions> {
const { integrations, credentialsProvider, repoUrl, token } = options;
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
if (!owner) {
throw new InputError(`No owner provided for repo ${repoUrl}`);
}
const integrationConfig = integrations.github.byHost(host)?.config;
if (!integrationConfig) {
throw new InputError(`No integration for host ${host}`);
}
// short circuit the `githubCredentialsProvider` if there is a token provided by the caller already
if (token) {
return {
auth: token,
baseUrl: integrationConfig.apiBaseUrl,
previews: ['nebula-preview'],
};
}
const githubCredentialsProvider =
credentialsProvider ??
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
// TODO(blam): Consider changing this API to take host and repo instead of repoUrl, as we end up parsing in this function
// and then parsing in the `getCredentials` function too the other side
const { token: credentialProviderToken } =
await githubCredentialsProvider.getCredentials({
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
repo,
)}`,
});
if (!credentialProviderToken) {
throw new InputError(
`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,
);
}
return {
auth: credentialProviderToken,
baseUrl: integrationConfig.apiBaseUrl,
previews: ['nebula-preview'],
};
}
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import {
DefaultGithubCredentialsProvider,
GithubCredentialsProvider,
ScmIntegrationRegistry,
} from '@backstage/integration';
@@ -22,11 +21,12 @@ import {
enableBranchProtectionOnDefaultRepoBranch,
initRepoAndPush,
} from '../helpers';
import { getRepoSourceDirectory } from './util';
import { getRepoSourceDirectory, parseRepoUrl } from './util';
import { createTemplateAction } from '../../createTemplateAction';
import { Config } from '@backstage/config';
import { OctokitProvider } from '../github/OctokitProvider';
import { assertError } from '@backstage/errors';
import { assertError, InputError } from '@backstage/errors';
import { getOctokitOptions } from '../github/helpers';
import { Octokit } from 'octokit';
export function createPublishGithubAction(options: {
integrations: ScmIntegrationRegistry;
@@ -34,11 +34,6 @@ export function createPublishGithubAction(options: {
githubCredentialsProvider?: GithubCredentialsProvider;
}) {
const { integrations, config, githubCredentialsProvider } = options;
const octokitProvider = new OctokitProvider(
integrations,
githubCredentialsProvider ||
DefaultGithubCredentialsProvider.fromIntegrations(integrations),
);
return createTemplateAction<{
repoUrl: string;
@@ -160,10 +155,20 @@ export function createPublishGithubAction(options: {
token: providedToken,
} = ctx.input;
const { client, token, owner, repo } = await octokitProvider.getOctokit(
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
if (!owner) {
throw new InputError('Invalid repository owner provided in repoUrl');
}
const octokitOptions = await getOctokitOptions({
integrations,
credentialsProvider: githubCredentialsProvider,
token: providedToken,
repoUrl,
{ token: providedToken },
);
});
const client = new Octokit(octokitOptions);
const user = await client.rest.users.getByUsername({
username: owner,
@@ -253,7 +258,7 @@ export function createPublishGithubAction(options: {
defaultBranch,
auth: {
username: 'x-access-token',
password: token,
password: octokitOptions.auth,
},
logger: ctx.logger,
commitMessage: config.getOptionalString(
@@ -20,7 +20,6 @@ import { parseRepoUrl, isExecutable } from './util';
import {
GithubCredentialsProvider,
ScmIntegrationRegistry,
SingleInstanceGithubCredentialsProvider,
} from '@backstage/integration';
import { zipObject } from 'lodash';
import { createTemplateAction } from '../../createTemplateAction';
@@ -29,6 +28,7 @@ import { InputError, CustomErrorBase } from '@backstage/errors';
import { createPullRequest } from 'octokit-plugin-create-pull-request';
import globby from 'globby';
import { resolveSafeChildPath } from '@backstage/backend-common';
import { getOctokitOptions } from '../github/helpers';
export type Encoding = 'utf-8' | 'base64';
@@ -65,40 +65,19 @@ export const defaultClientFactory = async ({
host = 'github.com',
token: providedToken,
}: ClientFactoryInput): Promise<PullRequestCreator> => {
const integrationConfig = integrations.github.byHost(host)?.config;
const [encodedHost, encodedOwner, encodedRepo] = [host, owner, repo].map(
encodeURIComponent,
);
const octokitOptions = await getOctokitOptions({
integrations,
credentialsProvider: githubCredentialsProvider,
repoUrl: `https://${encodedHost}/${encodedOwner}/${encodedRepo}`,
token: providedToken,
});
const OctokitPR = Octokit.plugin(createPullRequest);
if (!integrationConfig) {
throw new InputError(`No integration for host ${host}`);
}
if (providedToken) {
return new OctokitPR({
auth: providedToken,
baseUrl: integrationConfig.apiBaseUrl,
});
}
const credentialsProvider =
githubCredentialsProvider ||
SingleInstanceGithubCredentialsProvider.create(integrationConfig);
const { token } = await credentialsProvider.getCredentials({
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
repo,
)}`,
});
if (!token) {
throw new InputError(
`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,
);
}
return new OctokitPR({
auth: token,
baseUrl: integrationConfig.apiBaseUrl,
});
return new OctokitPR(octokitOptions);
};
interface CreateGithubPullRequestActionOptions {