UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

235 lines 12.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. */ 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