renovate/lib/manager/resolve.js
Rhys Arkins 33e4b51126 feat: raise config validation issue if onboarded repo has invalid package.json
Previously, if someone updated a package.json to include invalid JSON, Renovate fails to parse it (as expected), so ignores it. As a result, any existing PRs would be autoclosed. Instead, Renovate will now skip the repository and raise a warning issue until the file parsing is complete or the file is added to Renovate’s ignore list.
2017-12-18 13:43:29 +01:00

169 lines
6.1 KiB
JavaScript

const path = require('path');
const upath = require('upath');
const { migrateAndValidate } = require('../config/migrate-validate');
const presets = require('../config/presets');
const manager = require('./index');
const dockerResolve = require('../manager/docker/resolve');
const nodeResolve = require('../manager/node/resolve');
const bazelResolve = require('../manager/bazel/resolve');
const { mergeChildConfig } = require('../config');
const { checkMonorepos } = require('../manager/npm/monorepos');
module.exports = {
resolvePackageFiles,
};
async function resolvePackageFiles(config) {
logger.trace({ config }, 'resolvePackageFiles()');
const allPackageFiles = config.packageFiles.length
? config.packageFiles
: await manager.detectPackageFiles(config);
logger.debug({ allPackageFiles }, 'allPackageFiles');
const fileList = await platform.getFileList();
async function resolvePackageFile(p) {
const packageFile = typeof p === 'string' ? { packageFile: p } : p;
Object.assign(packageFile, config.npm);
if (packageFile.packageFile.endsWith('package.json')) {
logger.debug(`Resolving packageFile ${JSON.stringify(packageFile)}`);
const pFileRaw = await platform.getFile(packageFile.packageFile);
if (!pFileRaw) {
logger.info(
{ packageFile: packageFile.packageFile },
'Cannot find package.json'
);
config.errors.push({
depName: packageFile.packageFile,
message: 'Cannot find package.json',
});
return null;
}
try {
packageFile.content = JSON.parse(pFileRaw);
} catch (err) {
logger.info(
{ packageFile: packageFile.packageFile },
'Cannot parse package.json'
);
if (config.repoIsOnboarded) {
const error = new Error('config-validation');
error.configFile = packageFile.packageFile;
error.validationError = 'Cannot parse package.json';
error.validationMessage =
'This package.json contains invalid JSON and cannot be parsed. Please fix it, or add it to your "ignorePaths" array in your renovate config so that Renovate can continue.';
throw error;
}
config.errors.push({
depName: packageFile.packageFile,
message:
"Cannot parse package.json (invalid JSON). Please fix the contents or add the file/path to the `ignorePaths` array in Renovate's config",
});
return null;
}
if (!config.ignoreNpmrcFile) {
packageFile.npmrc = await platform.getFile(
upath.join(path.dirname(packageFile.packageFile), '.npmrc')
);
}
if (packageFile.npmrc) {
logger.info('Found .npmrc');
} else {
delete packageFile.npmrc;
}
packageFile.yarnrc = await platform.getFile(
upath.join(path.dirname(packageFile.packageFile), '.yarnrc')
);
if (packageFile.yarnrc) {
logger.info('Found .yarnrc');
} else {
delete packageFile.yarnrc;
}
// hoist renovate config if exists
if (packageFile.content.renovate) {
logger.debug(
{
packageFile: packageFile.packageFile,
config: packageFile.content.renovate,
},
`Found package.json renovate config`
);
const migratedConfig = migrateAndValidate(
config,
packageFile.content.renovate
);
logger.debug(
{ config: migratedConfig },
'package.json migrated config'
);
// istanbul ignore if
if (migratedConfig.errors.length) {
const error = new Error('config-validation');
error.configFile = packageFile.packageFile;
error.validationError =
'The `renovate` config inside `package.json` failed validation';
error.validationMessage = migratedConfig.errors
.map(e => e.message)
.join(', ');
throw error;
}
const resolvedConfig = await presets.resolveConfigPresets(
migratedConfig
);
logger.debug(
{ config: resolvedConfig },
'package.json resolved config'
);
Object.assign(packageFile, resolvedConfig);
delete packageFile.content.renovate;
} else {
logger.debug(
{ packageFile: packageFile.packageFile },
`No renovate config`
);
}
// Detect if lock files are used
const yarnLockFileName = upath.join(
path.dirname(packageFile.packageFile),
'yarn.lock'
);
if (fileList.includes(yarnLockFileName)) {
logger.debug(
{ packageFile: packageFile.packageFile },
'Found yarn.lock'
);
packageFile.yarnLock = yarnLockFileName;
}
const packageLockFileName = upath.join(
path.dirname(packageFile.packageFile),
'package-lock.json'
);
if (fileList.includes(packageLockFileName)) {
logger.debug(
{ packageFile: packageFile.packageFile },
'Found package-lock.json'
);
packageFile.packageLock = packageLockFileName;
}
return packageFile;
} else if (packageFile.packageFile.endsWith('package.js')) {
// meteor
return mergeChildConfig(config.meteor, packageFile);
} else if (packageFile.packageFile.endsWith('Dockerfile')) {
logger.debug('Resolving Dockerfile');
return dockerResolve.resolvePackageFile(config, packageFile);
} else if (packageFile.packageFile.endsWith('.travis.yml')) {
logger.debug('Resolving .travis.yml');
return nodeResolve.resolvePackageFile(config, packageFile);
} else if (packageFile.packageFile.endsWith('WORKSPACE')) {
logger.debug('Resolving WORKSPACE');
return bazelResolve.resolvePackageFile(config, packageFile);
}
return null;
}
logger.debug('queue');
const queue = allPackageFiles.map(p => resolvePackageFile(p));
const packageFiles = (await Promise.all(queue)).filter(p => p !== null);
platform.ensureIssueClosing('Action Required: Fix Renovate Configuration');
return checkMonorepos({ ...config, packageFiles });
}