Home Manual Reference Source

src/services/common/versionUtils.js

const fs = require('fs-extra');
const shell = require('shelljs');
const { provider } = require('jimple');
/**
 * A set of utilities to work with the version of the project.
 */
class VersionUtils {
  /**
   * Class constructor.
   * @param {EnvironmentUtils} environmentUtils To read the environment variables.
   * @param {Logger}           appLogger        To inform the user if something goes wrong.
   * @param {PathUtils}        pathUtils        To build paths for the revision file.
   */
  constructor(environmentUtils, appLogger, pathUtils) {
    /**
     * A local reference for the `environmentUtils` service.
     * @type {EnvironmentUtils}
     */
    this.environmentUtils = environmentUtils;
    /**
     * A local reference for the `appLogger` service.
     * @type {Logger}
     */
    this.appLogger = appLogger;
    /**
     * A local reference for the `pathUtils` service.
     * @type {PathUtils}
     */
    this.pathUtils = pathUtils;
    /**
     * The default fallback version in case none can be retrieved.
     * @type {string}
     */
    this.fallbackVersion = 'development';
    /**
     * This will be filled with the version on the revision file, if it exists.
     * @type {null|string}
     * @ignore
     * @access protected
     */
    this._loadedVersion = null;
  }
  /**
   * Get the version from an environment variable.
   * @param {string}  environmentVariable The name of the environment variable.
   * @param {boolean} [withFallback=true] If `true` and there's no version on the variable, it will
   *                                       return the fallback version.
   * @return {string}
   */
  getEnvironmentVersion(environmentVariable, withFallback = true) {
    const fallback = withFallback ? this.fallbackVersion : undefined;
    return this.environmentUtils.get(environmentVariable, fallback).trim();
  }
  /**
   * Get the version from the revision file. If the revision file doesn't exist or can't be loaded,
   * it will return an empty string.
   * @param  {string} filename The path to the revision file.
   * @return {string}
   */
  getVersionFromFile(filename) {
    let version;
    try {
      const filepath = this.pathUtils.join(filename);
      version = fs
      .readFileSync(filepath, 'utf-8')
      .trim();
    } catch (e) {
      version = '';
    }

    return version;
  }
  /**
   * Look for a version on both the revision file and the environment variable.
   * @param {string} revisionFilename    The path to the revision file.
   * @param {string} environmentVariable The name of the environment variable.
   * @return {string}
   */
  getVersion(revisionFilename, environmentVariable) {
    if (!this._loadedVersion || this._loadedVersion === this.fallbackVersion) {
      this._loadedVersion = this.getVersionFromFile(revisionFilename) ||
        this.getEnvironmentVersion(environmentVariable);
    }

    return this._loadedVersion;
  }
  /**
   * Create the revision file with either the version from the environment or, if the project is
   * on a GIT repository, with the first `7` letters of the last commit hash.
   * @param {string} revisionFilename    The path to where the revision file will be created.
   * @param {string} environmentVariable The name of the environment variable.
   * @return {Promise<string,Error>} If everything goes well, the promise will resolve with the
   *                                 version the method wrote on the file.
   */
  createRevisionFile(revisionFilename, environmentVariable) {
    let inRepository = true;
    try {
      fs.statSync('./.git');
    } catch (e) {
      inRepository = false;
    }

    let version = '';
    const envVersion = this.getEnvironmentVersion(environmentVariable, false);
    if (envVersion) {
      version = envVersion;
    } else if (shell.which('git') && inRepository) {
      const commitHash = shell.exec('git rev-parse HEAD', { silent: true });
      if (commitHash && commitHash.code === 0) {
        const hashLength = 7;
        version = commitHash.trim().substr(0, hashLength);
      }
    }

    let write;
    if (version) {
      const filepath = this.pathUtils.join(revisionFilename);
      write = fs.writeFile(filepath, version)
      .then(() => {
        this.appLogger.success(
          `The revision file was successfully created (${filepath})`
        );

        return version;
      })
      .catch((error) => {
        this.appLogger.error(
          `There was an error creating the revision file (${filepath})`
        );
        return Promise.reject(error);
      });
    } else {
      this.appLogger.error('The revision file couldn\'t be created');
      const errorMessage = 'The project is not running on a GIT environment and there\'s no ' +
        `${environmentVariable} variable set`;
      write = Promise.reject(new Error(errorMessage));
    }

    return write;
  }
}
/**
 * The service provider that once registered on the app container will set an instance of
 * `VersionUtils` as the `versionUtils` service.
 * @example
 * // Register it on the container
 * container.register(versionUtils);
 * // Getting access to the service instance
 * const versionUtils = container.get('versionUtils');
 * @type {Provider}
 */
const versionUtils = provider((app) => {
  app.set('versionUtils', () => new VersionUtils(
    app.get('environmentUtils'),
    app.get('appLogger'),
    app.get('pathUtils')
  ));
});

module.exports = {
  VersionUtils,
  versionUtils,
};