diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index 95f960fcc4..2701810ba5 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -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. diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK index 89153538d3..b04c345cf3 100644 --- a/gerrit-pgm/BUCK +++ b/gerrit-pgm/BUCK @@ -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', diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java new file mode 100644 index 0000000000..45ca87bb0c --- /dev/null +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsConfig.java @@ -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()); + } + } +} diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsNameOnInitProvider.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsNameOnInitProvider.java new file mode 100644 index 0000000000..1c9415ade0 --- /dev/null +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/AllProjectsNameOnInitProvider.java @@ -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 { + 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; + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java index e4ee1bff73..3a29587bfb 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java @@ -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"; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java index e2cee307db..79ffb1f0d4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java @@ -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) {