Merge "Add spinning icon to indicate code-owners are loading"
diff --git a/java/com/google/gerrit/plugins/codeowners/api/BUILD b/java/com/google/gerrit/plugins/codeowners/api/BUILD
index 9ab1591..f8d6d60 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/BUILD
+++ b/java/com/google/gerrit/plugins/codeowners/api/BUILD
@@ -6,6 +6,7 @@
     srcs = glob(["*.java"]),
     visibility = ["//visibility:public"],
     deps = PLUGIN_DEPS_NEVERLINK + [
+        "//lib/errorprone:annotations",
         "//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/backend",
         "//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/common",
         "//plugins/code-owners/proto:owners_metadata_java_proto",
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java b/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
index a2079b8..390e34c 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
@@ -17,6 +17,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.extensions.client.ListAccountsOption;
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -78,6 +79,7 @@
      *
      * <p>Appends to the options which have been set so far.
      */
+    @CanIgnoreReturnValue
     public QueryRequest withOptions(
         ListAccountsOption option, ListAccountsOption... furtherOptions) {
       this.options.add(requireNonNull(option, "option"));
@@ -90,6 +92,7 @@
      *
      * @param limit the limit
      */
+    @CanIgnoreReturnValue
     public QueryRequest withLimit(int limit) {
       this.limit = limit;
       return this;
@@ -100,6 +103,7 @@
      *
      * @param seed seed that should be used to shuffle code owners that have the same score
      */
+    @CanIgnoreReturnValue
     public QueryRequest withSeed(long seed) {
       this.seed = seed;
       return this;
@@ -112,6 +116,7 @@
      * @param resolveAllUsers whether code ownerships that are assigned to all users should be
      *     resolved to random users
      */
+    @CanIgnoreReturnValue
     public QueryRequest setResolveAllUsers(boolean resolveAllUsers) {
       this.resolveAllUsers = resolveAllUsers;
       return this;
@@ -123,6 +128,7 @@
      * @param highestScoreOnly whether only the code owners with the highest score should be
      *     returned
      */
+    @CanIgnoreReturnValue
     public QueryRequest withHighestScoreOnly(boolean highestScoreOnly) {
       this.highestScoreOnly = highestScoreOnly;
       return this;
@@ -135,6 +141,7 @@
      *
      * @param debug whether debug logs should be included into the response
      */
+    @CanIgnoreReturnValue
     public QueryRequest withDebug(boolean debug) {
       this.debug = debug;
       return this;
@@ -147,6 +154,7 @@
      *
      * @param revision the revision from which the code owner configs should be read
      */
+    @CanIgnoreReturnValue
     public QueryRequest forRevision(String revision) {
       this.revision = revision;
       return this;
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java b/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
index a0df99d..0be6cce 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
@@ -15,6 +15,8 @@
 package com.google.gerrit.plugins.codeowners.backend;
 
 import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.VERSIONED_META_DATA_CHANGE;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -30,6 +32,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.context.RefUpdateContext;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Optional;
@@ -251,7 +254,10 @@
                   codeOwnerConfigKey)
               .setCodeOwnerConfigUpdate(codeOwnerConfigUpdate);
 
-      try (MetaDataUpdate metaDataUpdate =
+      try (
+          RefUpdateContext pluginCtx = RefUpdateContext.open(PLUGIN);
+          RefUpdateContext ctx = RefUpdateContext.open(VERSIONED_META_DATA_CHANGE);
+          MetaDataUpdate metaDataUpdate =
           createMetaDataUpdate(codeOwnerConfigKey.project(), repository, currentUser)) {
         codeOwnerConfigFile.commit(metaDataUpdate);
       }
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
index 90fd878..0669b68 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.plugins.codeowners.backend;
 
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -25,6 +26,7 @@
 import com.google.gerrit.plugins.codeowners.util.JgitPath;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
+import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
@@ -260,8 +262,11 @@
         update.getRepository().exactRef(getRefName()) != null,
         "branch %s does not exist",
         getRefName());
-
-    return super.commit(update);
+    // The commit goes to an ordinary branch (e.g. refs/heads/main). PLUGIN context is enough for
+    // such cases.
+    try(RefUpdateContext ctx = RefUpdateContext.open(PLUGIN)) {
+      return super.commit(update);
+    }
   }
 
   @Override
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
index d0bd003..04b0827 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.plugins.codeowners.backend;
 
 import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
 
@@ -25,6 +26,7 @@
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -101,7 +103,9 @@
         "updating code owner files in branch %s of project %s",
         branchNameKey.branch(), branchNameKey.project());
 
-    try (Repository repository = repoManager.openRepository(branchNameKey.project());
+    try (
+        RefUpdateContext ctx = RefUpdateContext.open(PLUGIN);
+        Repository repository = repoManager.openRepository(branchNameKey.project());
         RevWalk rw = new RevWalk(repository);
         ObjectInserter oi = repository.newObjectInserter();
         CodeOwnerConfigTreeWalk treeWalk =
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
index 5052edb..78f0056 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.plugins.codeowners.backend;
 
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.collect.ImmutableList;
@@ -36,6 +38,7 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.gerrit.server.util.AccountTemplateUtil;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
@@ -157,7 +160,9 @@
               "addCodeOwnersMessageOnAddReviewer",
               updateFactory -> {
                 try (BatchUpdate batchUpdate =
-                    updateFactory.create(projectName, currentUser, when)) {
+                    updateFactory.create(projectName, currentUser, when);
+                    RefUpdateContext pluginCtx = RefUpdateContext.open(PLUGIN);
+                RefUpdateContext changeCtx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
                   batchUpdate.addOp(changeId, new Op(reviewers, maxPathsInChangeMessages));
                   batchUpdate.execute();
                 }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
index 83eb13f..66928ef 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.plugins.codeowners.acceptance.api;
 
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
@@ -100,7 +101,7 @@
   @Test
   public void checkCodeOwnerForSymbolicRefPointingToAnUnbornBranch() throws Exception {
     try (Repository repo = repoManager.openRepository(project)) {
-      repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+      testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
     }
     RestResponse response =
         adminRestSession.get(
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
index f3e7d1b..2d2c8c7 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.plugins.codeowners.acceptance.api;
 
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
 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.allowLabel;
@@ -716,7 +717,7 @@
     try (Repository repo = repoManager.openRepository(project)) {
       RefUpdate ru = repo.updateRef(RefNames.refsCacheAutomerge(mergeCommit.name()));
       ru.setForceUpdate(true);
-      assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+      assertThat(testRefAction(() -> ru.delete())).isEqualTo(RefUpdate.Result.FORCED);
       assertThat(repo.exactRef(RefNames.refsCacheAutomerge(mergeCommit.name()))).isNull();
     }
   }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
index 5ddd85f..843b656 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.plugins.codeowners.acceptance.api;
 
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerConfigInfoSubject.assertThatOptional;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
@@ -54,7 +55,7 @@
   @GerritConfig(name = "plugin.code-owners.enableExperimentalRestEndpoints", value = "true")
   public void getCodeOwnerConfigFromSymbolicRefPointingToAnUnbornBranch() throws Exception {
     try (Repository repo = repoManager.openRepository(project)) {
-      repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+      testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
     }
     RestResponse response =
         adminRestSession.get(
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
index a78d420..34d2271 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.plugins.codeowners.acceptance.api;
 
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerInfoSubject.hasAccountId;
 import static com.google.gerrit.plugins.codeowners.testing.CodeOwnersInfoSubject.assertThat;
@@ -73,7 +74,7 @@
   @Test
   public void getCodeOwnersFromSymbolicRefPointingToAnUnbornBranch() throws Exception {
     try (Repository repo = repoManager.openRepository(project)) {
-      repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+      testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
     }
     RestResponse response =
         adminRestSession.get(
diff --git a/resources/Documentation/backend-find-owners.md b/resources/Documentation/backend-find-owners.md
index 846e8d9..1ea48ab 100644
--- a/resources/Documentation/backend-find-owners.md
+++ b/resources/Documentation/backend-find-owners.md
@@ -371,10 +371,12 @@
 annotation, this annotation applies to all these users. E.g. if an annotation is
 set for the all users wildcard (aka `*`) it applies to all users.
 
-**NOTE:** Only [email lines](#userEmails) and [per-file lines](#perFile) support
-annotations, for other lines (e.g. [file lines](#fileKeyword) and [include
-lines](#includeKeyword)) annotations are interpreted as [comments](#comments)
-and are silently ignored.
+**NOTE:** Only [email lines](#userEmails) and [per-file lines](#perFile) that
+assign code ownership directly to users support annotations, for other lines
+(e.g.  [file lines](#fileKeyword), [include lines](#includeKeyword) and
+[per-file lines](#perFile) that reference other `OWNERS` files via the
+[file](#fileKeyword) keyword) annotations are interpreted as
+[comments](#comments) and are silently ignored.
 
 ### <a id="comments">Comments