Merge "Fix MissingObjectException when validating OWNERS imports on create change"
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
index b0da9f7..ae55e82 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/CheckCodeOwnerConfigFiles.java
@@ -41,6 +41,7 @@
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
import com.google.gerrit.plugins.codeowners.validation.CodeOwnerConfigValidator;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -54,6 +55,8 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
/**
* REST endpoint that checks/validates the code owner config files in a project.
@@ -72,6 +75,7 @@
private final Provider<CurrentUser> currentUser;
private final PermissionBackend permissionBackend;
+ private final GitRepositoryManager repoManager;
private final Provider<ListBranches> listBranches;
private final CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
private final CodeOwnerConfigScanner.Factory codeOwnerConfigScannerFactory;
@@ -81,12 +85,14 @@
public CheckCodeOwnerConfigFiles(
Provider<CurrentUser> currentUser,
PermissionBackend permissionBackend,
+ GitRepositoryManager repoManager,
Provider<ListBranches> listBranches,
CodeOwnersPluginConfiguration codeOwnersPluginConfiguration,
CodeOwnerConfigScanner.Factory codeOwnerConfigScannerFactory,
CodeOwnerConfigValidator codeOwnerConfigValidator) {
this.currentUser = currentUser;
this.permissionBackend = permissionBackend;
+ this.repoManager = repoManager;
this.listBranches = listBranches;
this.codeOwnersPluginConfiguration = codeOwnersPluginConfiguration;
this.codeOwnerConfigScannerFactory = codeOwnerConfigScannerFactory;
@@ -120,20 +126,23 @@
validateInput(projectResource.getNameKey(), branches, input);
- ImmutableMap.Builder<String, Map<String, List<ConsistencyProblemInfo>>> resultsByBranchBuilder =
- ImmutableMap.builder();
- branches.stream()
- .filter(branchNameKey -> shouldValidateBranch(input, branchNameKey))
- .filter(
- branchNameKey ->
- validateDisabledBranches(input)
- || !codeOwnersPluginConfiguration.isDisabled(branchNameKey))
- .forEach(
- branchNameKey ->
- resultsByBranchBuilder.put(
- branchNameKey.branch(),
- checkBranch(input.path, branchNameKey, input.verbosity)));
- return Response.ok(resultsByBranchBuilder.build());
+ try (Repository repo = repoManager.openRepository(projectResource.getNameKey());
+ RevWalk revWalk = new RevWalk(repo)) {
+ ImmutableMap.Builder<String, Map<String, List<ConsistencyProblemInfo>>>
+ resultsByBranchBuilder = ImmutableMap.builder();
+ branches.stream()
+ .filter(branchNameKey -> shouldValidateBranch(input, branchNameKey))
+ .filter(
+ branchNameKey ->
+ validateDisabledBranches(input)
+ || !codeOwnersPluginConfiguration.isDisabled(branchNameKey))
+ .forEach(
+ branchNameKey ->
+ resultsByBranchBuilder.put(
+ branchNameKey.branch(),
+ checkBranch(revWalk, input.path, branchNameKey, input.verbosity)));
+ return Response.ok(resultsByBranchBuilder.build());
+ }
}
private ImmutableSet<BranchNameKey> branches(ProjectResource projectResource)
@@ -145,6 +154,7 @@
}
private Map<String, List<ConsistencyProblemInfo>> checkBranch(
+ RevWalk revWalk,
String pathGlob,
BranchNameKey branchNameKey,
@Nullable ConsistencyProblemInfo.Status verbosity) {
@@ -162,7 +172,11 @@
problemsByPath.putAll(
codeOwnerBackend.getFilePath(codeOwnerConfig.key()).toString(),
checkCodeOwnerConfig(
- branchNameKey.project(), codeOwnerBackend, codeOwnerConfig, verbosity));
+ branchNameKey.project(),
+ revWalk,
+ codeOwnerBackend,
+ codeOwnerConfig,
+ verbosity));
return true;
},
(codeOwnerConfigFilePath, configInvalidException) -> {
@@ -178,12 +192,17 @@
private ImmutableList<ConsistencyProblemInfo> checkCodeOwnerConfig(
Project.NameKey project,
+ RevWalk revWalk,
CodeOwnerBackend codeOwnerBackend,
CodeOwnerConfig codeOwnerConfig,
@Nullable ConsistencyProblemInfo.Status verbosity) {
return codeOwnerConfigValidator
.validateCodeOwnerConfig(
- project, currentUser.get().asIdentifiedUser(), codeOwnerBackend, codeOwnerConfig)
+ project,
+ revWalk,
+ currentUser.get().asIdentifiedUser(),
+ codeOwnerBackend,
+ codeOwnerConfig)
.map(
commitValidationMessage ->
createConsistencyProblemInfo(commitValidationMessage, verbosity))
diff --git a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java
index cef6d52..2b8d479 100644
--- a/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java
+++ b/java/com/google/gerrit/plugins/codeowners/validation/CodeOwnerConfigValidator.java
@@ -488,7 +488,7 @@
// issues. Hence in this case we downgrade all validation errors in the new version to
// warnings so that the update is not blocked.
return validateCodeOwnerConfig(
- branchNameKey.project(), user, codeOwnerBackend, codeOwnerConfig)
+ branchNameKey.project(), revWalk, user, codeOwnerBackend, codeOwnerConfig)
.map(CodeOwnerConfigValidator::downgradeErrorToWarning);
}
@@ -500,13 +500,14 @@
if (baseCodeOwnerConfig.isPresent()) {
return validateCodeOwnerConfig(
branchNameKey.project(),
+ revWalk,
user,
codeOwnerBackend,
codeOwnerConfig,
baseCodeOwnerConfig.get());
}
return validateCodeOwnerConfig(
- branchNameKey.project(), user, codeOwnerBackend, codeOwnerConfig);
+ branchNameKey.project(), revWalk, user, codeOwnerBackend, codeOwnerConfig);
}
/**
@@ -672,6 +673,7 @@
* they are not newly introduced by the given code owner config).
*
* @param project the name of the project
+ * @param revWalk rev walk that should be used to load the code owner configs
* @param user user for which the code owner visibility checks should be performed
* @param codeOwnerBackend the code owner backend from which the code owner configs were loaded
* @param codeOwnerConfig the code owner config that should be validated
@@ -681,6 +683,7 @@
*/
private Stream<CommitValidationMessage> validateCodeOwnerConfig(
Project.NameKey project,
+ RevWalk revWalk,
IdentifiedUser user,
CodeOwnerBackend codeOwnerBackend,
CodeOwnerConfig codeOwnerConfig,
@@ -689,9 +692,9 @@
requireNonNull(baseCodeOwnerConfig, "baseCodeOwnerConfig");
ImmutableSet<CommitValidationMessage> issuesInBaseVersion =
- validateCodeOwnerConfig(project, user, codeOwnerBackend, baseCodeOwnerConfig)
+ validateCodeOwnerConfig(project, revWalk, user, codeOwnerBackend, baseCodeOwnerConfig)
.collect(toImmutableSet());
- return validateCodeOwnerConfig(project, user, codeOwnerBackend, codeOwnerConfig)
+ return validateCodeOwnerConfig(project, revWalk, user, codeOwnerBackend, codeOwnerConfig)
.map(
commitValidationMessage ->
issuesInBaseVersion.contains(commitValidationMessage)
@@ -703,6 +706,7 @@
* Validates the given code owner config and returns validation issues as stream.
*
* @param project the name of the project
+ * @param revWalk rev walk that should be used to load the code owner configs from {@code project}
* @param user user for which the code owner visibility checks should be performed
* @param codeOwnerBackend the code owner backend from which the code owner config was loaded
* @param codeOwnerConfig the code owner config that should be validated
@@ -711,6 +715,7 @@
*/
public Stream<CommitValidationMessage> validateCodeOwnerConfig(
Project.NameKey project,
+ RevWalk revWalk,
IdentifiedUser user,
CodeOwnerBackend codeOwnerBackend,
CodeOwnerConfig codeOwnerConfig) {
@@ -719,7 +724,10 @@
validateCodeOwnerReferences(
project, user, codeOwnerBackend.getFilePath(codeOwnerConfig.key()), codeOwnerConfig),
validateImports(
- project, codeOwnerBackend.getFilePath(codeOwnerConfig.key()), codeOwnerConfig));
+ project,
+ revWalk,
+ codeOwnerBackend.getFilePath(codeOwnerConfig.key()),
+ codeOwnerConfig));
}
/**
@@ -796,6 +804,7 @@
* Validates the imports of the given code owner config.
*
* @param project the name of the project
+ * @param revWalk rev walk that should be used to load the code owner configs from {@code project}
* @param codeOwnerConfigFilePath the path of the code owner config file which contains the code
* owner config
* @param codeOwnerConfig the code owner config for which the imports should be validated
@@ -803,13 +812,17 @@
* if there are no issues
*/
private Stream<CommitValidationMessage> validateImports(
- Project.NameKey project, Path codeOwnerConfigFilePath, CodeOwnerConfig codeOwnerConfig) {
+ Project.NameKey project,
+ RevWalk revWalk,
+ Path codeOwnerConfigFilePath,
+ CodeOwnerConfig codeOwnerConfig) {
return Streams.concat(
codeOwnerConfig.imports().stream()
.map(
codeOwnerConfigReference ->
validateCodeOwnerConfigReference(
project,
+ revWalk,
codeOwnerConfigFilePath,
codeOwnerConfig.key(),
codeOwnerConfig.revision(),
@@ -821,6 +834,7 @@
codeOwnerConfigReference ->
validateCodeOwnerConfigReference(
project,
+ revWalk,
codeOwnerConfigFilePath,
codeOwnerConfig.key(),
codeOwnerConfig.revision(),
@@ -834,6 +848,7 @@
* Validates a code owner config reference.
*
* @param project the name of the project
+ * @param revWalk rev walk that should be used to load the code owner configs from {@code project}
* @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
@@ -846,6 +861,7 @@
*/
private Optional<CommitValidationMessage> validateCodeOwnerConfigReference(
Project.NameKey project,
+ RevWalk revWalk,
Path codeOwnerConfigFilePath,
CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
ObjectId codeOwnerConfigRevision,
@@ -916,9 +932,15 @@
}
try {
- if (!codeOwnerBackend
- .getCodeOwnerConfig(keyOfImportedCodeOwnerConfig, revision.get())
- .isPresent()) {
+ // If a code owner config is imported from the same project, we must use the provided rev
+ // walk, otherwise the revision may not be visible yet and trying to load a code owner config
+ // from it could fail with MissingObjectException.
+ Optional<CodeOwnerConfig> importedCodeOwnerConfig =
+ keyOfImportedCodeOwnerConfig.project().equals(project)
+ ? codeOwnerBackend.getCodeOwnerConfig(
+ keyOfImportedCodeOwnerConfig, revWalk, revision.get())
+ : codeOwnerBackend.getCodeOwnerConfig(keyOfImportedCodeOwnerConfig, revision.get());
+ if (!importedCodeOwnerConfig.isPresent()) {
return nonResolvableImport(
project,
importType,
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 0b55946..8d3c3cf 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerConfigValidatorIT.java
@@ -1643,16 +1643,34 @@
}
private void testValidateMergeCommitCreatedViaTheCreateChangeRestApi() throws Exception {
+ skipTestIfImportsNotSupportedByCodeOwnersBackend();
+
// Create another branch.
String branchName = "stable";
createBranch(BranchNameKey.create(project, branchName));
- // Create a code owner config file in the other branch.
+ // Create a code owner config file in the other branch that can be imported.
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch(branchName)
+ .folderPath("/foo/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ CodeOwnerConfigReference.create(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
+ codeOwnerConfigOperations.codeOwnerConfig(keyOfImportedCodeOwnerConfig).getFilePath());
+
+ // Create a code owner config file in the other branch that contains an import.
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch(branchName)
.folderPath("/")
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerEmail(user.email())
.create();