@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
241 lines • 9.91 kB
JavaScript
"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.AbstractSoftwareDeliveryMachine = void 0;
const string_1 = require("@atomist/automation-client/lib/internal/util/string");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const _ = require("lodash");
const goalContribution_1 = require("../../api/dsl/goalContribution");
const ReportProgress_1 = require("../../api/goal/progress/ReportProgress");
const ConfigurationValues_1 = require("../../api/machine/ConfigurationValues");
const Registerable_1 = require("../../api/machine/Registerable");
const commonPushTests_1 = require("../../api/mapping/support/commonPushTests");
const PushRules_1 = require("../../api/mapping/support/PushRules");
const goalApprovalRequestVote_1 = require("../../api/registration/goalApprovalRequestVote");
const DefaultGoalImplementationMapper_1 = require("../goal/DefaultGoalImplementationMapper");
const goalSetListener_1 = require("../listener/goalSetListener");
const logInterpreters_1 = require("../log/logInterpreters");
const HandlerRegistrationManagerSupport_1 = require("./HandlerRegistrationManagerSupport");
const ListenerRegistrationManagerSupport_1 = require("./ListenerRegistrationManagerSupport");
/**
* Abstract support class for implementing a SoftwareDeliveryMachine.
*/
class AbstractSoftwareDeliveryMachine extends ListenerRegistrationManagerSupport_1.ListenerRegistrationManagerSupport {
/**
* Construct a new software delivery machine, with zero or
* more goal setters.
* @param {string} name
* @param configuration automation client configuration we're running in
* @param {GoalSetter} goalSetters tell me what to do on a push. Hint: start with "whenPushSatisfies(...)"
*/
constructor(name, configuration, goalSetters) {
super();
this.name = name;
this.configuration = configuration;
this.extensionPacks = [];
this.registrationManager = new HandlerRegistrationManagerSupport_1.HandlerRegistrationManagerSupport(this);
this.disposalGoalSetters = [];
this.goalApprovalRequestVoters = [];
this.goalApprovalRequestVoteDecisionManager = goalApprovalRequestVote_1.UnanimousGoalApprovalRequestVoteDecisionManager;
Registerable_1.registrableManager().register(this);
// If we didn't get any goal setters don't register a mapping
if (goalSetters.length > 0) {
this.pushMap = new PushRules_1.PushRules("Goal setters", _.flatten(goalSetters));
}
// Register the goal completion listener to update goal set state
this.addGoalCompletionListener(goalSetListener_1.GoalSetGoalCompletionListener);
}
get goalFulfillmentMapper() {
if (!this.fulfillmentMapper) {
this.fulfillmentMapper = new DefaultGoalImplementationMapper_1.DefaultGoalImplementationMapper();
}
return this.fulfillmentMapper;
}
/**
* Return the PushMapping that will be used on pushes.
* Useful in testing goal setting.
* @return {PushMapping<Goals>}
*/
get pushMapping() {
return this.pushMap;
}
/**
* Provide the implementation for a goal.
* The SDM will run it as soon as the goal is ready (all preconditions are met).
* If you provide a PushTest, then the SDM can assign different implementations
* to the same goal based on the code in the project.
* @param {string} implementationName
* @param {Goal} goal
* @param {ExecuteGoal} goalExecutor
* @param options PushTest to narrow matching & InterpretLog that can handle
* the log from the goalExecutor function
* @return {this}
*/
addGoalImplementation(implementationName, goal, goalExecutor, options) {
const implementation = {
implementationName, goal, goalExecutor,
pushTest: _.get(options, "pushTest") || commonPushTests_1.AnyPush,
logInterpreter: _.get(options, "logInterpreter") || logInterpreters_1.LogSuppressor,
progressReporter: _.get(options, "progressReporter") || ReportProgress_1.NoProgressReport,
projectListeners: _.get(options, "projectListeners") || [],
};
this.goalFulfillmentMapper.addImplementation(implementation);
return this;
}
addGoalApprovalRequestVoter(vote) {
this.goalApprovalRequestVoters.push(vote);
return this;
}
setGoalApprovalRequestVoteDecisionManager(manager) {
this.goalApprovalRequestVoteDecisionManager = manager;
return this;
}
addCommand(cmd) {
if (this.shouldRegister(cmd)) {
this.registrationManager.addCommand(cmd);
}
return this;
}
addGeneratorCommand(gen) {
if (this.shouldRegister(gen)) {
this.registrationManager.addGeneratorCommand(gen);
}
return this;
}
addCodeTransformCommand(ct) {
if (this.shouldRegister(ct)) {
this.registrationManager.addCodeTransformCommand(ct);
}
return this;
}
addCodeInspectionCommand(cir) {
if (this.shouldRegister(cir)) {
this.registrationManager.addCodeInspectionCommand(cir);
}
return this;
}
addEvent(e) {
this.registrationManager.addEvent(e);
return this;
}
addIngester(i) {
if (typeof i === "string") {
this.registrationManager.addIngester({
ingester: i,
});
}
else {
this.registrationManager.addIngester(i);
}
return this;
}
/**
* Declare that a goal will become successful based on something outside.
* For instance, ArtifactGoal succeeds because of an ImageLink event.
* This tells the SDM that it does not need to run anything when this
* goal becomes ready.
*/
addGoalSideEffect(goal, sideEffectName, registration, pushTest = commonPushTests_1.AnyPush) {
this.goalFulfillmentMapper.addSideEffect({
goal,
sideEffectName,
pushTest,
registration,
});
return this;
}
addGoalContributions(goalContributions) {
if (!this.pushMap) {
this.pushMap = goalContributions;
}
else {
this.pushMap = goalContribution_1.enrichGoalSetters(this.pushMap, goalContributions);
}
return this;
}
withPushRules(contributor, ...contributors) {
return this.addGoalContributions(goalContribution_1.goalContributors(contributor, ...contributors));
}
addExtensionPacks(...packs) {
for (const pack of packs) {
const found = this.extensionPacks.find(existing => existing.name === pack.name && existing.vendor === pack.vendor);
if (!!found) {
pack.name = `${pack.name}-${string_1.guid().slice(0, 7)}`;
}
this.addExtensionPack(pack);
if (!!pack.goalContributions) {
this.addGoalContributions(pack.goalContributions);
}
}
return this;
}
addExtensionPack(pack) {
logger_1.logger.debug("Adding extension pack '%s' version %s from %s", pack.name, pack.version, pack.vendor);
ConfigurationValues_1.validateConfigurationValues(this.configuration, pack);
pack.configure(this);
this.extensionPacks.push(pack);
return this;
}
/**
* Invoke StartupListeners.
*/
async notifyStartupListeners() {
const i = {
addressAdmin: this.configuration.sdm.adminAddressChannels || (async (msg) => {
logger_1.logger.debug("startup: %j", msg);
}),
sdm: this,
};
// Register the startup listener to schedule the triggered listeners
// It is important we schedule the triggers after the startup listeners are done!
this.startupListeners.push(async () => this.scheduleTriggeredListeners());
// Execute startupListeners in registration order
await this.startupListeners.map(l => l(i)).reduce(p => p.then(), Promise.resolve());
}
/**
* Schedule the triggered listeners
*/
scheduleTriggeredListeners() {
const i = {
addressAdmin: this.configuration.sdm.adminAddressChannels || (async (msg) => {
logger_1.logger.debug("trigger: %j", msg);
}),
sdm: this,
};
this.triggeredListeners.forEach(t => {
if (t.trigger && t.trigger.cron) {
const cj = require("cron");
const cron = new cj.CronJob({
cronTime: t.trigger.cron,
onTick: () => t.listener(i),
unrefTimeout: true,
});
cron.start();
}
if (t.trigger && t.trigger.interval) {
setInterval(() => t.listener(i), t.trigger.interval).unref();
}
});
}
shouldRegister(cm) {
return !!cm.registerWhen ?
cm.registerWhen(this.configuration) :
true;
}
}
exports.AbstractSoftwareDeliveryMachine = AbstractSoftwareDeliveryMachine;
//# sourceMappingURL=AbstractSoftwareDeliveryMachine.js.map