UNPKG

9.91 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright © 2020 Atomist, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.AbstractSoftwareDeliveryMachine = void 0;
19const string_1 = require("@atomist/automation-client/lib/internal/util/string");
20const logger_1 = require("@atomist/automation-client/lib/util/logger");
21const _ = require("lodash");
22const goalContribution_1 = require("../../api/dsl/goalContribution");
23const ReportProgress_1 = require("../../api/goal/progress/ReportProgress");
24const ConfigurationValues_1 = require("../../api/machine/ConfigurationValues");
25const Registerable_1 = require("../../api/machine/Registerable");
26const commonPushTests_1 = require("../../api/mapping/support/commonPushTests");
27const PushRules_1 = require("../../api/mapping/support/PushRules");
28const goalApprovalRequestVote_1 = require("../../api/registration/goalApprovalRequestVote");
29const DefaultGoalImplementationMapper_1 = require("../goal/DefaultGoalImplementationMapper");
30const goalSetListener_1 = require("../listener/goalSetListener");
31const logInterpreters_1 = require("../log/logInterpreters");
32const HandlerRegistrationManagerSupport_1 = require("./HandlerRegistrationManagerSupport");
33const ListenerRegistrationManagerSupport_1 = require("./ListenerRegistrationManagerSupport");
34/**
35 * Abstract support class for implementing a SoftwareDeliveryMachine.
36 */
37class AbstractSoftwareDeliveryMachine extends ListenerRegistrationManagerSupport_1.ListenerRegistrationManagerSupport {
38 /**
39 * Construct a new software delivery machine, with zero or
40 * more goal setters.
41 * @param {string} name
42 * @param configuration automation client configuration we're running in
43 * @param {GoalSetter} goalSetters tell me what to do on a push. Hint: start with "whenPushSatisfies(...)"
44 */
45 constructor(name, configuration, goalSetters) {
46 super();
47 this.name = name;
48 this.configuration = configuration;
49 this.extensionPacks = [];
50 this.registrationManager = new HandlerRegistrationManagerSupport_1.HandlerRegistrationManagerSupport(this);
51 this.disposalGoalSetters = [];
52 this.goalApprovalRequestVoters = [];
53 this.goalApprovalRequestVoteDecisionManager = goalApprovalRequestVote_1.UnanimousGoalApprovalRequestVoteDecisionManager;
54 Registerable_1.registrableManager().register(this);
55 // If we didn't get any goal setters don't register a mapping
56 if (goalSetters.length > 0) {
57 this.pushMap = new PushRules_1.PushRules("Goal setters", _.flatten(goalSetters));
58 }
59 // Register the goal completion listener to update goal set state
60 this.addGoalCompletionListener(goalSetListener_1.GoalSetGoalCompletionListener);
61 }
62 get goalFulfillmentMapper() {
63 if (!this.fulfillmentMapper) {
64 this.fulfillmentMapper = new DefaultGoalImplementationMapper_1.DefaultGoalImplementationMapper();
65 }
66 return this.fulfillmentMapper;
67 }
68 /**
69 * Return the PushMapping that will be used on pushes.
70 * Useful in testing goal setting.
71 * @return {PushMapping<Goals>}
72 */
73 get pushMapping() {
74 return this.pushMap;
75 }
76 /**
77 * Provide the implementation for a goal.
78 * The SDM will run it as soon as the goal is ready (all preconditions are met).
79 * If you provide a PushTest, then the SDM can assign different implementations
80 * to the same goal based on the code in the project.
81 * @param {string} implementationName
82 * @param {Goal} goal
83 * @param {ExecuteGoal} goalExecutor
84 * @param options PushTest to narrow matching & InterpretLog that can handle
85 * the log from the goalExecutor function
86 * @return {this}
87 */
88 addGoalImplementation(implementationName, goal, goalExecutor, options) {
89 const implementation = {
90 implementationName, goal, goalExecutor,
91 pushTest: _.get(options, "pushTest") || commonPushTests_1.AnyPush,
92 logInterpreter: _.get(options, "logInterpreter") || logInterpreters_1.LogSuppressor,
93 progressReporter: _.get(options, "progressReporter") || ReportProgress_1.NoProgressReport,
94 projectListeners: _.get(options, "projectListeners") || [],
95 };
96 this.goalFulfillmentMapper.addImplementation(implementation);
97 return this;
98 }
99 addGoalApprovalRequestVoter(vote) {
100 this.goalApprovalRequestVoters.push(vote);
101 return this;
102 }
103 setGoalApprovalRequestVoteDecisionManager(manager) {
104 this.goalApprovalRequestVoteDecisionManager = manager;
105 return this;
106 }
107 addCommand(cmd) {
108 if (this.shouldRegister(cmd)) {
109 this.registrationManager.addCommand(cmd);
110 }
111 return this;
112 }
113 addGeneratorCommand(gen) {
114 if (this.shouldRegister(gen)) {
115 this.registrationManager.addGeneratorCommand(gen);
116 }
117 return this;
118 }
119 addCodeTransformCommand(ct) {
120 if (this.shouldRegister(ct)) {
121 this.registrationManager.addCodeTransformCommand(ct);
122 }
123 return this;
124 }
125 addCodeInspectionCommand(cir) {
126 if (this.shouldRegister(cir)) {
127 this.registrationManager.addCodeInspectionCommand(cir);
128 }
129 return this;
130 }
131 addEvent(e) {
132 this.registrationManager.addEvent(e);
133 return this;
134 }
135 addIngester(i) {
136 if (typeof i === "string") {
137 this.registrationManager.addIngester({
138 ingester: i,
139 });
140 }
141 else {
142 this.registrationManager.addIngester(i);
143 }
144 return this;
145 }
146 /**
147 * Declare that a goal will become successful based on something outside.
148 * For instance, ArtifactGoal succeeds because of an ImageLink event.
149 * This tells the SDM that it does not need to run anything when this
150 * goal becomes ready.
151 */
152 addGoalSideEffect(goal, sideEffectName, registration, pushTest = commonPushTests_1.AnyPush) {
153 this.goalFulfillmentMapper.addSideEffect({
154 goal,
155 sideEffectName,
156 pushTest,
157 registration,
158 });
159 return this;
160 }
161 addGoalContributions(goalContributions) {
162 if (!this.pushMap) {
163 this.pushMap = goalContributions;
164 }
165 else {
166 this.pushMap = goalContribution_1.enrichGoalSetters(this.pushMap, goalContributions);
167 }
168 return this;
169 }
170 withPushRules(contributor, ...contributors) {
171 return this.addGoalContributions(goalContribution_1.goalContributors(contributor, ...contributors));
172 }
173 addExtensionPacks(...packs) {
174 for (const pack of packs) {
175 const found = this.extensionPacks.find(existing => existing.name === pack.name && existing.vendor === pack.vendor);
176 if (!!found) {
177 pack.name = `${pack.name}-${string_1.guid().slice(0, 7)}`;
178 }
179 this.addExtensionPack(pack);
180 if (!!pack.goalContributions) {
181 this.addGoalContributions(pack.goalContributions);
182 }
183 }
184 return this;
185 }
186 addExtensionPack(pack) {
187 logger_1.logger.debug("Adding extension pack '%s' version %s from %s", pack.name, pack.version, pack.vendor);
188 ConfigurationValues_1.validateConfigurationValues(this.configuration, pack);
189 pack.configure(this);
190 this.extensionPacks.push(pack);
191 return this;
192 }
193 /**
194 * Invoke StartupListeners.
195 */
196 async notifyStartupListeners() {
197 const i = {
198 addressAdmin: this.configuration.sdm.adminAddressChannels || (async (msg) => {
199 logger_1.logger.debug("startup: %j", msg);
200 }),
201 sdm: this,
202 };
203 // Register the startup listener to schedule the triggered listeners
204 // It is important we schedule the triggers after the startup listeners are done!
205 this.startupListeners.push(async () => this.scheduleTriggeredListeners());
206 // Execute startupListeners in registration order
207 await this.startupListeners.map(l => l(i)).reduce(p => p.then(), Promise.resolve());
208 }
209 /**
210 * Schedule the triggered listeners
211 */
212 scheduleTriggeredListeners() {
213 const i = {
214 addressAdmin: this.configuration.sdm.adminAddressChannels || (async (msg) => {
215 logger_1.logger.debug("trigger: %j", msg);
216 }),
217 sdm: this,
218 };
219 this.triggeredListeners.forEach(t => {
220 if (t.trigger && t.trigger.cron) {
221 const cj = require("cron");
222 const cron = new cj.CronJob({
223 cronTime: t.trigger.cron,
224 onTick: () => t.listener(i),
225 unrefTimeout: true,
226 });
227 cron.start();
228 }
229 if (t.trigger && t.trigger.interval) {
230 setInterval(() => t.listener(i), t.trigger.interval).unref();
231 }
232 });
233 }
234 shouldRegister(cm) {
235 return !!cm.registerWhen ?
236 cm.registerWhen(this.configuration) :
237 true;
238 }
239}
240exports.AbstractSoftwareDeliveryMachine = AbstractSoftwareDeliveryMachine;
241//# sourceMappingURL=AbstractSoftwareDeliveryMachine.js.map
\No newline at end of file