CheckCodeOwnerConfigFiles: Allow to specify branches that should be validated
This way you can limit the validation to those branches that you are
interested in.
The request is rejected if:
* any of the specified branches doesn't exist
* the code owners functionality is disabled for any of the specified
branches and validateDisabledBranches is false (or unset)
Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: I81f03cd940d03a363149d4916ffec488947f4684
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CheckCodeOwnerConfigFilesInput.java b/java/com/google/gerrit/plugins/codeowners/api/CheckCodeOwnerConfigFilesInput.java
index f06edf4..6e2abfc 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/CheckCodeOwnerConfigFilesInput.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/CheckCodeOwnerConfigFilesInput.java
@@ -14,6 +14,8 @@
package com.google.gerrit.plugins.codeowners.api;
+import java.util.List;
+
/**
* The input for the {@link com.google.gerrit.plugins.codeowners.restapi.CheckCodeOwnerConfigFiles}
* REST endpoint.
@@ -26,4 +28,14 @@
* <p>By default unset, {@code false}.
*/
public Boolean validateDisabledBranches;
+
+ /**
+ * Branches for which code owner config files should be validated.
+ *
+ * <p>The {@code refs/heads/} prefix may be omitted.
+ *
+ * <p>By default unset, which means that code owner config files in all branches should be
+ * validated.
+ */
+ public List<String> branches;
}
diff --git a/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwners.java b/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwners.java
index b2823ea..6f86d06 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwners.java
@@ -14,6 +14,8 @@
package com.google.gerrit.plugins.codeowners.api;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import java.util.List;
@@ -34,6 +36,7 @@
/** Request to check code owner config files. */
abstract class CheckCodeOwnerConfigFilesRequest {
private boolean validateDisabledBranches;
+ private ImmutableList<String> branches;
/**
* Includes code owner config files in branches for which the code owners functionality is
@@ -61,6 +64,22 @@
return validateDisabledBranches;
}
+ /** Sets the branches for which code owner config files should be validated. */
+ public CheckCodeOwnerConfigFilesRequest setBranches(List<String> branches) {
+ this.branches = ImmutableList.copyOf(branches);
+ return this;
+ }
+
+ /**
+ * Gets the branches for which code owner config files should be validated.
+ *
+ * <p>Returns {@code null} if no branches have been set.
+ */
+ @Nullable
+ public ImmutableList<String> getBranches() {
+ return branches;
+ }
+
/**
* Executes the request to check the code owner config files and retrieves the result of the
* validation.
diff --git a/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwnersImpl.java b/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwnersImpl.java
index 750d98d..0a4fdad 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwnersImpl.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/ProjectCodeOwnersImpl.java
@@ -84,6 +84,7 @@
try {
CheckCodeOwnerConfigFilesInput input = new CheckCodeOwnerConfigFilesInput();
input.validateDisabledBranches = isValidateDisabledBranches();
+ input.branches = getBranches();
return checkCodeOwnerConfigFiles.apply(projectResource, input).value();
} catch (Exception e) {
throw asRestApiException("Cannot check code owner config files", e);
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
index cb55949..a00f476 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
@@ -15,19 +15,24 @@
package com.google.gerrit.plugins.codeowners.restapi;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.plugins.codeowners.api.CheckCodeOwnerConfigFilesInput;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackend;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
@@ -48,7 +53,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.stream.Stream;
/**
* REST endpoint that checks/validates the code owner config files in a project.
@@ -103,12 +107,18 @@
.check(ProjectPermission.WRITE_CONFIG);
logger.atFine().log(
- "checking code owner config files for project %s (validateDisabledBranches = %s)",
- projectResource.getNameKey(), input.validateDisabledBranches);
+ "checking code owner config files for project %s"
+ + " (validateDisabledBranches = %s, branches = %s)",
+ projectResource.getNameKey(), input.validateDisabledBranches, input.branches);
+
+ ImmutableSet<BranchNameKey> branches = branches(projectResource);
+
+ validateInput(projectResource.getNameKey(), branches, input);
ImmutableMap.Builder<String, Map<String, List<ConsistencyProblemInfo>>> resultsByBranchBuilder =
ImmutableMap.builder();
- branches(projectResource)
+ branches.stream()
+ .filter(branchNameKey -> shouldValidateBranch(input, branchNameKey))
.filter(
branchNameKey ->
validateDisabledBranches(input)
@@ -119,11 +129,12 @@
return Response.ok(resultsByBranchBuilder.build());
}
- private Stream<BranchNameKey> branches(ProjectResource projectResource)
+ private ImmutableSet<BranchNameKey> branches(ProjectResource projectResource)
throws RestApiException, IOException, PermissionBackendException {
return listBranches.get().apply(projectResource).value().stream()
.filter(branchInfo -> !"HEAD".equals(branchInfo.ref))
- .map(branchInfo -> BranchNameKey.create(projectResource.getNameKey(), branchInfo.ref));
+ .map(branchInfo -> BranchNameKey.create(projectResource.getNameKey(), branchInfo.ref))
+ .collect(toImmutableSet());
}
private Map<String, List<ConsistencyProblemInfo>> checkBranch(BranchNameKey branchNameKey) {
@@ -179,6 +190,43 @@
commitValidationMessage.getType(), commitValidationMessage.getMessage()));
}
+ private void validateInput(
+ Project.NameKey projectName,
+ ImmutableSet<BranchNameKey> branches,
+ CheckCodeOwnerConfigFilesInput input)
+ throws RestApiException {
+ if (input.branches != null) {
+ for (String branchName : input.branches) {
+ BranchNameKey branchNameKey = BranchNameKey.create(projectName, branchName);
+ if (!branches.contains(branchNameKey)) {
+ throw new UnprocessableEntityException(String.format("branch %s not found", branchName));
+ }
+
+ if ((input.validateDisabledBranches == null || !input.validateDisabledBranches)
+ && codeOwnersPluginConfiguration.isDisabled(branchNameKey)) {
+ throw new BadRequestException(
+ String.format(
+ "code owners functionality for branch %s is disabled,"
+ + " set 'validate_disabled_braches' in the input to 'true' if code owner"
+ + " config files in this branch should be validated",
+ branchName));
+ }
+ }
+ }
+ }
+
+ private static boolean shouldValidateBranch(
+ CheckCodeOwnerConfigFilesInput input, BranchNameKey branchNameKey) {
+ boolean shouldValidateBranch =
+ input.branches == null
+ || input.branches.contains(branchNameKey.branch())
+ || input.branches.contains(branchNameKey.shortName());
+ if (!shouldValidateBranch) {
+ logger.atFine().log("skip validation for branch %s", branchNameKey.branch());
+ }
+ return shouldValidateBranch;
+ }
+
private static boolean validateDisabledBranches(CheckCodeOwnerConfigFilesInput input) {
return input.validateDisabledBranches != null && input.validateDisabledBranches;
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerConfigFilesIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerConfigFilesIT.java
index f742ffe..a2f5572 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerConfigFilesIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerConfigFilesIT.java
@@ -16,16 +16,23 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
+import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
+import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.plugins.codeowners.JgitPath;
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersIT;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackend;
@@ -53,6 +60,7 @@
*/
public class CheckCodeOwnerConfigFilesIT extends AbstractCodeOwnersIT {
@Inject private RequestScopeOperations requestScopeOperations;
+ @Inject private ProjectOperations projectOperations;
private BackendConfig backendConfig;
@@ -209,6 +217,106 @@
"refs/meta/config", ImmutableMap.of());
}
+ @Test
+ public void validateSpecifiedBranches() throws Exception {
+ gApi.projects().name(project.get()).branch("stable-1.0").create(new BranchInput());
+ gApi.projects().name(project.get()).branch("stable-1.1").create(new BranchInput());
+
+ assertThat(
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .setBranches(ImmutableList.of("refs/heads/stable-1.0", "refs/heads/stable-1.1"))
+ .check())
+ .containsExactly(
+ "refs/heads/stable-1.0", ImmutableMap.of(),
+ "refs/heads/stable-1.1", ImmutableMap.of());
+ }
+
+ @Test
+ public void validateSpecifiedBranches_shortNames() throws Exception {
+ gApi.projects().name(project.get()).branch("stable-1.0").create(new BranchInput());
+ gApi.projects().name(project.get()).branch("stable-1.1").create(new BranchInput());
+
+ assertThat(
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .setBranches(ImmutableList.of("stable-1.0", "stable-1.1"))
+ .check())
+ .containsExactly(
+ "refs/heads/stable-1.0", ImmutableMap.of(),
+ "refs/heads/stable-1.1", ImmutableMap.of());
+ }
+
+ @Test
+ public void cannotValidateNonExistingBranch() throws Exception {
+ UnprocessableEntityException exception =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () ->
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .setBranches(ImmutableList.of("refs/heads/non-existing"))
+ .check());
+ assertThat(exception).hasMessageThat().isEqualTo("branch refs/heads/non-existing not found");
+ }
+
+ @Test
+ public void cannotValidateNonVisibleBranch() throws Exception {
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/*").group(REGISTERED_USERS))
+ .update();
+
+ UnprocessableEntityException exception =
+ assertThrows(
+ UnprocessableEntityException.class,
+ () ->
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .setBranches(ImmutableList.of("refs/heads/master"))
+ .check());
+ assertThat(exception).hasMessageThat().isEqualTo("branch refs/heads/master not found");
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.disabledBranch", value = "refs/meta/config")
+ public void cannotValidateDisabledBranchWithoutEnablingValidationForDisabledBranches()
+ throws Exception {
+ BadRequestException exception =
+ assertThrows(
+ BadRequestException.class,
+ () ->
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .setBranches(ImmutableList.of("refs/meta/config"))
+ .check());
+ assertThat(exception)
+ .hasMessageThat()
+ .isEqualTo(
+ "code owners functionality for branch refs/meta/config is disabled,"
+ + " set 'validate_disabled_braches' in the input to 'true' if code owner config"
+ + " files in this branch should be validated");
+ }
+
+ @Test
+ @GerritConfig(name = "plugin.code-owners.disabledBranch", value = "refs/meta/config")
+ public void validateSpecifiedBranchThatIsDisabled() throws Exception {
+ assertThat(
+ projectCodeOwnersApiFactory
+ .project(project)
+ .checkCodeOwnerConfigFiles()
+ .validateDisabledBranches()
+ .setBranches(ImmutableList.of("refs/meta/config"))
+ .check())
+ .containsExactly("refs/meta/config", ImmutableMap.of());
+ }
+
private ConsistencyProblemInfo error(String message) {
return new ConsistencyProblemInfo(ConsistencyProblemInfo.Status.ERROR, message);
}
diff --git a/resources/Documentation/rest-api.md b/resources/Documentation/rest-api.md
index 5c23d5b..189ff23 100644
--- a/resources/Documentation/rest-api.md
+++ b/resources/Documentation/rest-api.md
@@ -382,6 +382,7 @@
| Field Name | | Description |
| ---------------------------- | -------- | ----------- |
| `validate_disabled_branches` | optional | Whether code owner config files in branches for which the code owners functionality is disabled should be validated too. By default unset, `false`.
+| `branches` | optional | List of branches for which code owner config files should be validated. The `refs/heads/` prefix may be omitted. By default unset, which means that code owner config files in all branches should be validated.
---