plugins_release_ap.js


/**
 * @file plugin/release/ap.js
 * @copyright @spmhome @_2025
 * @author Scott Meesseman @spmeesseman\
 *//** */

const WpwPlugin = require("../base");


/**
 * @augments WpwPlugin
 */
class WpwApPlugin extends WpwPlugin
{
    /**
     * @private
     * @type {string}
     */
    currentVersion;
    /**
     * @private
     * @type {string}
     */
    nextVersion;
    /**
     * @private
     * @type {boolean}
     */
    hasFileUpdates;
    /**
     * @private
     * @type {string}
     */
    versionBump;


    /**
     * @param {WpwPluginOptions} options Plugin options to be applied
     */
	constructor(options)
    {
        super(options);
        this.buildOptions = /** @type {WpwBuildOptionsPluginConfig<"ap">} */(this.buildOptions); // reset for typings
    }


	/**
     * @override
     */
	static create = WpwApPlugin.wrap.bind(this);


    /**
     * @override
     * @returns {WpwPluginTapOptions<any, any, boolean>}
     */
    onApply()
    {
        /** @type {WpwPluginTapOptions<any, any, boolean>} */
        const hooksConfig = {
            printApInfo: {
                hook: "beforeRun",
                async: true,
                callback: this.printApInfo.bind(this)
            },
            getVersions: {
                hook: "run",
                async: true,
                callback: this.getVersions.bind(this)
            }
        };

        if (!this.buildOptions.printVersionsOnly)
        {
            this._obj_.apply(hooksConfig, {
                updateVersionFiles: {
                    hook: "beforeCompile",
                    async: true,
                    callback: this.updateVersionFiles.bind(this)
                },
                cleanup: {
                    hook: "shutdown",
                    forceRun: true,
                    callback: this.cleanup.bind(this)
                },
                updateChangelog: {
                    hook: "done",
                    callback: this.updateChangelog.bind(this)
                }
            });
            if (this.buildOptions.github)
            {
                hooksConfig.executeGithubRelease = {
                    hook: "done",
                    async: true,
                    callback: this.executeGithubRelease.bind(this)
                };
            }
            else if (this.buildOptions.gitlab)
            {
                hooksConfig.executeGitlabRelease = {
                    hook: "done",
                    async: true,
                    callback: this.executeGitlabRelease.bind(this)
                };
            }
            if (this.buildOptions.mantis)
            {
                hooksConfig.executeMantisRelease = {
                    hook: "done",
                    async: true,
                    callback: this.executeMantisRelease.bind(this)
                };
            }
            if (this.buildOptions.npm)
            {
                hooksConfig.executeNpmRelease = {
                    hook: "done",
                    async: true,
                    callback: this.executeNpmRelease.bind(this)
                };
            }
        }

        return hooksConfig;
    }


    /**
     * @private
     */
    async cleanup()
    {
        if (this.buildOptions.dryRun || (this.build.hasError && this.hasFileUpdates))
        {
            await this.execAp({ logPad: "   " }, "--task-revert");
        }
    }


    /**
     * @param {Omit<WpwExecOptions, "command"|"program"|"logger"|"basePath"|"owner">} [options]
     * @param {string[]} args
     * @returns {Promise<WpwExecResult>} Promise<PluginExecResult>
     */
    execAp(options, ...args)
    {
        args.push("--no-ci");
        if (this.buildOptions.dryRun) {
            args.push("--dry-run");
        }
        if (this.buildOptions.promptVersion && !args.includes("--prompt-version-disable")) {
           args.push("--prompt-version");
        }
        //
        // Can't be verbose if we are reading stdout as a value i.e. versions
        //
        if (!options?.stdinOn)
        {
            if (this.buildOptions.verbose) {
                args.push("--verbose");
            }
            if (this.buildOptions.veryVerbose) {
                args.push("--verboseEx");
            }
        }

        return this.exec(`app-publisher ${args.join(" ")}`, "app-publisher", true, options);
    }


    /**
     * @private
     */
    async executeGithubRelease()
    {
        await this.execAp({ logPad: "   " }, "--task-github-release");
    }


    /**
     * @private
     */
    async executeGitlabRelease()
    {   //
        // TODO - app-publisher gitlab release
        //
        // await this.execAp("--task-gitlab-release");
        throw this.addMessage({
            code: this.MsgCode.ERROR_NOT_IMPLEMENTED, message: "app-publisher: --task-gitlab-release"
        });
    }


    /**
     * @private
     */
    async executeMantisRelease()
    {
        await this.execAp({ logPad: "   " }, "--task-mantis-release");
    }


    /**
     * @private
     * @returns {Promise<void>}
     */
    async executeNpmRelease()
    {
        await this.execAp({ logPad: "   " }, "--task-npm-release");
    }


    /**
     * @private
     */
    async getVersions()
    {
        const args = [ "--task-version-info" ],
              preTag = this.buildOptions.preVersion;
        //
        // a 1st release will auto-prompt, disable if not explicitly set
        //
        if (this.build.pkgJson.version === "0.0.1" && !this.buildOptions.promptVersion) {
            // args.push("--version-force-next", "0.0.1");
            args.push("--prompt-version-disable");
        }
        if (preTag) {
            args.push("--version-pre-release-id", preTag);
        }
        const versions = (await this.execAp({ stdinOn: true, logPad: "   " }, ...args)).stdout.split("|");
        this.currentVersion = versions[0];
        this.nextVersion = versions[1];
        this.versionBump = versions[2];
        this.build.logger.value("current version", this.currentVersion, 1);
        this.build.logger.value("next version", this.nextVersion, 1);
        this.build.logger.value("version bump", this.versionBump, 2);
    }


    /**
     * @private
     */
    async printApInfo()
    {
        await this.execAp({ logStdio: true, logPad: "   " }, "--version");
        this.hasFileUpdates = this.hasFileUpdates || !this.build.hasError;
    }


    /**
     * @private
     */
    async updateChangelog()
    {
        await this.execAp({ logPad: "   " }, "--task-changelog");
        this.hasFileUpdates = this.hasFileUpdates || !this.build.hasError;
    }


    /**
     * @private
     */
    async updateVersionFiles()
    {
        const preTag = this.buildOptions.preVersion;
        this.build.logger.write("update version files", 1);
        if (this.buildOptions.promptVersion)
        {
            // TODO - prompt for version, get npm package 'prompt'
        }
        if (!preTag) {
            await this.execAp({ logPad: "   " }, "--task-version-update", "--version-force-next", this.nextVersion);
        }
        else {
            await this.execAp({ logPad: "   " },
                "--task-version-update", "--version-pre-release-id", preTag, "--version-force-next", this.nextVersion
            );
        }
        this.hasFileUpdates = this.hasFileUpdates || !this.build.hasError;
    }

}


module.exports = WpwApPlugin.create;