diff --git a/lib/config/migrate-validate.js b/lib/config/migrate-validate.js index e0252a5204ad486d588623a352ac5861728b1d6a..e2e98b27bb82a2da89513329e3f3af4c7ef0f087 100644 --- a/lib/config/migrate-validate.js +++ b/lib/config/migrate-validate.js @@ -8,36 +8,29 @@ module.exports = { function migrateAndValidate(config, input) { logger.debug('migrateAndValidate()'); - try { - const { isMigrated, migratedConfig } = configMigration.migrateConfig(input); - if (isMigrated) { - logger.info( - { oldConfig: input, newConfig: migratedConfig }, - 'Config migration necessary' - ); - } else { - logger.debug('No config migration necessary'); - } - const massagedConfig = configMassage.massageConfig(migratedConfig); - logger.debug({ massagedConfig }, 'massaged config'); - const { warnings, errors } = configValidation.validateConfig( - massagedConfig + const { isMigrated, migratedConfig } = configMigration.migrateConfig(input); + if (isMigrated) { + logger.info( + { oldConfig: input, newConfig: migratedConfig }, + 'Config migration necessary' ); - // istanbul ignore if - if (warnings && warnings.length) { - logger.info({ warnings }, 'Found renovate config warnings'); - } - if (errors && errors.length) { - logger.info({ errors }, 'Found renovate config errors'); - } - massagedConfig.errors = (config.errors || []).concat(errors); - if (!config.repoIsOnboarded) { - // TODO #556 - enable warnings in real PRs - massagedConfig.warnings = (config.warnings || []).concat(warnings); - } - return massagedConfig; - } catch (err) /* istanbul ignore next */ { - logger.debug({ config }, 'migrateAndValidate error'); - throw err; + } else { + logger.debug('No config migration necessary'); } + const massagedConfig = configMassage.massageConfig(migratedConfig); + logger.debug({ massagedConfig }, 'massaged config'); + const { warnings, errors } = configValidation.validateConfig(massagedConfig); + // istanbul ignore if + if (warnings && warnings.length) { + logger.info({ warnings }, 'Found renovate config warnings'); + } + if (errors && errors.length) { + logger.info({ errors }, 'Found renovate config errors'); + } + massagedConfig.errors = (config.errors || []).concat(errors); + if (!config.repoIsOnboarded) { + // TODO #556 - enable warnings in real PRs + massagedConfig.warnings = (config.warnings || []).concat(warnings); + } + return massagedConfig; } diff --git a/lib/config/migration.js b/lib/config/migration.js index 7e1c80e2d252f070230fb5108ca220881b8c02ab..b6e53fa9ea72d766364de1cfb55fa073a37cf1b5 100644 --- a/lib/config/migration.js +++ b/lib/config/migration.js @@ -24,257 +24,245 @@ const removedOptions = [ // Returns a migrated config function migrateConfig(config) { - try { - if (!optionTypes) { - optionTypes = {}; - options.forEach(option => { - optionTypes[option.name] = option.type; - }); - } - let isMigrated = false; - const migratedConfig = deepcopy(config); - for (const [key, val] of Object.entries(config)) { - if (removedOptions.includes(key)) { - isMigrated = true; - delete migratedConfig[key]; - } else if (key === 'semanticPrefix') { - isMigrated = true; - delete migratedConfig.semanticPrefix; - let [text] = val.split(':'); - text = text.split('('); - [migratedConfig.semanticCommitType] = text; - if (text.length > 1) { - [migratedConfig.semanticCommitScope] = text[1].split(')'); - } else { - migratedConfig.semanticCommitScope = null; - } - } else if (key === 'extends' && Array.isArray(val)) { - for (let i = 0; i < val.length; i += 1) { - if (val[i] === 'config:application' || val[i] === ':js-app') { - isMigrated = true; - migratedConfig.extends[i] = 'config:js-app'; - } else if (val[i] === ':library' || val[i] === 'config:library') { - isMigrated = true; - migratedConfig.extends[i] = 'config:js-lib'; - } + if (!optionTypes) { + optionTypes = {}; + options.forEach(option => { + optionTypes[option.name] = option.type; + }); + } + let isMigrated = false; + const migratedConfig = deepcopy(config); + for (const [key, val] of Object.entries(config)) { + if (removedOptions.includes(key)) { + isMigrated = true; + delete migratedConfig[key]; + } else if (key === 'semanticPrefix') { + isMigrated = true; + delete migratedConfig.semanticPrefix; + let [text] = val.split(':'); + text = text.split('('); + [migratedConfig.semanticCommitType] = text; + if (text.length > 1) { + [migratedConfig.semanticCommitScope] = text[1].split(')'); + } else { + migratedConfig.semanticCommitScope = null; + } + } else if (key === 'extends' && Array.isArray(val)) { + for (let i = 0; i < val.length; i += 1) { + if (val[i] === 'config:application' || val[i] === ':js-app') { + isMigrated = true; + migratedConfig.extends[i] = 'config:js-app'; + } else if (val[i] === ':library' || val[i] === 'config:library') { + isMigrated = true; + migratedConfig.extends[i] = 'config:js-lib'; } - } else if (key === 'automergeMinor') { - isMigrated = true; + } + } else if (key === 'automergeMinor') { + isMigrated = true; + migratedConfig.minor = migratedConfig.minor || {}; + migratedConfig.minor.automerge = val == true; // eslint-disable-line eqeqeq + delete migratedConfig[key]; + } else if (key === 'automergeMajor') { + isMigrated = true; + migratedConfig.major = migratedConfig.major || {}; + migratedConfig.major.automerge = val == true; // eslint-disable-line eqeqeq + delete migratedConfig[key]; + } else if (key === 'automergePatch') { + isMigrated = true; + migratedConfig.patch = migratedConfig.patch || {}; + migratedConfig.patch.automerge = val == true; // eslint-disable-line eqeqeq + delete migratedConfig[key]; + } else if (key === 'ignoreNodeModules') { + isMigrated = true; + delete migratedConfig.ignoreNodeModules; + migratedConfig.ignorePaths = val ? ['node_modules/'] : []; + } else if ( + key === 'automerge' && + typeof val === 'string' && + ['none', 'patch', 'minor', 'any'].indexOf(val) !== -1 + ) { + delete migratedConfig.automerge; + isMigrated = true; + if (val === 'none') { + migratedConfig.automerge = false; + } else if (val === 'patch') { + migratedConfig.patch = migratedConfig.patch || {}; + migratedConfig.patch.automerge = true; migratedConfig.minor = migratedConfig.minor || {}; - migratedConfig.minor.automerge = val == true; // eslint-disable-line eqeqeq - delete migratedConfig[key]; - } else if (key === 'automergeMajor') { - isMigrated = true; + migratedConfig.minor.automerge = false; migratedConfig.major = migratedConfig.major || {}; - migratedConfig.major.automerge = val == true; // eslint-disable-line eqeqeq - delete migratedConfig[key]; - } else if (key === 'automergePatch') { - isMigrated = true; - migratedConfig.patch = migratedConfig.patch || {}; - migratedConfig.patch.automerge = val == true; // eslint-disable-line eqeqeq - delete migratedConfig[key]; - } else if (key === 'ignoreNodeModules') { - isMigrated = true; - delete migratedConfig.ignoreNodeModules; - migratedConfig.ignorePaths = val ? ['node_modules/'] : []; - } else if ( - key === 'automerge' && - typeof val === 'string' && - ['none', 'patch', 'minor', 'any'].indexOf(val) !== -1 - ) { - delete migratedConfig.automerge; - isMigrated = true; - if (val === 'none') { - migratedConfig.automerge = false; - } else if (val === 'patch') { - migratedConfig.patch = migratedConfig.patch || {}; - migratedConfig.patch.automerge = true; - migratedConfig.minor = migratedConfig.minor || {}; - migratedConfig.minor.automerge = false; - migratedConfig.major = migratedConfig.major || {}; - migratedConfig.major.automerge = false; - } else if (val === 'minor') { - migratedConfig.minor = migratedConfig.minor || {}; - migratedConfig.minor.automerge = true; - migratedConfig.major = migratedConfig.major || {}; - migratedConfig.major.automerge = false; - } else if (val === 'any') { - migratedConfig.automerge = true; - } - } else if (key === 'packages') { - isMigrated = true; - migratedConfig.packageRules = migratedConfig.packages.map( - p => migrateConfig(p).migratedConfig - ); - delete migratedConfig.packages; - } else if (key === 'excludedPackageNames') { - isMigrated = true; - migratedConfig.excludePackageNames = val; - delete migratedConfig.excludedPackageNames; - } else if (key === 'packageName') { - isMigrated = true; - migratedConfig.packageNames = [val]; - delete migratedConfig.packageName; - } else if (key === 'packagePattern') { - isMigrated = true; - migratedConfig.packagePatterns = [val]; - delete migratedConfig.packagePattern; - } else if (key === 'baseBranch') { - isMigrated = true; - migratedConfig.baseBranches = [val]; - delete migratedConfig.baseBranch; - } else if (key === 'schedule' && !val) { - isMigrated = true; - migratedConfig.schedule = []; - } else if (key === 'schedule') { - // massage to array first - const schedules = typeof val === 'string' ? [val] : val; - // split 'and' - for (let i = 0; i < schedules.length; i += 1) { - if ( - schedules[i].includes(' and ') && - schedules[i].includes('before ') && - schedules[i].includes('after ') - ) { - const parsedSchedule = later.parse.text( - // We need to massage short hours first before we can parse it - schedules[i].replace(/( \d?\d)((a|p)m)/g, '$1:00$2') - ).schedules[0]; - // Only migrate if the after time is greater than before, e.g. "after 10pm and before 5am" - if ( - parsedSchedule && - parsedSchedule.t_a[0] > parsedSchedule.t_b[0] - ) { - isMigrated = true; - const toSplit = schedules[i]; - schedules[i] = toSplit + migratedConfig.major.automerge = false; + } else if (val === 'minor') { + migratedConfig.minor = migratedConfig.minor || {}; + migratedConfig.minor.automerge = true; + migratedConfig.major = migratedConfig.major || {}; + migratedConfig.major.automerge = false; + } else if (val === 'any') { + migratedConfig.automerge = true; + } + } else if (key === 'packages') { + isMigrated = true; + migratedConfig.packageRules = migratedConfig.packages.map( + p => migrateConfig(p).migratedConfig + ); + delete migratedConfig.packages; + } else if (key === 'excludedPackageNames') { + isMigrated = true; + migratedConfig.excludePackageNames = val; + delete migratedConfig.excludedPackageNames; + } else if (key === 'packageName') { + isMigrated = true; + migratedConfig.packageNames = [val]; + delete migratedConfig.packageName; + } else if (key === 'packagePattern') { + isMigrated = true; + migratedConfig.packagePatterns = [val]; + delete migratedConfig.packagePattern; + } else if (key === 'baseBranch') { + isMigrated = true; + migratedConfig.baseBranches = [val]; + delete migratedConfig.baseBranch; + } else if (key === 'schedule' && !val) { + isMigrated = true; + migratedConfig.schedule = []; + } else if (key === 'schedule') { + // massage to array first + const schedules = typeof val === 'string' ? [val] : val; + // split 'and' + for (let i = 0; i < schedules.length; i += 1) { + if ( + schedules[i].includes(' and ') && + schedules[i].includes('before ') && + schedules[i].includes('after ') + ) { + const parsedSchedule = later.parse.text( + // We need to massage short hours first before we can parse it + schedules[i].replace(/( \d?\d)((a|p)m)/g, '$1:00$2') + ).schedules[0]; + // Only migrate if the after time is greater than before, e.g. "after 10pm and before 5am" + if (parsedSchedule && parsedSchedule.t_a[0] > parsedSchedule.t_b[0]) { + isMigrated = true; + const toSplit = schedules[i]; + schedules[i] = toSplit + .replace( + /^(after|before) (.*?) and (after|before) (.*?)( |$)(.*)/, + '$1 $2 $6' + ) + .trim(); + schedules.push( + toSplit .replace( /^(after|before) (.*?) and (after|before) (.*?)( |$)(.*)/, - '$1 $2 $6' + '$3 $4 $6' ) - .trim(); - schedules.push( - toSplit - .replace( - /^(after|before) (.*?) and (after|before) (.*?)( |$)(.*)/, - '$3 $4 $6' - ) - .trim() - ); - } - } - } - for (let i = 0; i < schedules.length; i += 1) { - if (schedules[i].indexOf('on the last day of the month') !== -1) { - isMigrated = true; - schedules[i] = schedules[i].replace( - 'on the last day of the month', - 'on the first day of the month' + .trim() ); } - if (schedules[i].indexOf('on every weekday') !== -1) { - isMigrated = true; - schedules[i] = schedules[i].replace( - 'on every weekday', - 'every weekday' - ); - } - if (schedules[i].endsWith(' every day')) { - isMigrated = true; - schedules[i] = schedules[i].replace(' every day', ''); - } - if ( - schedules[i].match( - /every (mon|tues|wednes|thurs|fri|satur|sun)day$/ - ) - ) { - isMigrated = true; - schedules[i] = schedules[i].replace(/every ([a-z]*day)$/, 'on $1'); - } - if (schedules[i].endsWith('days')) { - isMigrated = true; - schedules[i] = schedules[i].replace('days', 'day'); - } } - if (isMigrated) { - if (typeof val === 'string' && schedules.length === 1) { - [migratedConfig.schedule] = schedules; - } else { - migratedConfig.schedule = schedules; - } + } + for (let i = 0; i < schedules.length; i += 1) { + if (schedules[i].indexOf('on the last day of the month') !== -1) { + isMigrated = true; + schedules[i] = schedules[i].replace( + 'on the last day of the month', + 'on the first day of the month' + ); } - } else if ( - typeof val === 'string' && - val.indexOf('{{semanticPrefix}}') === 0 - ) { - isMigrated = true; - migratedConfig[key] = val.replace('{{semanticPrefix}}', ''); - } else if (key === 'depTypes' && Array.isArray(val)) { - val.forEach(depType => { - if (isObject(depType)) { - const depTypeName = depType.depType; - if (depTypeName) { - migratedConfig[depTypeName] = migrateConfig( - depType - ).migratedConfig; - delete migratedConfig[depTypeName].depType; - } - } - }); - isMigrated = true; - delete migratedConfig.depTypes; - } else if (optionTypes[key] === 'json' && typeof val === 'boolean') { - isMigrated = true; - migratedConfig[key] = { enabled: val }; - } else if (optionTypes[key] === 'boolean') { - if (val === 'true') { - migratedConfig[key] = true; - } else if (val === 'false') { - migratedConfig[key] = false; + if (schedules[i].indexOf('on every weekday') !== -1) { + isMigrated = true; + schedules[i] = schedules[i].replace( + 'on every weekday', + 'every weekday' + ); } - } else if ( - optionTypes[key] === 'string' && - Array.isArray(val) && - val.length === 1 - ) { - migratedConfig[key] = `${val[0]}`; - } else if (key === 'node' && val.enabled === true) { - isMigrated = true; - delete migratedConfig.node.enabled; - migratedConfig.travis = migratedConfig.travis || {}; - migratedConfig.travis.enabled = true; - if (!Object.keys(migratedConfig.node).length) { - delete migratedConfig.node; - } else { - const subMigrate = migrateConfig(migratedConfig.node); - migratedConfig.node = subMigrate.migratedConfig; + if (schedules[i].endsWith(' every day')) { + isMigrated = true; + schedules[i] = schedules[i].replace(' every day', ''); } - } else if (isObject(val)) { - const subMigrate = migrateConfig(val); - if (subMigrate.isMigrated) { + if ( + schedules[i].match(/every (mon|tues|wednes|thurs|fri|satur|sun)day$/) + ) { isMigrated = true; - migratedConfig[key] = subMigrate.migratedConfig; + schedules[i] = schedules[i].replace(/every ([a-z]*day)$/, 'on $1'); } - } else if (Array.isArray(val)) { - migratedConfig[key] = []; - for (const item of val) { - if (isObject(item)) { - const arrMigrate = migrateConfig(item); - migratedConfig[key].push(arrMigrate.migratedConfig); - if (arrMigrate.isMigrated) { - isMigrated = true; - } - } else { - migratedConfig[key].push(item); + if (schedules[i].endsWith('days')) { + isMigrated = true; + schedules[i] = schedules[i].replace('days', 'day'); + } + } + if (isMigrated) { + if (typeof val === 'string' && schedules.length === 1) { + [migratedConfig.schedule] = schedules; + } else { + migratedConfig.schedule = schedules; + } + } + } else if ( + typeof val === 'string' && + val.indexOf('{{semanticPrefix}}') === 0 + ) { + isMigrated = true; + migratedConfig[key] = val.replace('{{semanticPrefix}}', ''); + } else if (key === 'depTypes' && Array.isArray(val)) { + val.forEach(depType => { + if (isObject(depType)) { + const depTypeName = depType.depType; + if (depTypeName) { + migratedConfig[depTypeName] = migrateConfig(depType).migratedConfig; + delete migratedConfig[depTypeName].depType; } } + }); + isMigrated = true; + delete migratedConfig.depTypes; + } else if (optionTypes[key] === 'json' && typeof val === 'boolean') { + isMigrated = true; + migratedConfig[key] = { enabled: val }; + } else if (optionTypes[key] === 'boolean') { + if (val === 'true') { + migratedConfig[key] = true; + } else if (val === 'false') { + migratedConfig[key] = false; + } + } else if ( + optionTypes[key] === 'string' && + Array.isArray(val) && + val.length === 1 + ) { + migratedConfig[key] = `${val[0]}`; + } else if (key === 'node' && val.enabled === true) { + isMigrated = true; + delete migratedConfig.node.enabled; + migratedConfig.travis = migratedConfig.travis || {}; + migratedConfig.travis.enabled = true; + if (!Object.keys(migratedConfig.node).length) { + delete migratedConfig.node; + } else { + const subMigrate = migrateConfig(migratedConfig.node); + migratedConfig.node = subMigrate.migratedConfig; + } + } else if (isObject(val)) { + const subMigrate = migrateConfig(val); + if (subMigrate.isMigrated) { + isMigrated = true; + migratedConfig[key] = subMigrate.migratedConfig; + } + } else if (Array.isArray(val)) { + migratedConfig[key] = []; + for (const item of val) { + if (isObject(item)) { + const arrMigrate = migrateConfig(item); + migratedConfig[key].push(arrMigrate.migratedConfig); + if (arrMigrate.isMigrated) { + isMigrated = true; + } + } else { + migratedConfig[key].push(item); + } } } - return { isMigrated, migratedConfig }; - } catch (err) /* istanbul ignore next */ { - logger.debug({ config }, 'migrateConfig() error'); - throw err; } + return { isMigrated, migratedConfig }; } function isObject(obj) {