in java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java [215:336]
public OptionalResultWithMessages<PathCodeOwnersResult> resolveCodeOwnerConfig() {
if (this.pathCodeOwnersResult != null) {
return this.pathCodeOwnersResult;
}
try (Timer0.Context ctx = codeOwnerMetrics.resolveCodeOwnerConfig.start()) {
logger.atFine().log(
"resolve code owners for %s from code owner config %s", path, codeOwnerConfig.key());
List<String> messages = new ArrayList<>();
messages.add(
String.format(
"resolve code owners for %s from code owner config %s", path, codeOwnerConfig.key()));
// Create a code owner config builder to create the resolved code owner config (= code owner
// config that is scoped to the path and which has imports resolved)
CodeOwnerConfig.Builder resolvedCodeOwnerConfigBuilder =
CodeOwnerConfig.builder(codeOwnerConfig.key(), codeOwnerConfig.revision());
// Add all data from the original code owner config that is relevant for the path
// (ignoreParentCodeOwners flag, global code owner sets and matching per-file code owner
// sets). Effectively this means we are dropping all non-matching per-file rules.
resolvedCodeOwnerConfigBuilder.setIgnoreParentCodeOwners(
codeOwnerConfig.ignoreParentCodeOwners());
getGlobalCodeOwnerSets(codeOwnerConfig)
.forEach(resolvedCodeOwnerConfigBuilder::addCodeOwnerSet);
boolean globalCodeOwnersIgnored = false;
for (CodeOwnerSet codeOwnerSet :
getMatchingPerFileCodeOwnerSets(codeOwnerConfig).collect(toImmutableSet())) {
messages.add(
String.format(
"per-file code owner set with path expressions %s matches",
codeOwnerSet.pathExpressions()));
resolvedCodeOwnerConfigBuilder.addCodeOwnerSet(codeOwnerSet);
if (codeOwnerSet.ignoreGlobalAndParentCodeOwners()) {
globalCodeOwnersIgnored = true;
}
}
// Resolve global imports.
ImmutableList.Builder<UnresolvedImport> unresolvedImports = ImmutableList.builder();
ImmutableSet<CodeOwnerConfigImport> globalImports = getGlobalImports(0, codeOwnerConfig);
OptionalResultWithMessages<List<UnresolvedImport>> unresolvedGlobalImports;
if (!globalCodeOwnersIgnored) {
unresolvedGlobalImports =
resolveImports(codeOwnerConfig.key(), globalImports, resolvedCodeOwnerConfigBuilder);
} else {
// skip global import with mode GLOBAL_CODE_OWNER_SETS_ONLY,
// since we already know that global code owners will be ignored, we do not need to resolve
// these imports
unresolvedGlobalImports =
resolveImports(
codeOwnerConfig.key(),
globalImports.stream()
.filter(
codeOwnerConfigImport ->
codeOwnerConfigImport.referenceToImportedCodeOwnerConfig().importMode()
!= CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY)
.collect(toImmutableSet()),
resolvedCodeOwnerConfigBuilder);
}
messages.addAll(unresolvedGlobalImports.messages());
unresolvedImports.addAll(unresolvedGlobalImports.get());
// Remove all global code owner sets if any per-file code owner set has the
// ignoreGlobalAndParentCodeOwners flag set to true (as in this case they are ignored and
// hence not relevant).
// In this case also set ignoreParentCodeOwners to true, so that we do not need to inspect the
// ignoreGlobalAndParentCodeOwners flags on per-file code owner sets again, but can just rely
// on the global ignoreParentCodeOwners flag.
Optional<CodeOwnerSet> matchingPerFileCodeOwnerSetThatIgnoresGlobalAndParentCodeOwners =
getMatchingPerFileCodeOwnerSets(resolvedCodeOwnerConfigBuilder.build())
.filter(CodeOwnerSet::ignoreGlobalAndParentCodeOwners)
.findAny();
if (matchingPerFileCodeOwnerSetThatIgnoresGlobalAndParentCodeOwners.isPresent()) {
logger.atFine().log("remove folder code owner sets and set ignoreParentCodeOwners to true");
messages.add(
String.format(
"found matching per-file code owner set (with path expressions = %s) that ignores"
+ " parent code owners, hence ignoring the folder code owners",
matchingPerFileCodeOwnerSetThatIgnoresGlobalAndParentCodeOwners
.get()
.pathExpressions()));
// We use resolvedCodeOwnerConfigBuilder to build up a code owner config that is scoped to
// the path and which has imports resolved. When resolving imports the relevant code owner
// sets from the imported code owner configs are added to the builder.
// If a per-file rule ignores global and parent code owners we have to drop all global code
// owner sets. The problem is that AutoValue doesn't allow us to remove/override code owner
// sets that have previously been added to the builder (we cannot call setCodeOwnerSets(...)
// after addCodeOwnerSet(...) or codeOwnerSetsBuilder() has been invoked). To override the
// code owner sets we build the code owner config and then create a fresh builder from it.
// Since the builder is fresh addCodeOwnerSet(...) and codeOwnerSetsBuilder() haven't been
// invoked on it yet we can now call setCodeOwnerSets(...).
resolvedCodeOwnerConfigBuilder =
resolvedCodeOwnerConfigBuilder
.build()
.toBuilder()
.setIgnoreParentCodeOwners()
.setCodeOwnerSets(
resolvedCodeOwnerConfigBuilder.codeOwnerSets().stream()
.filter(codeOwnerSet -> !codeOwnerSet.pathExpressions().isEmpty())
.collect(toImmutableSet()));
}
// Resolve per-file imports.
ImmutableSet<CodeOwnerConfigImport> perFileImports =
getPerFileImports(
0, codeOwnerConfig.key(), resolvedCodeOwnerConfigBuilder.codeOwnerSets());
OptionalResultWithMessages<List<UnresolvedImport>> unresolvedPerFileImports =
resolveImports(codeOwnerConfig.key(), perFileImports, resolvedCodeOwnerConfigBuilder);
messages.addAll(unresolvedPerFileImports.messages());
unresolvedImports.addAll(unresolvedPerFileImports.get());
this.pathCodeOwnersResult =
OptionalResultWithMessages.create(
PathCodeOwnersResult.create(
path, resolvedCodeOwnerConfigBuilder.build(), unresolvedImports.build()),
messages);
logger.atFine().log("path code owners result = %s", pathCodeOwnersResult);
return this.pathCodeOwnersResult;
}
}