UNPKG

backport

Version:

A CLI tool that automates the process of backporting commits

134 lines 5.59 kB
import { graphql } from '../../../../graphql/generated/index.js'; import { BackportError } from '../../../backport-error.js'; import { getLocalConfigFileCommitDate, isLocalConfigFileUntracked, isLocalConfigFileModified, } from '../../../git/index.js'; import { logger } from '../../../logger.js'; import { parseRemoteConfigFile, isMissingConfigFileException, } from '../../../remote-config.js'; import { graphqlRequest } from '../client/graphql-client.js'; import { getInvalidGithubTokenMessage } from '../get-invalid-github-token-message.js'; export async function getOptionsFromGithub(options) { const { githubToken, githubApiBaseUrlV4, repoName, repoOwner, globalConfigFile, } = options; const query = graphql(` query GithubConfigOptions($repoOwner: String!, $repoName: String!) { viewer { login } repository(owner: $repoOwner, name: $repoName) { # check to see if a branch named "backport" exists illegalBackportBranch: ref(qualifiedName: "refs/heads/backport") { id } isPrivate defaultBranchRef { name target { __typename ...RemoteConfigHistoryFragment } } } } `); const variables = { repoOwner, repoName }; const result = await graphqlRequest({ githubToken, githubApiBaseUrlV4 }, query, variables); if (result.error) { const invalidGithubTokenMessage = getInvalidGithubTokenMessage({ result, repoName, repoOwner, globalConfigFile, githubToken, }); if (invalidGithubTokenMessage) { throw new BackportError({ code: 'invalid-credentials-exception', message: invalidGithubTokenMessage, }); } if (!isMissingConfigFileException(result)) { throw new BackportError({ code: 'github-api-exception', message: result.error.message, }); } } const insufficientPermissionsErrorMessage = getInsufficientPermissionsErrorMessage(result); if (insufficientPermissionsErrorMessage) { throw new BackportError({ code: 'invalid-credentials-exception', message: insufficientPermissionsErrorMessage, }); } const { data } = result; // it is not possible to have a branch named "backport" if (data?.repository?.illegalBackportBranch) { throw new BackportError({ code: 'config-error-exception', message: 'You must delete the branch "backport" to continue. See https://github.com/sorenlouv/backport/issues/155 for details', }); } const remoteConfig = await getRemoteConfigFileOptions(data, options.cwd, options.skipRemoteConfig); if (!data) { throw new BackportError({ code: 'config-error-exception', message: 'Failed to fetch options from GitHub. Please check your GitHub token and repository settings.', }); } return { authenticatedUsername: data.viewer.login, sourceBranch: options.sourceBranch ?? data.repository?.defaultBranchRef?.name ?? 'main', isRepoPrivate: data.repository?.isPrivate, ...remoteConfig, }; } async function getRemoteConfigFileOptions(res, cwd, skipRemoteConfig) { if (skipRemoteConfig) { logger.info('Remote config: Skipping. `--skip-remote-config` specified via config file or cli'); return; } if (res?.repository?.defaultBranchRef?.target?.__typename !== 'Commit') { logger.info('Remote config: Skipping. Default branch is not a commit'); return; } const remoteConfig = res.repository.defaultBranchRef.target.remoteConfigHistory.edges?.at(0)?.remoteConfig; if (!remoteConfig) { logger.info("Remote config: Skipping. Remote config doesn't exist"); return; } if (cwd) { const [isLocalConfigModified, isLocalConfigUntracked, localCommitDate] = await Promise.all([ isLocalConfigFileModified({ cwd }), isLocalConfigFileUntracked({ cwd }), getLocalConfigFileCommitDate({ cwd }), ]); if (isLocalConfigUntracked) { logger.info('Remote config: Skipping. Local config is new'); return; } if (isLocalConfigModified) { logger.info('Remote config: Skipping. Local config is modified'); return; } if (localCommitDate && localCommitDate > Date.parse(String(remoteConfig.committedDate))) { logger.info(`Remote config: Skipping. Local config is newer: ${new Date(localCommitDate).toISOString()} > ${String(remoteConfig.committedDate)}`); return; } } logger.info('Remote config: Parsing.'); return parseRemoteConfigFile(remoteConfig); } function getInsufficientPermissionsErrorMessage(res) { const accessScopesHeader = res.responseHeaders?.get('x-oauth-scopes'); if (accessScopesHeader == null) { return; } const tokenScopes = new Set(accessScopesHeader.split(',').map((scope) => scope.trim())); const isRepoPrivate = res.data?.repository?.isPrivate; if (isRepoPrivate && !tokenScopes.has('repo')) { return `You must grant the "repo" scope to your GitHub token`; } if (!tokenScopes.has('repo') && !tokenScopes.has('public_repo')) { return `You must grant the "repo" or "public_repo" scope to your GitHub token`; } } //# sourceMappingURL=get-options-from-github.js.map