Merge changes I1f101173,I32b0d8b9,Ic191c620 * changes: Fix a heading in the validation documentation Validation: Do not reject imports that are added in the same commit Add config option to disable validation of code owner configs on submit
diff --git a/java/com/google/gerrit/plugins/codeowners/config/CodeOwnersPluginConfiguration.java b/java/com/google/gerrit/plugins/codeowners/config/CodeOwnersPluginConfiguration.java index 60c14d2..1a7985d 100644 --- a/java/com/google/gerrit/plugins/codeowners/config/CodeOwnersPluginConfiguration.java +++ b/java/com/google/gerrit/plugins/codeowners/config/CodeOwnersPluginConfiguration.java
@@ -130,6 +130,20 @@ } /** + * Whether code owner configs should be validated when a change is submitted. + * + * @param project the project for it should be checked whether code owner configs should be + * validated when a change is submitted + * @return whether code owner configs should be validated when a change is submitted + */ + public CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForSubmit( + Project.NameKey project) { + requireNonNull(project, "project"); + return generalConfig.getCodeOwnerConfigValidationPolicyForSubmit( + project, getPluginConfig(project)); + } + + /** * Gets the merge commit strategy for the given project. * * @param project the project for which the merge commit strategy should be retrieved
diff --git a/java/com/google/gerrit/plugins/codeowners/config/GeneralConfig.java b/java/com/google/gerrit/plugins/codeowners/config/GeneralConfig.java index 8a15dcb..85fd19a 100644 --- a/java/com/google/gerrit/plugins/codeowners/config/GeneralConfig.java +++ b/java/com/google/gerrit/plugins/codeowners/config/GeneralConfig.java
@@ -66,6 +66,9 @@ public static final String KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED = "enableValidationOnCommitReceived"; + @VisibleForTesting + public static final String KEY_ENABLE_VALIDATION_ON_SUBMIT = "enableValidationOnSubmit"; + @VisibleForTesting public static final String KEY_MERGE_COMMIT_STRATEGY = "mergeCommitStrategy"; @VisibleForTesting public static final String KEY_GLOBAL_CODE_OWNER = "globalCodeOwner"; @@ -240,23 +243,45 @@ * <p>The enable validation on commit received controls whether code owner config files should be * validated when a commit is received. * - * @param pluginConfig the plugin config from which the read-only configuration should be read. + * @param pluginConfig the plugin config from which the enable validation on commit received + * configuration should be read. * @return whether code owner config files should be validated when a commit is received */ CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForCommitReceived( Project.NameKey project, Config pluginConfig) { + return getCodeOwnerConfigValidationPolicy( + KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, project, pluginConfig); + } + + /** + * Gets the enable validation on submit configuration from the given plugin config 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 pluginConfig the plugin config from which the enable validation on submit configuration + * should be read. + * @return whether code owner config files should be validated when a change is submitted + */ + CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicyForSubmit( + Project.NameKey project, Config pluginConfig) { + return getCodeOwnerConfigValidationPolicy( + KEY_ENABLE_VALIDATION_ON_SUBMIT, project, pluginConfig); + } + + private CodeOwnerConfigValidationPolicy getCodeOwnerConfigValidationPolicy( + String key, Project.NameKey project, Config pluginConfig) { + requireNonNull(key, "key"); requireNonNull(project, "project"); requireNonNull(pluginConfig, "pluginConfig"); String codeOwnerConfigValidationPolicyString = - pluginConfig.getString(SECTION_CODE_OWNERS, null, KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED); + pluginConfig.getString(SECTION_CODE_OWNERS, null, key); if (codeOwnerConfigValidationPolicyString != null) { try { return pluginConfig.getEnum( - SECTION_CODE_OWNERS, - null, - KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, - CodeOwnerConfigValidationPolicy.TRUE); + SECTION_CODE_OWNERS, null, key, CodeOwnerConfigValidationPolicy.TRUE); } catch (IllegalArgumentException e) { logger.atWarning().log( "Ignoring invalid value %s for the code owner config validation policy in '%s.config'" @@ -266,15 +291,14 @@ } try { - return pluginConfigFromGerritConfig.getEnum( - KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, CodeOwnerConfigValidationPolicy.TRUE); + return pluginConfigFromGerritConfig.getEnum(key, CodeOwnerConfigValidationPolicy.TRUE); } catch (IllegalArgumentException e) { logger.atWarning().log( "Ignoring invalid value %s for the code owner config validation policy in gerrit.config" + " (parameter plugin.%s.%s). Falling back to default value %s.", - pluginConfigFromGerritConfig.getString(KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED), + pluginConfigFromGerritConfig.getString(key), pluginName, - KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED, + key, CodeOwnerConfigValidationPolicy.TRUE); return CodeOwnerConfigValidationPolicy.TRUE; }
diff --git a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java index 85cfca4..e659580 100644 --- a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java +++ b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java
@@ -237,16 +237,44 @@ .username(caller.getLoggableName()) .patchSetId(patchSetId.get()) .build())) { - ChangeNotes changeNotes = - changeNotesFactory.create(projectState.getNameKey(), commit.change().getId()); - PatchSet patchSet = patchSetUtil.get(changeNotes, patchSetId); - IdentifiedUser patchSetUploader = userFactory.create(patchSet.uploader()); - Optional<ValidationResult> validationResult = - validateCodeOwnerConfig( - branchNameKey, repository.getConfig(), revWalk, commit, patchSetUploader); + CodeOwnerConfigValidationPolicy codeOwnerConfigValidationPolicy = + codeOwnersPluginConfiguration.getCodeOwnerConfigValidationPolicyForSubmit( + branchNameKey.project()); + logger.atFine().log("codeOwnerConfigValidationPolicy = %s", codeOwnerConfigValidationPolicy); + Optional<ValidationResult> validationResult; + if (!codeOwnerConfigValidationPolicy.runValidation()) { + validationResult = + Optional.of( + ValidationResult.create( + "skipping validation of code owner config files", + new CommitValidationMessage( + "code owners config validation is disabled", ValidationMessage.Type.HINT))); + } else { + try { + ChangeNotes changeNotes = + changeNotesFactory.create(projectState.getNameKey(), commit.change().getId()); + PatchSet patchSet = patchSetUtil.get(changeNotes, patchSetId); + IdentifiedUser patchSetUploader = userFactory.create(patchSet.uploader()); + validationResult = + validateCodeOwnerConfig( + branchNameKey, repository.getConfig(), revWalk, commit, patchSetUploader); + } catch (RuntimeException e) { + if (!codeOwnerConfigValidationPolicy.isDryRun()) { + throw e; + } + + // The validation was executed as dry-run and failures during the validation should not + // cause an error. Hence we swallow the exception here. + logger.atFine().withCause(e).log( + "ignoring failure during validation of code owner config files in revision %s" + + " (project = %s, branch = %s) because the validation was performed as dry-run", + commit.name(), branchNameKey.project(), branchNameKey.branch()); + validationResult = Optional.empty(); + } + } if (validationResult.isPresent()) { logger.atFine().log("validation result = %s", validationResult.get()); - validationResult.get().processForOnPreMerge(); + validationResult.get().processForOnPreMerge(codeOwnerConfigValidationPolicy.isDryRun()); } } } @@ -748,6 +776,7 @@ validateCodeOwnerConfigReference( codeOwnerConfigFilePath, codeOwnerConfig.key(), + codeOwnerConfig.revision(), CodeOwnerConfigImportType.GLOBAL, codeOwnerConfigReference)), codeOwnerConfig.codeOwnerSets().stream() @@ -757,6 +786,7 @@ validateCodeOwnerConfigReference( codeOwnerConfigFilePath, codeOwnerConfig.key(), + codeOwnerConfig.revision(), CodeOwnerConfigImportType.PER_FILE, codeOwnerConfigReference))) .filter(Optional::isPresent) @@ -769,6 +799,8 @@ * @param codeOwnerConfigFilePath the path of the code owner config file which contains the code * owner config reference * @param keyOfImportingCodeOwnerConfig key of the importing code owner config + * @param codeOwnerConfigRevision the commit from which the code owner config which contains the + * code owner config reference was loaded * @param importType the type of the import * @param codeOwnerConfigReference the code owner config reference that should be validated. * @return a validation message describing the issue with the code owner config reference, {@link @@ -777,6 +809,7 @@ private Optional<CommitValidationMessage> validateCodeOwnerConfigReference( Path codeOwnerConfigFilePath, CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig, + ObjectId codeOwnerConfigRevision, CodeOwnerConfigImportType importType, CodeOwnerConfigReference codeOwnerConfigReference) { CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig = @@ -804,7 +837,9 @@ projectState.get().getProject().getState().name())); } - Optional<ObjectId> revision = getRevision(keyOfImportedCodeOwnerConfig); + Optional<ObjectId> revision = + getRevision( + keyOfImportingCodeOwnerConfig, codeOwnerConfigRevision, keyOfImportedCodeOwnerConfig); if (!revision.isPresent() || !isBranchReadable(keyOfImportedCodeOwnerConfig)) { // we intentionally use the same error message for non-existing and non-readable branches so // that uploaders cannot probe for the existence of branches (e.g. deduce from the error @@ -888,7 +923,18 @@ } } - private Optional<ObjectId> getRevision(CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig) { + private Optional<ObjectId> getRevision( + CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig, + ObjectId codeOwnerConfigRevision, + CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig) { + if (keyOfImportingCodeOwnerConfig + .branchNameKey() + .equals(keyOfImportedCodeOwnerConfig.branchNameKey())) { + // load the imported code owner config from the same revision from which the importing code + // owner config was loaded + return Optional.of(codeOwnerConfigRevision); + } + try (Repository repo = repoManager.openRepository(keyOfImportedCodeOwnerConfig.project())) { return Optional.ofNullable(repo.exactRef(keyOfImportedCodeOwnerConfig.ref())) .map(Ref::getObjectId); @@ -959,8 +1005,8 @@ * <p>If there are no errors the validation messages are logged on fine level so that they show * up in a trace. Returning the message to the user without failing the submit is not possible. */ - void processForOnPreMerge() throws MergeValidationException { - if (hasError()) { + void processForOnPreMerge(boolean dryRun) throws MergeValidationException { + if (!dryRun && hasError()) { throw new MergeValidationException(getMessage(validationMessages())); }
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 bc42987..2e5e73b 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java +++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
@@ -22,6 +22,7 @@ import static com.google.gerrit.testing.GerritJUnit.assertThrows; import com.google.common.collect.ImmutableMap; +import com.google.gerrit.acceptance.GitUtil; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.acceptance.config.GerritConfig; @@ -166,6 +167,12 @@ .addCodeOwnerEmail(user.email()) .create(); + // Fetch the commit that created the imported code owner config into the local repository so + // that the commit that creates the importing code owner config becomes a successor of this + // commit. + GitUtil.fetch(testRepo, "refs/*:refs/*"); + testRepo.reset(projectOperations.project(project).getHead("master")); + CodeOwnerConfigReference codeOwnerConfigReference = CodeOwnerConfigReference.create( CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, @@ -282,6 +289,47 @@ } @Test + public void canUploadConfigWithoutIssues_withImportOfConfigThatIsAddedInSameCommit() + throws Exception { + // imports are not supported for the proto backend + assume().that(backendConfig.getDefaultBackend()).isNotInstanceOf(ProtoBackend.class); + + CodeOwnerConfig.Key codeOwnerConfigKey = createCodeOwnerConfigKey("/"); + CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig = createCodeOwnerConfigKey("/foo/"); + + CodeOwnerConfigReference codeOwnerConfigReference = + CodeOwnerConfigReference.create( + CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, + codeOwnerConfigOperations.codeOwnerConfig(keyOfImportedCodeOwnerConfig).getFilePath()); + + // Create a code owner config with import and without issues. + PushOneCommit.Result r = + createChange( + "Add code owners", + ImmutableMap.of( + codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(), + format( + CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION) + .addImport(codeOwnerConfigReference) + .addCodeOwnerSet( + CodeOwnerSet.builder() + .addCodeOwnerEmail(admin.email()) + .addPathExpression("foo") + .addImport(codeOwnerConfigReference) + .build()) + .build()), + codeOwnerConfigOperations + .codeOwnerConfig(keyOfImportedCodeOwnerConfig) + .getJGitFilePath(), + format( + CodeOwnerConfig.builder(keyOfImportedCodeOwnerConfig, TEST_REVISION) + .addCodeOwnerSet( + CodeOwnerSet.builder().addCodeOwnerEmail(user.email()).build()) + .build()))); + assertOkWithHints(r, "code owner config files validated, no issues found"); + } + + @Test @GerritConfig(name = "plugin.code-owners.backend", value = "non-existing-backend") public void canUploadNonParseableConfigIfCodeOwnersPluginConfigurationIsInvalid() throws Exception { @@ -1428,6 +1476,81 @@ gApi.changes().create(changeInput); } + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "false") + public void canSubmitNonParseableConfigIfValidationIsDisabled() throws Exception { + testCanSubmitNonParseableConfig(); + } + + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "dry_run") + public void canSubmitNonParseableConfigIfValidationIsDoneAsDryRun() throws Exception { + testCanSubmitNonParseableConfig(); + } + + private void testCanSubmitNonParseableConfig() throws Exception { + CodeOwnerConfig.Key codeOwnerConfigKey = createCodeOwnerConfigKey("/"); + + // disable the code owners functionality so that we can upload a non-parseable code owner config + // that we then try to submit + disableCodeOwnersForProject(project); + + PushOneCommit.Result r = + createChange( + "Add code owners", + codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(), + "INVALID"); + r.assertOkStatus(); + + // re-enable the code owners functionality for the project + enableCodeOwnersForProject(project); + + // submit the change + approve(r.getChangeId()); + gApi.changes().id(r.getChangeId()).current().submit(); + assertThat(gApi.changes().id(r.getChangeId()).get().status).isEqualTo(ChangeStatus.MERGED); + } + + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "false") + public void canSubmitConfigWithIssuesIfValidationIsDisabled() throws Exception { + testCanSubmitConfigWithIssues(); + } + + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "dry_run") + public void canSubmitConfigWithIssuesIfValidationIsDoneAsDryRun() throws Exception { + testCanSubmitConfigWithIssues(); + } + + private void testCanSubmitConfigWithIssues() throws Exception { + CodeOwnerConfig.Key codeOwnerConfigKey = createCodeOwnerConfigKey("/"); + + // disable the code owners functionality so that we can upload a code owner config with issues + // that we then try to submit + disableCodeOwnersForProject(project); + + // upload a code owner config that has issues (non-resolvable code owners) + String unknownEmail1 = "non-existing-email@example.com"; + PushOneCommit.Result r = + createChange( + "Add code owners", + codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getJGitFilePath(), + format( + CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION) + .addCodeOwnerSet(CodeOwnerSet.createWithoutPathExpressions(unknownEmail1)) + .build())); + r.assertOkStatus(); + + // re-enable the code owners functionality for the project + enableCodeOwnersForProject(project); + + // submit the change + 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/config/GeneralConfigTest.java b/javatests/com/google/gerrit/plugins/codeowners/config/GeneralConfigTest.java index 2a92d16..6bc8ac5 100644 --- a/javatests/com/google/gerrit/plugins/codeowners/config/GeneralConfigTest.java +++ b/javatests/com/google/gerrit/plugins/codeowners/config/GeneralConfigTest.java
@@ -18,6 +18,7 @@ import static com.google.gerrit.plugins.codeowners.config.CodeOwnersPluginConfiguration.SECTION_CODE_OWNERS; import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_ENABLE_IMPLICIT_APPROVALS; import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_ENABLE_VALIDATION_ON_COMMIT_RECEIVED; +import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_ENABLE_VALIDATION_ON_SUBMIT; import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_FALLBACK_CODE_OWNERS; import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_FILE_EXTENSION; import static com.google.gerrit.plugins.codeowners.config.GeneralConfig.KEY_GLOBAL_CODE_OWNER; @@ -177,6 +178,50 @@ } @Test + public void cannotGetEnableValidationOnSubmitForNullProject() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(null, new Config())); + assertThat(npe).hasMessageThat().isEqualTo("project"); + } + + @Test + public void cannotGetEnableValidationOnSubmitForNullPluginConfig() throws Exception { + NullPointerException npe = + assertThrows( + NullPointerException.class, + () -> generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(project, null)); + assertThat(npe).hasMessageThat().isEqualTo("pluginConfig"); + } + + @Test + public void noEnableValidationOnSubmitConfiguration() throws Exception { + assertThat(generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(project, new Config())) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "false") + public void + enableValidationOnSubmitConfigurationIsRetrievedFromGerritConfigIfNotSpecifiedOnProjectLevel() + throws Exception { + assertThat(generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(project, new Config())) + .isEqualTo(CodeOwnerConfigValidationPolicy.FALSE); + } + + @Test + @GerritConfig(name = "plugin.code-owners.enableValidationOnSubmit", value = "false") + public void + enableValidationOnSubmitConfigurationInPluginConfigOverridesEnableValidationOnSubmitConfigurationInGerritConfig() + throws Exception { + Config cfg = new Config(); + cfg.setString(SECTION_CODE_OWNERS, null, KEY_ENABLE_VALIDATION_ON_SUBMIT, "true"); + assertThat(generalConfig.getCodeOwnerConfigValidationPolicyForSubmit(project, cfg)) + .isEqualTo(CodeOwnerConfigValidationPolicy.TRUE); + } + + @Test public void cannotGetMergeCommitStrategyForNullPluginConfig() throws Exception { NullPointerException npe = assertThrows(
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md index 32d3ae9..28c65b9 100644 --- a/resources/Documentation/config.md +++ b/resources/Documentation/config.md
@@ -133,6 +133,20 @@ in `@PLUGIN@.config`.\ By default `true`. +<a id="pluginCodeOwnersEnableValidationOnSubmit">plugin.@PLUGIN@.enableValidationOnSubmit</a> +: Policy for validating code owner config files when a change is + submitted. Allowed values are `true` (the code owner config file + validation is enabled and the submit of invalid code owner config files + is rejected), `false` (the code owner config file validation is + disabled, invalid code owner config files are not rejected) and + `dry_run` (code owner config files are validated, but invalid code owner + config files are not rejected).\ + Disabling the submit validation is not recommended.\ + Can be overridden per project by setting + [codeOwners.enableValidationOnSubmit](#codeOwnersEnableValidationOnSubmit) + in `@PLUGIN@.config`.\ + By default `true`. + <a id="pluginCodeOwnersAllowedEmailDomain">plugin.@PLUGIN@.allowedEmailDomain</a> : Email domain that allows to assign code ownerships to emails with this domain.\ @@ -420,6 +434,22 @@ [plugin.@PLUGIN@.enableValidationOnCommitReceived](#pluginCodeOwnersEnableValidationOnCommitReceived) in `gerrit.config` is used. +<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 + validation is enabled and the submit of invalid code owner config files + is rejected), `false` (the code owner config file validation is + disabled, invalid code owner config files are not rejected) and + `dry_run` (code owner config files are validated, but invalid code owner + config files are not rejected).\ + Disabling the submit validation is not recommended.\ + Overrides the global setting + [plugin.@PLUGIN@.enableValidationOnSubmit](#pluginCodeOwnersEnableValidationOnSubmit) + in `gerrit.config`.\ + If not set, the global setting + [plugin.@PLUGIN@.enableValidationOnSubmit](#pluginCodeOwnersEnableValidationOnSubmit) + in `gerrit.config` is used. + <a id="codeOwnersRequiredApproval">codeOwners.requiredApproval</a> : Approval that is required from code owners to approve the files in a change.\
diff --git a/resources/Documentation/validation.md b/resources/Documentation/validation.md index b3b6405..ef1b4ce 100644 --- a/resources/Documentation/validation.md +++ b/resources/Documentation/validation.md
@@ -37,8 +37,10 @@ plugin gets installed/enabled, it is possible that invalid configuration files already exist in the repository) * updates happen behind Gerrit's back (e.g. pushes that bypass Gerrit) -* the validation is disabled in the - [plugin configuration](config.html#codeOwnersEnableValidationOnCommitReceived). +* the validation is disabled via the + [enableValidationOnCommitReceived](config.html#codeOwnersEnableValidationOnCommitReceived) + or [enableValidationOnSubmit](config.html#codeOwnersEnableValidationOnSubmit) + config options In addition for [code owner config files](user-guide.html#codeOwnerConfigFiles) no validation is done when: @@ -129,7 +131,7 @@ [code owner override](user-guide.html#codeOwnerOverride). -### <a id="codeOwnerConfigFileChecks">Validation checks for code owner config files +### <a id="codeOwnersConfigFileChecks">Validation checks for code-owners.config files For the [code-owner.config](config.html#projectLevelConfigFile) in the `refs/meta/config` branch the following checks are performed: