UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

705 lines (704 loc) • 29.7 kB
import { MANAGER_LOCKFILE_ERROR, PLATFORM_AUTHENTICATION_ERROR, PLATFORM_BAD_CREDENTIALS, REPOSITORY_CHANGED, SYSTEM_INSUFFICIENT_DISK_SPACE } from "../../../../constants/error-messages.js"; import { GlobalConfig } from "../../../../config/global.js"; import { logger, removeMeta } from "../../../../logger/index.js"; import { toMs } from "../../../../util/pretty-time.js"; import { compile } from "../../../../util/template/index.js"; import { isScheduledNow } from "./schedule.js"; import { ExternalHostError } from "../../../../types/errors/external-host-error.js"; import { coerceNumber } from "../../../../util/number.js"; import { getCount, isLimitReached } from "../../../global/limits.js"; import { getElapsedMs } from "../../../../util/date.js"; import { emojify } from "../../../../util/emoji.js"; import { scm } from "../../../../modules/platform/scm.js"; import { platform } from "../../../../modules/platform/index.js"; import { ensureComment, ensureCommentRemoval } from "../../../../modules/platform/comment.js"; import { embedChangelogs } from "../../changelog/index.js"; import { getMergeConfidenceLevel, isActiveConfidenceLevel, satisfiesConfidenceLevel } from "../../../../util/merge-confidence/index.js"; import { setConfidence, setStability } from "./status-checks.js"; import { ensurePr, getPlatformPrOptions } from "../pr/index.js"; import { getAdditionalFiles } from "../../../../modules/manager/npm/post-update/index.js"; import { checkAutoMerge } from "../pr/automerge.js"; import { setArtifactErrorStatus } from "./artifacts.js"; import { tryBranchAutomerge } from "./automerge.js"; import { bumpVersions } from "./bump-versions.js"; import { prAlreadyExisted } from "./check-existing.js"; import { commitFilesToBranch } from "./commit.js"; import executePostUpgradeCommands from "./execute-post-upgrade-commands.js"; import { getUpdatedPackageFiles } from "./get-updated.js"; import { handleClosedPr, handleModifiedPr } from "./handle-existing.js"; import { shouldReuseExistingBranch } from "./reuse.js"; import { isNonEmptyString } from "@sindresorhus/is"; import { DateTime } from "luxon"; //#region lib/workers/repository/update/branch/index.ts async function setBranchStatusChecks(config) { await setArtifactErrorStatus(config); await setStability(config); await setConfidence(config); } async function rebaseCheck(config, branchPr) { if (branchPr.title?.startsWith("rebase!")) { logger.debug(`Manual rebase requested via PR title for #${branchPr.number}`); return true; } if (!!branchPr.labels?.includes(config.rebaseLabel)) { logger.debug(`Manual rebase requested via PR labels for #${branchPr.number}`); /* v8 ignore next -- needs test */ if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would delete label ${config.rebaseLabel} from #${branchPr.number}`); else await platform.deleteLabel(branchPr.number, config.rebaseLabel); return true; } if (!!branchPr.bodyStruct?.rebaseRequested) { logger.debug(`Manual rebase requested via PR checkbox for #${branchPr.number}`); return true; } return false; } async function deleteBranchSilently(branchName) { try { await scm.deleteBranch(branchName); } catch (err) { /* v8 ignore next -- needs test */ logger.debug({ branchName, err }, "Branch auto-remove failed"); } } function userChangedTargetBranch(pr) { const oldTargetBranch = pr.bodyStruct?.debugData?.targetBranch; if (oldTargetBranch && pr.targetBranch) return pr.targetBranch !== oldTargetBranch; return false; } async function processBranch(branchConfig, forceRebase = false) { let commitSha = null; let config = { ...branchConfig }; logger.trace({ config }, "processBranch()"); let branchExists = await scm.branchExists(config.branchName); const dependencyDashboardCheck = config.dependencyDashboardChecks?.[config.branchName]; let updatesVerified = false; if (!branchExists && config.branchPrefix !== config.branchPrefixOld) { const branchName = config.branchName.replace(config.branchPrefix, config.branchPrefixOld); branchExists = await scm.branchExists(branchName); if (branchExists) { config.branchName = branchName; logger.debug("Found existing branch with branchPrefixOld"); } } if (!branchExists && branchConfig.minimumGroupSize && branchConfig.minimumGroupSize > branchConfig.upgrades.length && !dependencyDashboardCheck) { logger.debug(`Skipping branch creation as minimumGroupSize: ${branchConfig.minimumGroupSize} is not met`); return { branchExists: false, result: "minimum-group-size-not-met" }; } let branchPr = await platform.getBranchPr(config.branchName, config.baseBranch); logger.debug(`branchExists=${branchExists}`); logger.debug(`dependencyDashboardCheck=${dependencyDashboardCheck}`); if (branchPr) { config.rebaseRequested = await rebaseCheck(config, branchPr); logger.debug(`PR rebase requested=${config.rebaseRequested}`); } const keepUpdatedLabel = config.keepUpdatedLabel; const artifactErrorTopic = emojify(":warning: Artifact update problem"); const artifactNoticeTopic = emojify(":information_source: Artifact update notice"); try { const existingPr = !branchPr || config.automerge ? await prAlreadyExisted(config) : void 0; if (existingPr?.state === "merged") { logger.debug(`Matching PR #${existingPr.number} was merged previously`); if (config.automerge) { logger.debug("Disabling automerge because PR was merged previously"); config.automerge = false; config.automergedPreviously = true; } } else if (!branchPr && existingPr && !dependencyDashboardCheck) { logger.debug({ prTitle: config.prTitle }, `Closed PR #${existingPr.number} already exists. Skipping branch.`); await handleClosedPr(config, existingPr); return { branchExists: false, prNo: existingPr.number, result: "already-existed" }; } if (!branchExists && branchConfig.pendingChecks && !dependencyDashboardCheck) { logger.debug(`Branch ${config.branchName} creation is disabled because internalChecksFilter was not met`); return { branchExists: false, result: "pending" }; } if (!branchExists) { if (config.mode === "silent" && !dependencyDashboardCheck) { logger.debug(`Branch ${config.branchName} creation is disabled because mode=silent`); return { branchExists, result: "needs-approval" }; } if (config.dependencyDashboardApproval && !dependencyDashboardCheck) { logger.debug(`Branch ${config.branchName} creation is disabled because dependencyDashboardApproval=true`); return { branchExists, result: "needs-approval" }; } } logger.debug(`Open PR Count: ${getCount("ConcurrentPRs")}, Existing Branch Count: ${getCount("Branches")}, Hourly PR Count: ${getCount("HourlyPRs")}, Hourly Commit Count: ${getCount("HourlyCommits")}`); if (!branchExists && isLimitReached("Branches", branchConfig) && !dependencyDashboardCheck && !config.isVulnerabilityAlert) { logger.debug("Reached branch limit - skipping branch creation"); return { branchExists, result: "branch-limit-reached" }; } if (!branchConfig.rebaseRequested && isLimitReached("Commits") && !dependencyDashboardCheck && !config.isVulnerabilityAlert) { logger.debug("Reached commits per run limit - skipping branch"); return { branchExists, prNo: branchPr?.number, result: "commit-per-run-limit-reached" }; } if (!branchConfig.rebaseRequested && isLimitReached("HourlyCommits", branchConfig) && !dependencyDashboardCheck && !config.isVulnerabilityAlert) { logger.debug("Reached hourly commits limit - skipping branch"); return { branchExists, prNo: branchPr?.number, result: "commit-hourly-limit-reached" }; } if (branchExists) { config.stopUpdating = branchPr?.labels?.includes(config.stopUpdatingLabel); const prRebaseChecked = !!branchPr?.bodyStruct?.rebaseRequested; if (branchExists && !dependencyDashboardCheck && !prRebaseChecked) { if (config.stopUpdating) { logger.info("Branch updating is skipped because stopUpdatingLabel is present in config"); return { branchExists: true, prNo: branchPr?.number, result: "no-work" }; } if (config.pendingChecks) { logger.info("Branch updating is skipped because internalChecksFilter was not met"); return { branchExists: true, prNo: branchPr?.number, result: "pending" }; } } logger.debug("Checking if PR has been edited"); const branchIsModified = await scm.isBranchModified(config.branchName, config.baseBranch); if (branchPr) { logger.debug(`Found existing branch PR #${branchPr.number}`); if (branchPr.state !== "open") { logger.debug("PR has been closed or merged since this run started - aborting"); throw new Error(REPOSITORY_CHANGED); } if (branchIsModified || userChangedTargetBranch(branchPr)) { logger.debug(`PR has been edited, PrNo:${branchPr.number}`); await handleModifiedPr(config, branchPr); if (!(!!dependencyDashboardCheck || config.rebaseRequested)) return { branchExists, prNo: branchPr.number, result: "pr-edited" }; } } else if (branchIsModified) { const oldPr = await platform.findPr({ branchName: config.branchName, state: "!open", targetBranch: config.baseBranch }); if (!oldPr) { logger.debug("Branch has been edited but found no PR - skipping"); return { branchExists, result: "pr-edited" }; } const branchSha = await scm.getBranchCommit(config.branchName); const oldPrSha = oldPr?.sha; if (!oldPrSha || oldPrSha === branchSha) logger.debug({ oldPrNumber: oldPr.number, oldPrSha, branchSha }, "Found old PR matching this branch - will override it"); else { logger.debug({ oldPrNumber: oldPr.number, oldPrSha, branchSha }, "Found old PR but the SHA is different"); return { branchExists, result: "pr-edited" }; } } } config.isScheduledNow = isScheduledNow(config, "schedule"); if (!config.isScheduledNow && !dependencyDashboardCheck) { if (!branchExists) { logger.debug("Skipping branch creation as not within schedule"); return { branchExists, result: "not-scheduled" }; } if (config.updateNotScheduled === false && !config.rebaseRequested) { logger.debug("Skipping branch update as not within schedule"); return { branchExists, prNo: branchPr?.number, result: "update-not-scheduled" }; } if (!branchPr && !(config.automerge && config.automergeType === "branch")) { logger.debug("Skipping PR creation out of schedule"); return { branchExists, result: "not-scheduled" }; } logger.debug("Branch + PR exists but is not scheduled -- will update if necessary"); } if (config.upgrades.some((upgrade) => isNonEmptyString(upgrade.minimumReleaseAge) || isActiveConfidenceLevel(upgrade.minimumConfidence))) { const depNamesWithoutReleaseTimestamp = { "timestamp-required": [], "timestamp-optional": [] }; config.stabilityStatus = "green"; for (const upgrade of config.upgrades) { const minimumReleaseAgeMs = isNonEmptyString(upgrade.minimumReleaseAge) ? coerceNumber(toMs(upgrade.minimumReleaseAge), 0) : 0; if (minimumReleaseAgeMs) { const minimumReleaseAgeBehaviour = upgrade.minimumReleaseAgeBehaviour ?? "timestamp-required"; if (upgrade.releaseTimestamp) { const timeElapsed = getElapsedMs(upgrade.releaseTimestamp); if (timeElapsed < minimumReleaseAgeMs) { logger.debug({ depName: upgrade.depName, timeElapsed, minimumReleaseAge: upgrade.minimumReleaseAge }, "Update has not passed minimum release age"); config.stabilityStatus = "yellow"; continue; } } else if (minimumReleaseAgeBehaviour === "timestamp-required") { depNamesWithoutReleaseTimestamp["timestamp-required"].push({ depName: upgrade.depName, updateType: upgrade.updateType }); config.stabilityStatus = "yellow"; continue; } else depNamesWithoutReleaseTimestamp["timestamp-optional"].push({ depName: upgrade.depName, updateType: upgrade.updateType }); } const datasource = upgrade.datasource; const depName = upgrade.depName; const packageName = upgrade.packageName; const minimumConfidence = upgrade.minimumConfidence; const updateType = upgrade.updateType; const currentVersion = upgrade.currentVersion; const newVersion = upgrade.newVersion; if (isActiveConfidenceLevel(minimumConfidence)) { const confidence = await getMergeConfidenceLevel(datasource, packageName, currentVersion, newVersion, updateType) ?? "neutral"; if (satisfiesConfidenceLevel(confidence, minimumConfidence)) config.confidenceStatus = "green"; else { logger.debug({ depName, confidence, minimumConfidence }, "Update does not meet minimum confidence scores"); config.confidenceStatus = "yellow"; continue; } } } if (depNamesWithoutReleaseTimestamp["timestamp-required"].length) logger.once.debug({ updates: depNamesWithoutReleaseTimestamp["timestamp-required"] }, `Marking ${depNamesWithoutReleaseTimestamp["timestamp-required"].length} release(s) as pending, as they do not have a releaseTimestamp and we're running with minimumReleaseAgeBehaviour=timestamp-required`); if (depNamesWithoutReleaseTimestamp["timestamp-optional"].length) { logger.once.warn("Some upgrade(s) did not have a releaseTimestamp, but as we're running with minimumReleaseAgeBehaviour=timestamp-optional, proceeding. See debug logs for more information"); logger.once.debug({ updates: depNamesWithoutReleaseTimestamp["timestamp-optional"] }, `${depNamesWithoutReleaseTimestamp["timestamp-optional"].length} upgrade(s) did not have a releaseTimestamp, but as we're running with minimumReleaseAgeBehaviour=timestamp-optional, proceeding`); } if (!dependencyDashboardCheck && !branchExists && config.stabilityStatus === "yellow" && ["not-pending", "status-success"].includes(config.prCreation)) { logger.debug("Skipping branch creation due to internal status checks not met"); return { branchExists, result: "pending" }; } } let userRebaseRequested = dependencyDashboardCheck === "rebase" || !!config.dependencyDashboardRebaseAllOpen || !!config.rebaseRequested; const userApproveAllPendingPR = !!config.dependencyDashboardAllPending; const userOpenAllRateLimtedPR = !!config.dependencyDashboardAllRateLimited; const userOpenAllSchedulePendingPR = !!config.dependencyDashboardAllAwaitingSchedule; if (forceRebase) { logger.debug("Force rebase because branch needs updating"); config.reuseExistingBranch = false; } else if (userRebaseRequested) { logger.debug("User has requested rebase"); config.reuseExistingBranch = false; } else if (dependencyDashboardCheck === "global-config") { logger.debug(`Manual create/rebase requested via checkedBranches`); config.reuseExistingBranch = false; userRebaseRequested = true; } else if (userApproveAllPendingPR) logger.debug("A user manually approved all pending PRs via the Dependency Dashboard."); else if (userOpenAllRateLimtedPR) logger.debug("A user manually approved all rate-limited PRs via the Dependency Dashboard."); else if (userOpenAllSchedulePendingPR) logger.debug("A user manually requested all awaiting schedule PRs via the Dependency Dashboard."); else if (branchExists && config.rebaseWhen === "never" && !(keepUpdatedLabel && branchPr?.labels?.includes(keepUpdatedLabel)) && !dependencyDashboardCheck) { logger.debug("rebaseWhen=never so skipping branch update check"); return { branchExists, prNo: branchPr?.number, result: "no-work" }; } else if (branchPr?.targetBranch && branchPr.targetBranch !== config.baseBranch) { logger.debug("Base branch changed by user, rebasing the branch onto new base"); config.reuseExistingBranch = false; } else if (config.cacheFingerprintMatch === "no-match") { logger.debug("Cache fingerprint does not match, cannot reuse existing branch"); config.reuseExistingBranch = false; } else config = await shouldReuseExistingBranch(config); logger.debug(`Using reuseExistingBranch: ${config.reuseExistingBranch}`); if (!(config.reuseExistingBranch && config.cacheFingerprintMatch === "matched")) { await scm.checkoutBranch(config.baseBranch); const res = await getUpdatedPackageFiles(config); if (res.artifactErrors && config.artifactErrors) res.artifactErrors = config.artifactErrors.concat(res.artifactErrors); config = { ...config, ...res }; if (config.updatedPackageFiles?.length) { logger.debug(`Updated ${config.updatedPackageFiles.length} package files`); if (config.reuseExistingBranch && !forceRebase) { logger.debug("Existing branch needs updating. Restarting processBranch() with a clean branch"); return processBranch(branchConfig, true); } } else logger.debug("No package files need updating"); const additionalFiles = await getAdditionalFiles(config, branchConfig.packageFiles); config.artifactErrors = (config.artifactErrors ?? []).concat(additionalFiles.artifactErrors); config.artifactNotices = (config.artifactNotices ?? []).concat(additionalFiles.artifactNotices ?? []); config.updatedArtifacts = (config.updatedArtifacts ?? []).concat(additionalFiles.updatedArtifacts); if (config.updatedArtifacts?.length) { logger.debug({ updatedArtifacts: config.updatedArtifacts.map((f) => f.type === "deletion" ? `${f.path} (delete)` : f.path) }, `Updated ${config.updatedArtifacts.length} lock files`); if (config.reuseExistingBranch && !forceRebase) { logger.debug("Existing branch needs updating. Restarting processBranch() with a clean branch"); return processBranch(branchConfig, true); } } else logger.debug("No updated lock files in branch"); await embedChangelogs({ upgrades: config.upgrades, stage: "branch" }); const postUpgradeCommandResults = await executePostUpgradeCommands(config); if (postUpgradeCommandResults !== null) { const { updatedArtifacts, artifactErrors } = postUpgradeCommandResults; config.updatedArtifacts = updatedArtifacts; config.artifactErrors = artifactErrors; } await bumpVersions(config); removeMeta(["dep"]); if (config.artifactErrors?.length) if (config.releaseTimestamp) { logger.debug(`Branch timestamp: ${config.releaseTimestamp}`); if (DateTime.fromISO(config.releaseTimestamp).plus({ hours: 2 }) < DateTime.local()) logger.debug("PR is older than 2 hours, raise PR with lock file errors"); else if (branchExists) logger.debug("PR is less than 2 hours old but branchExists so updating anyway"); else { logger.debug("PR is less than 2 hours old - raise error instead of PR"); throw new Error(MANAGER_LOCKFILE_ERROR); } } else logger.debug("PR has no releaseTimestamp"); else if (config.updatedArtifacts?.length && branchPr) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would ensure comment removal in PR #${branchPr.number}`); else { await ensureCommentRemoval({ type: "by-topic", number: branchPr.number, topic: artifactErrorTopic }); if (!config.artifactNotices?.length) await ensureCommentRemoval({ type: "by-topic", number: branchPr.number, topic: artifactNoticeTopic }); } const forcedManually = userRebaseRequested || !branchExists; config.isConflicted ??= branchExists && await scm.isBranchConflicted(config.baseBranch, config.branchName); config.forceCommit = forcedManually || config.isConflicted; if (config.commitBody) { config.commitMessage = `${config.commitMessage}\n\n${compile(config.commitBody, { ...config, logJSON: config.upgrades[0].logJSON, releases: config.upgrades[0].releases })}`; logger.trace(`commitMessage: ${JSON.stringify(config.commitMessage)}`); } commitSha = await commitFilesToBranch(config); await scm.checkoutBranch(config.baseBranch); updatesVerified = true; } if (branchPr) { const platformPrOptions = getPlatformPrOptions(config); if (commitSha && platformPrOptions.usePlatformAutomerge && platform.reattemptPlatformAutomerge) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would reattempt platform automerge for PR #${branchPr.number}`); else await platform.reattemptPlatformAutomerge({ number: branchPr.number, platformPrOptions }); if (platform.refreshPr) await platform.refreshPr(branchPr.number); } if (!commitSha && !branchExists) return { branchExists, result: "no-work" }; if (commitSha) { const action = branchExists ? "updated" : "created"; logger.info({ commitSha }, `Branch ${action}`); } await setBranchStatusChecks(config); if (!branchPr && !config.artifactErrors?.length && !userRebaseRequested && commitSha && config.prCreation !== "immediate") { logger.debug(`Branch status pending, current sha: ${commitSha}`); return { branchExists: true, updatesVerified, result: "pending", commitSha }; } if (!config.artifactErrors?.length && (!commitSha || config.ignoreTests)) { const mergeStatus = await tryBranchAutomerge(config); logger.debug(`mergeStatus=${mergeStatus}`); if (mergeStatus === "automerged") { if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would delete branch${config.branchName}`); else await deleteBranchSilently(config.branchName); logger.debug("Branch is automerged - returning"); return { branchExists: false, result: "automerged" }; } if (mergeStatus === "off schedule") if (userRebaseRequested) config.forcePr = true; else { logger.debug("Branch cannot automerge now because automergeSchedule is off schedule - skipping"); return { branchExists, result: "not-scheduled", commitSha }; } if (mergeStatus === "stale" && ["conflicted", "never"].includes(config.rebaseWhen) && !(keepUpdatedLabel && branchPr?.labels?.includes(keepUpdatedLabel))) { logger.warn("Branch cannot automerge because it is behind base branch and rebaseWhen setting disallows rebasing - raising a PR instead"); config.forcePr = true; config.branchAutomergeFailureMessage = mergeStatus; } if (mergeStatus === "automerge aborted - PR exists" || mergeStatus === "branch status error" || mergeStatus === "failed") { logger.debug(`Branch automerge not possible, mergeStatus:${mergeStatus}`); config.forcePr = true; config.branchAutomergeFailureMessage = mergeStatus; } } } catch (err) { /* v8 ignore if -- needs test */ if (err.statusCode === 404) { logger.debug({ err }, "Received a 404 error - aborting run"); throw new Error(REPOSITORY_CHANGED); } /* v8 ignore if -- needs test */ if (err.message === "rate-limit-exceeded") { logger.debug("Passing rate-limit-exceeded error up"); throw err; } if (err.message === "repository-changed") { logger.debug("Passing repository-changed error up"); throw err; } /* v8 ignore if -- needs test */ if (err.message?.startsWith("remote: Invalid username or password")) { logger.debug("Throwing bad credentials"); throw new Error(PLATFORM_BAD_CREDENTIALS); } /* v8 ignore if -- needs test */ if (err.message?.startsWith("ssh_exchange_identification: Connection closed by remote host")) { logger.debug("Throwing bad credentials"); throw new Error(PLATFORM_BAD_CREDENTIALS); } /* v8 ignore if -- needs test */ if (err.message === "bad-credentials") { logger.debug("Passing bad-credentials error up"); throw err; } /* v8 ignore if -- needs test */ if (err.message === "integration-unauthorized") { logger.debug("Passing integration-unauthorized error up"); throw err; } if (err.message === "lockfile-error") { logger.debug("Passing lockfile-error up"); throw err; } /* v8 ignore if -- needs test */ if (err.message?.includes("space left on device")) throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE); /* v8 ignore if -- needs test */ if (err.message === "disk-space") { logger.debug("Passing disk-space error up"); throw err; } /* v8 ignore if -- needs test */ if (err.message.startsWith("Resource not accessible by integration")) { logger.debug("Passing 403 error up"); throw err; } /* v8 ignore next -- needs test */ if (err.message === "update-failure") logger.warn("Error updating branch: update failure"); else if (err.message.startsWith("bundler-")) return { branchExists: true, updatesVerified, prNo: branchPr?.number, result: "error", commitSha }; else if (err.message?.includes("fatal: Authentication failed")) throw new Error(PLATFORM_AUTHENTICATION_ERROR); else if (err.message?.includes("fatal: bad revision")) { logger.debug({ err }, "Aborting job due to bad revision error"); throw new Error(REPOSITORY_CHANGED); } else if (err.message === "config-validation") { logger.debug("Passing config validation error up"); throw err; } else if (err.message === "temporary-error") { logger.debug("Passing TEMPORARY_ERROR error up"); throw err; } else if (!(err instanceof ExternalHostError)) logger.warn({ err }, `Error updating branch`); return { branchExists, prNo: branchPr?.number, result: "error", commitSha }; } try { logger.debug("Ensuring PR"); logger.debug(`There are ${config.errors.length} errors and ${config.warnings.length} warnings`); const ensurePrResult = await ensurePr(config); if (ensurePrResult.type === "without-pr") { const { prBlockedBy } = ensurePrResult; branchPr = null; if (prBlockedBy === "RateLimited" && !config.isVulnerabilityAlert) { logger.debug("Reached PR limit - skipping PR creation"); return { branchExists, prBlockedBy, result: "pr-limit-reached", commitSha }; } if (prBlockedBy === "NeedsApproval") return { branchExists, prBlockedBy, result: "needs-pr-approval", commitSha }; if (prBlockedBy === "AwaitingTests") return { branchExists, prBlockedBy, result: "pending", commitSha }; if (prBlockedBy === "BranchAutomerge") return { branchExists, prBlockedBy, result: "done", commitSha }; if (prBlockedBy === "Error") return { branchExists, prBlockedBy, result: "error", commitSha }; logger.warn({ prBlockedBy }, "Unknown PrBlockedBy result"); return { branchExists, prBlockedBy, result: "error", commitSha }; } if (ensurePrResult.type === "with-pr") { const { pr } = ensurePrResult; branchPr = pr; await setBranchStatusChecks(config); if (config.artifactErrors?.length) { logger.warn({ artifactErrors: config.artifactErrors }, "artifactErrors"); let content = `Renovate failed to update `; content += config.artifactErrors.length > 1 ? "artifacts" : "an artifact"; content += " related to this branch. "; content += compile(config.userStrings.artifactErrorWarning, config); content += emojify(`\n\n:recycle: Renovate will retry this branch, including artifacts, only when one of the following happens:\n\n`); content += " - any of the package files in this branch needs updating, or \n"; content += " - the branch becomes conflicted, or\n"; content += " - you click the rebase/retry checkbox if found above, or\n"; content += " - you rename this PR's title to start with \"rebase!\" to trigger it manually"; content += "\n\nThe artifact failure details are included below:\n\n"; config.artifactErrors.forEach((error) => { content += `##### File name: ${error.fileName}\n\n`; content += `\`\`\`\n${error.stderr}\n\`\`\`\n\n`; }); content = platform.massageMarkdown(content, config.rebaseLabel); if (!(config.suppressNotifications.includes("artifactErrors") || config.suppressNotifications.includes("lockFileErrors"))) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would ensure lock file error comment in PR #${pr.number}`); else await ensureComment({ number: pr.number, topic: artifactErrorTopic, content }); } else { if (config.artifactNotices?.length) { const contentLines = []; for (const notice of config.artifactNotices) { contentLines.push(`##### File name: ${notice.file}`); contentLines.push(notice.message); } const content = contentLines.join("\n\n"); await ensureComment({ number: pr.number, topic: artifactNoticeTopic, content }); } if (config.automerge) { logger.debug("PR is configured for automerge"); if (config.ignoreTests === true || !commitSha) { logger.debug("checking auto-merge"); if ((await checkAutoMerge(pr, config))?.automerged) return { branchExists, result: "automerged", commitSha }; } } else logger.debug("PR is not configured for automerge"); } } } catch (err) { /* v8 ignore if -- needs test */ if (err instanceof ExternalHostError || ["rate-limit-exceeded", "repository-changed"].includes(err.message)) { logger.debug("Passing PR error up"); throw err; } logger.error({ err }, `Error ensuring PR`); } if (!branchExists) return { branchExists: true, updatesVerified, prNo: branchPr?.number, result: "pr-created", commitSha }; return { branchExists, updatesVerified, prNo: branchPr?.number, result: "done", commitSha }; } //#endregion export { processBranch }; //# sourceMappingURL=index.js.map