Files
renovate/lib/workers/repository/result.ts
RahulGautamSingh 9cecb86eca feat(config): add experimental exitCodeForErrors option (#45752)
* 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
2026-09-10 11:07:26 +00:00

129 lines
3.9 KiB
TypeScript

import type { RenovateConfig } from '../../config/types.ts';
import {
CONFIG_SECRETS_EXPOSED,
CONFIG_VALIDATION,
ConfigErrors,
EXTERNAL_HOST_ERROR,
MANAGER_LOCKFILE_ERROR,
MISSING_API_CREDENTIALS,
PlatformErrors,
RepositoryErrors,
SystemErrors,
TemporaryErrors,
UNKNOWN_ERROR,
} from '../../constants/error-messages.ts';
import { logger } from '../../logger/index.ts';
export type ProcessStatus =
/**
* The repository has been disabled, and will not be processed.
*
* See {@link RepositoryErrors} for information on which statuses lead to this.
*/
| 'disabled'
/**
* The repository has onboarded to Renovate, so will see PRs and/or branches raised by Renovate.
*
* The repository has not yet merged any PRs, or the repository performs branch-based automerge and those have not been detected (TODO #40635).
*/
| 'onboarded'
/**
* The repository has onboarded to Renovate, and has merged at least 1 Renovate PR.
*
* The repository may also perform branch-based automerge which has not been detected (TODO #40635).
*/
| 'activated'
/**
* The repository has an onboarding PR from Renovate that has not yet been reviewed and merged.
*/
| 'onboarding'
| 'unknown';
export interface ProcessResult {
res: RepositoryResult;
status: ProcessStatus;
enabled: boolean | undefined;
onboarded: boolean | undefined;
/** the process exit code this result maps to, used when `exitCodeForErrors` is enabled */
exitCode: number;
}
/** a strong type for any repository result status that Renovate may report */
export type RepositoryResult =
/** repository was processed successfully */
| 'done'
/** Renovate performed branch-based automerge on one branch during its run */
| 'automerged'
/** the repository is not yet onboarded, and its onboarding PR was processed during this run */
| 'onboarding'
// common set of errors
| (typeof SystemErrors)[number]
| (typeof RepositoryErrors)[number]
| (typeof TemporaryErrors)[number]
| (typeof ConfigErrors)[number]
| (typeof PlatformErrors)[number]
// other errors
| typeof EXTERNAL_HOST_ERROR
| typeof MISSING_API_CREDENTIALS
| typeof MANAGER_LOCKFILE_ERROR
| typeof UNKNOWN_ERROR;
/**
* Exit code per known error group, used when `exitCodeForErrors` is enabled.
*
* `RepositoryErrors` are deliberately absent: they mean the repository is disabled, not that the run failed.
*/
const exitCodes: [readonly string[], number][] = [
[SystemErrors, 3],
[PlatformErrors, 4],
[ConfigErrors, 5],
[TemporaryErrors, 6],
[[EXTERNAL_HOST_ERROR, MANAGER_LOCKFILE_ERROR, MISSING_API_CREDENTIALS], 7],
[[UNKNOWN_ERROR], 8],
];
function getExitCode(res: RepositoryResult): number {
return exitCodes.find(([errors]) => errors.includes(res))?.[1] ?? 0;
}
export function processResult(
config: RenovateConfig,
res: RepositoryResult,
): ProcessResult {
const enabledStatuses = [
CONFIG_SECRETS_EXPOSED,
CONFIG_VALIDATION,
MISSING_API_CREDENTIALS,
];
let status: ProcessStatus;
let enabled: boolean | undefined;
let onboarded: boolean | undefined;
// istanbul ignore next
if (RepositoryErrors.includes(res as (typeof RepositoryErrors)[number])) {
status = 'disabled';
enabled = false;
} else if (config.repoIsActivated) {
status = 'activated';
enabled = true;
onboarded = true;
} else if (enabledStatuses.includes(res) || config.repoIsOnboarded) {
status = 'onboarded';
enabled = true;
onboarded = true;
} else if (config.repoIsOnboarded === false) {
status = 'onboarding';
enabled = true;
onboarded = false;
} else {
logger.debug(`Unknown res: ${res}`);
status = 'unknown';
}
logger.debug(
// TODO: types (#22198)
`Repository result: ${res}, status: ${status}, enabled: ${enabled!}, onboarded: ${onboarded!}`,
);
return { res, status, enabled, onboarded, exitCode: getExitCode(res) };
}