Skip to content
Snippets Groups Projects
config-validator.ts 4.41 KiB
Newer Older
#!/usr/bin/env node
import 'source-map-support/register';
import './punycode.cjs';
Rhys Arkins's avatar
Rhys Arkins committed
import { dequal } from 'dequal';
import { pathExists, readFile } from 'fs-extra';
import { configFileNames } from './config/app-strings';
import { massageConfig } from './config/massage';
import { migrateConfig } from './config/migration';
import type { RenovateConfig } from './config/types';
import { validateConfig } from './config/validation';
import { logger } from './logger';
import {
  getConfig as getFileConfig,
  getParsedContent,
} from './workers/global/config/parse/file';
let returnVal = 0;

async function validate(
  configType: 'global' | 'repo',
  desc: string,
  config: RenovateConfig,
): Promise<void> {
  const { isMigrated, migratedConfig } = migrateConfig(config);
  if (isMigrated) {
    logger.warn(
      {
        oldConfig: config,
        newConfig: migratedConfig,
      },
      'Config migration necessary',
  }
  const massagedConfig = massageConfig(migratedConfig);
  const res = await validateConfig(configType, massagedConfig, isPreset);
    logger.error(
      { file: desc, errors: res.errors },
      'Found errors in configuration',
    returnVal = 1;
  }
  if (res.warnings.length) {
    logger.warn(
      { file: desc, warnings: res.warnings },
      'Found errors in configuration',
    returnVal = 1;
  }
}

  renovate?: RenovateConfig;
  'renovate-config'?: Record<string, RenovateConfig>;
  const strictArgIndex = process.argv.indexOf('--strict');
  const strict = strictArgIndex >= 0;
  if (strict) {
  if (process.argv.length > 2) {
    for (const file of process.argv.slice(2)) {
        if (!(await pathExists(file))) {
          returnVal = 1;
          logger.error({ file }, 'File does not exist');
          break;
        }
        const parsedContent = await getParsedContent(file);
        try {
          logger.info(`Validating ${file}`);
          await validate('global', file, parsedContent, strict);
          logger.warn({ file, err }, 'File is not valid Renovate config');
        logger.warn({ file, err }, 'File could not be parsed');
  } else {
    for (const file of configFileNames.filter(
      (name) => name !== 'package.json',
        if (!(await pathExists(file))) {
          continue;
        }
        const parsedContent = await getParsedContent(file);
        try {
          logger.info(`Validating ${file}`);
          await validate('repo', file, parsedContent, strict);
          logger.warn({ file, err }, 'File is not valid Renovate config');
        logger.warn({ file, err }, 'File could not be parsed');
        await readFile('package.json', 'utf8'),
      ) as PackageJson;
      if (pkgJson.renovate) {
        logger.info(`Validating package.json > renovate`);
          'package.json > renovate',
          pkgJson.renovate,
          strict,
        );
      }
      if (pkgJson['renovate-config']) {
        logger.info(`Validating package.json > renovate-config`);
        for (const presetConfig of Object.values(pkgJson['renovate-config'])) {
            'package.json > renovate-config',
            presetConfig,
            strict,
      // ignore
    }
    try {
      const fileConfig = await getFileConfig(process.env);
      if (!dequal(fileConfig, {})) {
        const file = process.env.RENOVATE_CONFIG_FILE ?? 'config.js';
        logger.info(`Validating ${file}`);
        try {
          await validate('global', file, fileConfig, strict);
          logger.error({ file, err }, 'File is not valid Renovate config');
  if (returnVal !== 0) {
    process.exit(returnVal);
  logger.info('Config validated successfully');
})().catch((e) => {
  // eslint-disable-next-line no-console
  console.error(e);
  process.exit(99);
});