UNPKG

renovate

Version:

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

188 lines (187 loc) • 6.61 kB
import { logger } from "../../../logger/index.js"; import { hash } from "../../../util/hash.js"; import { branchExists, deleteBranch, fetchRevSpec, getBranchCommit, getBranchUpdateDate, hasDiff, mergeToLocal, prepareCommit, pushCommit } from "../../../util/git/index.js"; import { DefaultGitScm } from "../default-scm.js"; import { convertGerritDateToISO } from "./utils.js"; import { client } from "./client.js"; import { isNonEmptyArray } from "@sindresorhus/is"; import { randomUUID } from "node:crypto"; import { DateTime } from "luxon"; //#region lib/modules/platform/gerrit/scm.ts let repository; let username; function configureScm(repo, login) { repository = repo; username = login; } /** Branches with a local commit but no Gerrit change yet (push deferred to createPr()). */ const pendingChangeBranches = /* @__PURE__ */ new Set(); async function pushForReview(options) { const pushOptions = ["notify=NONE", "ready"]; if (options.autoApprove) pushOptions.push("label=Code-Review+2"); if (isNonEmptyArray(options.labels)) for (const label of options.labels) pushOptions.push(`hashtag=${label}`); const result = await pushCommit({ sourceRef: options.sourceRef, targetRef: `refs/for/${options.targetBranch}`, files: options.files, pushOptions }); if (result) pendingChangeBranches.delete(options.sourceRef); return result; } var GerritScm = class extends DefaultGitScm { async branchExists(branchName) { const searchConfig = { state: "open", branchName, singleChange: true }; if ((await client.findChanges(repository, searchConfig)).pop()) return true; return branchExists(branchName); } async getBranchCommit(branchName) { const searchConfig = { state: "open", branchName, singleChange: true, requestDetails: ["CURRENT_REVISION"] }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) return change.current_revision; return getBranchCommit(branchName); } async getBranchUpdateDate(branchName) { const searchConfig = { state: "open", branchName, singleChange: true, refreshCache: true, requestDetails: ["CURRENT_REVISION"] }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) { const date = convertGerritDateToISO(change.revisions[change.current_revision].created); return DateTime.fromISO(date).toUTC(); } return getBranchUpdateDate(branchName); } async isBranchBehindBase(branchName, baseBranch) { const searchConfig = { state: "open", branchName, targetBranch: baseBranch, singleChange: true, requestDetails: ["CURRENT_REVISION", "CURRENT_ACTIONS"] }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) return change.revisions[change.current_revision].actions.rebase.enabled === true; return true; } async isBranchConflicted(baseBranch, branch) { const searchConfig = { state: "open", branchName: branch, targetBranch: baseBranch, singleChange: true }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) return !(await client.getMergeableInfo(change)).mergeable; else { logger.warn({ branch, baseBranch }, "There is no open change with this branch"); return true; } } async isBranchModified(branchName, baseBranch) { const searchConfig = { state: "open", branchName, targetBranch: baseBranch, singleChange: true, requestDetails: ["CURRENT_REVISION", "DETAILED_ACCOUNTS"] }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) return change.revisions[change.current_revision].uploader.username !== username; return false; } async commitAndPush(commit) { logger.debug(`commitAndPush(${commit.branchName})`); const existingChange = await client.getBranchChange(repository, { branchName: commit.branchName, state: "open", targetBranch: commit.baseBranch, requestDetails: ["CURRENT_REVISION"] }); let hasChanges = true; const message = typeof commit.message === "string" ? [commit.message] : commit.message; // v8 ignore else -- TODO: add test #40625 if (commit.prTitle) { const firstMessageLines = message[0].split("\n"); firstMessageLines[0] = commit.prTitle; message[0] = firstMessageLines.join("\n"); } const changeId = existingChange?.change_id ?? generateChangeId(); commit.message = [...message, `Renovate-Branch: ${commit.branchName}\nChange-Id: ${changeId}`]; const commitResult = await prepareCommit({ ...commit, force: true }); if (commitResult) { const { commitSha } = commitResult; if (existingChange) { const fetchRefSpec = existingChange.revisions[existingChange.current_revision].ref; await fetchRevSpec(fetchRefSpec); hasChanges = await hasDiff("HEAD", "FETCH_HEAD"); if (hasChanges || commit.force) { /* v8 ignore else -- should never happen */ if (await pushForReview({ sourceRef: commit.branchName, targetBranch: existingChange.branch, files: commit.files, autoApprove: commit.autoApprove })) return commitSha; } } else { logger.debug(`Commit prepared, push deferred to createPr()`); pendingChangeBranches.add(commit.branchName); return commitSha; } } return null; } async deleteBranch(branchName) { pendingChangeBranches.delete(branchName); await deleteBranch(branchName, { localBranch: true }); } async mergeToLocal(branchName) { if (pendingChangeBranches.has(branchName)) { logger.debug(`Merging local branch ${branchName} (not yet pushed)`); return mergeToLocal(branchName, { localBranch: true }); } const searchConfig = { state: "open", branchName, singleChange: true, requestDetails: ["CURRENT_REVISION"] }; const change = (await client.findChanges(repository, searchConfig)).pop(); if (change) { const currentRevision = change.revisions[change.current_revision]; return super.mergeToLocal(currentRevision.ref); } return super.mergeToLocal(branchName); } }; /** * This function should generate a Gerrit Change-ID analogous to the commit hook. We avoid the commit hook cause of security concerns. * random=$( (whoami ; hostname ; date; cat $1 ; echo $RANDOM) | git hash-object --stdin) prefixed with an 'I'. * TODO: Gerrit don't accept longer Change-IDs (sha256), but what happens with this https://git-scm.com/docs/hash-function-transition/ ? */ function generateChangeId() { return `I${hash(randomUUID(), "sha1")}`; } //#endregion export { GerritScm, configureScm, pushForReview }; //# sourceMappingURL=scm.js.map