Skip to content
Snippets Groups Projects
Select Git revision
  • 00bf898cd77612340655c9729ea66bb1da964012
  • main default protected
  • renovate/main-ghcr.io-renovatebot-base-image-10.x
  • renovate/main-ghcr.io-containerbase-devcontainer-13.x
  • next
  • revert-31645-feat/rename-gradle-wrapper-validation-action
  • renovate/main-redis-5.x
  • fix/36615b-branch-reuse-no-cache
  • chore/punycode
  • fix/36615-branch-reuse-bug
  • refactor/pin-new-value
  • feat/36219--git-x509-signing
  • feat/structured-logger
  • hotfix/39.264.1
  • feat/skip-dangling
  • gh-readonly-queue/next/pr-36034-7a061c4ca1024a19e2c295d773d9642625d1c2be
  • hotfix/39.238.3
  • refactor/gitlab-auto-approve
  • feat/template-strings
  • gh-readonly-queue/next/pr-35654-137d934242c784e0c45d4b957362214f0eade1d7
  • fix/32307-global-extends-merging
  • 41.30.4
  • 41.30.3
  • 41.30.2
  • 41.30.1
  • 41.30.0
  • 41.29.1
  • 41.29.0
  • 41.28.2
  • 41.28.1
  • 41.28.0
  • 41.27.1
  • 41.27.0
  • 41.26.2
  • 41.26.1
  • 41.26.0
  • 41.25.1
  • 41.25.0
  • 41.24.0
  • 41.23.5
  • 41.23.4
41 results

index.ts

Blame
  • generate.ts 10.72 KiB
    import { DateTime } from 'luxon';
    import semver from 'semver';
    import mdTable from 'markdown-table';
    import { logger } from '../../../logger';
    import { mergeChildConfig } from '../../../config';
    import { BranchConfig, BranchUpgradeConfig } from '../../common';
    import * as template from '../../../util/template';
    
    function ifTypesGroup(
      depNames: string[],
      hasGroupName: boolean,
      branchUpgrades: any[]
    ): boolean {
      return (
        depNames.length === 2 &&
        !hasGroupName &&
        ((branchUpgrades[0].depName &&
          branchUpgrades[0].depName.startsWith('@types/') &&
          branchUpgrades[0].depName.endsWith(branchUpgrades[1].depName)) ||
          (branchUpgrades[1].depName &&
            branchUpgrades[1].depName.startsWith('@types/') &&
            branchUpgrades[1].depName.endsWith(branchUpgrades[0].depName)))
      );
    }
    
    function getTableValues(
      upgrade: BranchUpgradeConfig
    ): [string, string, string, string] | null {
      if (!upgrade.commitBodyTable) {
        return null;
      }
      const {
        datasource,
        lookupName,
        depName,
        fromVersion,
        toVersion,
        displayFrom,
        displayTo,
      } = upgrade;
      const name = lookupName || depName;
      const from = fromVersion || displayFrom;
      const to = toVersion || displayTo;
      if (datasource && name && from && to) {
        return [datasource, name, from, to];
      }
      logger.debug(
        {
          datasource,
          lookupName,
          depName,
          fromVersion,
          toVersion,
          displayFrom,
          displayTo,
        },
        'Cannot determine table values'
      );
      return null;
    }
    
    export function generateBranchConfig(
      branchUpgrades: BranchUpgradeConfig[]
    ): BranchConfig {
      logger.trace({ config: branchUpgrades }, 'generateBranchConfig');
      let config: BranchConfig = {
        upgrades: [],
      } as any;
      const hasGroupName = branchUpgrades[0].groupName !== null;
      logger.trace(`hasGroupName: ${hasGroupName}`);
      // Use group settings only if multiple upgrades or lazy grouping is disabled
      const depNames = [];
      const newValue = [];
      const toVersions = [];
      branchUpgrades.forEach((upg) => {
        if (!depNames.includes(upg.depName)) {
          depNames.push(upg.depName);
        }
        if (!toVersions.includes(upg.toVersion)) {
          toVersions.push(upg.toVersion);
        }
        if (upg.commitMessageExtra) {
          const extra = template.compile(upg.commitMessageExtra, upg);
          if (!newValue.includes(extra)) {
            newValue.push(extra);
          }
        }
      });
      const groupEligible =
        depNames.length > 1 ||
        toVersions.length > 1 ||
        (!toVersions[0] && newValue.length > 1) ||
        branchUpgrades[0].lazyGrouping === false;
      if (newValue.length > 1 && !groupEligible) {
        // eslint-disable-next-line no-param-reassign
        branchUpgrades[0].commitMessageExtra = `to v${toVersions[0]}`;
      }
      logger.trace(`groupEligible: ${groupEligible}`);
      const useGroupSettings = hasGroupName && groupEligible;
      logger.trace(`useGroupSettings: ${useGroupSettings}`);
      let releaseTimestamp: string;
      for (const branchUpgrade of branchUpgrades) {
        let upgrade: BranchUpgradeConfig = { ...branchUpgrade };
        if (upgrade.currentDigest) {
          upgrade.currentDigestShort =
            upgrade.currentDigestShort ||
            upgrade.currentDigest.replace('sha256:', '').substring(0, 7);
        }
        if (upgrade.newDigest) {
          upgrade.newDigestShort =
            upgrade.newDigestShort ||
            upgrade.newDigest.replace('sha256:', '').substring(0, 7);
        }
        if (!upgrade.displayFrom) {
          if (upgrade.currentValue === upgrade.newValue) {
            upgrade.displayFrom =
              upgrade.currentDigestShort || upgrade.currentVersion || '';
            upgrade.displayTo =
              upgrade.displayTo ||
              upgrade.newDigestShort ||
              upgrade.newVersion ||
              '';
          } else {
            upgrade.displayFrom =
              upgrade.currentValue ||
              upgrade.currentVersion ||
              upgrade.currentDigestShort ||
              '';
            upgrade.displayTo =
              upgrade.displayTo ||
              upgrade.newValue ||
              upgrade.newVersion ||
              upgrade.newDigestShort ||
              '';
          }
        }
    
        if (
          upgrade.updateType !== 'lockFileMaintenance' &&
          upgrade.displayFrom.length * upgrade.displayTo.length === 0
        ) {
          logger.debug({ config: upgrade }, 'empty displayFrom/displayTo');
        }
        upgrade.prettyDepType =
          upgrade.prettyDepType || upgrade.depType || 'dependency';
        if (useGroupSettings) {
          // Now overwrite original config with group config
          upgrade = mergeChildConfig(upgrade, upgrade.group);
          upgrade.isGroup = true;
        } else {
          delete upgrade.groupName;
        }
        // Delete group config regardless of whether it was applied
        delete upgrade.group;
        delete upgrade.lazyGrouping;
    
        const isTypesGroup = ifTypesGroup(depNames, hasGroupName, branchUpgrades);
    
        // istanbul ignore else
        if (toVersions.length > 1 && !isTypesGroup) {
          logger.trace({ toVersions });
          delete upgrade.commitMessageExtra;
          upgrade.recreateClosed = true;
        } else if (newValue.length > 1 && upgrade.isDigest) {
          logger.trace({ newValue });
          delete upgrade.commitMessageExtra;
          upgrade.recreateClosed = true;
        } else if (semver.valid(toVersions[0])) {
          upgrade.isRange = false;
        }
        // Use templates to generate strings
        logger.trace('Compiling branchName: ' + upgrade.branchName);
        upgrade.branchName = template.compile(upgrade.branchName, upgrade);
        if (upgrade.semanticCommits && !upgrade.commitMessagePrefix) {
          logger.trace('Upgrade has semantic commits enabled');
          let semanticPrefix = upgrade.semanticCommitType;
          if (upgrade.semanticCommitScope) {
            semanticPrefix += `(${template.compile(
              upgrade.semanticCommitScope,
              upgrade
            )})`;
          }
          upgrade.commitMessagePrefix = semanticPrefix;
          upgrade.commitMessagePrefix += semanticPrefix.endsWith(':') ? ' ' : ': ';
          upgrade.toLowerCase =
            // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
            upgrade.semanticCommitType.match(/[A-Z]/) === null &&
            !upgrade.semanticCommitType.startsWith(':');
        }
        // Compile a few times in case there are nested templates
        upgrade.commitMessage = template.compile(
          upgrade.commitMessage || '',
          upgrade
        );
        upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade);
        upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade);
        upgrade.commitMessage = upgrade.commitMessage.trim(); // Trim exterior whitespace
        upgrade.commitMessage = upgrade.commitMessage.replace(/\s+/g, ' '); // Trim extra whitespace inside string
        upgrade.commitMessage = upgrade.commitMessage.replace(
          /to vv(\d)/,
          'to v$1'
        );
        if (upgrade.toLowerCase) {
          // We only need to lowercvase the first line
          const splitMessage = upgrade.commitMessage.split('\n');
          splitMessage[0] = splitMessage[0].toLowerCase();
          upgrade.commitMessage = splitMessage.join('\n');
        }
        if (upgrade.commitBody) {
          upgrade.commitMessage = `${upgrade.commitMessage}\n\n${template.compile(
            upgrade.commitBody,
            upgrade
          )}`;
        }
        logger.trace(`commitMessage: ` + JSON.stringify(upgrade.commitMessage));
        if (upgrade.prTitle) {
          upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
          upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
          upgrade.prTitle = template
            .compile(upgrade.prTitle, upgrade)
            .trim()
            .replace(/\s+/g, ' ');
          if (upgrade.toLowerCase) {
            upgrade.prTitle = upgrade.prTitle.toLowerCase();
          }
        } else {
          [upgrade.prTitle] = upgrade.commitMessage.split('\n');
        }
        upgrade.prTitle += upgrade.hasBaseBranches ? ' ({{baseBranch}})' : '';
        if (upgrade.isGroup) {
          upgrade.prTitle +=
            upgrade.updateType === 'major' && upgrade.separateMajorMinor
              ? ' (major)'
              : '';
          upgrade.prTitle +=
            upgrade.updateType === 'minor' && upgrade.separateMinorPatch
              ? ' (minor)'
              : '';
          upgrade.prTitle += upgrade.updateType === 'patch' ? ' (patch)' : '';
        }
        // Compile again to allow for nested templates
        upgrade.prTitle = template.compile(upgrade.prTitle, upgrade);
        logger.trace(`prTitle: ` + JSON.stringify(upgrade.prTitle));
        config.upgrades.push(upgrade);
        if (upgrade.releaseTimestamp) {
          if (releaseTimestamp) {
            const existingStamp = DateTime.fromISO(releaseTimestamp);
            const upgradeStamp = DateTime.fromISO(upgrade.releaseTimestamp);
            if (upgradeStamp > existingStamp) {
              releaseTimestamp = upgrade.releaseTimestamp; // eslint-disable-line
            }
          } else {
            releaseTimestamp = upgrade.releaseTimestamp; // eslint-disable-line
          }
        }
      }
      if (
        depNames.length === 2 &&
        !hasGroupName &&
        config.upgrades[0].depName &&
        config.upgrades[0].depName.startsWith('@types/') &&
        config.upgrades[0].depName.endsWith(config.upgrades[1].depName)
      ) {
        logger.debug('Found @types - reversing upgrades to use depName in PR');
        config.upgrades.reverse();
        config.upgrades[0].recreateClosed = false;
        config.hasTypes = true;
      } else if (
        depNames.length === 2 &&
        !hasGroupName &&
        config.upgrades[1].depName &&
        config.upgrades[1].depName.startsWith('@types/') &&
        config.upgrades[1].depName.endsWith(config.upgrades[0].depName)
      ) {
        // do nothing
      } else {
        config.upgrades.sort((a, b) => {
          if (a.fileReplacePosition && b.fileReplacePosition) {
            // This is because we need to replace from the bottom of the file up
            return a.fileReplacePosition > b.fileReplacePosition ? -1 : 1;
          }
          if (a.depName < b.depName) {
            return -1;
          }
          if (a.depName > b.depName) {
            return 1;
          }
          return 0;
        });
      }
      // Now assign first upgrade's config as branch config
      config = { ...config, ...config.upgrades[0], releaseTimestamp }; // TODO: fixme
      config.canBeUnpublished = config.upgrades.some(
        (upgrade) => upgrade.canBeUnpublished
      );
      config.reuseLockFiles = config.upgrades.every(
        (upgrade) => upgrade.updateType !== 'lockFileMaintenance'
      );
      config.masterIssueApproval = config.upgrades.some(
        (upgrade) => upgrade.masterIssueApproval
      );
      config.masterIssuePrApproval = config.upgrades.some(
        (upgrade) => upgrade.prCreation === 'approval'
      );
      config.automerge = config.upgrades.every((upgrade) => upgrade.automerge);
      config.blockedByPin = config.upgrades.every(
        (upgrade) => upgrade.blockedByPin
      );
      const tableRows = config.upgrades
        .map((upgrade) => getTableValues(upgrade))
        .filter(Boolean);
      if (tableRows.length) {
        let table = [];
        table.push(['datasource', 'package', 'from', 'to']);
        table = table.concat(tableRows);
        config.commitMessage += '\n\n' + mdTable(table) + '\n';
      }
      return config;
    }