renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
188 lines (187 loc) • 6.61 kB
JavaScript
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