UNPKG

renovate

Version:

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

330 lines • 14.9 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.resolveStaticRepoConfig = resolveStaticRepoConfig; exports.tryReadStaticRepoFileConfig = tryReadStaticRepoFileConfig; exports.mergeStaticConfig = mergeStaticConfig; 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 configValidation = tslib_1.__importStar(require("../../../config/validation")); 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 config_2 = require("../onboarding/branch/config"); 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, common_2.getDefaultConfigFileName)(config); repoConfig = { configFileName, configFileParsed: await (0, config_2.getOnboardingConfig)(config), }; } const configFileParsed = repoConfig?.configFileParsed ?? {}; const resolvedRepoConfig = await resolveStaticRepoConfig(configFileParsed, process.env.RENOVATE_X_STATIC_REPO_CONFIG_FILE); if (is_1.default.nonEmptyArray(returnConfig.extends)) { resolvedRepoConfig.extends = [ ...returnConfig.extends, ...(resolvedRepoConfig.extends ?? []), ]; delete returnConfig.extends; } checkForRepoConfigError(repoConfig); const migratedConfig = await (0, migrate_validate_1.migrateAndValidate)(config, resolvedRepoConfig); 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.applySecretsAndVariablesToConfig)({ config: resolvedConfig, secrets: (0, config_1.mergeChildConfig)(config.secrets ?? {}, resolvedConfig.secrets ?? {}), variables: (0, config_1.mergeChildConfig)(config.variables ?? {}, resolvedConfig.variables ?? {}), }); // 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 resolveStaticRepoConfig(config, filename) { if (!is_1.default.nonEmptyString(filename)) { return config; } let staticRepoConfig; try { staticRepoConfig = await tryReadStaticRepoFileConfig(filename); } catch (err) { logger_1.logger.fatal({ err }, 'Failed to load static repository config file'); process.exit(1); } if (!is_1.default.nonEmptyObject(staticRepoConfig)) { return config; } return mergeStaticConfig(config, staticRepoConfig); } async function tryReadStaticRepoFileConfig(staticRepoConfigFile) { logger_1.logger.debug(`Reading static repo config file from ${staticRepoConfigFile}`); let staticRepoConfigRaw; try { staticRepoConfigRaw = await (0, fs_1.readSystemFile)(staticRepoConfigFile, 'utf8'); } catch (err) { throw new Error(`Failed to read static repo config file: "${staticRepoConfigFile}"`, { cause: err }); } const staticRepoConfig = (0, common_1.parseJson)(staticRepoConfigRaw, staticRepoConfigFile); // validate and log issues here to preserve context, caller handles migration and full validation. const { errors, warnings } = await configValidation.validateConfig('repo', staticRepoConfig); if (is_1.default.nonEmptyArray(errors) || is_1.default.nonEmptyArray(warnings)) { logger_1.logger.info({ errors, warnings }, 'Static repo config validation issues detected'); } else { logger_1.logger.debug({ staticRepoConfig }, 'Static repository config file successfully parsed and validated'); } return staticRepoConfig; } function mergeStaticConfig(config, staticRepoConfig) { // merge extends if (is_1.default.nonEmptyArray(staticRepoConfig.extends)) { config.extends = [...staticRepoConfig.extends, ...(config.extends ?? [])]; delete staticRepoConfig.extends; } // renovate repo config overrides RENOVATE_STATIC_REPO_CONFIG[_FILE] return (0, config_1.mergeChildConfig)(staticRepoConfig, config); } //# sourceMappingURL=merge.js.map