@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
235 lines • 12.7 kB
JavaScript
;
/*
* 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.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.reportEndAndClose = exports.reportStart = exports.FulfillGoalOnRequested = void 0;
const decorators_1 = require("@atomist/automation-client/lib/decorators");
const globals_1 = require("@atomist/automation-client/lib/globals");
const graphQL_1 = require("@atomist/automation-client/lib/graph/graphQL");
const HandlerResult_1 = require("@atomist/automation-client/lib/HandlerResult");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const os = require("os");
const executeGoal_1 = require("../../../../../api-helper/goal/executeGoal");
const storeGoals_1 = require("../../../../../api-helper/goal/storeGoals");
const cancelGoals_1 = require("../../../../../api-helper/listener/cancelGoals");
const LoggingProgressLog_1 = require("../../../../../api-helper/log/LoggingProgressLog");
const WriteToAllProgressLog_1 = require("../../../../../api-helper/log/WriteToAllProgressLog");
const handlerRegistrations_1 = require("../../../../../api-helper/machine/handlerRegistrations");
const dateFormat_1 = require("../../../../../api-helper/misc/dateFormat");
const result_1 = require("../../../../../api-helper/misc/result");
const addressChannels_1 = require("../../../../../api/context/addressChannels");
const skillContext_1 = require("../../../../../api/context/skillContext");
const SdmGoalMessage_1 = require("../../../../../api/goal/SdmGoalMessage");
const types_1 = require("../../../../../typings/types");
const validateGoal_1 = require("../../../../delivery/goals/support/validateGoal");
const goalCaching_1 = require("../../../../goal/cache/goalCaching");
const goalSigning_1 = require("../../../../signing/goalSigning");
const array_1 = require("../../../../util/misc/array");
const time_1 = require("../../../../util/misc/time");
/**
* Handle an SDM request goal. Used for many implementation types.
*/
let FulfillGoalOnRequested = class FulfillGoalOnRequested {
constructor(implementationMapper, goalExecutionListeners) {
this.implementationMapper = implementationMapper;
this.goalExecutionListeners = goalExecutionListeners;
}
/* tslint:disable:cyclomatic-complexity */
async handle(event, ctx) {
let sdmGoal = event.data.SdmGoal[0];
if (!validateGoal_1.shouldFulfill(sdmGoal)) {
logger_1.logger.debug(`Goal ${sdmGoal.uniqueName} skipped because not fulfilled by this SDM`);
return HandlerResult_1.Success;
}
sdmGoal = await goalSigning_1.verifyGoal(sdmGoal, this.configuration.sdm.goalSigning, ctx);
if ((await cancelGoals_1.cancelableGoal(sdmGoal, this.configuration)) && (await cancelGoals_1.isGoalCanceled(sdmGoal, ctx))) {
logger_1.logger.debug(`Goal ${sdmGoal.uniqueName} has been canceled. Not fulfilling`);
return HandlerResult_1.Success;
}
if (sdmGoal.fulfillment.method === SdmGoalMessage_1.SdmGoalFulfillmentMethod.SideEffect &&
sdmGoal.fulfillment.registration !== this.configuration.name) {
logger_1.logger.debug("Not fulfilling side-effected goal '%s' with method '%s/%s'", sdmGoal.uniqueName, sdmGoal.fulfillment.method, sdmGoal.fulfillment.name);
return HandlerResult_1.Success;
}
else if (sdmGoal.fulfillment.method === SdmGoalMessage_1.SdmGoalFulfillmentMethod.Other) {
// fail goal with neither Sdm nor SideEffect fulfillment
await storeGoals_1.updateGoal(ctx, sdmGoal, {
state: types_1.SdmGoalState.failure,
description: `No fulfillment for ${sdmGoal.uniqueName}`,
});
return HandlerResult_1.Success;
}
const id = this.configuration.sdm.repoRefResolver.repoRefFromSdmGoal(sdmGoal);
const credentials = await handlerRegistrations_1.resolveCredentialsPromise(this.configuration.sdm.credentialsResolver.eventHandlerCredentials(ctx, id));
const addressChannels = addressChannels_1.addressChannelsFor(sdmGoal.push.repo, ctx);
const preferences = this.configuration.sdm.preferenceStoreFactory(ctx);
const implementation = this.implementationMapper.findImplementationBySdmGoal(sdmGoal);
const { goal } = implementation;
const progressLog = new WriteToAllProgressLog_1.WriteToAllProgressLog(sdmGoal.name, new LoggingProgressLog_1.LoggingProgressLog(sdmGoal.name, "debug"), await this.configuration.sdm.logFactory(ctx, sdmGoal));
const goalInvocation = {
configuration: this.configuration,
goalEvent: sdmGoal,
goal,
progressLog,
context: ctx,
addressChannels,
preferences,
id,
credentials,
skill: skillContext_1.createSkillContext(ctx),
parameters: !!event.data.SdmGoal[0].parameters ? JSON.parse(event.data.SdmGoal[0].parameters) : {},
};
const goalScheduler = await findGoalScheduler(goalInvocation, this.configuration);
if (!!goalScheduler) {
const start = Date.now();
const result = await goalScheduler.schedule(goalInvocation);
if (!!result && result.code !== undefined && result.code !== 0) {
await storeGoals_1.updateGoal(ctx, sdmGoal, {
state: types_1.SdmGoalState.failure,
description: `Failed to schedule goal`,
url: progressLog.url,
});
await reportEndAndClose(result, start, progressLog);
}
else {
await storeGoals_1.updateGoal(ctx, sdmGoal, {
state: !!result && !!result.state ? result.state : types_1.SdmGoalState.in_process,
phase: !!result && !!result.phase ? result.phase : "scheduled",
description: !!result && !!result.description
? result.description
: storeGoals_1.descriptionFromState(goal, types_1.SdmGoalState.in_process, sdmGoal),
url: progressLog.url,
externalUrls: !!result ? result.externalUrls : undefined,
});
}
return Object.assign(Object.assign({}, result), {
// successfully handled event even if goal failed
code: 0 });
}
else {
delete sdmGoal.id;
const listeners = [];
// Prepare cache project listeners for parameters
if (!!goalInvocation.parameters) {
if (!!goalInvocation.parameters[goalCaching_1.CacheInputGoalDataKey]) {
const input = goalInvocation.parameters[goalCaching_1.CacheInputGoalDataKey];
if (!!input && input.length > 0) {
listeners.push(goalCaching_1.cacheRestore({ entries: input }));
}
}
if (!!goalInvocation.parameters[goalCaching_1.CacheOutputGoalDataKey]) {
const output = goalInvocation.parameters[goalCaching_1.CacheOutputGoalDataKey];
if (!!output && output.length > 0) {
listeners.push(goalCaching_1.cachePut({ entries: output }));
}
}
}
await reportStart(sdmGoal, progressLog);
const start = Date.now();
try {
const result = await executeGoal_1.executeGoal({
projectLoader: this.configuration.sdm.projectLoader,
goalExecutionListeners: this.goalExecutionListeners,
}, Object.assign(Object.assign({}, implementation), { projectListeners: [...array_1.toArray(implementation.projectListeners || []), ...listeners] }), goalInvocation);
const terminatingStates = [
types_1.SdmGoalState.canceled,
types_1.SdmGoalState.failure,
types_1.SdmGoalState.skipped,
types_1.SdmGoalState.stopped,
types_1.SdmGoalState.success,
types_1.SdmGoalState.waiting_for_approval,
];
if (!result || !result.state || terminatingStates.includes(result.state)) {
await reportEndAndClose(result, start, progressLog);
}
return Object.assign(Object.assign({}, result), {
// successfully handled event even if goal failed
code: 0 });
}
catch (e) {
e.message = `Goal executor threw exception: ${e.message}`;
const egr = {
code: 1,
message: e.message,
state: types_1.SdmGoalState.failure,
};
await reportEndAndClose(egr, start, progressLog);
throw e;
}
}
}
};
__decorate([
decorators_1.Value("") // empty path returns the entire configuration
,
__metadata("design:type", Object)
], FulfillGoalOnRequested.prototype, "configuration", void 0);
FulfillGoalOnRequested = __decorate([
decorators_1.EventHandler("Fulfill a goal when it reaches 'requested' state", graphQL_1.subscription("OnAnyRequestedSdmGoal")),
__metadata("design:paramtypes", [Object, Array])
], FulfillGoalOnRequested);
exports.FulfillGoalOnRequested = FulfillGoalOnRequested;
async function findGoalScheduler(gi, configuration) {
let goalSchedulers;
if (!configuration.sdm.goalScheduler) {
return undefined;
}
else if (!Array.isArray(configuration.sdm.goalScheduler)) {
goalSchedulers = [configuration.sdm.goalScheduler];
}
else {
goalSchedulers = configuration.sdm.goalScheduler;
}
for (const gl of goalSchedulers) {
if (await gl.supports(gi)) {
return gl;
}
}
return undefined;
}
async function reportStart(sdmGoal, progressLog) {
progressLog.write(`/--`);
progressLog.write(`Start: ${dateFormat_1.formatDate(new Date(), "yyyy-mm-dd HH:MM:ss.l")}`);
progressLog.write(`Repository: ${sdmGoal.push.repo.owner}/${sdmGoal.push.repo.name}/${sdmGoal.branch}`);
progressLog.write(`Sha: ${sdmGoal.sha}`);
progressLog.write(`Goal: ${sdmGoal.name} (${sdmGoal.uniqueName})`);
progressLog.write(`Environment: ${sdmGoal.environment.slice(2)}`);
progressLog.write(`GoalSet: ${sdmGoal.goalSet} - ${sdmGoal.goalSetId}`);
progressLog.write(`Host: ${os.hostname()}`);
progressLog.write(`SDM: ${globals_1.automationClientInstance().configuration.name}:${globals_1.automationClientInstance().configuration.version}`);
progressLog.write("\\--");
await progressLog.flush();
}
exports.reportStart = reportStart;
async function reportEndAndClose(result, start, progressLog) {
progressLog.write(`/--`);
progressLog.write(`Result: ${result_1.serializeResult(result)}`);
progressLog.write(`Duration: ${time_1.formatDuration(Date.now() - start)}`);
progressLog.write(`Finish: ${dateFormat_1.formatDate(new Date(), "yyyy-mm-dd HH:MM:ss.l")}`);
progressLog.write("\\--");
await progressLog.close();
}
exports.reportEndAndClose = reportEndAndClose;
//# sourceMappingURL=FulfillGoalOnRequested.js.map