diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts new file mode 100644 index 0000000000..4f369b7263 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts @@ -0,0 +1,84 @@ +/* + * 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'; + +interface GetOctokitOptionsInput { + integrations: ScmIntegrationRegistry; + credentialsProvider?: GithubCredentialsProvider; + token?: string; + repoUrl: string; +} +export const getOctokitOptions = async ({ + integrations, + credentialsProvider, + repoUrl, + token, +}: GetOctokitOptionsInput): Promise => { + 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 internal Github Token provider the token provided + // by the action or the caller. + if (token) { + return { + auth: token, + baseUrl: integrationConfig.apiBaseUrl, + previews: ['nebula-preview'], + }; + } + + const githubCredentialsProvider = + credentialsProvider ?? + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + + // TODO(blam): Consider changing this API to have owner, repo interface instead of URL as the it's + // needless to create URL and then parse again 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'], + }; +};