renovate/lib/workers/repository/index.js

163 lines
6.1 KiB
JavaScript

const convertHrTime = require('convert-hrtime');
const tmp = require('tmp-promise');
const manager = require('../../manager');
// Workers
const branchWorker = require('../branch');
// children
const apis = require('./apis');
const onboarding = require('./onboarding');
const upgrades = require('./upgrades');
const cleanup = require('./cleanup');
const { checkMonorepos } = require('../../manager/npm/monorepos');
const { ensureOnboardingPr } = require('./onboarding/pr');
module.exports = {
renovateRepository,
};
async function renovateRepository(repoConfig, token) {
let config = { ...repoConfig };
const { logger } = config;
logger.trace({ config }, 'renovateRepository');
config.tmpDir = await tmp.dir({ unsafeCleanup: true });
config.errors = [];
config.warnings = [];
async function renovateRepositoryInner(count = 1) {
// istanbul ignore if
if (count > 5) {
// This is an arbitrary number added in to cut short any unintended infinite recursion
throw new Error('Existing renovateRepositoryInner after 5 loops');
}
logger.info(`renovateRepository loop ${count}`);
let branchList = [];
config = await apis.initApis(config, token);
config = await apis.mergeRenovateJson(config);
if (config.enabled === false) {
logger.debug('repository is disabled');
await cleanup.pruneStaleBranches(config, []);
return null;
}
if (config.isFork) {
if (config.renovateFork) {
logger.info('Processing forked repository');
} else {
logger.debug('repository is a fork and not manually configured');
await cleanup.pruneStaleBranches(config, []);
return null;
}
}
if (config.baseBranch) {
// Renovate should read content and target PRs here
if (await config.api.branchExists(config.baseBranch)) {
config.api.setBaseBranch(config.baseBranch);
} else {
// Warn and ignore setting (use default branch)
const message = `The configured baseBranch "${config.baseBranch}" is not present. Ignoring`;
config.errors.push({
depName: 'baseBranch',
message,
});
logger.warn(message);
}
}
config = await onboarding.getOnboardingStatus(config);
// Detect package files in default branch if not manually provisioned
if (config.packageFiles.length === 0) {
logger.debug('Detecting package files');
config.packageFiles = await manager.detectPackageFiles(config);
// If we can't detect any package.json then return
if (config.packageFiles.length === 0) {
logger.info('Cannot detect package files');
// istanbul ignore if
if (config.repoIsOnboarded === false) {
logger.warn('Need to delete onboarding PR');
const pr = await config.api.getBranchPr(config.onboardingBranch);
if (pr) {
logger.info('Found onboarding PR');
await config.api.updatePr(
pr.number,
'Configure Renovate - canceled',
'This PR was created in error and is now being deleted automatically. Sorry for the inconvenience.'
);
await config.api.deleteBranch(config.onboardingBranch);
throw new Error('no package files');
}
}
return null;
}
logger.info(
{
packageFiles: config.packageFiles,
count: config.packageFiles.length,
},
`Detected package files`
);
}
logger.debug('Resolving package files and content');
config = await apis.resolvePackageFiles(config);
config = await checkMonorepos(config);
logger.trace({ config }, 'post-packageFiles config');
// TODO: why is this fix needed?!
config.logger = logger;
const allUpgrades = await upgrades.determineRepoUpgrades(config);
const res = await upgrades.branchifyUpgrades(allUpgrades, logger);
config.errors = config.errors.concat(res.errors);
config.warnings = config.warnings.concat(res.warnings);
let branchUpgrades = res.upgrades;
logger.info(
{ branches: branchUpgrades.map(upg => upg.branchName) },
'branchUpgrades'
);
logger.debug(`Updating ${branchUpgrades.length} branch(es)`);
logger.trace({ config: branchUpgrades }, 'branchUpgrades');
if (config.repoIsOnboarded) {
logger.info(`Processing ${branchUpgrades.length} branch(es)`);
const branchStartTime = process.hrtime();
branchList = branchUpgrades.map(upgrade => upgrade.branchName);
if (branchUpgrades.some(upg => upg.isPin)) {
logger.info('Processing only pin branches first');
branchUpgrades = branchUpgrades.filter(upg => upg.isPin);
}
for (const branchUpgrade of branchUpgrades) {
const branchResult = await branchWorker.processBranch(
branchUpgrade,
config.errors,
config.warnings
);
if (branchResult === 'automerged') {
// Stop procesing other branches because base branch has been changed by an automerge
logger.info('Restarting repo renovation after automerge');
return renovateRepositoryInner(count + 1);
}
}
logger.info(
{ seconds: convertHrTime(process.hrtime(branchStartTime)).seconds },
'Finished updating branches'
);
} else {
await ensureOnboardingPr({ ...config, branches: branchUpgrades });
logger.info('"Configure Renovate" PR needs to be closed first');
branchList = [`${config.branchPrefix}configure`];
}
logger.debug(`branchList=${branchList}`);
return branchList;
}
try {
const branchList = await renovateRepositoryInner();
if (branchList) {
await cleanup.pruneStaleBranches(config, branchList);
}
} catch (err) {
// Swallow this error so that other repositories can be processed
if (err.message === 'uninitiated') {
logger.info('Repository is uninitiated - skipping');
} else if (err.message === 'no package files') {
logger.info('Repository has no package files - skipping');
} else {
logger.error(`Failed to process repository: ${err.message}`);
logger.debug({ err });
}
}
config.tmpDir.cleanup();
}