UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

293 lines • 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectConfigFile = detectConfigFile; exports.detectRepoFileConfig = detectRepoFileConfig; exports.checkForRepoConfigError = checkForRepoConfigError; exports.mergeRenovateConfig = mergeRenovateConfig; exports.setNpmTokenInNpmrc = setNpmTokenInNpmrc; exports.mergeStaticRepoEnvConfig = mergeStaticRepoEnvConfig; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); const config_1 = require("../../../config"); const app_strings_1 = require("../../../config/app-strings"); const decrypt_1 = require("../../../config/decrypt"); const migrate_validate_1 = require("../../../config/migrate-validate"); const migration_1 = require("../../../config/migration"); const parse_1 = require("../../../config/parse"); const presets = tslib_1.__importStar(require("../../../config/presets")); const secrets_1 = require("../../../config/secrets"); const error_messages_1 = require("../../../constants/error-messages"); const logger_1 = require("../../../logger"); const npmApi = tslib_1.__importStar(require("../../../modules/datasource/npm")); const platform_1 = require("../../../modules/platform"); const scm_1 = require("../../../modules/platform/scm"); const external_host_error_1 = require("../../../types/errors/external-host-error"); const repository_1 = require("../../../util/cache/repository"); const common_1 = require("../../../util/common"); const env_1 = require("../../../util/env"); const fs_1 = require("../../../util/fs"); const hostRules = tslib_1.__importStar(require("../../../util/host-rules")); const queue = tslib_1.__importStar(require("../../../util/http/queue")); const throttle = tslib_1.__importStar(require("../../../util/http/throttle")); const mask_1 = require("../../../util/mask"); const regex_1 = require("../../../util/regex"); const env_2 = require("../../global/config/parse/env"); const config_2 = require("../onboarding/branch/config"); const create_1 = require("../onboarding/branch/create"); const onboarding_branch_cache_1 = require("../onboarding/branch/onboarding-branch-cache"); const common_2 = require("../onboarding/common"); async function detectConfigFile() { const fileList = await scm_1.scm.getFileList(); for (const fileName of app_strings_1.configFileNames) { if (fileName === 'package.json') { try { const pJson = JSON.parse((await (0, fs_1.readLocalFile)('package.json', 'utf8'))); if (pJson.renovate) { logger_1.logger.warn('Using package.json for Renovate config is deprecated - please use a dedicated configuration file instead'); return 'package.json'; } } catch { // Do nothing } } else if (fileList.includes(fileName)) { return fileName; } } return null; } async function detectRepoFileConfig(branchName) { const cache = (0, repository_1.getCache)(); let { configFileName } = cache; if (is_1.default.nonEmptyString(configFileName)) { let configFileRaw; try { configFileRaw = await platform_1.platform.getRawFile(configFileName, undefined, branchName); } catch (err) { // istanbul ignore if if (err instanceof external_host_error_1.ExternalHostError) { throw err; } configFileRaw = null; } if (configFileRaw) { let configFileParsed = (0, common_1.parseJson)(configFileRaw, configFileName); if (configFileName === 'package.json') { configFileParsed = configFileParsed.renovate; } return { configFileName, configFileParsed }; } else { logger_1.logger.debug('Existing config file no longer exists'); delete cache.configFileName; } } if (common_2.OnboardingState.onboardingCacheValid) { configFileName = (0, onboarding_branch_cache_1.getOnboardingFileNameFromCache)(); } else { configFileName = (await detectConfigFile()) ?? undefined; } if (!configFileName) { logger_1.logger.debug('No renovate config file found'); cache.configFileName = ''; return {}; } cache.configFileName = configFileName; logger_1.logger.debug(`Found ${configFileName} config file`); // TODO #22198 let configFileParsed; let configFileRaw; if (common_2.OnboardingState.onboardingCacheValid) { const cachedConfig = (0, onboarding_branch_cache_1.getOnboardingConfigFromCache)(); const parsedConfig = cachedConfig ? JSON.parse(cachedConfig) : undefined; if (parsedConfig) { (0, onboarding_branch_cache_1.setOnboardingConfigDetails)(configFileName, JSON.stringify(parsedConfig)); return { configFileName, configFileParsed: parsedConfig }; } } if (configFileName === 'package.json') { // We already know it parses configFileParsed = JSON.parse( // TODO #22198 (await (0, fs_1.readLocalFile)('package.json', 'utf8'))).renovate; if (is_1.default.string(configFileParsed)) { logger_1.logger.debug('Massaging string renovate config to extends array'); configFileParsed = { extends: [configFileParsed] }; } logger_1.logger.debug({ config: configFileParsed }, 'package.json>renovate config'); } else { configFileRaw = await (0, fs_1.readLocalFile)(configFileName, 'utf8'); // istanbul ignore if if (!is_1.default.string(configFileRaw)) { logger_1.logger.warn({ configFileName }, 'Null contents when reading config file'); throw new Error(error_messages_1.REPOSITORY_CHANGED); } // istanbul ignore if if (!configFileRaw.length) { configFileRaw = '{}'; } const parseResult = (0, parse_1.parseFileConfig)(configFileName, configFileRaw); if (!parseResult.success) { return { configFileName, configFileParseError: { validationError: parseResult.validationError, validationMessage: parseResult.validationMessage, }, }; } configFileParsed = parseResult.parsedContents; logger_1.logger.debug({ fileName: configFileName, config: configFileParsed }, 'Repository config'); } (0, onboarding_branch_cache_1.setOnboardingConfigDetails)(configFileName, JSON.stringify(configFileParsed)); return { configFileName, configFileParsed }; } function checkForRepoConfigError(repoConfig) { if (!repoConfig.configFileParseError) { return; } const error = new Error(error_messages_1.CONFIG_VALIDATION); error.validationSource = repoConfig.configFileName; error.validationError = repoConfig.configFileParseError.validationError; error.validationMessage = repoConfig.configFileParseError.validationMessage; throw error; } // Check for repository config async function mergeRenovateConfig(config, branchName) { let returnConfig = { ...config }; let repoConfig = {}; if (config.requireConfig !== 'ignored') { repoConfig = await detectRepoFileConfig(branchName); } if (!repoConfig.configFileParsed && config.mode === 'silent') { logger_1.logger.debug('When mode=silent and repo has no config file, we use the onboarding config as repo config'); const configFileName = (0, create_1.getDefaultConfigFileName)(config); repoConfig = { configFileName, configFileParsed: await (0, config_2.getOnboardingConfig)(config), }; } const configFileParsed = repoConfig?.configFileParsed ?? {}; // I think we do not need to use combined env here as static repo config is meant to be in the env var and not file/repo config const configFileAndEnv = await mergeStaticRepoEnvConfig(configFileParsed, process.env); if (is_1.default.nonEmptyArray(returnConfig.extends)) { configFileAndEnv.extends = [ ...returnConfig.extends, ...(configFileAndEnv.extends ?? []), ]; delete returnConfig.extends; } checkForRepoConfigError(repoConfig); const migratedConfig = await (0, migrate_validate_1.migrateAndValidate)(config, configFileAndEnv); if (migratedConfig.errors?.length) { const error = new Error(error_messages_1.CONFIG_VALIDATION); error.validationSource = repoConfig.configFileName; error.validationError = 'The renovate configuration file contains some invalid settings'; error.validationMessage = migratedConfig.errors .map((e) => e.message) .join(', '); throw error; } if (migratedConfig.warnings) { returnConfig.warnings = [ ...(returnConfig.warnings ?? []), ...migratedConfig.warnings, ]; } delete migratedConfig.errors; delete migratedConfig.warnings; // TODO #22198 const repository = config.repository; // Decrypt before resolving in case we need npm authentication for any presets const decryptedConfig = await (0, decrypt_1.decryptConfig)(migratedConfig, repository); setNpmTokenInNpmrc(decryptedConfig); // istanbul ignore if if (is_1.default.string(decryptedConfig.npmrc)) { logger_1.logger.debug('Found npmrc in decrypted config - setting'); npmApi.setNpmrc(decryptedConfig.npmrc); } // Decrypt after resolving in case the preset contains npm authentication instead let resolvedConfig = await (0, decrypt_1.decryptConfig)(await presets.resolveConfigPresets(decryptedConfig, config, config.ignorePresets), repository); logger_1.logger.trace({ config: resolvedConfig }, 'resolved config'); const migrationResult = (0, migration_1.migrateConfig)(resolvedConfig); if (migrationResult.isMigrated) { logger_1.logger.debug('Resolved config needs migrating'); logger_1.logger.trace({ config: resolvedConfig }, 'resolved config after migrating'); resolvedConfig = migrationResult.migratedConfig; } setNpmTokenInNpmrc(resolvedConfig); // istanbul ignore if if (is_1.default.string(resolvedConfig.npmrc)) { logger_1.logger.debug('Ignoring any .npmrc files in repository due to configured npmrc'); npmApi.setNpmrc(resolvedConfig.npmrc); } resolvedConfig = (0, secrets_1.applySecretsToConfig)(resolvedConfig, (0, config_1.mergeChildConfig)(config.secrets ?? {}, resolvedConfig.secrets ?? {})); // istanbul ignore if if (resolvedConfig.hostRules) { logger_1.logger.debug('Setting hostRules from config'); for (const rule of resolvedConfig.hostRules) { try { hostRules.add(rule); } catch (err) { logger_1.logger.warn({ err, config: rule }, 'Error setting hostRule from config'); } } // host rules can change concurrency queue.clear(); throttle.clear(); delete resolvedConfig.hostRules; } returnConfig = (0, config_1.mergeChildConfig)(returnConfig, resolvedConfig); returnConfig = await presets.resolveConfigPresets(returnConfig, config); returnConfig.renovateJsonPresent = true; // istanbul ignore if if (returnConfig.ignorePaths?.length) { logger_1.logger.debug({ ignorePaths: returnConfig.ignorePaths }, `Found repo ignorePaths`); } (0, env_1.setUserEnv)(returnConfig.env); delete returnConfig.env; return returnConfig; } /** needed when using portal secrets for npmToken */ function setNpmTokenInNpmrc(config) { if (!is_1.default.string(config.npmToken)) { return; } const token = config.npmToken; logger_1.logger.debug({ npmToken: (0, mask_1.maskToken)(token) }, 'Migrating npmToken to npmrc'); if (!is_1.default.string(config.npmrc)) { logger_1.logger.debug('Adding npmrc to config'); config.npmrc = `//registry.npmjs.org/:_authToken=${token}\n`; delete config.npmToken; return; } if (config.npmrc.includes(`\${NPM_TOKEN}`)) { logger_1.logger.debug(`Replacing \${NPM_TOKEN} with npmToken`); config.npmrc = config.npmrc.replace((0, regex_1.regEx)(/\${NPM_TOKEN}/g), token); } else { logger_1.logger.debug('Appending _authToken= to end of existing npmrc'); config.npmrc = config.npmrc.replace((0, regex_1.regEx)(/\n?$/), `\n_authToken=${token}\n`); } delete config.npmToken; } async function mergeStaticRepoEnvConfig(config, env) { const repoEnvConfig = await (0, env_2.parseAndValidateOrExit)(env, 'RENOVATE_STATIC_REPO_CONFIG'); if (!is_1.default.nonEmptyObject(repoEnvConfig)) { return config; } // merge extends if (is_1.default.nonEmptyArray(repoEnvConfig.extends)) { config.extends = [...repoEnvConfig.extends, ...(config.extends ?? [])]; delete repoEnvConfig.extends; } // renovate repo config overrides RENOVATE_STATIC_REPO_CONFIG return (0, config_1.mergeChildConfig)(repoEnvConfig, config); } //# sourceMappingURL=merge.js.map