Allow disabling the owners on a per-branch basis

Define a new global configuration file <plugin-name>.config which allows
to selectively disable the resolution of owners on a per-branch basis.

Change-Id: I7c036785a490f4f14ba503775bf0b2b826d4571b
diff --git a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
index 756332c..70cc5a6 100644
--- a/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
+++ b/owners-autoassign/src/main/java/com/googlesource/gerrit/owners/common/GitRefListener.java
@@ -51,6 +51,7 @@
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -208,7 +209,7 @@
       ChangeInfo change = cApi.get();
       PatchList patchList = getPatchList(repository, event, change);
       if (patchList != null) {
-        PathOwners owners = new PathOwners(accounts, repository, change.branch, patchList);
+        PathOwners owners = new PathOwners(accounts, repository, Optional.of(change.branch), patchList);
         Set<Account.Id> allReviewers = Sets.newHashSet();
         allReviewers.addAll(owners.get().values());
         allReviewers.addAll(owners.getReviewers().values());
diff --git a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/PathOwners.java b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/PathOwners.java
index 6bf288b..d9ab78d 100644
--- a/owners-common/src/main/java/com/googlesource/gerrit/owners/common/PathOwners.java
+++ b/owners-common/src/main/java/com/googlesource/gerrit/owners/common/PathOwners.java
@@ -64,13 +64,17 @@
 
   private Map<String, Set<Id>> fileOwners;
 
-  public PathOwners(Accounts accounts, Repository repository, String branch, PatchList patchList) {
+  public PathOwners(
+      Accounts accounts,
+      Repository repository,
+      Optional<String> branchWhenEnabled,
+      PatchList patchList) {
     this.repository = repository;
     this.patchList = patchList;
     this.parser = new ConfigurationParser(accounts);
     this.accounts = accounts;
 
-    OwnersMap map = fetchOwners(branch);
+    OwnersMap map = branchWhenEnabled.map(branch -> fetchOwners(branch)).orElse(new OwnersMap());
     owners = Multimaps.unmodifiableSetMultimap(map.getPathOwners());
     reviewers = Multimaps.unmodifiableSetMultimap(map.getPathReviewers());
     matchers = map.getMatchers();
diff --git a/owners-common/src/test/java/com/googlesource/gerrit/owners/common/Config.java b/owners-common/src/test/java/com/googlesource/gerrit/owners/common/Config.java
index ea00372..9b502ea 100644
--- a/owners-common/src/test/java/com/googlesource/gerrit/owners/common/Config.java
+++ b/owners-common/src/test/java/com/googlesource/gerrit/owners/common/Config.java
@@ -37,7 +37,7 @@
   protected PatchList patchList;
   protected ConfigurationParser parser;
   protected TestAccounts accounts = new TestAccounts();
-  protected String branch = "master";
+  protected Optional<String> branch = Optional.of("master");
 
   public void setup() throws Exception {
     PowerMock.mockStatic(JgitWrapper.class);
diff --git a/owners-common/src/test/java/com/googlesource/gerrit/owners/common/PathOwnersTest.java b/owners-common/src/test/java/com/googlesource/gerrit/owners/common/PathOwnersTest.java
index 573c82e..75f0bb7 100644
--- a/owners-common/src/test/java/com/googlesource/gerrit/owners/common/PathOwnersTest.java
+++ b/owners-common/src/test/java/com/googlesource/gerrit/owners/common/PathOwnersTest.java
@@ -19,6 +19,7 @@
 import static org.powermock.api.easymock.PowerMock.replayAll;
 
 import com.google.gerrit.entities.Account;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Optional;
 import java.util.Set;
@@ -34,6 +35,8 @@
 @PrepareForTest(JgitWrapper.class)
 public class PathOwnersTest extends ClassicConfig {
 
+  private static final String CLASSIC_OWNERS = "classic/OWNERS";
+
   @Override
   @Before
   public void setup() throws Exception {
@@ -42,29 +45,34 @@
 
   @Test
   public void testClassic() throws Exception {
-    expectNoConfig("OWNERS");
-    expectConfig("classic/OWNERS", createConfig(false, owners(USER_A_EMAIL_COM, USER_B_EMAIL_COM)));
-
-    creatingPatchList(Arrays.asList("classic/file.txt"));
-    replayAll();
+    mockOwners(USER_A_EMAIL_COM, USER_B_EMAIL_COM);
 
     PathOwners owners = new PathOwners(accounts, repository, branch, patchList);
-    Set<Account.Id> ownersSet = owners.get().get("classic/OWNERS");
+    Set<Account.Id> ownersSet = owners.get().get(CLASSIC_OWNERS);
     assertEquals(2, ownersSet.size());
     assertTrue(ownersSet.contains(USER_A_ID));
     assertTrue(ownersSet.contains(USER_B_ID));
   }
 
   @Test
+  public void testDisabledBranch() throws Exception {
+    mockOwners(USER_A_EMAIL_COM);
+
+    PathOwners owners = new PathOwners(accounts, repository, Optional.empty(), patchList);
+    Set<Account.Id> ownersSet = owners.get().get(CLASSIC_OWNERS);
+    assertEquals(0, ownersSet.size());
+  }
+
+  @Test
   public void testClassicWithInheritance() throws Exception {
     expectConfig("OWNERS", createConfig(true, owners(USER_C_EMAIL_COM)));
-    expectConfig("classic/OWNERS", createConfig(true, owners(USER_A_EMAIL_COM, USER_B_EMAIL_COM)));
+    expectConfig(CLASSIC_OWNERS, createConfig(true, owners(USER_A_EMAIL_COM, USER_B_EMAIL_COM)));
 
     creatingPatchList(Arrays.asList("classic/file.txt"));
     replayAll();
 
     PathOwners owners2 = new PathOwners(accounts, repository, branch, patchList);
-    Set<Account.Id> ownersSet2 = owners2.get().get("classic/OWNERS");
+    Set<Account.Id> ownersSet2 = owners2.get().get(CLASSIC_OWNERS);
 
     // in this case we are inheriting the acct3 from /OWNERS
     assertEquals(3, ownersSet2.size());
@@ -100,4 +108,12 @@
     assertEquals(1, config.get().getOwners().size());
     assertTrue(config.get().getOwners().contains(USER_C_EMAIL_COM));
   }
+
+  private void mockOwners(String... owners) throws IOException {
+    expectNoConfig("OWNERS");
+    expectConfig(CLASSIC_OWNERS, createConfig(false, owners(owners)));
+
+    creatingPatchList(Arrays.asList("classic/file.txt"));
+    replayAll();
+  }
 }
diff --git a/owners/src/main/java/com/googlesource/gerrit/owners/OwnerPredicateProvider.java b/owners/src/main/java/com/googlesource/gerrit/owners/OwnerPredicateProvider.java
index ef0b92b..4225631 100644
--- a/owners/src/main/java/com/googlesource/gerrit/owners/OwnerPredicateProvider.java
+++ b/owners/src/main/java/com/googlesource/gerrit/owners/OwnerPredicateProvider.java
@@ -18,16 +18,21 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.annotations.Listen;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.rules.PredicateProvider;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.owners.common.Accounts;
+import org.eclipse.jgit.lib.Config;
 
 /** Gerrit OWNERS Prolog Predicate Provider. */
 @Listen
 public class OwnerPredicateProvider implements PredicateProvider {
   @Inject
-  public OwnerPredicateProvider(Accounts accounts) {
-    OwnersStoredValues.initialize(accounts);
+  public OwnerPredicateProvider(
+      Accounts accounts, PluginConfigFactory configFactory, @PluginName String pluginName) {
+    Config config = configFactory.getGlobalPluginConfig(pluginName);
+    OwnersStoredValues.initialize(accounts, config.getStringList("owners", "disable", "branch"));
   }
 
   @Override
diff --git a/owners/src/main/java/com/googlesource/gerrit/owners/OwnersStoredValues.java b/owners/src/main/java/com/googlesource/gerrit/owners/OwnersStoredValues.java
index f721539..997bc06 100644
--- a/owners/src/main/java/com/googlesource/gerrit/owners/OwnersStoredValues.java
+++ b/owners/src/main/java/com/googlesource/gerrit/owners/OwnersStoredValues.java
@@ -22,6 +22,7 @@
 import com.googlecode.prolog_cafe.lang.Prolog;
 import com.googlesource.gerrit.owners.common.Accounts;
 import com.googlesource.gerrit.owners.common.PathOwners;
+import java.util.Optional;
 import org.eclipse.jgit.lib.Repository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,7 +33,7 @@
 
   public static StoredValue<PathOwners> PATH_OWNERS;
 
-  public static synchronized void initialize(Accounts accounts) {
+  public static synchronized void initialize(Accounts accounts, String[] disablePatterns) {
     if (PATH_OWNERS != null) {
       return;
     }
@@ -44,7 +45,12 @@
             PatchList patchList = StoredValues.PATCH_LIST.get(engine);
             Repository repository = StoredValues.REPOSITORY.get(engine);
             String branch = StoredValues.getChange(engine).getDest().branch();
-            return new PathOwners(accounts, repository, branch, patchList);
+            for (String pattern : disablePatterns) {
+              if (branch.matches(pattern)) {
+                return new PathOwners(accounts, repository, Optional.empty(), patchList);
+              }
+            }
+            return new PathOwners(accounts, repository, Optional.of(branch), patchList);
           }
         };
   }
diff --git a/owners/src/main/resources/Documentation/config.md b/owners/src/main/resources/Documentation/config.md
index b294ae8..660f5d5 100644
--- a/owners/src/main/resources/Documentation/config.md
+++ b/owners/src/main/resources/Documentation/config.md
@@ -1,3 +1,19 @@
+## Global configuration
+
+The global plugin configuration is read from the `$GERRIT_SITE/etc/owners.config`
+and is applied across all projects in Gerrit.
+
+owners.disable.branch
+:	List of branches regex where the resolution of owners is disabled.
+
+Example:
+
+```
+[owners "disable"]
+  branch = refs/meta/config
+  branch = refs/heads/sandboxes.*
+```
+
 ## Configuration
 
 Owner approval is determined based on OWNERS files located in the same