@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
179 lines • 9.72 kB
JavaScript
;
/*
* Copyright © 2019 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.computeStartingPoint = exports.isSeedDrivenGeneratorParameters = exports.toGeneratorParametersMaker = exports.generatorCommand = void 0;
const HandlerResult_1 = require("@atomist/automation-client/lib/HandlerResult");
const onCommand_1 = require("@atomist/automation-client/lib/onCommand");
const RepoId_1 = require("@atomist/automation-client/lib/operations/common/RepoId");
const generatorUtils_1 = require("@atomist/automation-client/lib/operations/generate/generatorUtils");
const Project_1 = require("@atomist/automation-client/lib/project/Project");
const MessageClient_1 = require("@atomist/automation-client/lib/spi/message/MessageClient");
const constructionUtils_1 = require("@atomist/automation-client/lib/util/constructionUtils");
const slack_messages_1 = require("@atomist/slack-messages");
const storeGoals_1 = require("../../goal/storeGoals");
const handlerRegistrations_1 = require("../../machine/handlerRegistrations");
const projectLoaderRepoLoader_1 = require("../../machine/projectLoaderRepoLoader");
const toMachineOptions_1 = require("../../machine/toMachineOptions");
const messages_1 = require("../../misc/slack/messages");
const CachingProjectLoader_1 = require("../../project/CachingProjectLoader");
/**
* Create a command handler for project generation
* @param sdm this machine or its options
* @param {EditorFactory<P extends SeedDrivenGeneratorParameters>} editorFactory to create editorCommand to perform transformation
* @param {Maker<P extends SeedDrivenGeneratorParameters>} paramsMaker
* @param {string} name
* @param {Partial<GeneratorCommandDetails<P extends SeedDrivenGeneratorParameters>>} details
* @return {HandleCommand}
*/
function generatorCommand(sdm, editorFactory, name, paramsMaker, fallbackTarget, startingPoint, details = {}, cr) {
const detailsToUse = Object.assign(Object.assign({}, defaultDetails(toMachineOptions_1.toMachineOptions(sdm), name)), details);
return onCommand_1.commandHandlerFrom(handleGenerate(editorFactory, detailsToUse, startingPoint, cr, toMachineOptions_1.toMachineOptions(sdm)), toGeneratorParametersMaker(paramsMaker, constructionUtils_1.toFactory(fallbackTarget)()), name, detailsToUse.description, detailsToUse.intent, detailsToUse.tags);
}
exports.generatorCommand = generatorCommand;
/**
* Return a parameters maker that is targeting aware
* @param {Maker<PARAMS>} paramsMaker
* @return {Maker<EditorOrReviewerParameters & PARAMS>}
*/
function toGeneratorParametersMaker(paramsMaker, target) {
const sampleParams = constructionUtils_1.toFactory(paramsMaker)();
return isSeedDrivenGeneratorParameters(sampleParams) ?
paramsMaker :
() => {
// This way we won't bother with source, but rely on startingPoint
const rawParms = constructionUtils_1.toFactory(paramsMaker)();
const allParms = rawParms;
allParms.target = target;
return allParms;
};
}
exports.toGeneratorParametersMaker = toGeneratorParametersMaker;
function isSeedDrivenGeneratorParameters(p) {
const maybe = p;
return !!maybe && !!maybe.target;
}
exports.isSeedDrivenGeneratorParameters = isSeedDrivenGeneratorParameters;
function handleGenerate(editorFactory, details, startingPoint, cr, sdmo) {
return (ctx, parameters) => {
return handle(ctx, editorFactory, parameters, details, startingPoint, cr, sdmo);
};
}
async function handle(ctx, editorFactory, params, details, startingPoint, cr, sdmo) {
try {
const pi = Object.assign(Object.assign({}, handlerRegistrations_1.toCommandListenerInvocation(cr, ctx, params, sdmo)), params);
pi.credentials = await handlerRegistrations_1.resolveCredentialsPromise(pi.credentials);
const r = await generatorUtils_1.generate(computeStartingPoint(params, ctx, details.repoLoader(params), details, startingPoint, cr, sdmo), ctx, pi.credentials, editorFactory(params, ctx), details.projectPersister, params.target.repoRef, params, undefined);
if (!!cr.afterAction && r.success === true) {
const afterActions = Array.isArray(cr.afterAction) ? cr.afterAction : [cr.afterAction];
for (const afterAction of afterActions) {
await afterAction(r.target, pi);
}
}
// TODO cd support other providers which needs to start upstream from this
if (params.target.repoRef.providerType === RepoId_1.ProviderType.github_com && r.success === true) {
const repoProvenance = {
repo: {
name: params.target.repoRef.repo,
owner: params.target.repoRef.owner,
providerId: "zjlmxjzwhurspem",
},
provenance: storeGoals_1.constructProvenance(ctx),
};
await ctx.messageClient.send(repoProvenance, MessageClient_1.addressEvent("SdmRepoProvenance"));
}
await ctx.messageClient.respond(messages_1.slackSuccessMessage(`Create Project`, `Successfully created new project ${slack_messages_1.bold(`${params.target.repoRef.owner}/${params.target.repoRef.repo}`)} at ${slack_messages_1.url(params.target.repoRef.url)}`));
return {
code: 0,
// Redirect to local project page
redirect: details.redirecter(params.target.repoRef),
// local SDM uses this to print instructions
generatedRepositoryUrl: params.target.repoRef.url,
};
}
catch (err) {
if (err instanceof handlerRegistrations_1.CommandListenerExecutionInterruptError) {
// We're continuing
return HandlerResult_1.Success;
}
await ctx.messageClient.respond(messages_1.slackErrorMessage(`Create Project`, `Project creation for ${slack_messages_1.bold(`${params.target.repoRef.owner}/${params.target.repoRef.repo}`)} failed:
${slack_messages_1.codeBlock(err.message)}`, ctx));
}
return undefined;
}
/**
* Retrieve a seed. Set the seed location on the parameters if possible and necessary.
*/
async function computeStartingPoint(params, ctx, repoLoader, details, startingPoint, cr, sdmo) {
if (!startingPoint) {
if (!params.source || !params.source.repoRef) {
throw new Error("If startingPoint is not provided in GeneratorRegistration, parameters.source must specify seed project location: " +
`Offending registration had intent ${details.intent}`);
}
await infoMessage(`Cloning seed project from parameters ${slack_messages_1.url(params.source.repoRef.url)}`, ctx);
return repoLoader(params.source.repoRef);
}
if (Project_1.isProject(startingPoint)) {
await infoMessage(`Using starting point project specified in registration`, ctx);
return startingPoint;
}
else if (RepoId_1.isRemoteRepoRef(startingPoint)) {
const source = startingPoint;
await infoMessage(`Cloning seed project from starting point ${slack_messages_1.bold(`${source.owner}/${source.repo}`)} at ${slack_messages_1.url(source.url)}`, ctx);
const repoRef = startingPoint;
params.source = { repoRef };
return repoLoader(repoRef);
}
else {
// Combine this for backward compatibility
const pi = Object.assign(Object.assign({}, handlerRegistrations_1.toCommandListenerInvocation(cr, ctx, params, sdmo)), params);
pi.credentials = await handlerRegistrations_1.resolveCredentialsPromise(pi.credentials);
// It's a function that takes the parameters and returns either a project or a RemoteRepoRef
const rr = startingPoint(pi);
if (isProjectPromise(rr)) {
const p = await rr;
await infoMessage(`Using dynamically chosen starting point project ${slack_messages_1.bold(`${p.id.owner}:${p.id.repo}`)}`, ctx);
return p;
}
if (Project_1.isProject(rr)) {
await infoMessage(`Using dynamically chosen starting point project ${slack_messages_1.bold(`${rr.id.owner}:${rr.id.repo}`)}`, ctx);
// params.source will remain undefined in this case
return rr;
}
else {
await infoMessage(`Cloning dynamically chosen starting point from ${slack_messages_1.url(rr.url)}`, ctx);
params.source = { repoRef: rr };
return repoLoader(rr);
}
}
}
exports.computeStartingPoint = computeStartingPoint;
function isProjectPromise(a) {
return !!a.then;
}
function defaultDetails(opts, name) {
return {
description: name,
repoFinder: opts.repoFinder,
repoLoader: (p) => projectLoaderRepoLoader_1.projectLoaderRepoLoader(opts.projectLoader || new CachingProjectLoader_1.CachingProjectLoader(), p.target.credentials, true),
projectPersister: opts.projectPersister,
redirecter: () => undefined,
};
}
async function infoMessage(text, ctx) {
return ctx.messageClient.respond(messages_1.slackInfoMessage("Create Project", text));
}
//# sourceMappingURL=generatorCommand.js.map