Merge branch 'stable-3.1' into stable-3.2

* stable-3.1:
  When writing temporary plugin files, ensure the directory exists
  Update git submodules
  Update git submodules
  Fix gr-label-info test
  Set version to 2.16.21-SNAPSHOT
  Set version to 2.16.20
  Trim parameterized strings evaluated from LdapRealm
  Update account full name when it changes in LDAP
  Set Api version for plugin jars
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  e2e-tests: Add {Approve|Submit}Change core scenarios
  Hide "No Votes" notice for for labels added and approved by rules

Change-Id: I5c2450f3ead115d143102eeaff990e8daf66f5e8
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json
new file mode 100644
index 0000000..670aa9f
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json
@@ -0,0 +1,5 @@
+{
+  "labels": {
+    "Code-Review": 2
+  }
+}
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
new file mode 100644
index 0000000..3577a6a
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -0,0 +1,6 @@
+[
+  {
+    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "number": "NUMBER"
+  }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index c267ab3..b4ee549 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
 [
   {
     "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
-    "project": "_PROJECT"
+    "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 53b947a..3577a6a 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
 [
   {
     "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
-    "number": "_NUMBER"
+    "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
new file mode 100644
index 0000000..a371757
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -0,0 +1,5 @@
+[
+  {
+    "url": "http://HOSTNAME:HTTP_PORT/a/changes/"
+  }
+]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
new file mode 100644
index 0000000..fe46bd6
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.scenarios
+
+import io.gatling.core.Predef.{atOnceUsers, _}
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+class ApproveChange extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private var createChange: Option[CreateChange] = None
+
+  def this(createChange: CreateChange) {
+    this()
+    this.createChange = Some(createChange)
+  }
+
+  val test: ScenarioBuilder = scenario(unique)
+      .feed(data)
+      .exec(session => {
+        if (createChange.nonEmpty) {
+          session.set("number", createChange.get.number)
+        } else {
+          session
+        }
+      })
+      .exec(http(unique)
+          .post("${url}${number}/revisions/current/review")
+          .body(ElFileBody(body)).asJson)
+
+  setUp(
+    test.inject(
+      atOnceUsers(1)
+    )).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
index 57e6bcd..c7fb8ed 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
@@ -21,26 +21,31 @@
 
 import scala.concurrent.duration._
 
-class CreateChange extends GerritSimulation {
+class CreateChange extends ProjectSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  private val default: String = name
   private val numberKey = "_number"
+  var number = 0
 
   override def relativeRuntimeWeight = 2
 
-  private val test: ScenarioBuilder = scenario(unique)
+  def this(default: String) {
+    this()
+    this.default = default
+  }
+
+  val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(httpRequest
           .body(ElFileBody(body)).asJson
           .check(regex("\"" + numberKey + "\":(\\d+),").saveAs(numberKey)))
       .exec(session => {
-        deleteChange.number = Some(session(numberKey).as[Int])
+        number = session(numberKey).as[Int]
         session
       })
 
   private val createProject = new CreateProject(default)
   private val deleteProject = new DeleteProject(default)
-  private val deleteChange = new DeleteChange
+  private val deleteChange = new DeleteChange(this)
 
   setUp(
     createProject.test.inject(
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
index 1b3bbc1..aa6fe0d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
@@ -21,15 +21,20 @@
 
 class DeleteChange extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  var number: Option[Int] = None
+  private var createChange: Option[CreateChange] = None
 
   override def relativeRuntimeWeight = 2
 
+  def this(createChange: CreateChange) {
+    this()
+    this.createChange = Some(createChange)
+  }
+
   val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(session => {
-        if (number.nonEmpty) {
-          session.set("number", number.get)
+        if (createChange.nonEmpty) {
+          session.set("number", createChange.get.number)
         } else {
           session
         }
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index b427c0d..5d6176d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -66,7 +66,8 @@
       val precedes = replaceKeyWith("_number", 0, number.toString)
       replaceProperty("number", 1, precedes)
     case ("project", project) =>
-      val precedes = replaceKeyWith("_project", name, project.toString)
+      var precedes = replaceKeyWith("_project", name, project.toString)
+      precedes = replaceOverride(precedes)
       replaceProperty("project", precedes)
     case ("entries", entries) =>
       replaceProperty("projects_entries", "1", entries.toString)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
new file mode 100644
index 0000000..2f67274
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.scenarios
+
+import io.gatling.core.Predef.{atOnceUsers, _}
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+import scala.concurrent.duration._
+
+class SubmitChange extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private val default: String = name
+
+  private val test: ScenarioBuilder = scenario(unique)
+      .feed(data)
+      .exec(session => {
+        session.set("number", createChange.number)
+      })
+      .exec(http(unique).post("${url}${number}/submit"))
+
+  private val createProject = new CreateProject(default)
+  private val createChange = new CreateChange(default)
+  private val approveChange = new ApproveChange(createChange)
+  private val deleteProject = new DeleteProject(default)
+
+  setUp(
+    createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
+      atOnceUsers(1)
+    ),
+    createChange.test.inject(
+      nothingFor(stepWaitTime(createChange) seconds),
+      atOnceUsers(1)
+    ),
+    approveChange.test.inject(
+      nothingFor(stepWaitTime(approveChange) seconds),
+      atOnceUsers(1)
+    ),
+    test.inject(
+      nothingFor(stepWaitTime(this) seconds),
+      atOnceUsers(1)
+    ),
+    deleteProject.test.inject(
+      nothingFor(stepWaitTime(deleteProject) seconds),
+      atOnceUsers(1)
+    ),
+  ).protocols(httpProtocol)
+}
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index 345da81..7a5b1aa 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -18,6 +18,7 @@
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -78,8 +79,9 @@
   private final boolean autoUpdateAccountActiveStatus;
   private final SetInactiveFlag setInactiveFlag;
 
+  @VisibleForTesting
   @Inject
-  AccountManager(
+  public AccountManager(
       Sequences sequences,
       @GerritServerConfig Config cfg,
       Accounts accounts,
@@ -239,13 +241,7 @@
 
     if (!Strings.isNullOrEmpty(who.getDisplayName())
         && !Objects.equals(user.getAccount().fullName(), who.getDisplayName())) {
-      if (realm.allowsEdit(AccountFieldName.FULL_NAME)) {
-        accountUpdates.add(a -> a.setFullName(who.getDisplayName()));
-      } else {
-        logger.atWarning().log(
-            "Not changing already set display name '%s' to '%s'",
-            user.getAccount().fullName(), who.getDisplayName());
-      }
+      accountUpdates.add(a -> a.setFullName(who.getDisplayName()));
     }
 
     if (!realm.allowsEdit(AccountFieldName.USER_NAME)
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index c53ba83..1421f17 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -218,7 +218,7 @@
       values.put(name, m.get(name));
     }
 
-    String r = p.replace(values);
+    String r = p.replace(values).trim();
     return r.isEmpty() ? null : r;
   }
 
diff --git a/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index 5b80059..82f97c9 100644
--- a/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -110,9 +110,6 @@
 
   public static Path storeInTemp(String pluginName, InputStream in, SitePaths sitePaths)
       throws IOException {
-    if (!Files.exists(sitePaths.tmp_dir)) {
-      Files.createDirectories(sitePaths.tmp_dir);
-    }
     return PluginUtil.asTemp(in, tempNameFor(pluginName), ".jar", sitePaths.tmp_dir);
   }
 
diff --git a/java/com/google/gerrit/server/plugins/PluginUtil.java b/java/com/google/gerrit/server/plugins/PluginUtil.java
index d4110ca..932a01d 100644
--- a/java/com/google/gerrit/server/plugins/PluginUtil.java
+++ b/java/com/google/gerrit/server/plugins/PluginUtil.java
@@ -53,6 +53,7 @@
   }
 
   static Path asTemp(InputStream in, String prefix, String suffix, Path dir) throws IOException {
+    Files.createDirectories(dir);
     Path tmp = Files.createTempFile(dir, prefix, suffix);
     boolean keep = false;
     try (OutputStream out = Files.newOutputStream(tmp)) {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index a09284e..b41a2f3 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -26,6 +26,8 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.client.AccountFieldName;
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountException;
 import com.google.gerrit.server.account.AccountManager;
@@ -33,12 +35,16 @@
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.AuthResult;
+import com.google.gerrit.server.account.SetInactiveFlag;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIdNotes;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.notedb.Sequences;
+import com.google.gerrit.server.ssh.SshKeyCache;
 import com.google.inject.Inject;
+import com.google.inject.util.Providers;
 import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jgit.lib.Repository;
@@ -51,6 +57,12 @@
   @Inject @ServerInitiated private AccountsUpdate accountsUpdate;
   @Inject private ExternalIdNotes.Factory extIdNotesFactory;
 
+  @Inject private Sequences sequences;
+  @Inject private IdentifiedUser.GenericFactory userFactory;
+  @Inject private SshKeyCache sshKeyCache;
+  @Inject private GroupsUpdate.Factory groupsUpdateFactory;
+  @Inject private SetInactiveFlag setInactiveFlag;
+
   @Test
   public void authenticateNewAccountWithEmail() throws Exception {
     String email = "foo@example.com";
@@ -200,6 +212,31 @@
 
   @Test
   public void authenticateWithUsernameAndUpdateDisplayName() throws Exception {
+    authenticateWithUsernameAndUpdateDisplayName(accountManager);
+  }
+
+  @Test
+  public void readOnlyFullNameField_authenticateWithUsernameAndUpdateDisplayName()
+      throws Exception {
+    TestRealm realm = server.getTestInjector().getInstance(TestRealm.class);
+    realm.denyEdit(AccountFieldName.FULL_NAME);
+    authenticateWithUsernameAndUpdateDisplayName(
+        new AccountManager(
+            sequences,
+            cfg,
+            accounts,
+            Providers.of(accountsUpdate),
+            accountCache,
+            realm,
+            userFactory,
+            sshKeyCache,
+            projectCache,
+            externalIds,
+            groupsUpdateFactory,
+            setInactiveFlag));
+  }
+
+  private void authenticateWithUsernameAndUpdateDisplayName(AccountManager am) throws Exception {
     String username = "foo";
     String email = "foo@example.com";
     Account.Id accountId = Account.id(seq.nextAccountId());
@@ -215,7 +252,7 @@
     AuthRequest who = AuthRequest.forUser(username);
     String newName = "Updated Name";
     who.setDisplayName(newName);
-    AuthResult authResult = accountManager.authenticate(who);
+    AuthResult authResult = am.authenticate(who);
     assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
 
     Optional<AccountState> accountState = accounts.get(accountId);
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
index 2167d27..673379d 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/BUILD
@@ -1,3 +1,4 @@
+load("@rules_java//java:defs.bzl", "java_library")
 load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
 
 acceptance_tests(
@@ -9,8 +10,18 @@
         "no_windows",
     ],
     deps = [
+        ":util",
         "//java/com/google/gerrit/git",
         "//java/com/google/gerrit/mail",
         "//java/com/google/gerrit/server/util/time",
     ],
 )
+
+java_library(
+    name = "util",
+    testonly = True,
+    srcs = glob(["TestRealm.java"]),
+    deps = [
+        "//java/com/google/gerrit/acceptance:lib",
+    ],
+)
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/TestRealm.java b/javatests/com/google/gerrit/acceptance/api/accounts/TestRealm.java
new file mode 100644
index 0000000..94b6cd3
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/TestRealm.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.api.accounts;
+
+import com.google.gerrit.extensions.client.AccountFieldName;
+import com.google.gerrit.server.account.DefaultRealm;
+import com.google.gerrit.server.account.EmailExpander;
+import com.google.gerrit.server.account.Emails;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.HashSet;
+import java.util.Set;
+
+@Singleton
+public class TestRealm extends DefaultRealm {
+
+  private final Set<AccountFieldName> readOnlyFields = new HashSet<>();
+
+  @Inject
+  public TestRealm(EmailExpander emailExpander, Provider<Emails> emails, AuthConfig authConfig) {
+    super(emailExpander, emails, authConfig);
+  }
+
+  public void denyEdit(AccountFieldName field) {
+    readOnlyFields.add(field);
+  }
+
+  @Override
+  public boolean allowsEdit(AccountFieldName field) {
+    return !readOnlyFields.contains(field);
+  }
+}
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
index 2c5589f..22bdce1 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
@@ -170,6 +170,11 @@
    *    order to trigger computation when a label is removed from the change.
    */
   _computeShowPlaceholder(labelInfo, changeLabelsRecord) {
+    if (labelInfo &&
+        !labelInfo.values && (labelInfo.rejected || labelInfo.approved)) {
+      return 'hidden';
+    }
+
     if (labelInfo && labelInfo.all) {
       for (const label of labelInfo.all) {
         if (label.value && label.value != labelInfo.default_value) {
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
index a340550..d7ccc45 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_test.html
@@ -232,6 +232,18 @@
     element.labelInfo = {all: [{value: 1}]};
     assert.isTrue(isHidden(element.shadowRoot
         .querySelector('.placeholder')));
+    element.labelInfo = {rejected: []};
+    assert.isTrue(isHidden(element.shadowRoot
+        .querySelector('.placeholder')));
+    element.labelInfo = {values: [], rejected: [], all: [{value: 1}]};
+    assert.isTrue(isHidden(element.shadowRoot
+        .querySelector('.placeholder')));
+    element.labelInfo = {approved: []};
+    assert.isTrue(isHidden(element.shadowRoot
+        .querySelector('.placeholder')));
+    element.labelInfo = {values: [], approved: [], all: [{value: 1}]};
+    assert.isTrue(isHidden(element.shadowRoot
+        .querySelector('.placeholder')));
   });
 });
 </script>
diff --git a/tools/bzl/plugin.bzl b/tools/bzl/plugin.bzl
index 3cffb79..61b12f1 100644
--- a/tools/bzl/plugin.bzl
+++ b/tools/bzl/plugin.bzl
@@ -1,5 +1,6 @@
 load("@rules_java//java:defs.bzl", "java_binary", "java_library")
 load("//tools/bzl:genrule2.bzl", "genrule2")
+load("//:version.bzl", "GERRIT_VERSION")
 
 PLUGIN_DEPS = ["//plugins:plugin-lib"]
 
@@ -63,7 +64,7 @@
             "GEN_VERSION=$$(cat bazel-out/stable-status.txt | grep -w STABLE_BUILD_%s_LABEL | cut -d ' ' -f 2)" % dir_name.upper(),
             "cd $$TMP",
             "unzip -q $$ROOT/$<",
-            "echo \"Implementation-Version: $$GEN_VERSION\n$$(cat META-INF/MANIFEST.MF)\" > META-INF/MANIFEST.MF",
+            "echo \"Implementation-Version: $$GEN_VERSION\nGerrit-ApiVersion: " + GERRIT_VERSION + "\n$$(cat META-INF/MANIFEST.MF)\" > META-INF/MANIFEST.MF",
             "find . -exec touch '{}' ';'",
             "zip -Xqr $$ROOT/$@ .",
         ]),