UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

259 lines 11.7 kB
"use strict"; /* * Copyright © 2020 Atomist, Inc. * * 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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.repoSlug = exports.scmCredentials = exports.repoCredentials = exports.isRemoteRepo = exports.queryForScmProvider = void 0; const RepoId_1 = require("@atomist/automation-client/lib/operations/common/RepoId"); const GitCommandGitProject_1 = require("@atomist/automation-client/lib/project/git/GitCommandGitProject"); const GraphClient_1 = require("@atomist/automation-client/lib/spi/graph/GraphClient"); const logger_1 = require("@atomist/automation-client/lib/util/logger"); const stringify = require("json-stringify-safe"); const _ = require("lodash"); const types_1 = require("../../../typings/types"); const clone_1 = require("./clone"); const defaultDefaultBranch = "master"; /** * If called and no sync repo is provided in the SDM configuration, a * warning is emitted and `undefined` is returned. * * If the SDM configuration contains a RemoteRepoRef as the value of * the `sdm.configuration.sdm.k8s.options.sync.repo` option and truthy * credentials, those are returned. * * Otherwise, the code cycles through all workspaces, querying cortex * for the information it needs. If the value of the `sync.repo` * option is a [[SyncRepoRef]], each workspace is queried for a repo * matching the provided sync repo. If none is found, it cycles * through all the workspaces again querying for all SCM providers and * try to clone the repo with the provided credentials or, if no * credentials are provided, the SCM provider credentials from cortex. * Once a repo is found using either method, an object is returned * with its remote repo ref and the credentials able to clone it. In * addition, the `sdm` passed in will have its * `sdm.configuration.sdm.k8s.options.sync.repo` * and`sdm.configuration.sdm.k8s.options.sync.credentials` updated * with the objects appropriate objects. * * @param sdm this SDM object (modified if repo credentials found) * @param repoRef repository to look for * @return true if sync options set and repo found or false and sync options deleted */ async function queryForScmProvider(configuration) { var _a, _b, _c; const syncOptions = (_c = (_b = (_a = configuration.sdm) === null || _a === void 0 ? void 0 : _a.k8s) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.sync; if (!syncOptions) { logger_1.logger.debug(`SDM configuration contains no sync repo`); return false; } const repoRef = syncOptions.repo; if (!repoRef || !repoRef.owner || !repoRef.repo) { logger_1.logger.error(`Provided sync repo does not contain all required properties: ${stringify(repoRef)}`); return false; } const repoProvided = isRemoteRepo(repoRef); const credsProvided = !!syncOptions.credentials; if (repoProvided && credsProvided) { logger_1.logger.info(`Using provided remote repo ref and credentials for sync repo`); return true; } const repoCreds = await queryRepo(configuration) || await queryScm(configuration); if (repoCreds) { if (!repoProvided) { configuration.sdm.k8s.options.sync.repo = repoCreds.repo; } if (!credsProvided) { configuration.sdm.k8s.options.sync.credentials = repoCreds.credentials; } return true; } logger_1.logger.warn(`Failed to find sync repo: ${stringify(repoRef)}`); return false; } exports.queryForScmProvider = queryForScmProvider; /** * See if provided sync repo is a RemoteRepoRef. */ function isRemoteRepo(repo) { return !!repo && RepoId_1.isRemoteRepoRef(repo); } exports.isRemoteRepo = isRemoteRepo; /** * Query cortex across all available workspaces for repo. */ async function queryRepo(configuration) { const repoRef = syncRepoRef(configuration); const slug = repoSlug(repoRef); const repoProviderId = repoRef.providerId; for (const workspaceId of configuration.workspaceIds) { const graphClient = configuration.graphql.client.factory.create(workspaceId, configuration); logger_1.logger.debug(`Querying workspace ${workspaceId} for repo ${slug}`); const repos = await graphClient.query({ name: "RepoScmProvider", variables: { repo: repoRef.repo, owner: repoRef.owner }, options: GraphClient_1.QueryNoCacheOptions, }); if (!repos || !repos.Repo || repos.Repo.length < 1) { logger_1.logger.debug(`Repo ${slug} not found in workspace ${workspaceId}`); continue; } let searchRepos = repos.Repo; if (repoProviderId) { logger_1.logger.debug(`Filtering repos from workspace ${workspaceId} on providerId '${repoProviderId}'`); searchRepos = searchRepos.filter(r => r.org.scmProvider.providerId === repoProviderId); } if (searchRepos.length > 1) { logger_1.logger.warn(`More than one repo found in workspace ${workspaceId} with owner/repo ${slug}`); } for (const repo of searchRepos) { const rc = await repoCredentials(configuration, repo, workspaceId); if (rc) { rc.repo.branch = rc.repo.branch || repo.defaultBranch || defaultDefaultBranch; logger_1.logger.info(`Returning first ${slug} repo with valid SCM provider`); return rc; } } } return undefined; } /** * For each SDM provider in cortex in each workspace, try to clone the * sync repo. Return the information for the first successful clone. */ async function queryScm(configuration) { const repoRef = syncRepoRef(configuration); const slug = repoSlug(repoRef); const repoProviderId = repoRef.providerId; for (const workspaceId of configuration.workspaceIds) { const graphClient = configuration.graphql.client.factory.create(workspaceId, configuration); logger_1.logger.debug(`Querying workspace ${workspaceId} for SCM providers`); const providers = await graphClient.query({ name: "ScmProviders", options: GraphClient_1.QueryNoCacheOptions, }); if (!providers || !providers.SCMProvider || providers.SCMProvider.length < 1) { logger_1.logger.debug(`Found no SCM providers in workspace ${workspaceId}`); continue; } for (const provider of providers.SCMProvider) { if (repoProviderId && provider.providerId !== repoProviderId) { logger_1.logger.debug(`SCM provider '${provider.providerId}' does not match '${repoProviderId}'`); continue; } const rc = await scmCredentials(configuration, provider, workspaceId); if (rc) { logger_1.logger.debug(`Attempting to clone ${slug} using ${rc.repo.cloneUrl}`); try { const p = await GitCommandGitProject_1.GitCommandGitProject.cloned(rc.credentials, rc.repo, clone_1.defaultCloneOptions); if (p) { rc.repo.branch = rc.repo.branch || p.branch || defaultDefaultBranch; return rc; } } catch (e) { logger_1.logger.debug(`Failed to clone ${slug} from ${rc.repo.cloneUrl}: ${e.message}`); } } } } return undefined; } /** * Create RemoteRepoRef and Credentials object from SDM and repo from * cortex. If the provided repo does not contain an org with a * provider, it returns `undefined`. Otherwise it uses the SCM * provider to call [[scmCredentials]] and return its value. */ async function repoCredentials(configuration, repo, workspaceId) { if (repo.org && repo.org.scmProvider) { return scmCredentials(configuration, repo.org.scmProvider, workspaceId); } return undefined; } exports.repoCredentials = repoCredentials; /** * Given the SDM and an SCM, use the configured repo ref resolver to * create a RemoteRepoRef. Use the SDM Kubernetes option sync * credentials or SCM `credential.secret` to create the credentials, * giving the SDM sync credentials preference. Return `undefined` if * there is not enough information to created the repo credential * object. */ async function scmCredentials(configuration, scm, workspaceId) { var _a, _b, _c, _d, _e; const repoRef = syncRepoRef(configuration); const credentials = (_d = (_c = (_b = (_a = configuration.sdm) === null || _a === void 0 ? void 0 : _a.k8s) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.sync) === null || _d === void 0 ? void 0 : _d.credentials; let secret = (_e = scm === null || scm === void 0 ? void 0 : scm.credential) === null || _e === void 0 ? void 0 : _e.secret; if (!credentials && !secret && scm.providerType === types_1.ProviderType.github_com) { const graphClient = configuration.graphql.client.factory.create(workspaceId, configuration); const app = await graphClient.query({ name: "GitHubAppInstallationByOwner", variables: { name: repoRef.owner, }, }); secret = _.get(app, "GitHubAppInstallation[0].token.secret"); } if (repoRef && repoRef.owner && repoRef.repo && scm.apiUrl && (credentials || secret)) { const repoResolver = configuration.sdm.repoRefResolver; const repoFrag = { owner: repoRef.owner, name: repoRef.repo, org: { owner: repoRef.owner, provider: { providerId: scm.providerId, providerType: scm.providerType, apiUrl: scm.apiUrl, url: scm.url, }, }, }; const options = { branch: repoRef.branch, }; try { const repo = repoResolver.toRemoteRepoRef(repoFrag, options); return { credentials: credentials || { token: secret }, repo, }; } catch (e) { logger_1.logger.warn(`Failed to resolve remote repo ref for ${repoFrag.owner}/${repoFrag.name}: ${e.message}`); } } return undefined; } exports.scmCredentials = scmCredentials; /** Create repo slug string. */ function repoSlug(repo) { return `${repo.owner}/${repo.repo}`; } exports.repoSlug = repoSlug; /** * Extract the Kubernetes option sync repo from the SDM configuration. * This function should only be called if the sync repo object is * defined. */ function syncRepoRef(configuration) { var _a, _b, _c, _d; const repo = (_d = (_c = (_b = (_a = configuration.sdm) === null || _a === void 0 ? void 0 : _a.k8s) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.sync) === null || _d === void 0 ? void 0 : _d.repo; if (!repo) { throw new Error(`Failed to get sync repo from SDM configuration`); } return repo; } //# sourceMappingURL=repo.js.map