UNPKG

@atomist/sdm

Version:

Atomist Software Delivery Machine SDK

116 lines (104 loc) 4.73 kB
/* * 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. */ import { EventHandler, Value, } from "@atomist/automation-client/lib/decorators"; import { automationClientInstance } from "@atomist/automation-client/lib/globals"; import { subscription } from "@atomist/automation-client/lib/graph/graphQL"; import { EventFired, HandleEvent, } from "@atomist/automation-client/lib/HandleEvent"; import { HandlerContext } from "@atomist/automation-client/lib/HandlerContext"; import { HandlerResult, Success, } from "@atomist/automation-client/lib/HandlerResult"; import { logger } from "@atomist/automation-client/lib/util/logger"; import { fetchGoalsFromPush } from "../../../../../api-helper/goal/fetchGoalsOnCommit"; import { mapKeyToGoal } from "../../../../../api-helper/goal/sdmGoal"; import { updateGoal } from "../../../../../api-helper/goal/storeGoals"; import { SdmGoalEvent } from "../../../../../api/goal/SdmGoalEvent"; import { SdmGoalKey } from "../../../../../api/goal/SdmGoalMessage"; import { SoftwareDeliveryMachineConfiguration } from "../../../../../api/machine/SoftwareDeliveryMachineOptions"; import { OnAnyFailedSdmGoal, SdmGoalState, } from "../../../../../typings/types"; import { shouldHandle } from "../../../../delivery/goals/support/validateGoal"; import { verifyGoal } from "../../../../signing/goalSigning"; /** * Skip downstream goals on failed or stopped goal */ @EventHandler("Skip downstream goals on failed, stopped or canceled goal", () => subscription({ name: "OnAnyFailedSdmGoal", variables: { registration: () => [automationClientInstance()?.configuration?.name] }, })) export class SkipDownstreamGoalsOnGoalFailure implements HandleEvent<OnAnyFailedSdmGoal.Subscription> { @Value("") public configuration: SoftwareDeliveryMachineConfiguration; public async handle(event: EventFired<OnAnyFailedSdmGoal.Subscription>, context: HandlerContext): Promise<HandlerResult> { let failedGoal = event.data.SdmGoal[0] as SdmGoalEvent; if (!shouldHandle(failedGoal)) { logger.debug(`Goal ${failedGoal.uniqueName} skipped because not managed by this SDM`); return Success; } failedGoal = await verifyGoal(failedGoal, this.configuration.sdm.goalSigning, context); const goals = fetchGoalsFromPush(failedGoal); const goalsToSkip = goals.filter(g => isDependentOn(failedGoal, g, mapKeyToGoal(goals))) .filter(g => g.state === "planned"); let failedGoalState; let failedGoalDescription; switch (failedGoal.state) { case SdmGoalState.failure: failedGoalDescription = "failed"; failedGoalState = SdmGoalState.skipped; break; case SdmGoalState.stopped: failedGoalDescription = "stopped goals"; failedGoalState = SdmGoalState.skipped; break; case SdmGoalState.canceled: failedGoalDescription = "was canceled"; failedGoalState = SdmGoalState.canceled; break; } await Promise.all(goalsToSkip.map(g => updateGoal(context, g, { state: failedGoalState, description: `${failedGoalState === SdmGoalState.skipped ? "Skipped" : "Canceled" } ${g.name} because ${failedGoal.name} ${failedGoalDescription}`, }))); return Success; } } function isDependentOn(failedGoal: SdmGoalKey, goal: SdmGoalEvent, preconditionToGoal: (g: SdmGoalKey) => SdmGoalEvent): boolean { if (!goal) { return false; } if (!goal.preConditions || goal.preConditions.length === 0) { return false; // no preconditions? not dependent } if (mapKeyToGoal(goal.preConditions)(failedGoal)) { return true; // the failed goal is one of my preconditions? dependent } // otherwise, recurse on my preconditions return !!goal.preConditions .map(precondition => isDependentOn(failedGoal, preconditionToGoal(precondition), preconditionToGoal)) .find(a => a); // if one is true, return true }