"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.K8sSync = exports.syncGoals = exports.isSyncRepoCommit = void 0;
const child_process_1 = require("@atomist/automation-client/lib/util/child_process");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const stringify = require("json-stringify-safe");
const _ = require("lodash");
const minimalClone_1 = require("../../../api-helper/goal/minimalClone");
const logInterpreters_1 = require("../../../api-helper/log/logInterpreters");
const goalDsl_1 = require("../../../api/dsl/goalDsl");
const Goals_1 = require("../../../api/goal/Goals");
const GoalWithFulfillment_1 = require("../../../api/goal/GoalWithFulfillment");
const environment_1 = require("../../../api/goal/support/environment");
const PushTest_1 = require("../../../api/mapping/PushTest");
const modes_1 = require("../../../core/machine/modes");
const error_1 = require("../support/error");
const change_1 = require("./change");
const diff_1 = require("./diff");
const repo_1 = require("./repo");
const tag_1 = require("./tag");
function isSyncRepoCommit(sdm) {
    const syncOptions = _.get(sdm, "configuration.sdm.k8s.options.sync");
    if (!syncOptions || !syncOptions.repo) {
        logger_1.logger.debug(`SDM configuration contains no sync repo, will not create sync repo push test`);
        return undefined;
    }
    return PushTest_1.pushTest("IsSyncRepoCommit", async (p) => {
        const repo = _.get(sdm, "configuration.sdm.k8s.options.sync.repo");
        if (!repo_1.isRemoteRepo(repo) && !modes_1.isInLocalMode()) {
            throw new Error(`SyncRepoRef did not get converted to proper RemoteRepoRef at startup: ${stringify(repo)}`);
        }
        if (modes_1.isInLocalMode() || (p.id.providerType === repo.providerType) &&
            p.id.owner === repo.owner &&
            p.id.repo === repo.repo && p.id.branch === repo.branch) {
            const tag = tag_1.commitTag(sdm.configuration);
            return p.push.commits.some(c => !c.message.includes(tag));
        }
        return false;
    });
}
exports.isSyncRepoCommit = isSyncRepoCommit;
/**
 * Add goals for pushes on the sync repo.
 */
function syncGoals(sdm) {
    const syncRepoPushTest = isSyncRepoCommit(sdm);
    if (!syncRepoPushTest) {
        logger_1.logger.debug(`No sync repo push test, not adding sync repo goals`);
        return sdm;
    }
    const sync = new GoalWithFulfillment_1.GoalWithFulfillment({
        uniqueName: "sync",
        environment: environment_1.IndependentOfEnvironment,
        displayName: "sync",
        workingDescription: "Syncing",
        completedDescription: "Synced",
        failedDescription: "Sync failed",
        isolated: true,
    }).with({
        name: "K8sSyncRepo",
        goalExecutor: exports.K8sSync,
        logInterpreter: logInterpreters_1.LogSuppressor,
    });
    const syncGoalSet = Goals_1.goals("Sync Kubernetes Resources").plan(sync);
    sdm.addGoalContributions(goalDsl_1.whenPushSatisfies(syncRepoPushTest).setGoals(syncGoalSet));
    return sdm;
}
exports.syncGoals = syncGoals;
/**
 * Create resources of added specs, update resources of changed specs,
 * and remove resources of deleted specs.
 */
const K8sSync = async (gi) => {
    const push = gi.goalEvent.push;
    const log = gi.progressLog;
    const params = {
        cloneOptions: minimalClone_1.minimalClone(push),
        context: gi.context,
        credentials: gi.credentials,
        id: gi.id,
        log,
        readOnly: true,
    };
    const tag = tag_1.commitTag(gi.configuration);
    return gi.configuration.sdm.projectLoader.doWithProject(params, async (p) => {
        if (params.cloneOptions.depth > 1) {
            // work around https://github.com/atomist/sdm/issues/729
            try {
                await child_process_1.execPromise("git", ["fetch", `--depth=${params.cloneOptions.depth}`], { cwd: p.baseDir });
            }
            catch (e) {
                logger_1.logger.warn(`Failed to undo shallow clone, proceeding anyway: ${e.message}`);
            }
        }
        const changes = await diff_1.diffPush(p, push, tag, log);
        const errs = [];
        for (const change of changes) {
            const verb = (change.change === "delete") ? "Deleting" : "Applying";
            log.write(`${verb} '${change.path}' from commit ${change.sha}`);
            try {
                await change_1.changeResource(p, change);
            }
            catch (e) {
                e.message = `Failed to ${change.change} '${change.path}' resource for commit ${change.sha}: ${error_1.k8sErrMsg(e)}`;
                log.write(e.message);
                errs.push(e);
            }
        }
        if (errs.length > 0) {
            return { code: errs.length, message: errs.map(e => e.message).join("; ") };
        }
        const message = `Changed ${changes.length} resources`;
        log.write(message);
        return { code: 0, message };
    });
};
exports.K8sSync = K8sSync;
//# sourceMappingURL=goals.js.map