@atomist/sdm
Version:
Atomist Software Delivery Machine SDK
330 lines • 13.5 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.mapGoals = void 0;
const configuration_1 = require("@atomist/automation-client/lib/configuration");
const GitHubRepoRef_1 = require("@atomist/automation-client/lib/operations/common/GitHubRepoRef");
const ProjectOperationCredentials_1 = require("@atomist/automation-client/lib/operations/common/ProjectOperationCredentials");
const httpClient_1 = require("@atomist/automation-client/lib/spi/http/httpClient");
const yaml = require("js-yaml");
const stringify = require("json-stringify-safe");
const _ = require("lodash");
const Cancel_1 = require("../../../api/goal/common/Cancel");
const Immaterial_1 = require("../../../api/goal/common/Immaterial");
const Locking_1 = require("../../../api/goal/common/Locking");
const Queue_1 = require("../../../api/goal/common/Queue");
const pushTestUtils_1 = require("../../../api/mapping/support/pushTestUtils");
const goalCaching_1 = require("../../goal/cache/goalCaching");
const skill_1 = require("../../goal/common/skill");
const action_1 = require("../../goal/container/action");
const container_1 = require("../../goal/container/container");
const execute_1 = require("../../goal/container/execute");
const array_1 = require("../../util/misc/array");
const mapPushTests_1 = require("./mapPushTests");
const resolvePlaceholder_1 = require("./resolvePlaceholder");
const util_1 = require("./util");
const MapContainer = async (goals, sdm, additionalGoals, goalMakers, additionalTests, extensionTests) => {
if (!!goals.containers) {
if (!goals.name) {
throw new Error(`Property 'name' missing on container goal:\n${JSON.stringify(goals, undefined, 2)}`);
}
const containers = [];
for (const gc of goals.containers) {
containers.push(Object.assign(Object.assign({}, gc), { name: gc.name.replace(/ /g, "-"), test: !!gc.test ? await mapPushTests_1.mapTests(gc.test, additionalTests, extensionTests) : undefined }));
}
const g = container_1.container(goals.name, {
callback: containerCallback(),
containers,
initContainers: goals.initContainers,
volumes: array_1.toArray(goals.volumes),
progressReporter: container_1.ContainerProgressReporter,
input: goals.input,
output: goals.output,
parameters: goals.parameters,
fulfillment: goals.fulfillment,
});
return g;
}
return undefined;
};
const MapExecute = async (goals) => {
if (!!goals.execute) {
if (!goals.name) {
throw new Error(`Property 'name' missing on execute goal:\n${JSON.stringify(goals, undefined, 2)}`);
}
const g = goals.execute;
return execute_1.execute(g.name, {
cmd: g.command || g.cmd,
args: array_1.toArray(g.args),
secrets: g.secrets,
});
}
return undefined;
};
const MapImmaterial = async (goals) => {
if (goals.use === "immaterial") {
return Immaterial_1.ImmaterialGoals.andLock().goals;
}
return undefined;
};
const MapLock = async (goals) => {
if (goals.use === "lock") {
return Locking_1.Locking;
}
return undefined;
};
const MapQueue = async (goals) => {
if (goals.use === "queue") {
return new Queue_1.Queue({
fetch: goals.fetch,
concurrent: goals.concurrent,
});
}
return undefined;
};
const MapCancel = async (goals) => {
if (goals.use === "cancel") {
return new Cancel_1.Cancel({ goals: [], goalNames: array_1.toArray(goals.goals) });
}
return undefined;
};
const MapAdditional = async (goals, sdm, additionalGoals) => {
if (!!additionalGoals[goals.use]) {
return additionalGoals[goals.use];
}
return undefined;
};
const MapReferenced = async (goals, sdm, additionalGoals, goalMakers, additionalTests, extensionTests) => {
const ref = goals.ref;
if (!!ref && ref.includes("/")) {
const parameters = goals.parameters || {};
const referencedGoal = await mapReferencedGoal(sdm, ref, parameters);
if (!!referencedGoal) {
return mapGoals(sdm, _.merge({}, referencedGoal, (goals || {})), additionalGoals, goalMakers, additionalTests, extensionTests);
}
}
return undefined;
};
const MapGoalMakers = async (goals, sdm, additionalGoals, goalMakers) => {
const use = goals.use;
if (!!use && !!goalMakers[use]) {
const goalMaker = goalMakers[use];
try {
return goalMaker(sdm, (goals.parameters || {}));
}
catch (e) {
e.message = `Failed to make goal using ${use}: ${e.message}`;
throw e;
}
}
return undefined;
};
const MapFulfillment = async (goals) => {
const regexp = /([@a-zA-Z-_]*)\/([a-zA-Z-_]*)(?:\/([a-zA-Z-_]*))?@?([a-zA-Z-_0-9\.]*)/i;
const use = goals.use;
if (!!use) {
const match = regexp.exec(use);
if (!!match) {
return skill_1.skill(match[3].replace(/_/g, " "), `${match[1]}/${match[2]}`, {
uniqueName: goals.name || match[3],
parameters: goals.parameters,
input: goals.input,
output: goals.output,
secrets: goals.secrets,
});
}
}
return undefined;
};
const MapAction = async (goals) => {
if (!!goals.action) {
return action_1.action({
name: goals.name,
image: goals.action,
input: goals.input,
output: goals.output,
parameters: goals.parameters,
secrets: goals.secrets,
});
}
return undefined;
};
const MapGoals = [
MapContainer,
MapExecute,
MapImmaterial,
MapLock,
MapCancel,
MapQueue,
MapAdditional,
MapGoalMakers,
MapReferenced,
MapFulfillment,
MapAction,
];
async function mapGoals(sdm, goals, additionalGoals, goalMakers, additionalTests, extensionTests) {
if (Array.isArray(goals)) {
const newGoals = [];
for (const g of array_1.toArray(goals)) {
newGoals.push(await mapGoals(sdm, g, additionalGoals, goalMakers, additionalTests, extensionTests));
}
return newGoals;
}
else {
let goal;
for (const mapGoal of MapGoals) {
goal = await mapGoal(goals, sdm, additionalGoals, goalMakers, additionalTests, extensionTests);
if (!!goal) {
if (!Array.isArray(goal)) {
addDetails(goal, goals);
// Container goal handle their own caching
if (!(goal instanceof container_1.Container)) {
addCaching(goal, goals);
}
}
return goal;
}
}
}
throw new Error(`Unable to construct goal from '${stringify(goals)}'`);
}
exports.mapGoals = mapGoals;
function addDetails(goal, goals) {
goal.definition = _.cloneDeep(goal.definition);
if (goals.approval !== undefined) {
goal.definition.approvalRequired = goals.approval;
}
if (goals.preApproval !== undefined) {
goal.definition.preApprovalRequired = goals.preApproval;
}
if (goals.retry !== undefined) {
goal.definition.retryFeasible = goals.retry;
}
if (!!goals.descriptions) {
const descriptions = goals.descriptions;
goal.definition.canceledDescription = descriptions.canceled;
goal.definition.completedDescription = descriptions.completed;
goal.definition.failedDescription = descriptions.failed;
goal.definition.plannedDescription = descriptions.planned;
goal.definition.requestedDescription = descriptions.requested;
goal.definition.stoppedDescription = descriptions.stopped;
goal.definition.waitingForApprovalDescription = descriptions.waitingForApproval;
goal.definition.waitingForPreApprovalDescription = descriptions.waitingForPreApproval;
goal.definition.workingDescription = descriptions.inProcess;
}
return goal;
}
function addCaching(goal, goals) {
if (!!(goals === null || goals === void 0 ? void 0 : goals.input)) {
goal.withProjectListener(goalCaching_1.cacheRestore({ entries: array_1.toArray(goals.input) }));
}
if (!!(goals === null || goals === void 0 ? void 0 : goals.output)) {
goal.withProjectListener(goalCaching_1.cachePut({ entries: array_1.toArray(goals.output) }));
}
return goal;
}
function containerCallback() {
return async (r, p, g, e, ctx) => {
const pli = Object.assign(Object.assign({}, ctx), { push: e.push, project: p });
const containersToRemove = [];
for (const gc of r.containers) {
let test;
if (Array.isArray(gc.test)) {
test = pushTestUtils_1.allSatisfied(...gc.test);
}
else {
test = gc.test;
}
if (!!test && !(await test.mapping(pli))) {
containersToRemove.push(gc);
}
}
const registration = Object.assign(Object.assign({}, r), { containers: r.containers.filter(c => !containersToRemove.includes(c)) });
return resolvePlaceholderContainerSpecCallback(registration, p, g, e, ctx);
};
}
async function mapReferencedGoal(sdm, goalRef, parameters) {
var _a, _b, _c, _d, _e, _f, _g;
const regexp = /([a-zA-Z-_]*)\/([a-zA-Z-_]*)(?:\/([a-zA-Z-_]*))?@?([a-zA-Z-_0-9\.]*)/i;
const match = regexp.exec(goalRef);
if (!match) {
return undefined;
}
const owner = match[1];
const repo = match[2];
const goalName = match[3];
const goalNames = !!goalName ? [goalName] : [repo, repo.replace(/-goal/, "")];
const ref = match[4] || "master";
// Check if we have a github token to authenticate our requests
let token = ((_c = (_b = (_a = sdm.configuration) === null || _a === void 0 ? void 0 : _a.sdm) === null || _b === void 0 ? void 0 : _b.github) === null || _c === void 0 ? void 0 : _c.token) || ((_g = (_f = (_e = (_d = sdm.configuration) === null || _d === void 0 ? void 0 : _d.sdm) === null || _e === void 0 ? void 0 : _e.goal) === null || _f === void 0 ? void 0 : _f.yaml) === null || _g === void 0 ? void 0 : _g.token);
if (!token) {
const workspaceId = _.get(sdm.configuration, "workspaceIds[0]");
if (!!workspaceId) {
try {
const creds = await sdm.configuration.sdm.credentialsResolver.eventHandlerCredentials({ graphClient: sdm.configuration.graphql.client.factory.create(workspaceId, sdm.configuration) }, GitHubRepoRef_1.GitHubRepoRef.from({
owner: undefined,
repo,
}));
if (!!creds && ProjectOperationCredentials_1.isTokenCredentials(creds)) {
token = creds.token;
_.set(sdm.configuration, "sdm.goal.yaml.token", token);
}
}
catch (e) {
// Intentionally ignore that error here
}
}
}
const url = `https://api.github.com/repos/${owner}/${repo}/contents/goal.yaml?ref=${ref}`;
try {
const cacheKey = `configuration.sdm.goal.definition.cache[${url}]`;
const cachedDocuments = _.get(sdm, cacheKey);
let documents;
if (!!cachedDocuments) {
documents = cachedDocuments;
}
else {
const client = sdm.configuration.http.client.factory.create(url);
const response = await client.exchange(url, {
method: httpClient_1.HttpMethod.Get,
headers: Object.assign({}, (!!token ? { Authorization: `Bearer ${token}` } : {})),
retry: { retries: 0 },
});
const content = Buffer.from(response.body.content, "base64").toString();
documents = yaml.safeLoadAll(content);
_.set(sdm, cacheKey, documents);
}
for (const document of documents) {
for (const key in document) {
if (document.hasOwnProperty(key) && goalNames.includes(key)) {
const pdg = document[key];
await configuration_1.resolvePlaceholders(pdg, value => resolvePlaceholder_1.resolvePlaceholder(value, undefined, {}, parameters, false));
return util_1.camelCase(pdg);
}
}
}
}
catch (e) {
throw new Error(`Referenced goal '${goalRef}' can not be created: ${e.message}`);
}
return undefined;
}
async function resolvePlaceholderContainerSpecCallback(r, p, g, e, ctx) {
await configuration_1.resolvePlaceholders(r, value => resolvePlaceholder_1.resolvePlaceholder(value, e, ctx, ctx.parameters));
return r;
}
//# sourceMappingURL=mapGoals.js.map