CodeOwnerApprovalCheck: Factor out some methods for better readability
This made it more obvious that we actually forgot to check if any of the
global code owners is a reviewer (see newly added TODOs, they will be
addressed in a follow-up change).
Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: Id19090715227b4fbfc8e83c7b6e86a0cb836b0ac
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
index 5ae315b..e5a2a39 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java
@@ -345,42 +345,15 @@
logger.atFine().log("computing path status for %s (bootstrapping mode)", absolutePath);
CodeOwnerStatus codeOwnerStatus = CodeOwnerStatus.INSUFFICIENT_REVIEWERS;
-
- if (enableImplicitApprovalFromUploader && isProjectOwner(branch.project(), patchSetUploader)) {
- // The uploader of the patch set is a project owner and thus a code owner. This means there
- // is an implicit code owner approval from the patch set uploader so that the path is
- // automatically approved.
- logger.atFine().log(
- "the status for path %s is %s since the patch set uploader is a project owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
+ if (isApprovedBootstrappingMode(
+ branch.project(),
+ absolutePath,
+ globalCodeOwnerAccountIds,
+ approverAccountIds,
+ enableImplicitApprovalFromUploader,
+ patchSetUploader)) {
codeOwnerStatus = CodeOwnerStatus.APPROVED;
- } else if (enableImplicitApprovalFromUploader
- && globalCodeOwnerAccountIds.contains(patchSetUploader)) {
- // If the uploader of the patch set is a global code owner, there is an implicit code owner
- // approval from the patch set uploader so that the path is automatically approved.
- logger.atFine().log(
- "the status for path %s is %s since the patch set uploader is a global code owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
- codeOwnerStatus = CodeOwnerStatus.APPROVED;
- } else if (!Collections.disjoint(approverAccountIds, globalCodeOwnerAccountIds)) {
- // At least one of the global code owners approved the change.
- logger.atFine().log(
- "the status for path %s is %s since at least one global code owner approved it",
- absolutePath, CodeOwnerStatus.APPROVED.name());
- codeOwnerStatus = CodeOwnerStatus.APPROVED;
- } else if (approverAccountIds.stream()
- .anyMatch(approverAccountId -> isProjectOwner(branch.project(), approverAccountId))) {
- // At least one of the approvers is a project owner and thus a code owner.
- logger.atFine().log(
- "the status for path %s is %s since at least one approvers is a project owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
- codeOwnerStatus = CodeOwnerStatus.APPROVED;
- } else if (reviewerAccountIds.stream()
- .anyMatch(reviewerAccountId -> isProjectOwner(branch.project(), reviewerAccountId))) {
- // At least one of the reviewers is a project owner and thus a code owner.
- logger.atFine().log(
- "the status for path %s is %s since at least one reviewer is a project owner",
- absolutePath, CodeOwnerStatus.PENDING.name());
+ } else if (isPendingBootstrappingMode(branch.project(), absolutePath, reviewerAccountIds)) {
codeOwnerStatus = CodeOwnerStatus.PENDING;
}
@@ -390,6 +363,76 @@
return pathCodeOwnerStatus;
}
+ private boolean isApprovedBootstrappingMode(
+ Project.NameKey projectName,
+ Path absolutePath,
+ ImmutableSet<Account.Id> globalCodeOwnerAccountIds,
+ ImmutableSet<Account.Id> approverAccountIds,
+ boolean enableImplicitApprovalFromUploader,
+ Account.Id patchSetUploader) {
+ return (enableImplicitApprovalFromUploader
+ && isImplicitlyApprovedBootstrappingMode(
+ projectName, absolutePath, globalCodeOwnerAccountIds, patchSetUploader))
+ || isExplicitlyApprovedBootstrappingMode(
+ projectName, absolutePath, globalCodeOwnerAccountIds, approverAccountIds);
+ }
+
+ private boolean isImplicitlyApprovedBootstrappingMode(
+ Project.NameKey projectName,
+ Path absolutePath,
+ ImmutableSet<Account.Id> globalCodeOwnerAccountIds,
+ Account.Id patchSetUploader) {
+ if (isProjectOwner(projectName, patchSetUploader)) {
+ // The uploader of the patch set is a project owner and thus a code owner. This means there
+ // is an implicit code owner approval from the patch set uploader so that the path is
+ // automatically approved.
+ logger.atFine().log(
+ "%s was implicitly approved by the patch set uploader who is a project owner",
+ absolutePath);
+ return true;
+ } else if (globalCodeOwnerAccountIds.contains(patchSetUploader)) {
+ // If the uploader of the patch set is a global code owner, there is an implicit code owner
+ // approval from the patch set uploader so that the path is automatically approved.
+ logger.atFine().log(
+ "%s was implicitly approved by the patch set uploader who is a global owner",
+ absolutePath);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isExplicitlyApprovedBootstrappingMode(
+ Project.NameKey projectName,
+ Path absolutePath,
+ ImmutableSet<Account.Id> globalCodeOwnerAccountIds,
+ ImmutableSet<Account.Id> approverAccountIds) {
+ if (!Collections.disjoint(approverAccountIds, globalCodeOwnerAccountIds)) {
+ // At least one of the global code owners approved the change.
+ logger.atFine().log("%s was approved by a global code owner", absolutePath);
+ return true;
+ } else if (approverAccountIds.stream()
+ .anyMatch(approverAccountId -> isProjectOwner(projectName, approverAccountId))) {
+ // At least one of the approvers is a project owner and thus a code owner.
+ logger.atFine().log("%s was approved by a project owner", absolutePath);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isPendingBootstrappingMode(
+ Project.NameKey projectName, Path absolutePath, ImmutableSet<Account.Id> reviewerAccountIds) {
+ if (reviewerAccountIds.stream()
+ .anyMatch(reviewerAccountId -> isProjectOwner(projectName, reviewerAccountId))) {
+ // At least one of the reviewers is a project owner and thus a code owner.
+ logger.atFine().log("%s is owned by a reviewer who is project owner", absolutePath);
+ return true;
+ }
+
+ // TODO: check if a global code owner is a reviewer
+
+ return false;
+ }
+
/**
* Gets the code owner status for the given path when the branch contains at least one code owner
* config file (regular mode).
@@ -408,23 +451,19 @@
AtomicReference<CodeOwnerStatus> codeOwnerStatus =
new AtomicReference<>(CodeOwnerStatus.INSUFFICIENT_REVIEWERS);
- if (enableImplicitApprovalFromUploader
- && globalCodeOwnerAccountIds.contains(patchSetUploader)) {
- // If the uploader of the patch set owns the path, there is an implicit code owner
- // approval from the patch set uploader so that the path is automatically approved.
- logger.atFine().log(
- "the status for path %s is %s since the patch set uploader is a global code owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
+ if (isApproved(
+ absolutePath,
+ globalCodeOwnerAccountIds,
+ approverAccountIds,
+ enableImplicitApprovalFromUploader,
+ patchSetUploader)) {
+ logger.atFine().log("%s was approved by a global code owner", absolutePath);
codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
- } else if (!Collections.disjoint(approverAccountIds, globalCodeOwnerAccountIds)) {
- // At least one of the global code owners approved the change.
- logger.atFine().log(
- "the status for path %s is %s since at least one global code owner approved it",
- absolutePath, CodeOwnerStatus.APPROVED.name());
- codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
- }
+ } else {
+ logger.atFine().log("%s was not approved by a global code owner", absolutePath);
- if (!CodeOwnerStatus.APPROVED.equals(codeOwnerStatus.get())) {
+ // TODO check if a global code owner is a reviewer
+
codeOwnerConfigHierarchy.visit(
branch,
revision,
@@ -438,49 +477,25 @@
codeOwnerConfig.key().folderPath(),
codeOwnerConfig.key().fileName().orElse("<default>"));
- if (enableImplicitApprovalFromUploader
- && codeOwnerAccountIds.contains(patchSetUploader)) {
- // If the uploader of the patch set owns the path, there is an implicit code owner
- // approval from the patch set uploader so that the path is automatically approved.
- logger.atFine().log(
- "the status for path %s is %s since the patch set uploader is a code owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
+ if (isApproved(
+ absolutePath,
+ codeOwnerAccountIds,
+ approverAccountIds,
+ enableImplicitApprovalFromUploader,
+ patchSetUploader)) {
codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
-
- // We can abort since we already found that the path was approved.
return false;
- }
-
- if (Collections.disjoint(codeOwnerAccountIds, reviewerAccountIds)) {
- // We need to continue to check if any of the higher-level code owners is a reviewer.
- logger.atFine().log(
- "None of the code owners is a reviewer, continue checking higher-level code owner configs");
- return true;
- }
-
- logger.atFine().log(
- "One of the code owners is a reviewer, the path status is at least %s",
- CodeOwnerStatus.PENDING.name());
-
- if (Collections.disjoint(codeOwnerAccountIds, approverAccountIds)) {
- // At least one of the code owners is a reviewer on the change.
- logger.atFine().log(
- "None of the code owners is an approver, continue checking higher-level code owner configs");
+ } else if (isPending(absolutePath, codeOwnerAccountIds, reviewerAccountIds)) {
codeOwnerStatus.set(CodeOwnerStatus.PENDING);
- // We need to continue to check if any of the higher-level code owners has approved
- // the change.
+ // We need to continue to check if any of the higher-level code owners approved the
+ // change.
return true;
}
- // At least one of the code owners approved the change.
- logger.atFine().log(
- "the status for path %s is %s since one of the approvers is a code owner",
- absolutePath, CodeOwnerStatus.APPROVED.name());
- codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
-
- // We can abort since we already found that the path was approved.
- return false;
+ // We need to continue to check if any of the higher-level code owners approved the
+ // change or is a reviewer.
+ return true;
});
}
@@ -490,6 +505,37 @@
return pathCodeOwnerStatus;
}
+ private boolean isApproved(
+ Path absolutePath,
+ ImmutableSet<Account.Id> codeOwnerAccountIds,
+ ImmutableSet<Account.Id> approverAccountIds,
+ boolean enableImplicitApprovalFromUploader,
+ Account.Id patchSetUploader) {
+ if (enableImplicitApprovalFromUploader && codeOwnerAccountIds.contains(patchSetUploader)) {
+ // If the uploader of the patch set owns the path, there is an implicit code owner
+ // approval from the patch set uploader so that the path is automatically approved.
+ logger.atFine().log("%s was implicitly approved by the patch set uploader", absolutePath);
+ return true;
+ } else if (!Collections.disjoint(approverAccountIds, codeOwnerAccountIds)) {
+ // At least one of the global code owners approved the change.
+ logger.atFine().log("%s was explicitly approved by a code owner", absolutePath);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isPending(
+ Path absolutePath,
+ ImmutableSet<Account.Id> codeOwnerAccountIds,
+ ImmutableSet<Account.Id> reviewerAccountIds) {
+ if (!Collections.disjoint(codeOwnerAccountIds, reviewerAccountIds)) {
+ logger.atFine().log("%s is owned by a reviewer", absolutePath);
+ return true;
+ }
+
+ return false;
+ }
+
/** Whether the given account is a project owner of the given project. */
private boolean isProjectOwner(Project.NameKey project, Account.Id accountId) {
try {