blob: c3574884dfaf5eb2dbdabb0d835029eb97b43d97 [file] [log] [blame]
/**
* @license
* Copyright 2022 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
import {
ApprovalInfo,
ChangeInfo,
DetailedLabelInfo,
LabelInfo,
isDetailedLabelInfo,
isQuickLabelInfo,
} from '@gerritcodereview/typescript-api/rest-api';
import {PluginApi} from '@gerritcodereview/typescript-api/plugin';
// Common constants and helper functions used by multiple behaviors below.
export const REVIEW_LABEL = 'Code-Review';
export const AS_LABEL = 'Auto-Submit';
export const CQ_LABEL = 'Commit-Queue';
export const STATUS_SUBMITTED = 'MERGED';
/**
* Returns the name of the Gerrit host.
*
* @param host - a URL
* @returns Gerrit host
*/
export function getGerritHost(host: string): string {
const match =
/^(?:canary-)?(.+)-review\.(?:googlesource|git\.corp\.google)\.com/.exec(
host
);
return (match && match[1]) ?? '';
}
/**
* Returns whether the specified vote is permitted on the label.
*
* @param change - gr-change-view
* @param labelName - The name of the label we want to check
* @param vote - The vote we will check (e.g. '+2')
* @returns
*/
export function isLabelVotePermitted(
change: ChangeInfo,
labelName: string,
vote: string
) {
const permitted_labels = change.permitted_labels;
if (!permitted_labels) {
return false;
}
const permitted = permitted_labels[labelName];
return permitted && permitted.indexOf(vote) !== -1;
}
/**
* Returns the max value of the specified label. Returns 0 if the label
* cannot be found or if it has no values.
*
* @param change - gr-change-view
* @param labelName - The name of the label we want to check
* @returns
*/
export function getLabelMaxValue(change: ChangeInfo, labelName: string) {
const label = getChangeLabel(change, labelName);
if (!label || !isDetailedLabelInfo(label) || !label.values) {
return 0;
}
const votes = Object.keys(label.values).map((v: string) =>
// convert string to int
Number(v)
);
return Math.max(...votes);
}
/**
* Parses git trailers (and Rietveld tags) from a commit description.
*
* @param message - the commit message to parse
* @param token - the LHS of the trailer to parse (e.g. 'Bug')
* @returns all found trailers in normalized form (e.g. 'Bug: 1')
*/
export function getTrailer(message: string, token: string): string {
// TODO(http://crbug.com/753425): Remove support for CAPS= style tags.
const caps = token.replace(/-/g, '_').toUpperCase();
const trailerRegexp = new RegExp(
`^[ \t]*((${token}:|${caps}=)[ \t]*(.*))$`,
'gm'
);
const response = new Set();
for (const match of message.matchAll(trailerRegexp)) {
response.add(`${token}: ${match[3]}\n`);
}
return Array.from(response).join('');
}
/**
* Returns the specified label from the change if it exists else {} is
* returned.
*
* @param change - gr-change-view
* @param labelName - The name of the label we want
*/
export function getChangeLabel(
change: ChangeInfo,
labelName: string
): LabelInfo | null {
if (change.labels) {
return change.labels[labelName];
}
return {};
}
export function getApprovalsForLabel(
change: ChangeInfo,
labelName: string
): ApprovalInfo[] {
const label = getChangeLabel(change, labelName);
if (!label) {
return [];
}
return (label as DetailedLabelInfo).all || [];
}
export function isLabelApproved(change: ChangeInfo, labelName: string) {
const label = getChangeLabel(change, labelName);
return label && isQuickLabelInfo(label) ? label.approved : false;
}
/**
* Calls the server to get an updated change obj and returns its promise.
*/
export function getChangeObj(
plugin: PluginApi,
change: ChangeInfo
): Promise<ChangeInfo> {
return plugin.restApi().get(`/changes/${change._number}/detail`);
}
/**
* Calls the server to get list of of all draft comments that below to the
* calling user.
*/
export function getChangeDrafts(plugin: PluginApi, change: ChangeInfo) {
return plugin.restApi().get(`/changes/${change._number}/drafts`);
}
/**
* Add email-style quoting (> ) in front of the original commit message.
*/
export function quoteOriginalMessage(revertMsg: string, originalMsg: string) {
// Show reverts-of-reverts as relands instead.
let newMessage = revertMsg.replace(/^Revert "Revert "(.*)""/, 'Reland "$1"');
newMessage += "\nOriginal change's description:\n";
// Add email-style quoting ('> ') in front of the original commit text. Empty
// lines might end up with '> ' so change them into '>'.
newMessage += originalMsg.trim().replace(/^/gm, '> ').replace(/^> $/gm, '>');
return newMessage;
}