Home Manual Reference Source Test

src/cli/commands/Update.js

import { get } from 'axios';
import { gt } from 'semver';
import Logger from '../../lib/util/Logger';
import Command from '../../lib/cli/Command';
import ExternalCommand from '../../lib/util/ExternalCommand';
import CliOptions from '../Options';

/**
 * The command invoked by running "update".
 */
export default class UpdateCommand extends Command {
  /**
   * Creates a new {@link UpdateCommand} with the specified name and description.
   * @param {string} name The command's name.
   * @param {string} description The command's description.
   */
  constructor(name, description) {
    super(name, description, {
      options: {
        beta: CliOptions.beta,
      },
    });
  }

  /**
   * Checks atscm's dist-tags in the npm registry and resolves with the latest version available.
   * @param {boolean} [useBetaRelease=false] If beta versions should be used.
   * @return {Promise<string>} Fulfilled with the latest atscm version available.
   */
  getLatestVersion(useBetaRelease = false) {
    return get('https://registry.npmjs.org/-/package/atscm/dist-tags').then(
      (res) => res.data[useBetaRelease ? 'beta' : 'latest']
    );
  }

  /**
   * Checks if an update is required with the given latest and current version.
   * @param {string} latestVersion The latest version available.
   * @param {string} currentVersion The current version to check against.
   * @return {boolean|string} Returns `false` if no update is required or the version to install if
   * an update is required.
   */
  updateNeeded(latestVersion, currentVersion) {
    Logger.debug('Latest version:    ', Logger.format.value(latestVersion));
    Logger.debug('Current version:   ', Logger.format.value(currentVersion));

    return gt(latestVersion, currentVersion) && latestVersion;
  }

  /**
   * Runs `npm install --save-dev atscm@latest` in a separate process.
   * @param {AtSCMCli} cli The cli instance used.
   * @param {boolean} [useBetaRelease=false] If beta versions should be used.
   * @return {Promise<string, Error>} Fulfilled with npm's stdout or rejected with a spawn error or
   * error code.
   */
  update(cli, useBetaRelease = false) {
    return ExternalCommand.run(
      'npm',
      ['install', '--save-dev', `atscm@${useBetaRelease ? 'beta' : 'latest'}`],
      {
        spawn: {
          cwd: cli.environment.cwd,
        },
      }
    );
  }

  /**
   * Updates atscm if a newer version is available.
   * @param {AtSCMCli} cli The cli instance used.
   * @return {Promise<*>} Resolved if the command completed suceessfully, rejected with the error
   * that occurred otherwise.
   */
  run(cli) {
    return Promise.all([
      this.getLatestVersion(cli.options.beta),
      Promise.resolve(cli.environment.modulePackage.version),
    ])
      .then((versions) => this.updateNeeded(...versions))
      .then((needed) => {
        if (needed) {
          Logger.info('Updating to version', Logger.format.value(needed));

          return this.update(cli, cli.options.beta).then(() => Logger.info('Done.'));
        }

        return Logger.info('Already up-to-date.');
      });
  }

  /**
   * This command always needs to be run inside an existant atscm project.
   * @return {boolean} Always `true`.
   */
  requiresEnvironment() {
    return true;
  }
}