Skip to content
Snippets Groups Projects
Select Git revision
  • fc3e652f585c08be3bd0ea449874db2034dd29e7
  • master default protected
  • 6
  • 6.1
  • 6.1.7
  • 6.1.6
  • 6.1.5
  • 6.1.4
  • 6.1.3
  • 6.1.2
  • 6.1.1
  • 6.1.0
  • 6.0
  • 6.0.0
  • 5.14
  • 5.14.1
  • 5.14.0
  • 5.13
  • 5.13.3
  • 5.13.2
  • 5.13.1
  • 5.13.0
22 results

post-release.sh

Blame
  • index.js 6.43 KiB
    const changelog = require('changelog');
    const github = require('./helpers/github');
    const npm = require('./helpers/npm');
    const packageJson = require('./helpers/packageJson');
    
    let config = null;
    let logger = null;
    
    module.exports = function init(setConfig) {
      config = setConfig;
      logger = config.logger;
    
      // Initialize helpers
      github.setLogger(logger);
      npm.setLogger(logger);
      packageJson.setLogger(logger);
      changelog.setGitHubToken(config.token);
    
      return processPackageFile;
    };
    
    // This function manages the queue per-package file
    function processPackageFile(repoName, packageFile) {
      return github.initRepo(config.token, repoName)
        .then(() => {
          logger.info(`Processing ${repoName} ${packageFile}`);
          return github.getPackageFileContents(packageFile);
        })
        .then(npm.getAllDependencyUpgrades)
        .then(processUpgradesSequentially)
        .then(() => { // eslint-disable-line promise/always-return
          logger.info(`${repoName} ${packageFile} done`);
        })
        .catch((error) => {
          logger.error(`renovate caught error: ${error}`);
        });
    }
    
    function processUpgradesSequentially(upgrades) {
      if (Object.keys(upgrades).length) {
        logger.verbose('Processing upgrades');
      } else {
        logger.verbose('No upgrades to process');
      }
      logger.verbose(`All upgrades: ${JSON.stringify(upgrades)}`);
      // We are processing each upgrade sequentially for two major reasons:
      // 1. Reduce chances of GitHub API rate limiting
      // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency
      return upgrades.reduce(
        (promise, upgrade) => promise
          .then(() => getChangelog(upgrade))
          .then(updateDependency), Promise.resolve());
    }
    
    function getChangelog(upgrade) {
      if (!upgrade.workingVersion || upgrade.workingVersion === upgrade.newVersion) {
        return Object.assign(upgrade, { changelog: '' });
      }
      const semverString = `>${upgrade.workingVersion} <=${upgrade.newVersion}`;
      let log = '';
      logger.debug(`semverString: ${semverString}`);
      return changelog.generate(upgrade.depName, semverString)
        .then(changelog.markdown)
        .then((res) => {
          log = res;
          return logger.silly(`${upgrade.depName} ${upgrade.newVersion} changelog: ${res}`);
        })
        .catch((error) => {
          logger.verbose(`getChangelog error: ${error}`);
        })
        .then(() => Object.assign(upgrade, { changelog: log }));
    }
    
    function updateDependency(upgrade) {
      // Expand upgrade params
      const depType = upgrade.depType;
      const depName = upgrade.depName;
      const newVersion = upgrade.newVersion;
      // Use templates to generate strings
      const branchName = config.templates.branchName(upgrade);
      let prFunction = null;
      if (upgrade.upgradeType === 'pin') {
        prFunction = config.templates.prTitlePin;
      } else if (upgrade.upgradeType === 'minor') {
        // Use same title for range or minor
        prFunction = config.templates.prTitleMinor;
      } else {
        prFunction = config.templates.prTitleMajor;
      }
      const prTitle = prFunction(upgrade);
      const prBody = config.templates.prBody(upgrade);
      const commitMessage = config.templates.commitMessage(upgrade);
    
      // Check if same PR already existed and skip if so
      // This allows users to close an unwanted upgrade PR and not worry about seeing it raised again
      return github.checkForClosedPr(branchName, prTitle).then((prExisted) => {
        if (prExisted) {
          logger.verbose(`${depName}: Skipping due to existing PR found.`);
          return Promise.resolve();
        }
        return ensureAll();
      });
      function ensureAll() {
        return ensureBranch()
        .then(ensureCommit)
        .then(ensurePr)
        .catch((error) => {
          logger.error(`Error updating dependency ${depName}:  ${error}`);
          // Don't throw here - we don't want to stop the other renovations
        });
      }
      function ensureBranch() {
        // Save an API call by attempting to create branch without checking for existence first
        return github.createBranch(branchName).catch((error) => {
          // Check in case it's because the branch already existed
          if (error.response.body.message !== 'Reference already exists') {
            // In this case it means we really do have a problem and can't continue
            logger.error(`Error creating branch: ${branchName}`);
            logger.error(`Response body: ${error.response.body}`);
            throw error;
          }
          // Otherwise we swallow this error and continue
        });
      }
      function ensureCommit() {
        // Retrieve the package.json from this renovate branch
        return github.getPackageFile(branchName).then((res) => {
          const currentSHA = res.body.sha;
          const currentFileContent = new Buffer(res.body.content, 'base64').toString();
          const currentJson = JSON.parse(currentFileContent);
          if (currentJson[depType][depName] === newVersion) {
            logger.verbose(`${depName}: branch ${branchName} is already up-to-date`);
            return Promise.resolve();
          }
          // Branch must need updating
          logger.verbose(`${depName}: Updating to ${newVersion} in branch ${branchName}`);
          const newPackageContents = packageJson.setNewValue(
            currentFileContent,
            depType,
            depName,
            newVersion);
          return github.writePackageFile(
            branchName,
            currentSHA,
            newPackageContents,
            commitMessage);
        });
      }
    
      // Ensures that PR exists with matching title/body
      function ensurePr() {
        // Create PR based on current state
        function createPr() {
          return github.createPr(branchName, prTitle, prBody).then((newPr) => {
            logger.info(`${depName}: Created PR #${newPr.number}`);
            return Promise.resolve();
          });
        }
        // Update PR based on current state
        function updatePr(existingPr) {
          return github.updatePr(existingPr.number, prTitle, prBody).then(() => {
            logger.info(`${depName}: Updated PR #${existingPr.number}`);
            return Promise.resolve();
          });
        }
        // Process a returned PR
        function processExistingPr(existingPr) {
          if (!existingPr) {
            // We need to create a new PR
            return createPr();
          }
          // Check if existing PR needs updating
          if (existingPr.title === prTitle && existingPr.body === prBody) {
            logger.verbose(`${depName}: PR #${existingPr.number} already up-to-date`);
            return Promise.resolve();
          }
          // PR must need updating
          return updatePr(existingPr);
        }
    
        return github.getPr(branchName)
        .then(processExistingPr)
        .catch((error) => {
          logger.error(`${depName} failed to ensure PR: ${error}`);
        });
      }
    }