Enable plugins on init to read/edit the All-Projects project.config

Some plugins may store their configuration in the project.config file
of the All-Projects project. The configuration may depend on user
input during the plugin initalization. Plugins are now able to write
configuration parameters into the All-Projects project.config during
an InitStep.

E.g. plugins may provide functionality which should be
enabled/disabled per project, which is reflected by an 'enabled'
parameter in project.config:

  [plugin "myPlugin"]
    enabled = true

If this parameter is not set, the value should be inherited from the
parent project. Having the possibility to edit the All-Projects
project.config on init the user can decide during the plugin
initialization if the functionality should by default be
enabled/disabled for all projects.

Change-Id: I8e240858e0778469c0d511ff380865f317448e15
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2013-11-27 11:22:34 +01:00
parent 415f8e8405
commit d4cfac1cd6
6 changed files with 234 additions and 7 deletions

View File

@@ -280,6 +280,41 @@ and accessing / changing configuration settings using Section.Factory.
In addition to the standard Gerrit init injections, plugins receive
the @PluginName String injection containing their own plugin name.
During their initialization plugins may get access to the
`project.config` file of the `All-Projects` project and they are able
to store configuration parameters in it. For this a plugin `InitStep`
can get `com.google.gerrit.pgm.init.AllProjectsConfig` injected:
[source,java]
----
public class MyInitStep implements InitStep {
private final String pluginName;
private final ConsoleUI ui;
private final AllProjectsConfig allProjectsConfig;
public MyInitStep(@PluginName String pluginName, ConsoleUI ui,
AllProjectsConfig allProjectsConfig) {
this.pluginName = pluginName;
this.ui = ui;
this.allProjectsConfig = allProjectsConfig;
}
@Override
public void run() throws Exception {
ui.message("\n");
ui.header(pluginName + " Integration");
boolean enabled = ui.yesno(true, "By default enabled for all projects");
Config cfg = allProjectsConfig.load();
if (enabled) {
cfg.setBoolean("plugin", pluginName, "enabled", enabled);
} else {
cfg.unset("plugin", pluginName, "enabled");
}
allProjectsConfig.save(pluginName, "Initialize " + pluginName + " Integration");
}
}
----
Bear in mind that the Plugin's InitStep class will be loaded but
the standard Gerrit runtime environment is not available and the plugin's
own Guice modules were not initialized.

View File

@@ -1,13 +1,15 @@
SRCS = 'src/main/java/com/google/gerrit/pgm/'
INIT_API_SRCS = [SRCS + n for n in [
'init/AllProjectsConfig.java',
'init/AllProjectsNameOnInitProvider.java',
'util/ConsoleUI.java',
'util/Die.java',
'init/InitFlags.java',
'init/InitStep.java',
'init/InitStep.java',
'init/InstallPlugins.java',
'init/Section.java',
'util/ConsoleUI.java',
'util/Die.java',
]]
java_library(
@@ -16,6 +18,7 @@ java_library(
deps = [
'//gerrit-common:server',
'//gerrit-server:server',
'//lib:guava',
'//lib/guice:guice',
'//lib/guice:guice-assistedinject',
'//lib/jgit:jgit',

View File

@@ -0,0 +1,153 @@
// Copyright (C) 2013 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.pgm.init;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.VersionedMetaData;
import com.google.inject.Inject;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
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 java.io.File;
import java.io.IOException;
public class AllProjectsConfig extends VersionedMetaData {
private final String project;
private final File path;
private Config cfg;
private ObjectId revision;
@Inject
AllProjectsConfig(AllProjectsNameOnInitProvider allProjects, SitePaths site,
Section.Factory sections) {
project = allProjects.get();
File basePath = site.resolve(sections.get("gerrit", null).get("basePath"));
if (basePath == null) {
throw new IllegalStateException("gerrit.basePath must be configured");
}
path = FileKey.resolve(new File(basePath, project), FS.DETECTED);
if (path == null) {
throw new IllegalStateException(project + " project not found");
}
}
@Override
protected String getRefName() {
return GitRepositoryManager.REF_CONFIG;
}
public Config load() throws IOException, ConfigInvalidException {
Repository repo = new FileRepository(path);
try {
load(repo);
} finally {
repo.close();
}
return cfg;
}
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
cfg = readConfig(ProjectConfig.PROJECT_CONFIG);
revision = getRevision();
}
@Override
protected void onSave(CommitBuilder commit) throws IOException,
ConfigInvalidException {
throw new UnsupportedOperationException();
}
public void save(String pluginName, String message) throws IOException {
Repository repo = new FileRepository(path);
try {
inserter = repo.newObjectInserter();
reader = repo.newObjectReader();
try {
RevWalk rw = new RevWalk(reader);
try {
RevTree srcTree = revision != null ? rw.parseTree(revision) : null;
newTree = readTree(srcTree);
saveConfig(ProjectConfig.PROJECT_CONFIG, cfg);
ObjectId res = newTree.writeTree(inserter);
if (res.equals(srcTree)) {
// If there are no changes to the content, don't create the commit.
return;
}
PersonIdent ident = new PersonIdent(pluginName, pluginName + "@gerrit");
String msg = "Update from plugin " + pluginName + ": " + message;
CommitBuilder commit = new CommitBuilder();
commit.setAuthor(ident);
commit.setCommitter(ident);
commit.setMessage(msg);
commit.setTreeId(res);
if (revision != null) {
commit.addParentId(revision);
}
ObjectId newRevision = inserter.insert(commit);
updateRef(repo, ident, newRevision, "commit: " + msg);
revision = newRevision;
} finally {
rw.release();
}
} finally {
if (inserter != null) {
inserter.release();
inserter = null;
}
if (reader != null) {
reader.release();
reader = null;
}
}
} finally {
repo.close();
}
}
private void updateRef(Repository repo, PersonIdent ident,
ObjectId newRevision, String refLogMsg) throws IOException {
RefUpdate ru = repo.updateRef(getRefName());
ru.setRefLogIdent(ident);
ru.setNewObjectId(newRevision);
ru.setExpectedOldObjectId(revision);
ru.setRefLogMessage(refLogMsg, false);
RefUpdate.Result r = ru.update();
switch(r) {
case FAST_FORWARD:
case NEW:
case NO_CHANGE:
break;
default:
throw new IOException("Failed to update " + getRefName() + " of "
+ project + ": " + r.name());
}
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (C) 2013 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.pgm.init;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class AllProjectsNameOnInitProvider implements Provider<String> {
private final String name;
@Inject
AllProjectsNameOnInitProvider(Section.Factory sections) {
String n = sections.get("gerrit", null).get("allProjects");
name = Objects.firstNonNull(
Strings.emptyToNull(n), AllProjectsNameProvider.DEFAULT);
}
public String get() {
return name;
}
}

View File

@@ -80,7 +80,7 @@ public class ProjectConfig extends VersionedMetaData {
private static final String KEY_LINK = "link";
private static final String KEY_ENABLED = "enabled";
private static final String PROJECT_CONFIG = "project.config";
public static final String PROJECT_CONFIG = "project.config";
private static final String GROUP_LIST = "groups";
private static final String PROJECT = "project";

View File

@@ -54,9 +54,9 @@ import java.io.IOException;
*/
public abstract class VersionedMetaData {
private RevCommit revision;
private ObjectReader reader;
private ObjectInserter inserter;
private DirCache newTree;
protected ObjectReader reader;
protected ObjectInserter inserter;
protected DirCache newTree;
/** @return name of the reference storing this configuration. */
protected abstract String getRefName();
@@ -319,7 +319,7 @@ public abstract class VersionedMetaData {
};
}
private DirCache readTree(RevTree tree) throws IOException,
protected DirCache readTree(RevTree tree) throws IOException,
MissingObjectException, IncorrectObjectTypeException {
DirCache dc = DirCache.newInCore();
if (tree != null) {