Allow to manipulate All-Projects's group list in init step

The group list in the All-Projects project can now be manipulated
in an init step. This is required to configure access rights
programmatically in an init step.

Change-Id: If9d633b3642131d8d656025ad89beca2d3346132
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 3e76edc..d63d403 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -300,7 +300,7 @@
       ui.message("\n");
       ui.header(pluginName + " Integration");
       boolean enabled = ui.yesno(true, "By default enabled for all projects");
-      Config cfg = allProjectsConfig.load();
+      Config cfg = allProjectsConfig.load().getConfig();
       if (enabled) {
         cfg.setBoolean("plugin", pluginName, "enabled", enabled);
       } else {
diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK
index 3de5a2a..16b21d6 100644
--- a/gerrit-pgm/BUCK
+++ b/gerrit-pgm/BUCK
@@ -16,6 +16,9 @@
     '//lib/guice:guice-assistedinject',
     '//lib/jgit:jgit',
   ],
+  provided_deps = [
+    '//lib/log:api',
+  ],
   visibility = ['PUBLIC'],
 )
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java
index 5f9aade..8fb05ca 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitLabels.java
@@ -44,7 +44,7 @@
 
   @Override
   public void run() throws Exception {
-    Config cfg = allProjectsConfig.load();
+    Config cfg = allProjectsConfig.load().getConfig();
     if (cfg == null || !cfg.getSubsections(KEY_LABEL).contains(LABEL_VERIFIED)) {
       ui.header("Review Labels");
       installVerified = ui.yesno(false, "Install Verified label");
@@ -53,7 +53,7 @@
 
   @Override
   public void postRun() throws Exception {
-    Config cfg = allProjectsConfig.load();
+    Config cfg = allProjectsConfig.load().getConfig();
     if (installVerified) {
       cfg.setString(KEY_LABEL, LABEL_VERIFIED, KEY_FUNCTION, "MaxWithBlock");
       cfg.setStringList(KEY_LABEL, LABEL_VERIFIED, KEY_VALUE,
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
index dfb2f38..303969b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -16,7 +16,9 @@
 
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.GroupList;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.VersionedMetaData;
 import com.google.inject.Inject;
 
@@ -28,21 +30,28 @@
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.util.FS;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
 
 public class AllProjectsConfig extends VersionedMetaData {
+
+  private static final Logger log = LoggerFactory.getLogger(AllProjectsConfig.class);
+
   private final String project;
   private final SitePaths site;
   private final InitFlags flags;
 
   private Config cfg;
   private ObjectId revision;
+  private GroupList groupList;
 
   @Inject
   AllProjectsConfig(AllProjectsNameOnInitProvider allProjects, SitePaths site,
@@ -66,27 +75,46 @@
     return FileKey.resolve(new File(basePath, project), FS.DETECTED);
   }
 
-  public Config load() throws IOException, ConfigInvalidException {
+  public AllProjectsConfig load() throws IOException, ConfigInvalidException {
     File path = getPath();
-    if (path == null) {
-      return null;
+    if (path != null) {
+      Repository repo = new FileRepository(path);
+      try {
+        load(repo);
+      } finally {
+        repo.close();
+      }
     }
+    return this;
+  }
 
-    Repository repo = new FileRepository(path);
-    try {
-      load(repo);
-    } finally {
-      repo.close();
-    }
+  public Config getConfig() throws ConfigInvalidException {
     return cfg;
   }
 
+  public GroupList getGroups() throws ConfigInvalidException {
+    return groupList;
+  }
+
   @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
+    groupList = readGroupList();
     cfg = readConfig(ProjectConfig.PROJECT_CONFIG);
     revision = getRevision();
   }
 
+  private GroupList readGroupList() throws IOException {
+    ValidationError.Sink errors = new ValidationError.Sink() {
+      @Override
+      public void error(ValidationError error) {
+        log.error("Error parsing file " + GroupList.FILE_NAME + ": " + error.getMessage());
+      }
+    };
+    String text = readUTF8(GroupList.FILE_NAME);
+
+    return GroupList.parse(text, errors);
+  }
+
   @Override
   protected boolean onSave(CommitBuilder commit) throws IOException,
       ConfigInvalidException {
@@ -118,6 +146,7 @@
           RevTree srcTree = revision != null ? rw.parseTree(revision) : null;
           newTree = readTree(srcTree);
           saveConfig(ProjectConfig.PROJECT_CONFIG, cfg);
+          saveGroupList();
           ObjectId res = newTree.writeTree(inserter);
           if (res.equals(srcTree)) {
             // If there are no changes to the content, don't create the commit.
@@ -151,6 +180,14 @@
     } finally {
       repo.close();
     }
+
+    // we need to invalidate the JGit cache if the group list is invalidated in
+    // an unattended init step
+    RepositoryCache.clear();
+  }
+
+  private void saveGroupList() throws IOException {
+    saveUTF8(GroupList.FILE_NAME, groupList.asText());
   }
 
   private void updateRef(Repository repo, PersonIdent ident,