mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-01 10:09:50 +00:00
9cecb86eca
* feat(config): add experimental exitCodeForErrors option * feat(config): give unknown-error its own exit code * docs: restore exit codes 1 and 2 to the table
272 lines
8.6 KiB
TypeScript
272 lines
8.6 KiB
TypeScript
import { GlobalConfig } from '../../config/global.ts';
|
|
import { applySecretsAndVariablesToConfig } from '../../config/secrets.ts';
|
|
import type { RenovateConfig } from '../../config/types.ts';
|
|
import {
|
|
REPOSITORY_DISABLED_BY_CONFIG,
|
|
REPOSITORY_FORKED,
|
|
REPOSITORY_NO_CONFIG,
|
|
} from '../../constants/error-messages.ts';
|
|
import { pkg } from '../../expose.ts';
|
|
import { instrument } from '../../instrumentation/index.ts';
|
|
import { addExtractionStats } from '../../instrumentation/reporting.ts';
|
|
import { ATTR_RENOVATE_SPLIT } from '../../instrumentation/types.ts';
|
|
import { logger, setMeta } from '../../logger/index.ts';
|
|
import { resetRepositoryLogLevelRemaps } from '../../logger/remap.ts';
|
|
import { getInheritedOrGlobal } from '../../util/common.ts';
|
|
import { removeDanglingContainers } from '../../util/exec/docker/index.ts';
|
|
import {
|
|
deleteLocalFile,
|
|
ensureDir,
|
|
privateCacheDir,
|
|
rmCache,
|
|
} from '../../util/fs/index.ts';
|
|
import { isCloned } from '../../util/git/index.ts';
|
|
import { detectSemanticCommits } from '../../util/git/semantic.ts';
|
|
import * as queue from '../../util/http/queue.ts';
|
|
import * as throttle from '../../util/http/throttle.ts';
|
|
import { addSplit, getSplits, splitInit } from '../../util/split.ts';
|
|
import {
|
|
AbandonedPackageStats,
|
|
DatasourceCacheStats,
|
|
GetDatasourceReleasesStats,
|
|
GitOperationStats,
|
|
HttpCacheStats,
|
|
HttpStats,
|
|
LookupStats,
|
|
ObsoleteCacheHitLogger,
|
|
PackageCacheStats,
|
|
} from '../../util/stats.ts';
|
|
import { setBranchCache } from './cache.ts';
|
|
import { extractRepoProblems } from './common.ts';
|
|
import { configMigration } from './config-migration/index.ts';
|
|
import { ensureDependencyDashboard } from './dependency-dashboard.ts';
|
|
import handleError from './error.ts';
|
|
import { finalizeRepo } from './finalize/index.ts';
|
|
import { pruneStaleBranches } from './finalize/prune.ts';
|
|
import { initRepo } from './init/index.ts';
|
|
import { OnboardingState } from './onboarding/common.ts';
|
|
import { ensureOnboardingPr } from './onboarding/pr/index.ts';
|
|
import type { ExtractResult } from './process/extract-update.ts';
|
|
import { extractDependencies, updateRepo } from './process/index.ts';
|
|
import type { ProcessResult, RepositoryResult } from './result.ts';
|
|
import { processResult } from './result.ts';
|
|
|
|
// istanbul ignore next
|
|
export async function renovateRepository(
|
|
repoConfig: RenovateConfig,
|
|
canRetry = true,
|
|
): Promise<ProcessResult | undefined> {
|
|
splitInit();
|
|
|
|
let repoResult: ProcessResult | undefined;
|
|
const { config, localDir, error } = await instrument(
|
|
'init',
|
|
async (): Promise<{
|
|
config: RenovateConfig;
|
|
localDir: string;
|
|
error?: Error;
|
|
}> => {
|
|
let config = GlobalConfig.set(
|
|
applySecretsAndVariablesToConfig({
|
|
config: repoConfig,
|
|
deleteVariables: false,
|
|
deleteSecrets: false,
|
|
}),
|
|
);
|
|
await removeDanglingContainers();
|
|
setMeta({ repository: config.repository });
|
|
logger.info({ renovateVersion: pkg.version }, 'Repository started');
|
|
logger.trace({ config });
|
|
queue.clear();
|
|
throttle.clear();
|
|
const localDir = GlobalConfig.get('localDir');
|
|
|
|
try {
|
|
await ensureDir(localDir);
|
|
logger.debug(`Using localDir: ${localDir}`);
|
|
config = await initRepo(config);
|
|
addSplit('init');
|
|
} catch (err) /* istanbul ignore next */ {
|
|
setMeta({ repository: config.repository });
|
|
const errorRes = await handleError(config, err);
|
|
const pruneWhenErrors = [
|
|
REPOSITORY_DISABLED_BY_CONFIG,
|
|
REPOSITORY_FORKED,
|
|
REPOSITORY_NO_CONFIG,
|
|
];
|
|
if (pruneWhenErrors.includes(errorRes)) {
|
|
await pruneStaleBranches(config, []);
|
|
}
|
|
repoResult = processResult(config, errorRes);
|
|
|
|
return { config, localDir, error: err };
|
|
}
|
|
|
|
return { config, localDir };
|
|
},
|
|
{
|
|
attributes: {
|
|
[ATTR_RENOVATE_SPLIT]: 'init',
|
|
},
|
|
},
|
|
);
|
|
|
|
// only continue if init stage was successful
|
|
if (error === undefined) {
|
|
try {
|
|
const performExtract =
|
|
config.repoIsOnboarded! ||
|
|
!OnboardingState.onboardingCacheValid ||
|
|
OnboardingState.prUpdateRequested;
|
|
const extractResult = performExtract
|
|
? await extractDependencies(config)
|
|
: emptyExtract();
|
|
addExtractionStats(config, extractResult);
|
|
|
|
const { branches, branchList, packageFiles } = extractResult;
|
|
|
|
if (config.semanticCommits === 'auto') {
|
|
config.semanticCommits = await detectSemanticCommits();
|
|
}
|
|
|
|
if (
|
|
GlobalConfig.get('dryRun') !== 'lookup' &&
|
|
GlobalConfig.get('dryRun') !== 'extract'
|
|
) {
|
|
let res: RepositoryResult;
|
|
if (config.repoIsOnboarded) {
|
|
addSplit('onboarding'); // no onboarding work was needed
|
|
res = await instrument('update', () => updateRepo(config, branches), {
|
|
attributes: {
|
|
[ATTR_RENOVATE_SPLIT]: 'update',
|
|
},
|
|
});
|
|
setMeta({ repository: config.repository });
|
|
} else {
|
|
res = await instrument(
|
|
'onboarding',
|
|
() => ensureOnboardingPr(config, packageFiles, branches),
|
|
{
|
|
attributes: {
|
|
[ATTR_RENOVATE_SPLIT]: 'onboarding',
|
|
},
|
|
},
|
|
);
|
|
addSplit('onboarding');
|
|
}
|
|
addSplit('update');
|
|
if (performExtract) {
|
|
await setBranchCache(branches); // update branch cache if performed extraction
|
|
}
|
|
if (res === 'automerged') {
|
|
if (canRetry) {
|
|
logger.info('Restarting repository job after automerge result');
|
|
const recursiveRes = await renovateRepository(repoConfig, false);
|
|
return recursiveRes;
|
|
}
|
|
logger.debug(`Automerged but already retried once`);
|
|
} else {
|
|
const configMigrationRes = await configMigration(config, branchList);
|
|
await ensureDependencyDashboard(
|
|
config,
|
|
branches,
|
|
packageFiles,
|
|
configMigrationRes,
|
|
);
|
|
}
|
|
await finalizeRepo(config, branchList, repoConfig);
|
|
repoResult = processResult(config, res);
|
|
}
|
|
printRepositoryProblems(config.repository);
|
|
} catch (err) /* istanbul ignore next */ {
|
|
setMeta({ repository: config.repository });
|
|
const errorRes = await handleError(config, err);
|
|
const pruneWhenErrors = [
|
|
REPOSITORY_DISABLED_BY_CONFIG,
|
|
REPOSITORY_FORKED,
|
|
REPOSITORY_NO_CONFIG,
|
|
];
|
|
if (pruneWhenErrors.includes(errorRes)) {
|
|
await pruneStaleBranches(config, []);
|
|
}
|
|
repoResult = processResult(config, errorRes);
|
|
}
|
|
} else {
|
|
logger.debug(
|
|
{ err: error },
|
|
'Skipping the rest to the Renovate run due to error in `init` phase',
|
|
);
|
|
}
|
|
|
|
if (localDir && !repoConfig.persistRepoData) {
|
|
try {
|
|
await deleteLocalFile('.');
|
|
} catch (err) /* istanbul ignore if */ {
|
|
logger.warn({ err }, 'localDir deletion error');
|
|
}
|
|
}
|
|
try {
|
|
await rmCache(privateCacheDir());
|
|
} catch (err) /* istanbul ignore if */ {
|
|
logger.warn({ err }, 'privateCacheDir deletion error');
|
|
}
|
|
const splits = getSplits();
|
|
logger.debug(splits, 'Repository timing splits (milliseconds)');
|
|
PackageCacheStats.report();
|
|
DatasourceCacheStats.report();
|
|
HttpStats.report();
|
|
HttpCacheStats.report();
|
|
LookupStats.report();
|
|
GetDatasourceReleasesStats.report();
|
|
ObsoleteCacheHitLogger.report();
|
|
AbandonedPackageStats.report();
|
|
GitOperationStats.report();
|
|
const cloned = isCloned();
|
|
/* v8 ignore next -- coverage not required of these `undefined` checks, as we're happy receiving an `undefined` in the logs */
|
|
logger.info(
|
|
{
|
|
cloned,
|
|
durationMs: splits.total,
|
|
result: repoResult?.res,
|
|
status: repoResult?.status,
|
|
enabled: repoResult?.enabled,
|
|
onboarded: repoResult?.onboarded,
|
|
exitCode: repoResult?.exitCode,
|
|
},
|
|
'Repository finished',
|
|
);
|
|
resetRepositoryLogLevelRemaps();
|
|
return repoResult;
|
|
}
|
|
|
|
// istanbul ignore next: renovateRepository is ignored
|
|
function emptyExtract(): ExtractResult {
|
|
return instrument(
|
|
'extract',
|
|
() => {
|
|
addSplit('extract');
|
|
addSplit('lookup');
|
|
return {
|
|
branches: [],
|
|
branchList: [getInheritedOrGlobal('onboardingBranch')!], // to prevent auto closing
|
|
packageFiles: {},
|
|
};
|
|
},
|
|
{
|
|
attributes: {
|
|
[ATTR_RENOVATE_SPLIT]: 'extract',
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function printRepositoryProblems(repository: string | undefined): void {
|
|
const repoProblems = extractRepoProblems(repository);
|
|
if (repoProblems.size) {
|
|
logger.debug(
|
|
{ repoProblems: Array.from(repoProblems) },
|
|
'repository problems',
|
|
);
|
|
}
|
|
}
|