in java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerApprovalCheck.java [573:718]
private PathCodeOwnerStatus getPathCodeOwnerStatus(
CodeOwnerConfigHierarchy codeOwnerConfigHierarchy,
CodeOwnerResolver codeOwnerResolver,
BranchNameKey branch,
@Nullable ObjectId revision,
CodeOwnerResolverResult globalCodeOwners,
@Nullable Account.Id implicitApprover,
ImmutableSet<Account.Id> reviewerAccountIds,
ImmutableSet<Account.Id> approverAccountIds,
FallbackCodeOwners fallbackCodeOwners,
ImmutableSet<PatchSetApproval> overrides,
Path absolutePath) {
logger.atFine().log("computing path status for %s", absolutePath);
if (!overrides.isEmpty()) {
logger.atFine().log(
"the status for path %s is %s since an override is present (overrides = %s)",
absolutePath, CodeOwnerStatus.APPROVED.name(), overrides);
Optional<PatchSetApproval> override = overrides.stream().findAny();
checkState(override.isPresent(), "no override found");
return PathCodeOwnerStatus.create(
absolutePath,
CodeOwnerStatus.APPROVED,
String.format(
"override approval %s by %s is present",
override.get().label() + LabelValue.formatValue(override.get().value()),
AccountTemplateUtil.getAccountTemplate(override.get().accountId())));
}
AtomicReference<CodeOwnerStatus> codeOwnerStatus =
new AtomicReference<>(CodeOwnerStatus.INSUFFICIENT_REVIEWERS);
AtomicReference<String> reason = new AtomicReference<>(/* initialValue= */ null);
if (isApproved(
globalCodeOwners,
CodeOwnerKind.GLOBAL_CODE_OWNER,
approverAccountIds,
implicitApprover,
reason)) {
codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
} else {
logger.atFine().log("%s was not approved by a global code owner", absolutePath);
if (isPending(
globalCodeOwners, CodeOwnerKind.GLOBAL_CODE_OWNER, reviewerAccountIds, reason)) {
codeOwnerStatus.set(CodeOwnerStatus.PENDING);
}
AtomicBoolean hasRevelantCodeOwnerDefinitions = new AtomicBoolean(false);
AtomicBoolean parentCodeOwnersAreIgnored = new AtomicBoolean(false);
codeOwnerConfigHierarchy.visitForFile(
branch,
revision,
absolutePath,
(PathCodeOwnersVisitor)
pathCodeOwners -> {
CodeOwnerKind codeOwnerKind =
RefNames.REFS_CONFIG.equals(pathCodeOwners.getCodeOwnerConfig().key().ref())
? CodeOwnerKind.DEFAULT_CODE_OWNER
: CodeOwnerKind.REGULAR_CODE_OWNER;
CodeOwnerResolverResult codeOwners =
resolveCodeOwners(codeOwnerResolver, pathCodeOwners);
logger.atFine().log(
"code owners = %s (code owner kind = %s, code owner config folder path = %s,"
+ " file name = %s)",
codeOwners,
codeOwnerKind,
pathCodeOwners.getCodeOwnerConfig().key().folderPath(),
pathCodeOwners.getCodeOwnerConfig().key().fileName().orElse("<default>"));
if (codeOwners.hasRevelantCodeOwnerDefinitions()) {
hasRevelantCodeOwnerDefinitions.set(true);
}
if (isApproved(
codeOwners, codeOwnerKind, approverAccountIds, implicitApprover, reason)) {
codeOwnerStatus.set(CodeOwnerStatus.APPROVED);
return false;
} else if (isPending(codeOwners, codeOwnerKind, reviewerAccountIds, reason)) {
codeOwnerStatus.set(CodeOwnerStatus.PENDING);
// We need to continue to check if any of the higher-level code owners approved
// the change.
return true;
}
// We need to continue to check if any of the higher-level code owners approved the
// change or is a reviewer.
return true;
},
codeOwnerConfigKey -> {
logger.atFine().log(
"code owner config %s ignores parent code owners for %s",
codeOwnerConfigKey, absolutePath);
parentCodeOwnersAreIgnored.set(true);
});
// If no code owners have been defined for the file and if parent code owners are not ignored,
// the fallback code owners apply if they are configured. We can skip checking them if we
// already found that the file was approved.
if (codeOwnerStatus.get() != CodeOwnerStatus.APPROVED
&& !hasRevelantCodeOwnerDefinitions.get()
&& !parentCodeOwnersAreIgnored.get()) {
CodeOwnerStatus codeOwnerStatusForFallbackCodeOwners =
getCodeOwnerStatusForFallbackCodeOwners(
codeOwnerStatus.get(),
implicitApprover,
reviewerAccountIds,
approverAccountIds,
fallbackCodeOwners,
absolutePath,
reason);
// Merge codeOwnerStatusForFallbackCodeOwners into codeOwnerStatus:
// * codeOwnerStatus is the code owner status without taking fallback code owners into
// account
// * codeOwnerStatusForFallbackCodeOwners is the code owner status for fallback code owners
// only
// When merging both the "better" code owner status should take precedence (APPROVED is
// better than PENDING which is better than INSUFFICIENT_REVIEWERS):
// * if codeOwnerStatus == APPROVED we do not compute the code owner status for the fallback
// code owners and never reach this point. Hence we can ignore this case below.
// * if codeOwnerStatus == PENDING (e.g. because a global code owner is a reviewer) we must
// override it if codeOwnerStatusForFallbackCodeOwners is APPROVED
// * if codeOwnerStatus == INSUFFICIENT_REVIEWERS we must override it if
// codeOwnerStatusForFallbackCodeOwners is PENDING or APPROVED
// This means if codeOwnerStatusForFallbackCodeOwners is INSUFFICIENT_REVIEWERS it is never
// "better" than codeOwnerStatus, hence in this case we do not override codeOwnerStatus.
// On the other hand if codeOwnerStatusForFallbackCodeOwners is PENDING or APPROVED (aka not
// INSUFFICIENT_REVIEWERS) it is always as good or "better" than codeOwnerStatus (which can
// only be INSUFFICIENT_REVIEWERS or PENDING at this point), hence in this case we can/must
// override codeOwnerStatus.
if (!codeOwnerStatusForFallbackCodeOwners.equals(CodeOwnerStatus.INSUFFICIENT_REVIEWERS)) {
codeOwnerStatus.set(codeOwnerStatusForFallbackCodeOwners);
}
}
}
logger.atFine().log(
"%s has code owner status %s (reason = %s)",
absolutePath, codeOwnerStatus.get(), reason.get() != null ? reason.get() : "n/a");
PathCodeOwnerStatus pathCodeOwnerStatus =
PathCodeOwnerStatus.create(absolutePath, codeOwnerStatus.get(), reason.get());
logger.atFine().log("pathCodeOwnerStatus = %s", pathCodeOwnerStatus);
return pathCodeOwnerStatus;
}