"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.RepositoryDrivenContainer = exports.repositoryDrivenContainer = exports.hasRepositoryGoals = void 0;
const configuration_1 = require("@atomist/automation-client/lib/configuration");
const camelcaseKeys = require("camelcase-keys");
const yaml = require("js-yaml");
const _ = require("lodash");
const os = require("os");
const Goal_1 = require("../../../api/goal/Goal");
const GoalInvocation_1 = require("../../../api/goal/GoalInvocation");
const GoalNameGenerator_1 = require("../../../api/goal/GoalNameGenerator");
const GoalWithFulfillment_1 = require("../../../api/goal/GoalWithFulfillment");
const PushTest_1 = require("../../../api/mapping/PushTest");
const pushTestUtils_1 = require("../../../api/mapping/support/pushTestUtils");
const mapPushTests_1 = require("../../machine/yaml/mapPushTests");
const array_1 = require("../../util/misc/array");
const goalCaching_1 = require("../cache/goalCaching");
const container_1 = require("./container");
const docker_1 = require("./docker");
exports.hasRepositoryGoals = PushTest_1.pushTest("has SDM goals", async (pli) => {
    return (await pli.project.getFiles(".atomist/*goals.{yml,yaml}")).length > 0;
});
function repositoryDrivenContainer(options = {}) {
    return new RepositoryDrivenContainer(options.tests || {});
}
exports.repositoryDrivenContainer = repositoryDrivenContainer;
class RepositoryDrivenContainer extends GoalWithFulfillment_1.FulfillableGoal {
    constructor(tests) {
        super({ uniqueName: "repository-driven-goal" });
        this.tests = tests;
        this.addFulfillment({
            progressReporter: container_1.ContainerProgressReporter,
            goalExecutor: async (gi) => {
                const registration = gi.parameters.registration;
                const c = new container_1.Container({ displayName: this.definition.displayName });
                c.register = () => {
                };
                c.addFulfillment = () => c;
                c.addFulfillmentCallback = () => c;
                c.withProjectListener = () => c;
                c.with(registration);
                return docker_1.executeDockerJob(c, registration)(gi);
            },
            name: GoalNameGenerator_1.DefaultGoalNameGenerator.generateName(`container-docker-${this.definition.displayName}`),
        });
        this.withProjectListener({
            name: "cache-restore",
            events: [GoalInvocation_1.GoalProjectListenerEvent.before],
            listener: async (p, gi, e) => {
                const registration = gi.parameters.registration;
                if (registration.input && registration.input.length > 0) {
                    await goalCaching_1.cacheRestore({ entries: registration.input }).listener(p, gi, e);
                }
            },
        }).withProjectListener({
            name: "cache-put",
            events: [GoalInvocation_1.GoalProjectListenerEvent.after],
            listener: async (p, gi, e) => {
                const registration = gi.parameters.registration;
                if (registration.output && registration.output.length > 0) {
                    await goalCaching_1.cachePut({ entries: registration.output }).listener(p, gi, e);
                }
            },
        });
    }
    async plan(pli, goals) {
        const configYamls = (await pli.project.getFiles(".atomist/*goals.{yml,yaml}"))
            .sort((f1, f2) => f1.path.localeCompare(f2.path));
        const plan = {};
        for (const configYaml of configYamls) {
            const configs = yaml.safeLoadAll(await configYaml.getContent());
            for (const config of configs) {
                for (const k in config) {
                    if (config.hasOwnProperty(k)) {
                        const value = config[k];
                        const v = camelcaseKeys(value, { deep: true });
                        const test = pushTestUtils_1.and(...array_1.toArray(await mapPushTests_1.mapTests(v.test, this.tests, {})));
                        if (await test.mapping(pli)) {
                            const plannedGoals = array_1.toArray(mapGoals(v.goals, {}));
                            plan[k] = {
                                goals: plannedGoals,
                                dependsOn: v.dependsOn,
                            };
                        }
                    }
                }
            }
        }
        await configuration_1.resolvePlaceholders(plan, value => resolvePlaceholder(value, pli));
        return plan;
    }
}
exports.RepositoryDrivenContainer = RepositoryDrivenContainer;
function mapGoals(goals, additionalGoals) {
    if (Array.isArray(goals)) {
        return array_1.toArray(goals).map(g => mapGoals(g, additionalGoals));
    }
    else {
        if (!!goals.containers) {
            const name = _.get(goals, "containers.name") || _.get(goals, "containers[0].name");
            return mapPlannedGoal(name, goals, array_1.toArray(goals.containers), array_1.toArray(goals.volumes));
        }
        else if (!!goals.script) {
            const script = goals.script;
            return mapPlannedGoal(script.name, script, [{
                    name: script.name,
                    image: script.image || "ubuntu:latest",
                    command: script.command,
                    args: script.args,
                }], []);
        }
        else {
            throw new Error(`Unable to construct goal from '${JSON.stringify(goals)}'`);
        }
    }
}
function mapPlannedGoal(name, details, containers, volumes) {
    const gd = new Goal_1.Goal({ uniqueName: name, displayName: name });
    return {
        details: {
            displayName: gd.definition.displayName,
            descriptions: {
                planned: gd.plannedDescription,
                requested: gd.requestedDescription,
                inProcess: gd.inProcessDescription,
                completed: gd.successDescription,
                failed: gd.failureDescription,
                canceled: gd.canceledDescription,
                stopped: gd.stoppedDescription,
                waitingForApproval: gd.waitingForApprovalDescription,
                waitingForPreApproval: gd.waitingForPreApprovalDescription,
            },
            retry: details.retry,
            preApproval: details.preApproval,
            approval: details.approval,
        },
        parameters: {
            registration: {
                containers,
                volumes,
                input: details.input,
                output: details.output,
            },
        },
    };
}
const PlaceholderExpression = /\$\{([.a-zA-Z_-]+)([.:0-9a-zA-Z-_ \" ]+)*\}/g;
async function resolvePlaceholder(value, pli) {
    if (!PlaceholderExpression.test(value)) {
        return value;
    }
    PlaceholderExpression.lastIndex = 0;
    let currentValue = value;
    let result;
    // tslint:disable-next-line:no-conditional-assignment
    while (result = PlaceholderExpression.exec(currentValue)) {
        const fm = result[0];
        let envValue = _.get(pli, result[1]);
        if (result[1] === "home") {
            envValue = os.userInfo().homedir;
        }
        const defaultValue = result[2] ? result[2].trim().slice(1) : undefined;
        if (envValue) {
            currentValue = currentValue.split(fm).join(envValue);
        }
        else if (defaultValue) {
            currentValue = currentValue.split(fm).join(defaultValue);
        }
        else {
            throw new Error(`Placeholder '${result[1]}' can't be resolved`);
        }
        PlaceholderExpression.lastIndex = 0;
    }
    return currentValue;
}
//# sourceMappingURL=repositoryDrivenContainer.js.map