Allow to disable the validation of code owner config files per branch Being able to skip the validation on some branches is useful for branches that are fully operated by bots. Bots cannot fix code owner config files and any issue that blocks them from uploading/submitting is more severe than an invalid code owner config file. So it's better to allow invalid code owner config files and fix them at a later point in time. Whether code owner config files are validated on commit received and submit can be controlled on project-level by setting `codeOwners.enableValidationOnCommitReceived` and `codeOwners.enableValidationOnSubmit` in code-owners.config. With this change we now add a possibility to override this configuration on branch-level by setting `validation.<branch>.enableValidationOnCommitReceived` and `validation.<branch>.enableValidationOnSubmit` in code-owners.config. As all configuration parameters the new configuration parameters are inherited from parent projects if not set. An inherited branch-level configuration also overrides a local project-level configuration. This allows to exempt branches globally from code owner config validations. '<branch>' can be an exact ref, a ref pattern (e.g. "refs/heads/*") or a regular expression (e.g. "^refs/heads/.*"). If a branch is matched by multiple validation subsections it is undefined which subsection takes precedence. This is because Config#getSubsections returns a Set which doesn't preserve the order in which subsections appear in the config file. In contrast to other configuration parameters branch-level validation parameters can only be set in code-owners.config, but not in gerrit.config (because as plugin we cannot have subsections in gerrit.config). This means to exempt branches globally the configuration needs to be done in the code-owners.config of the All-Projects project. Signed-off-by: Edwin Kempin <ekempin@google.com> Change-Id: Ifaa955d56fa499c440ee338ef4c0f40b495a2542
diff --git a/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java b/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java index a06525d..c85c506 100644 --- a/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java +++ b/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java
@@ -168,7 +168,7 @@ CodeOwnersPluginConfiguration.SECTION_CODE_OWNERS, subsection, key, values)); } - private void updateCodeOwnersConfig(Project.NameKey project, Consumer<Config> configUpdater) + protected void updateCodeOwnersConfig(Project.NameKey project, Consumer<Config> configUpdater) throws Exception { Config codeOwnersConfig = new Config(); configUpdater.accept(codeOwnersConfig);
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshot.java b/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshot.java index 72747a6..4595fff 100644 --- a/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshot.java +++ b/java/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshot.java
@@ -131,14 +131,44 @@ return generalConfig.getRejectNonResolvableImports(projectName, pluginConfig); } - /** Whether code owner configs should be validated when a commit is received. */ - public CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForCommitReceived() { + /** + * Whether code owner configs should be validated when a commit is received. + * + * @param branchName the branch for which it should be checked whether code owner configs should + * be validated on commit received + */ + public CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForCommitReceived( + String branchName) { + requireNonNull(branchName, "branchName"); + + Optional<CodeOwnerConfigValidationPolicy> branchSpecificPolicy = + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(projectName, branchName), pluginConfig); + if (branchSpecificPolicy.isPresent()) { + return branchSpecificPolicy.get(); + } + return generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceived( projectName, pluginConfig); } - /** Whether code owner configs should be validated when a change is submitted. */ - public CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForSubmit() { + /** + * Whether code owner configs should be validated when a change is submitted. + * + * @param branchName the branch for which it should be checked whether code owner configs should + * be validated on submit + */ + public CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForSubmit( + String branchName) { + requireNonNull(branchName, "branchName"); + + Optional<CodeOwnerConfigValidationPolicy> branchSpecificPolicy = + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(projectName, branchName), pluginConfig); + if (branchSpecificPolicy.isPresent()) { + return branchSpecificPolicy.get(); + } + return generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(projectName, pluginConfig); }
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java index 3558582..e0f38cb 100644 --- a/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java +++ b/java/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfig.java
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; +import com.google.gerrit.entities.BranchNameKey; import com.google.gerrit.entities.Project; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.plugins.codeowners.backend.CodeOwnerReference; @@ -33,12 +34,14 @@ import com.google.gerrit.server.config.PluginConfigFactory; import com.google.gerrit.server.git.validators.CommitValidationMessage; import com.google.gerrit.server.git.validators.ValidationMessage; +import com.google.gerrit.server.project.RefPatternMatcher; import com.google.inject.Inject; import com.google.inject.Singleton; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.regex.PatternSyntaxException; import org.eclipse.jgit.lib.Config; /** @@ -56,6 +59,8 @@ public class GeneralConfig { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + public static final String SECTION_VALIDATION = "validation"; + public static final String KEY_FILE_EXTENSION = "fileExtension"; public static final String KEY_READ_ONLY = "readOnly"; public static final String KEY_EXEMPT_PURE_REVERTS = "exemptPureReverts"; @@ -388,14 +393,16 @@ } /** - * Gets the enable validation on commit received configuration from the given plugin config with - * fallback to {@code gerrit.config} and default to {@code true}. + * Gets the enable validation on commit received configuration from the given plugin config for + * the specified project with fallback to {@code gerrit.config} and default to {@code true}. * * <p>The enable validation on commit received controls whether code owner config files should be * validated when a commit is received. * + * @param project the project for which the enable validation on commit received configuration + * should be read * @param pluginConfig the plugin config from which the enable validation on commit received - * configuration should be read. + * configuration should be read * @return whether code owner config files should be validated when a commit is received */ CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForCommitReceived( @@ -405,14 +412,40 @@ } /** - * Gets the enable validation on submit configuration from the given plugin config with fallback - * to {@code gerrit.config} and default to {@code true}. + * Gets the enable validation on commit received configuration from the given plugin config for + * the specified branch. + * + * <p>If multiple branch-specific configurations match the specified branch, it is undefined which + * of the matching branch configurations takes precedence. + * + * <p>The enable validation on commit received controls whether code owner config files should be + * validated when a commit is received. + * + * @param branchNameKey the branch and project for which the enable validation on commit received + * configuration should be read + * @param pluginConfig the plugin config from which the enable validation on commit received + * configuration should be read + * @return the enable validation on commit received configuration that is configured for the + * branch, {@link Optional#empty()} if no branch specific configuration exists + */ + Optional<CodeOwnerConfigValidationPolicy> + getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey branchNameKey, Config pluginConfig) { + return getCodeOwnerConfigValidationPolicyForBranch( + KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, branchNameKey, pluginConfig); + } + + /** + * Gets the enable validation on submit configuration from the given plugin config for the + * specified project with fallback to {@code gerrit.config} and default to {@code true}. * * <p>The enable validation on submit controls whether code owner config files should be validated * when a change is submitted. * + * @param project the project for which the enable validation on submit configuration should be + * read * @param pluginConfig the plugin config from which the enable validation on submit configuration - * should be read. + * should be read * @return whether code owner config files should be validated when a change is submitted */ CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForSubmit( @@ -421,6 +454,45 @@ KEY_ENABLE_VALIDATION_ON_SUBMIT, project, pluginConfig); } + /** + * Gets the enable validation on submit configuration from the given plugin config for the + * specified branch. + * + * <p>If multiple branch-specific configurations match the specified branch, it is undefined which + * of the matching branch configurations takes precedence. + * + * <p>The enable validation on submit controls whether code owner config files should be validated + * when a change is submitted. + * + * @param branchNameKey the branch and project for which the enable validation on submit + * configuration should be read + * @param pluginConfig the plugin config from which the enable validation on submit configuration + * should be read + * @return the enable validation on submit configuration that is configured for the branch, {@link + * Optional#empty()} if no branch specific configuration exists + */ + Optional<CodeOwnerConfigValidationPolicy> getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey branchNameKey, Config pluginConfig) { + return getCodeOwnerConfigValidationPolicyForBranch( + KEY_ENABLE_VALIDATION_ON_SUBMIT, branchNameKey, pluginConfig); + } + + private Optional<CodeOwnerConfigValidationPolicy> getCodeOwnerConfigValidationPolicyForBranch( + String key, BranchNameKey branchNameKey, Config pluginConfig) { + requireNonNull(key, "key"); + requireNonNull(branchNameKey, "branchNameKey"); + requireNonNull(pluginConfig, "pluginConfig"); + + Optional<String> validationSectionForBranch = + getValidationSectionForBranch(branchNameKey, pluginConfig); + if (!validationSectionForBranch.isPresent()) { + return Optional.empty(); + } + + return getCodeOwnerConfigValidationPolicyForBranch( + validationSectionForBranch.get(), key, branchNameKey.project(), pluginConfig); + } + private CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicy( String key, Project.NameKey project, Config pluginConfig) { requireNonNull(key, "key"); @@ -459,6 +531,76 @@ } } + private Optional<String> getValidationSectionForBranch( + BranchNameKey branchNameKey, Config pluginConfig) { + ImmutableSet<String> matchingValidationSubsections = + pluginConfig.getSubsections(SECTION_VALIDATION).stream() + .filter( + refPattern -> { + try { + return RefPatternMatcher.getMatcher(refPattern) + .match(branchNameKey.branch(), /* user= */ null); + } catch (PatternSyntaxException e) { + logger.atWarning().withCause(e).log( + "invalid ref pattern %s for subsection %s.%s in %s.config of project %s", + refPattern, + SECTION_VALIDATION, + refPattern, + pluginName, + branchNameKey.project()); + return false; + } + }) + .collect(toImmutableSet()); + + if (matchingValidationSubsections.isEmpty()) { + return Optional.empty(); + } + + String matchingValidationSubsection = matchingValidationSubsections.asList().get(0); + if (matchingValidationSubsections.size() > 1) { + logger.atWarning().log( + "branch %s matches multiple %s subsections in %.config of project %s: %s," + + " subsection %s takes precedence", + branchNameKey.branch(), + SECTION_VALIDATION, + pluginName, + branchNameKey.project(), + matchingValidationSubsections, + matchingValidationSubsection); + } + return Optional.of(matchingValidationSubsection); + } + + private Optional<CodeOwnerConfigValidationPolicy> getCodeOwnerConfigValidationPolicyForBranch( + String branchSubsection, String key, Project.NameKey project, Config pluginConfig) { + requireNonNull(branchSubsection, "branchSubsection"); + requireNonNull(key, "key"); + requireNonNull(project, "project"); + requireNonNull(pluginConfig, "pluginConfig"); + + String codeOwnerConfigValidationPolicyString = + pluginConfig.getString(SECTION_VALIDATION, branchSubsection, key); + if (codeOwnerConfigValidationPolicyString != null) { + try { + return Optional.of( + pluginConfig.getEnum( + SECTION_VALIDATION, branchSubsection, key, CodeOwnerConfigValidationPolicy.TRUE)); + } catch (IllegalArgumentException e) { + logger.atWarning().withCause(e).log( + "Ignoring invalid value %s for the code owner config validation policy in '%s.config'" + + " of project %s (parameter %s.%s.%s). Falling back to project-level setting.", + codeOwnerConfigValidationPolicyString, + pluginName, + project.get(), + SECTION_VALIDATION, + branchSubsection, + key); + } + } + return Optional.empty(); + } + /** * Gets the merge commit strategy from the given plugin config with fallback to {@code * gerrit.config}.
diff --git a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java index 3f79135..4f03274 100644 --- a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java +++ b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java
@@ -177,7 +177,7 @@ CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy = codeOwnersPluginConfiguration .getProjectConfig(receiveEvent.getProjectNameKey()) - .getCodeOwnerConfigValidationPolicyForCommitReceived(); + .getCodeOwnerConfigValidationPolicyForCommitReceived(receiveEvent.refName); logger.atFine().log("codeOwnerConfigValidationPolicy = %s", codeOwnerConfigValidationPolicy); Optional<ValidationResult> validationResult; if (!codeOwnerConfigValidationPolicy.runValidation()) { @@ -247,7 +247,7 @@ CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy = codeOwnersPluginConfiguration .getProjectConfig(branchNameKey.project()) - .getCodeOwnerConfigValidationPolicyForSubmit(); + .getCodeOwnerConfigValidationPolicyForSubmit(branchNameKey.branch()); logger.atFine().log("codeOwnerConfigValidationPolicy = %s", codeOwnerConfigValidationPolicy); Optional<ValidationResult> validationResult; if (!codeOwnerConfigValidationPolicy.runValidation()) {
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java index 3465717..b501277 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java +++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
@@ -53,10 +53,12 @@ import com.google.gerrit.plugins.codeowners.backend.CodeOwnerSet; import com.google.gerrit.plugins.codeowners.backend.PathExpressionMatcher; import com.google.gerrit.plugins.codeowners.backend.config.BackendConfig; +import com.google.gerrit.plugins.codeowners.backend.config.GeneralConfig; import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend; import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersCodeOwnerConfigParser; import com.google.gerrit.plugins.codeowners.backend.proto.ProtoBackend; import com.google.gerrit.plugins.codeowners.backend.proto.ProtoCodeOwnerConfigParser; +import com.google.gerrit.plugins.codeowners.common.CodeOwnerConfigValidationPolicy; import com.google.gerrit.server.IdentifiedUser; import com.google.inject.Inject; import com.google.inject.Key; @@ -1968,6 +1970,43 @@ } } + @Test + public void disableValidationForBranch() throws Exception { + setAsDefaultCodeOwners(admin); + + // Disable the validation for the master branch. + updateCodeOwnersConfig( + project, + codeOwnersConfig -> { + codeOwnersConfig.setString( + GeneralConfig.SECTION_VALIDATION, + "refs/heads/master", + GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + CodeOwnerConfigValidationPolicy.FALSE.name()); + codeOwnersConfig.setString( + GeneralConfig.SECTION_VALIDATION, + "refs/heads/master", + GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT, + CodeOwnerConfigValidationPolicy.FALSE.name()); + }); + + PushOneCommit.Result r = + createChange( + "Add code owners", + codeOwnerConfigOperations + .codeOwnerConfig(createCodeOwnerConfigKey("/")) + .getJGitFilePath(), + "INVALID"); + assertOkWithHints( + r, + "skipping validation of code owner config files", + "code owners config validation is disabled"); + + approve(r.getChangeId()); + gApi.changes().id(r.getChangeId()).current().submit(); + assertThat(gApi.changes().id(r.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED); + } + private CodeOwnerConfig createCodeOwnerConfigWithImport( CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig, CodeOwnerConfigImportType importType,
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/PutCodeOwnerProjectConfigIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/PutCodeOwnerProjectConfigIT.java index 655b45e..50114fe 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/PutCodeOwnerProjectConfigIT.java +++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/PutCodeOwnerProjectConfigIT.java
@@ -467,7 +467,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForCommitReceived()) + .getCodeOwnerConfigValidationPolicyForCommitReceived("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); CodeOwnerProjectConfigInput input = new CodeOwnerProjectConfigInput(); @@ -476,7 +476,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForCommitReceived()) + .getCodeOwnerConfigValidationPolicyForCommitReceived("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); input.enableValidationOnCommitReceived = CodeOwnerConfigValidationPolicy.TRUE; @@ -484,7 +484,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForCommitReceived()) + .getCodeOwnerConfigValidationPolicyForCommitReceived("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); } @@ -493,7 +493,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForSubmit()) + .getCodeOwnerConfigValidationPolicyForSubmit("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); CodeOwnerProjectConfigInput input = new CodeOwnerProjectConfigInput(); @@ -502,7 +502,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForSubmit()) + .getCodeOwnerConfigValidationPolicyForSubmit("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); input.enableValidationOnSubmit = CodeOwnerConfigValidationPolicy.TRUE; @@ -510,7 +510,7 @@ assertThat( codeOwnersPluginConfiguration .getProjectConfig(project) - .getCodeOwnerConfigValidationPolicyForSubmit()) + .getCodeOwnerConfigValidationPolicyForSubmit("master")) .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshotTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshotTest.java index 529cbd4..eff2303 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshotTest.java +++ b/javatests/com/google/gerrit/plugins/codeowners/backend/config/CodeOwnersPluginConfigSnapshotTest.java
@@ -40,6 +40,7 @@ import com.google.gerrit.plugins.codeowners.backend.FallbackCodeOwners; import com.google.gerrit.plugins.codeowners.backend.PathExpressionMatcher; import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend; +import com.google.gerrit.plugins.codeowners.common.CodeOwnerConfigValidationPolicy; import com.google.gerrit.plugins.codeowners.common.MergeCommitStrategy; import com.google.gerrit.server.IdentifiedUser; import com.google.inject.Inject; @@ -910,6 +911,196 @@ assertThat(cfgSnapshot().areImplicitApprovalsEnabled()).isFalse(); } + @Test + public void cannotGetCodeOwnerConfigValidationPolicyForCommitReceivedForNullBranch() + throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + cfgSnapshot() + .getCodeOwnerConfigValidationPolicyForCommitReceived(/* branchName= */ null)); + assertThat(npe).hasMessageThat().isEqualTo("branchName"); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForCommitReceived_notConfigured() throws Exception { + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("non-existing")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForCommitReceived_configuredOnProjectLevel() + throws Exception { + configureEnableValidationOnCommitReceived(project, CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("non-existing")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForCommitReceived_configuredOnBranchLevel() + throws Exception { + configureEnableValidationOnCommitReceivedForBranch( + project, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat( + cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForCommitReceived_branchLevelConfigTakesPrecedence() + throws Exception { + updateCodeOwnersConfig( + project, + codeOwnersConfig -> { + codeOwnersConfig.setEnum( + CodeOwnersPluginConfiguration.SECTION_CODE_OWNERS, + /* subsection= */ null, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + CodeOwnerConfigValidationPolicy.DRY_RUN); + codeOwnersConfig.setEnum( + GeneralConfig.SECTION_VALIDATION, + "refs/heads/master", + GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + CodeOwnerConfigValidationPolicy.FALSE); + }); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat( + cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + + @Test + public void + getCodeOwnerConfigValidationPolicyForCommitReceived_inheritedBranchLevelConfigTakesPrecedence() + throws Exception { + configureEnableValidationOnCommitReceivedForBranch( + allProjects, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + configureEnableValidationOnCommitReceived(project, CodeOwnerConfigValidationPolicy.DRY_RUN); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat( + cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + + @Test + public void + getCodeOwnerConfigValidationPolicyForCommitReceived_inheritedBranchLevelCanBeOverridden() + throws Exception { + configureEnableValidationOnCommitReceivedForBranch( + allProjects, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + configureEnableValidationOnCommitReceivedForBranch( + project, "refs/heads/master", CodeOwnerConfigValidationPolicy.DRY_RUN); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForCommitReceived("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + + @Test + public void cannotGetCodeOwnerConfigValidationPolicyForSubmitForNullBranch() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit(/* branchName= */ null)); + assertThat(npe).hasMessageThat().isEqualTo("branchName"); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForSubmitd_notConfigured() throws Exception { + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("non-existing")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForSubmit_configuredOnProjectLevel() + throws Exception { + configureEnableValidationOnSubmit(project, CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("non-existing")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForSubmit_configuredOnBranchLevel() + throws Exception { + configureEnableValidationOnSubmitForBranch( + project, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForSubmit_branchLevelConfigTakesPrecedence() + throws Exception { + updateCodeOwnersConfig( + project, + codeOwnersConfig -> { + codeOwnersConfig.setEnum( + CodeOwnersPluginConfiguration.SECTION_CODE_OWNERS, + /* subsection= */ null, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT, + CodeOwnerConfigValidationPolicy.DRY_RUN); + codeOwnersConfig.setEnum( + GeneralConfig.SECTION_VALIDATION, + "refs/heads/master", + GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT, + CodeOwnerConfigValidationPolicy.FALSE); + }); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + + @Test + public void + getCodeOwnerConfigValidationPolicyForSubmit_inheritedBranchLevelConfigTakesPrecedence() + throws Exception { + configureEnableValidationOnSubmitForBranch( + allProjects, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + configureEnableValidationOnSubmit(project, CodeOwnerConfigValidationPolicy.DRY_RUN); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("refs/heads/master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("foo")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + + @Test + public void getCodeOwnerConfigValidationPolicyForSubmit_inheritedBranchLevelCanBeOverridden() + throws Exception { + configureEnableValidationOnSubmitForBranch( + allProjects, "refs/heads/master", CodeOwnerConfigValidationPolicy.FALSE); + configureEnableValidationOnSubmitForBranch( + project, "refs/heads/master", CodeOwnerConfigValidationPolicy.DRY_RUN); + assertThat(cfgSnapshot().getCodeOwnerConfigValidationPolicyForSubmit("master")) + .isEqualTo(CodeOwnerConfigValidationPolicy.DRY_RUN); + } + private CodeOwnersPluginConfigSnapshot cfgSnapshot() { return codeOwnersPluginConfigSnapshotFactory.create(project); } @@ -997,6 +1188,56 @@ requiredApproval); } + private void configureEnableValidationOnCommitReceived( + Project.NameKey project, CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy) + throws Exception { + setCodeOwnersConfig( + project, + /* subsection= */ null, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + codeOwnerConfigValidationPolicy.name()); + } + + private void configureEnableValidationOnCommitReceivedForBranch( + Project.NameKey project, + String branchSubsection, + CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy) + throws Exception { + updateCodeOwnersConfig( + project, + codeOwnersConfig -> + codeOwnersConfig.setString( + GeneralConfig.SECTION_VALIDATION, + branchSubsection, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + codeOwnerConfigValidationPolicy.name())); + } + + private void configureEnableValidationOnSubmit( + Project.NameKey project, CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy) + throws Exception { + setCodeOwnersConfig( + project, + /* subsection= */ null, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT, + codeOwnerConfigValidationPolicy.name()); + } + + private void configureEnableValidationOnSubmitForBranch( + Project.NameKey project, + String branchSubsection, + CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy) + throws Exception { + updateCodeOwnersConfig( + project, + codeOwnersConfig -> + codeOwnersConfig.setString( + GeneralConfig.SECTION_VALIDATION, + branchSubsection, + GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT, + codeOwnerConfigValidationPolicy.name())); + } + private AutoCloseable registerTestBackend() { RegistrationHandle registrationHandle = ((PrivateInternals_DynamicMapImpl<CodeOwnerBackend>) codeOwnerBackends)
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java index 456deb5..3c9fc09 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java +++ b/javatests/com/google/gerrit/plugins/codeowners/backend/config/GeneralConfigTest.java
@@ -31,12 +31,14 @@ import static com.google.gerrit.plugins.codeowners.backend.config.GeneralConfig.KEY_READ_ONLY; import static com.google.gerrit.plugins.codeowners.backend.config.GeneralConfig.KEY_REJECT_NON_RESOLVABLE_CODE_OWNERS; import static com.google.gerrit.plugins.codeowners.backend.config.GeneralConfig.KEY_REJECT_NON_RESOLVABLE_IMPORTS; +import static com.google.gerrit.plugins.codeowners.backend.config.GeneralConfig.SECTION_VALIDATION; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static com.google.gerrit.truth.OptionalSubject.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.config.GerritConfig; +import com.google.gerrit.entities.BranchNameKey; import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest; import com.google.gerrit.plugins.codeowners.backend.CodeOwnerReference; import com.google.gerrit.plugins.codeowners.backend.EnableImplicitApprovals; @@ -417,6 +419,165 @@ } @Test + public void cannotGetEnableValidationOnCommitReceivedForBranchForNullBranch() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + /* branchNameKey= */ null, new Config())); + assertThat(npe).hasMessageThat().isEqualTo("branchNameKey"); + } + + @Test + public void cannotGetEnableValidationOnCommitReceivedForBranchForNullPluginConfig() + throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), /* pluginConfig= */ null)); + assertThat(npe).hasMessageThat().isEqualTo("pluginConfig"); + } + + @Test + public void noBranchSpecificEnableValidationOnCommitReceivedConfiguration() throws Exception { + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), new Config())) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_exact() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/foo", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_refPattern() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/foo/*", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_regEx() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, + "^refs/heads/.*foo.*", + KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_invalidRegEx() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "^refs/heads/[", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void matchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_exact() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/master", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void matchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_refPattern() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/*", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void matchingBranchSpecificEnableValidationOnCommitReceivedConfiguration_regEx() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, + "^refs/heads/.*bar.*", + KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "foobarbaz"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void branchSpecificEnableValidationOnCommitReceivedConfigurationIsIgnoredIfValueIsInvalid() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, + "refs/heads/master", + KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + "INVALID"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void multipleMatchingBranchSpecificEnableValidationOnCommitReceivedConfiguration() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/master", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + cfg.setString( + SECTION_VALIDATION, "refs/heads/*", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + cfg.setString( + SECTION_VALIDATION, "^refs/heads/.*", KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, "false"); + + // it is non-deterministic which of the branch-specific configurations takes precedence, but + // since they all configure the same value it's not important for this assertion + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test public void cannotGetEnableValidationOnSubmitForNullProject() throws Exception { NullPointerException npe = assertThrows( @@ -485,6 +646,147 @@ } @Test + public void cannotGetEnableValidationOnSubmitForBranchForNullBranch() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + /* branchNameKey= */ null, new Config())); + assertThat(npe).hasMessageThat().isEqualTo("branchNameKey"); + } + + @Test + public void cannotGetEnableValidationOnSubmitForBranchForNullPluginConfig() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), /* pluginConfig= */ null)); + assertThat(npe).hasMessageThat().isEqualTo("pluginConfig"); + } + + @Test + public void noBranchSpecificEnableValidationOnSubmitConfiguration() throws Exception { + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), new Config())) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnSubmitConfiguration_exact() + throws Exception { + Config cfg = new Config(); + cfg.setString(SECTION_VALIDATION, "refs/heads/foo", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnSubmitConfiguration_refPattern() + throws Exception { + Config cfg = new Config(); + cfg.setString(SECTION_VALIDATION, "refs/heads/foo/*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnSubmitConfiguration_regEx() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "^refs/heads/.*foo.*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForCommitReceivedForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void noMatchingBranchSpecificEnableValidationOnSubmitConfiguration_invalidRegEx() + throws Exception { + Config cfg = new Config(); + cfg.setString(SECTION_VALIDATION, "^refs/heads/[", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void matchingBranchSpecificEnableValidationOnSubmitConfiguration_exact() throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/master", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void matchingBranchSpecificEnableValidationOnSubmitConfiguration_refPattern() + throws Exception { + Config cfg = new Config(); + cfg.setString(SECTION_VALIDATION, "refs/heads/*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void matchingBranchSpecificEnableValidationOnSubmitConfiguration_regEx() throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "^refs/heads/.*bar.*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "foobarbaz"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + public void branchSpecificEnableValidationOnSubmitConfigurationIsIgnoredIfValueIsInvalid() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/master", KEY_ENABLE_VALIDATION_ON_SUBMIT, "INVALID"); + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .isEmpty(); + } + + @Test + public void multipleMatchingBranchSpecificEnableValidationOnSubmitConfiguration() + throws Exception { + Config cfg = new Config(); + cfg.setString( + SECTION_VALIDATION, "refs/heads/master", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + cfg.setString(SECTION_VALIDATION, "refs/heads/*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + cfg.setString(SECTION_VALIDATION, "^refs/heads/.*", KEY_ENABLE_VALIDATION_ON_SUBMIT, "false"); + + // it is non-deterministic which of the branch-specific configurations takes precedence, but + // since they all configure the same value it's not important for this assertion + assertThat( + generalConfig.getCodeOwnerConfigValidationPolicyForSubmitForBranch( + BranchNameKey.create(project, "master"), cfg)) + .value() + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test public void cannotGetMergeCommitStrategyForNullPluginConfig() throws Exception { NullPointerException npe = assertThrows(
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md index ffa4018..70e5630 100644 --- a/resources/Documentation/config.md +++ b/resources/Documentation/config.md
@@ -605,10 +605,26 @@ [plugin.@PLUGIN@.enableValidationOnCommitReceived](#pluginCodeOwnersEnableValidationOnCommitReceived) in `gerrit.config` and the `codeOwners.enableValidationOnCommitReceived` setting from parent projects.\ + Can be overriden on branch-level by setting + [validation.\<branch\>.enableValidationOnCommitReceived](#validationBranchEnableValidationOnCommitReceived).\ If not set, the global setting [plugin.@PLUGIN@.enableValidationOnCommitReceived](#pluginCodeOwnersEnableValidationOnCommitReceived) in `gerrit.config` is used. +<a id="validationBranchEnableValidationOnCommitReceived">validation.\<branch\>.enableValidationOnCommitReceived</a> +: Branch-level policy for validating code owner config files when a commit + is received.\ + Applies to all branches that are matched by `<branch>`, which can be + an exact ref name (e.g. `refs/heads/master`), a ref pattern (e.g. + `refs/heads/*`) or a regular expression (e.g. `^refs/heads/stable-.*`).\ + If a branches matches multiple validation subsections it is undefined + which of the subsections takes precedence.\ + Overrides the project-level configuration for validating code owner + config files when a commit is received that is configured by + [codeOwners.enableValidationOnCommitReceived](#codeOwnersEnableValidationOnCommitReceived).\ + For further details see the description of + [codeOwners.enableValidationOnCommitReceived](#codeOwnersEnableValidationOnCommitReceived). + <a id="codeOwnersEnableValidationOnSubmit">codeOwners.enableValidationOnSubmit</a> : Policy for validating code owner config files when a change is submitted. Allowed values are `true` (the code owner config file @@ -622,10 +638,26 @@ [plugin.@PLUGIN@.enableValidationOnSubmit](#pluginCodeOwnersEnableValidationOnSubmit) in `gerrit.config` and the `codeOwners.enableValidationOnSubmit` setting from parent projects.\ + Can be overriden on branch-level by setting + [validation.\<branch\>.enableValidationOnSubmit](#validationBranchEnableValidationOnSubmit).\ If not set, the global setting [plugin.@PLUGIN@.enableValidationOnSubmit](#pluginCodeOwnersEnableValidationOnSubmit) in `gerrit.config` is used. +<a id="validationBranchEnableValidationOnSubmit">validation.\<branch\>.enableValidationOnSubmit</a> +: Branch-level policy for validating code owner config files when a change + is submitted.\ + Applies to all branches that are matched by `<branch>`, which can be + an exact ref name (e.g. `refs/heads/master`), a ref pattern (e.g. + `refs/heads/*`) or a regular expression (e.g. `^refs/heads/stable-.*`).\ + If a branches matches multiple validation subsections it is undefined + which of the subsections takes precedence.\ + Overrides the project-level configuration for validating code owner + config files when a change is submitted that is configured by + [codeOwners.enableValidationOnSubmit](#codeOwnersEnableValidationOnSubmit).\ + For further details see the description of + [codeOwners.enableValidationOnSubmit](#codeOwnersEnableValidationOnSubmit). + <a id="codeOwnersRejectNonResolvableCodeOwners">codeOwners.rejectNonResolvableCodeOwners</a> : Whether modifications of code owner config files that newly add non-resolvable code owners should be rejected on commit received and