Merge branch 'stable-3.0' into stable-3.1

* stable-3.0: (27 commits)
  Use shallow clone while expanding definitions
  Move match() and matchOrNull() to MatchCache
  fixup! Cache Task definition lists for ChangeNodes
  Cache Task definition lists for ChangeNodes
  Split SubNodeFactory out of SubNodeAdder
  Moving caching logic out of loadSubNodes()
  Use a TaskTree.Node.Invalid instead of nulls
  Use overloading for special cases instead of nulls
  Add a TaskTree ApplicableNodeFilter
  Add an isCacheableByBranch() to the PredicateCache
  Track whether Task.applicable needs to be refreshed
  Allow Expander to expand a single Task's field
  Minor cleanup of task Properties.expandText()
  Remove cached Change subNodes on Node completion
  Explicitly signal end of Task Properties expansion
  Use a SubNodeAdder to add TaskTree SubNodes
  Avoid hard coding the refs/meta/config ref
  Inject All-Projects in TaskConfigFactory
  Change TaskExpressions to iterate over TaskKeys
  Avoid passing isTrusted in TaskConfigFactory.getTaskConfig()
  ...

Change-Id: I7d061a78b29b6b6fb36bf508a1038848b14c2388
diff --git a/.bazelversion b/.bazelversion
index 7c69a55..fcdb2e1 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.7.0
+4.0.0
diff --git a/.zuul.yaml b/.zuul.yaml
index e55f00e..9a93255 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -2,7 +2,8 @@
     name: plugins-task-build
     parent: gerrit-plugin-build
     pre-run:
-        tools/playbooks/install_docker.yaml
+        - tools/playbooks/install_docker.yaml
+        - tools/playbooks/install_python3-distutils.yaml
 
 - project:
     description: |
diff --git a/BUILD b/BUILD
index 39b3d0d..e721ac7 100644
--- a/BUILD
+++ b/BUILD
@@ -69,7 +69,7 @@
     name = "junit-tests",
     size = "small",
     srcs = glob(["src/test/java/**/*Test.java"]),
-    deps = PLUGIN_TEST_DEPS + [plugin_name],
+    deps = PLUGIN_TEST_DEPS + PLUGIN_DEPS + [plugin_name],
 )
 
 sh_test(
diff --git a/WORKSPACE b/WORKSPACE
index 0c9c56f..f6120db 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "a88174652e6f853ead5bbc5dacc1030dbb2d50c3",
+    commit = "321fab31e6fbb63c940aad3252f0167f88d52e2e",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/gr-task-plugin/gr-task-plugin.html b/gr-task-plugin/gr-task-plugin.html
index 4a5e5c5..ea59b9f 100644
--- a/gr-task-plugin/gr-task-plugin.html
+++ b/gr-task-plugin/gr-task-plugin.html
@@ -17,7 +17,10 @@
 <dom-module id="gr-task-plugin">
   <template>
       <style>
-        ul { padding-left: 0.5em; }
+        ul {
+          padding-left: 0.5em;
+          margin-top: 0;
+        }
         h3 { padding-left: 0.1em; }
         .cursor { cursor: pointer; }
         #tasks_header {
@@ -32,6 +35,7 @@
           cursor: pointer;
           text-decoration: underline;
         }
+        .no-margins { margin: 0 0 0 0; }
       </style>
 
       <div id="tasks" hidden$="[[!_tasks.length]]">
@@ -47,16 +51,16 @@
               on-tap="_switch_expand"
               class="cursor"> </iron-icon>
           <div style="display: flex; align-items: center; column-gap: 1em;">
-          <h3 on-tap="_switch_expand" class="cursor"> Tasks </h3>
+          <h3 class="no-margins" on-tap="_switch_expand" class="cursor"> Tasks </h3>
           <template is="dom-if" if="[[_is_show_all(_show_all)]]">
-            <p>All ([[_all_count]]) |&nbsp;
+            <p class="no-margins">All ([[_all_count]]) |&nbsp;
               <span
                   on-click="_needs_and_blocked_tap"
                   class="links">Needs ([[_ready_count]]) + Blocked ([[_fail_count]])</span>
             <p>
           </template>
           <template is="dom-if" if="[[!_is_show_all(_show_all)]]">
-            <p> <span
+            <p class="no-margins"> <span
                   class="links"
                   on-click="_show_all_tap">All ([[_all_count]])</span>
               &nbsp;| Needs ([[_ready_count]]) + Blocked ([[_fail_count]])</p>
diff --git a/src/main/java/com/google/gerrit/server/git/meta/AbstractVersionedMetaData.java b/src/main/java/com/google/gerrit/server/git/meta/AbstractVersionedMetaData.java
index e5c1809..7997a05 100644
--- a/src/main/java/com/google/gerrit/server/git/meta/AbstractVersionedMetaData.java
+++ b/src/main/java/com/google/gerrit/server/git/meta/AbstractVersionedMetaData.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.git.meta;
 
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.CommitBuilder;
@@ -22,18 +22,18 @@
 
 /** Versioned Configuration file living in git */
 public class AbstractVersionedMetaData extends VersionedMetaData {
-  protected final Branch.NameKey branch;
+  protected final BranchNameKey branch;
   protected final String fileName;
   protected Config cfg;
 
-  public AbstractVersionedMetaData(Branch.NameKey branch, String fileName) {
+  public AbstractVersionedMetaData(BranchNameKey branch, String fileName) {
     this.branch = branch;
     this.fileName = fileName;
   }
 
   @Override
   protected String getRefName() {
-    return branch.get();
+    return branch.branch();
   }
 
   protected String getFileName() {
@@ -52,7 +52,7 @@
     return cfg;
   }
 
-  public Branch.NameKey getBranch() {
+  public BranchNameKey getBranch() {
     return branch;
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java
index c320aa7..2d44757 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/FileKey.java
@@ -15,16 +15,16 @@
 package com.googlesource.gerrit.plugins.task;
 
 import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
 
 /** An immutable reference to a fully qualified file in gerrit repo. */
 @AutoValue
 public abstract class FileKey {
-  public static FileKey create(Branch.NameKey branch, String file) {
+  public static FileKey create(BranchNameKey branch, String file) {
     return new AutoValue_FileKey(branch, file);
   }
 
-  public abstract Branch.NameKey branch();
+  public abstract BranchNameKey branch();
 
   public abstract String file();
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/Properties.java b/src/main/java/com/googlesource/gerrit/plugins/task/Properties.java
index 609511b..8a13291 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/Properties.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/Properties.java
@@ -15,8 +15,8 @@
 package com.googlesource.gerrit.plugins.task;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.entities.Change;
 import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.googlesource.gerrit.plugins.task.TaskConfig.NamesFactory;
 import com.googlesource.gerrit.plugins.task.TaskConfig.Task;
@@ -164,7 +164,7 @@
         case "project":
           return change().getProject().get();
         case "branch":
-          return change().getDest().get();
+          return change().getDest().branch();
         case "status":
           return change().getStatus().toString();
         case "topic":
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
index b4e79e0..578ce31 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
@@ -15,7 +15,7 @@
 package com.googlesource.gerrit.plugins.task;
 
 import com.google.gerrit.common.Container;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.server.git.meta.AbstractVersionedMetaData;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -209,7 +209,7 @@
   }
 
   public TaskConfig(
-      Branch.NameKey masqueraded, FileKey file, boolean isVisible, boolean isMasqueraded) {
+      BranchNameKey masqueraded, FileKey file, boolean isVisible, boolean isMasqueraded) {
     super(masqueraded, file.file());
     this.file = file;
     this.isVisible = isVisible;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
index b309dd4..908ff0b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigFactory.java
@@ -15,10 +15,10 @@
 package com.googlesource.gerrit.plugins.task;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllProjectsNameProvider;
@@ -46,7 +46,7 @@
   protected final CurrentUser user;
   protected final AllProjectsName allProjects;
 
-  protected final Map<Branch.NameKey, PatchSetArgument> psaMasquerades = new HashMap<>();
+  protected final Map<BranchNameKey, PatchSetArgument> psaMasquerades = new HashMap<>();
   protected final Map<FileKey, TaskConfig> taskCfgByFile = new HashMap<>();
 
   @Inject
@@ -69,8 +69,8 @@
     psaMasquerades.put(psa.change.getDest(), psa);
   }
 
-  protected Branch.NameKey getRootBranch() {
-    return new Branch.NameKey(allProjects, RefNames.REFS_CONFIG);
+  protected BranchNameKey getRootBranch() {
+    return BranchNameKey.create(allProjects, RefNames.REFS_CONFIG);
   }
 
   public TaskConfig getTaskConfig(FileKey key) throws ConfigInvalidException, IOException {
@@ -83,7 +83,7 @@
   }
 
   private TaskConfig loadTaskConfig(FileKey file) throws ConfigInvalidException, IOException {
-    Branch.NameKey branch = file.branch();
+    BranchNameKey branch = file.branch();
     PatchSetArgument psa = psaMasquerades.get(branch);
     boolean visible = true; // invisible psas are filtered out by commandline
     boolean isMasqueraded = false;
@@ -91,10 +91,10 @@
       visible = canRead(branch);
     } else {
       isMasqueraded = true;
-      branch = new Branch.NameKey(psa.change.getProject(), psa.patchSet.getId().toRefName());
+      branch = BranchNameKey.create(psa.change.getProject(), psa.patchSet.refName());
     }
 
-    Project.NameKey project = file.branch().getParentKey();
+    Project.NameKey project = file.branch().project();
     TaskConfig cfg =
         isMasqueraded
             ? new TaskConfig(branch, file, visible, isMasqueraded)
@@ -110,11 +110,11 @@
     return cfg;
   }
 
-  public boolean canRead(Branch.NameKey branch) {
+  public boolean canRead(BranchNameKey branch) {
     try {
       PermissionBackend.ForProject permissions =
-          permissionBackend.user(user).project(branch.getParentKey());
-      permissions.ref(branch.get()).check(RefPermission.READ);
+          permissionBackend.user(user).project(branch.project());
+      permissions.ref(branch.branch()).check(RefPermission.READ);
       return true;
     } catch (AuthException | PermissionBackendException e) {
       return false;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
index 481cbd5..c56202d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
@@ -15,7 +15,7 @@
 package com.googlesource.gerrit.plugins.task;
 
 import com.google.auto.value.AutoValue;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.entities.BranchNameKey;
 
 /** An immutable reference to a task in task config file. */
 @AutoValue
@@ -37,7 +37,7 @@
     return new AutoValue_TaskKey(section, task);
   }
 
-  public Branch.NameKey branch() {
+  public BranchNameKey branch() {
     return subSection().file().branch();
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
index c99b6a2..47b9e75 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -17,12 +17,12 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountResolver;
@@ -216,7 +216,7 @@
 
     protected final Properties properties;
     protected final TaskKey taskKey;
-    protected Map<Branch.NameKey, List<Node>> nodesByBranch;
+    protected Map<BranchNameKey, List<Node>> nodesByBranch;
     protected boolean hasUnfilterableSubNodes = false;
 
     protected Node() { // Only for Invalid
@@ -449,7 +449,7 @@
     public class ApplicableNodeFilter {
       protected MatchCache matchCache;
       protected PredicateCache pcache;
-      protected Branch.NameKey branch = getChangeData().change().getDest();
+      protected BranchNameKey branch = getChangeData().change().getDest();
 
       public ApplicableNodeFilter(MatchCache matchCache)
           throws ConfigInvalidException, IOException, StorageException {
@@ -533,18 +533,18 @@
     return p.toString();
   }
 
-  protected Branch.NameKey resolveUserBranch(String user)
+  protected BranchNameKey resolveUserBranch(String user)
       throws ConfigInvalidException, IOException, StorageException {
     if (user == null) {
       throw new ConfigInvalidException("External user not defined");
     }
     Account.Id acct;
     try {
-      acct = accountResolver.resolve(user).asUnique().getAccount().getId();
+      acct = accountResolver.resolve(user).asUnique().account().id();
     } catch (UnprocessableEntityException e) {
       throw new ConfigInvalidException("Cannot resolve user: " + user);
     }
-    return new Branch.NameKey(allUsers.get(), RefNames.refsUsers(acct));
+    return BranchNameKey.create(allUsers.get(), RefNames.refsUsers(acct));
   }
 
   protected static List<Node> refresh(List<Node> nodes)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/cli/PatchSetArgument.java b/src/main/java/com/googlesource/gerrit/plugins/task/cli/PatchSetArgument.java
index 9fbddeb..e0e2317 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/cli/PatchSetArgument.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/cli/PatchSetArgument.java
@@ -13,10 +13,10 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.task.cli;
 
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -48,7 +48,7 @@
     public PatchSetArgument createForArgument(String token) {
       try {
         PatchSet.Id patchSetId = parsePatchSet(token);
-        ChangeNotes changeNotes = notesFactory.createChecked(patchSetId.getParentKey());
+        ChangeNotes changeNotes = notesFactory.createChecked(patchSetId.changeId());
         permissionBackend.user(user).change(changeNotes).check(ChangePermission.READ);
         return new PatchSetArgument(changeNotes.getChange(), psUtil.get(changeNotes, patchSetId));
       } catch (PermissionBackendException | AuthException e) {
@@ -90,7 +90,7 @@
   }
 
   public void ensureLatest() {
-    if (!change.currentPatchSetId().equals(patchSet.getId())) {
+    if (!change.currentPatchSetId().equals(patchSet.id())) {
       throw new IllegalArgumentException(patchSet + " is not the latest patch set");
     }
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
index 1f7f529..2f27439 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
@@ -14,8 +14,8 @@
 
 package com.googlesource.gerrit.plugins.task;
 
-import com.google.gerrit.reviewdb.client.Branch;
-import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import junit.framework.TestCase;
@@ -185,6 +185,6 @@
   }
 
   protected static FileKey createFileKey(String project, String branch, String file) {
-    return FileKey.create(new Branch.NameKey(new Project.NameKey(project), branch), file);
+    return FileKey.create(BranchNameKey.create(Project.NameKey.parse(project), branch), file);
   }
 }
diff --git a/test/docker/gerrit/Dockerfile b/test/docker/gerrit/Dockerfile
index b9d1715..94c40ff 100755
--- a/test/docker/gerrit/Dockerfile
+++ b/test/docker/gerrit/Dockerfile
@@ -1,4 +1,4 @@
-FROM gerritcodereview/gerrit:3.0.16-ubuntu18
+FROM gerritcodereview/gerrit:3.1.15-ubuntu18
 
 USER root
 
diff --git a/tools/playbooks/install_python3-distutils.yaml b/tools/playbooks/install_python3-distutils.yaml
new file mode 100644
index 0000000..75f5a2a
--- /dev/null
+++ b/tools/playbooks/install_python3-distutils.yaml
@@ -0,0 +1,10 @@
+- hosts: all
+  roles:
+    - name: ensure-python
+  tasks:
+    - name: Install python3-distutils
+      become: true
+      package:
+        name:
+          - python3-distutils
+        state: present