"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionAction = exports.DEFAULT_CHANGELOG_UPDATE_MESSAGE = exports.DEFAULT_PACKAGE_UPDATE_MESSAGE = void 0;
const semver = __importStar(require("semver"));
const node_core_library_1 = require("@rushstack/node-core-library");
const VersionPolicy_1 = require("../../api/VersionPolicy");
const RushConfiguration_1 = require("../../api/RushConfiguration");
const VersionMismatchFinder_1 = require("../../logic/versionMismatch/VersionMismatchFinder");
const PolicyValidator = __importStar(require("../../logic/policy/PolicyValidator"));
const BaseRushAction_1 = require("./BaseRushAction");
const PublishGit_1 = require("../../logic/PublishGit");
const Git_1 = require("../../logic/Git");
const RushConstants_1 = require("../../logic/RushConstants");
exports.DEFAULT_PACKAGE_UPDATE_MESSAGE = 'Bump versions [skip ci]';
exports.DEFAULT_CHANGELOG_UPDATE_MESSAGE = 'Update changelogs [skip ci]';
class VersionAction extends BaseRushAction_1.BaseRushAction {
    constructor(parser) {
        super({
            actionName: 'version',
            summary: 'Manage package versions in the repo.',
            documentation: 'use this "rush version" command to ensure version policies and bump versions.',
            parser
        });
        this._targetBranch = this.defineStringParameter({
            parameterLongName: '--target-branch',
            parameterShortName: '-b',
            argumentName: 'BRANCH',
            description: 'If this flag is specified, changes will be committed and merged into the target branch.'
        });
        this._ensureVersionPolicy = this.defineFlagParameter({
            parameterLongName: '--ensure-version-policy',
            description: 'Updates package versions if needed to satisfy version policies.'
        });
        this._overrideVersion = this.defineStringParameter({
            parameterLongName: '--override-version',
            argumentName: 'NEW_VERSION',
            description: 'Override the version in the specified --version-policy. ' +
                'This setting only works for lock-step version policy and when --ensure-version-policy is specified.'
        });
        this._bumpVersion = this.defineFlagParameter({
            parameterLongName: '--bump',
            description: 'Bumps package version based on version policies.'
        });
        this._bypassPolicy = this.defineFlagParameter({
            parameterLongName: RushConstants_1.RushConstants.bypassPolicyFlagLongName,
            description: 'Overrides "gitPolicy" enforcement (use honorably!)'
        });
        this._versionPolicy = this.defineStringParameter({
            parameterLongName: '--version-policy',
            argumentName: 'POLICY',
            description: 'The name of the version policy'
        });
        this._overwriteBump = this.defineStringParameter({
            parameterLongName: '--override-bump',
            argumentName: 'BUMPTYPE',
            description: 'Overrides the bump type in the version-policy.json for the specified version policy. ' +
                'Valid BUMPTYPE values include: prerelease, patch, preminor, minor, major. ' +
                'This setting only works for lock-step version policy in bump action.'
        });
        this._prereleaseIdentifier = this.defineStringParameter({
            parameterLongName: '--override-prerelease-id',
            argumentName: 'ID',
            description: 'Overrides the prerelease identifier in the version value of version-policy.json ' +
                'for the specified version policy. ' +
                'This setting only works for lock-step version policy. ' +
                'This setting increases to new prerelease id when "--bump" is provided but only replaces the ' +
                'prerelease name when "--ensure-version-policy" is provided.'
        });
        this._ignoreGitHooksParameter = this.defineFlagParameter({
            parameterLongName: '--ignore-git-hooks',
            description: `Skips execution of all git hooks. Make sure you know what you are skipping.`
        });
    }
    async runAsync() {
        const currentlyInstalledVariant = await this.rushConfiguration.getCurrentlyInstalledVariantAsync();
        for (const subspace of this.rushConfiguration.subspaces) {
            await PolicyValidator.validatePolicyAsync(this.rushConfiguration, subspace, currentlyInstalledVariant, {
                bypassPolicyAllowed: true,
                bypassPolicy: this._bypassPolicy.value
            });
        }
        const git = new Git_1.Git(this.rushConfiguration);
        const userEmail = await git.getGitEmailAsync();
        this._validateInput();
        const versionManagerModule = await Promise.resolve().then(() => __importStar(require(
        /* webpackChunkName: 'VersionManager' */
        '../../logic/VersionManager')));
        const versionManager = new versionManagerModule.VersionManager(this.rushConfiguration, userEmail, this.rushConfiguration.versionPolicyConfiguration);
        if (this._ensureVersionPolicy.value) {
            this._overwritePolicyVersionIfNeeded();
            const tempBranch = 'version/ensure-' + new Date().getTime();
            versionManager.ensure(this._versionPolicy.value, true, !!this._overrideVersion.value || !!this._prereleaseIdentifier.value);
            const updatedPackages = versionManager.updatedProjects;
            if (updatedPackages.size > 0) {
                // eslint-disable-next-line no-console
                console.log(`${updatedPackages.size} packages are getting updated.`);
                await this._gitProcessAsync(tempBranch, this._targetBranch.value, currentlyInstalledVariant);
            }
        }
        else if (this._bumpVersion.value) {
            const tempBranch = 'version/bump-' + new Date().getTime();
            await versionManager.bumpAsync(this._versionPolicy.value, this._overwriteBump.value ? node_core_library_1.Enum.getValueByKey(VersionPolicy_1.BumpType, this._overwriteBump.value) : undefined, this._prereleaseIdentifier.value, true);
            await this._gitProcessAsync(tempBranch, this._targetBranch.value, currentlyInstalledVariant);
        }
    }
    _overwritePolicyVersionIfNeeded() {
        if (!this._overrideVersion.value && !this._prereleaseIdentifier.value) {
            // No need to overwrite policy version
            return;
        }
        if (this._overrideVersion.value && this._prereleaseIdentifier.value) {
            throw new Error(`The parameters "--override-version" and "--override-prerelease-id" cannot be used together.`);
        }
        if (this._versionPolicy.value) {
            const versionConfig = this.rushConfiguration.versionPolicyConfiguration;
            const policy = versionConfig.getVersionPolicy(this._versionPolicy.value);
            if (!policy || !policy.isLockstepped) {
                throw new Error(`The lockstep version policy "${policy.policyName}" is not found.`);
            }
            let newVersion = undefined;
            if (this._overrideVersion.value) {
                newVersion = this._overrideVersion.value;
            }
            else if (this._prereleaseIdentifier.value) {
                const newPolicyVersion = new semver.SemVer(policy.version);
                if (newPolicyVersion.prerelease.length) {
                    // Update 1.5.0-alpha.10 to 1.5.0-beta.10
                    // For example, if we are parsing "1.5.0-alpha.10" then the newPolicyVersion.prerelease array
                    // would contain [ "alpha", 10 ], so we would replace "alpha" with "beta"
                    newPolicyVersion.prerelease = [
                        this._prereleaseIdentifier.value,
                        ...newPolicyVersion.prerelease.slice(1)
                    ];
                }
                else {
                    // Update 1.5.0 to 1.5.0-beta
                    // Since there is no length, we can just set to a new array
                    newPolicyVersion.prerelease = [this._prereleaseIdentifier.value];
                }
                newVersion = newPolicyVersion.format();
            }
            if (newVersion) {
                versionConfig.update(this._versionPolicy.value, newVersion, true);
            }
        }
        else {
            throw new Error('Missing --version-policy parameter to specify which version policy should be overwritten.');
        }
    }
    _validateInput() {
        if (this._bumpVersion.value && this._ensureVersionPolicy.value) {
            throw new Error('Please choose --bump or --ensure-version-policy but not together.');
        }
        if (this._overwriteBump.value && !node_core_library_1.Enum.tryGetValueByKey(VersionPolicy_1.BumpType, this._overwriteBump.value)) {
            throw new Error('The value of override-bump is not valid.  ' +
                'Valid values include prerelease, patch, preminor, minor, and major');
        }
    }
    _validateResult(variant) {
        // Load the config from file to avoid using inconsistent in-memory data.
        const rushConfig = RushConfiguration_1.RushConfiguration.loadFromConfigurationFile(this.rushConfiguration.rushJsonFile);
        // Validate result of all subspaces
        for (const subspace of rushConfig.subspaces) {
            // Respect the `ensureConsistentVersions` field in rush.json
            if (!subspace.shouldEnsureConsistentVersions(variant)) {
                continue;
            }
            const mismatchFinder = VersionMismatchFinder_1.VersionMismatchFinder.getMismatches(rushConfig, {
                subspace,
                variant
            });
            if (mismatchFinder.numberOfMismatches) {
                throw new Error('Unable to finish version bump because inconsistencies were encountered. ' +
                    'Run "rush check" to find more details.');
            }
        }
    }
    async _gitProcessAsync(tempBranch, targetBranch, variant) {
        // Validate the result before commit.
        this._validateResult(variant);
        const git = new Git_1.Git(this.rushConfiguration);
        const publishGit = new PublishGit_1.PublishGit(git, targetBranch);
        // Make changes in temp branch.
        await publishGit.checkoutAsync(tempBranch, true);
        const uncommittedChanges = await git.getUncommittedChangesAsync();
        // Stage, commit, and push the changes to remote temp branch.
        // Need to commit the change log updates in its own commit
        const changeLogUpdated = uncommittedChanges.some((changePath) => {
            return changePath.indexOf('CHANGELOG.json') > 0;
        });
        if (changeLogUpdated) {
            await publishGit.addChangesAsync('.', this.rushConfiguration.changesFolder);
            await publishGit.addChangesAsync(':/**/CHANGELOG.json');
            await publishGit.addChangesAsync(':/**/CHANGELOG.md');
            await publishGit.commitAsync(this.rushConfiguration.gitChangeLogUpdateCommitMessage || exports.DEFAULT_CHANGELOG_UPDATE_MESSAGE, !this._ignoreGitHooksParameter.value);
        }
        // Commit the package.json and change files updates.
        const packageJsonUpdated = uncommittedChanges.some((changePath) => {
            return changePath.indexOf(node_core_library_1.FileConstants.PackageJson) > 0;
        });
        if (packageJsonUpdated) {
            await publishGit.addChangesAsync(this.rushConfiguration.versionPolicyConfigurationFilePath);
            await publishGit.addChangesAsync(':/**/package.json');
            await publishGit.commitAsync(this.rushConfiguration.gitVersionBumpCommitMessage || exports.DEFAULT_PACKAGE_UPDATE_MESSAGE, !this._ignoreGitHooksParameter.value);
        }
        if (changeLogUpdated || packageJsonUpdated) {
            await publishGit.pushAsync(tempBranch, !this._ignoreGitHooksParameter.value, false);
            // Now merge to target branch.
            await publishGit.fetchAsync();
            await publishGit.checkoutAsync(targetBranch);
            await publishGit.pullAsync(!this._ignoreGitHooksParameter.value);
            await publishGit.mergeAsync(tempBranch, !this._ignoreGitHooksParameter.value);
            await publishGit.pushAsync(targetBranch, !this._ignoreGitHooksParameter.value, false);
            await publishGit.deleteBranchAsync(tempBranch, true, !this._ignoreGitHooksParameter.value);
        }
        else {
            // skip commits
            await publishGit.fetchAsync();
            await publishGit.checkoutAsync(targetBranch);
            await publishGit.deleteBranchAsync(tempBranch, false, !this._ignoreGitHooksParameter.value);
        }
    }
}
exports.VersionAction = VersionAction;
//# sourceMappingURL=VersionAction.js.map