Merge "Support project specific plugin configuration in own config file"
This commit is contained in:
@@ -584,9 +584,9 @@ The plugin configuration is loaded only once and is then cached.
|
||||
Similar to changes in 'gerrit.config', changes to the plugin
|
||||
configuration file will only become effective after a Gerrit restart.
|
||||
|
||||
[[project-specific-configuration]]
|
||||
Project Specific Configuration
|
||||
------------------------------
|
||||
[[simple-project-specific-configuration]]
|
||||
Simple Project Specific Configuration in `project.config`
|
||||
---------------------------------------------------------
|
||||
|
||||
In Gerrit, project specific configuration is stored in the project's
|
||||
`project.config` file on the `refs/meta/config` branch. If a plugin
|
||||
@@ -598,12 +598,12 @@ This approach of storing the plugin configuration is only suitable for
|
||||
plugins that have a simple configuration that only consists of
|
||||
key-value pairs. With this approach it is not possible to have
|
||||
subsections in the plugin configuration. Plugins that require a complex
|
||||
configuration need to store their configuration in their own
|
||||
configuration file where they can make use of subsections. On the other
|
||||
hand storing the plugin configuration in a 'plugin' subsection in the
|
||||
`project.config` file has the advantage that project owners have all
|
||||
configuration parameters in one file, instead of having one
|
||||
configuration file per plugin.
|
||||
configuration need to store their configuration in their
|
||||
link:#project-specific-configuration[own configuration file] where they
|
||||
can make use of subsections. On the other hand storing the plugin
|
||||
configuration in a 'plugin' subsection in the `project.config` file has
|
||||
the advantage that project owners have all configuration parameters in
|
||||
one file, instead of having one configuration file per plugin.
|
||||
|
||||
To avoid conflicts with other plugins, it is recommended that plugins
|
||||
only use the `plugin` subsection with their own name. For example the
|
||||
@@ -648,6 +648,49 @@ Project owners can edit the project configuration by fetching the
|
||||
`refs/meta/config` branch, editing the `project.config` file and
|
||||
pushing the commit back.
|
||||
|
||||
[[project-specific-configuration]]
|
||||
Project Specific Configuration in own config file
|
||||
-------------------------------------------------
|
||||
|
||||
Plugins can store their project specific configuration in an own
|
||||
configuration file in the projects `refs/meta/config` branch.
|
||||
This makes sense if the plugins project specific configuration is
|
||||
rather complex and requires the usage of subsections. Plugins that
|
||||
have a simple key-value pair configuration can store their project
|
||||
specific configuration in a link:#simple-project-specific-configuration[
|
||||
`plugin` subsection of the `project.config` file].
|
||||
|
||||
The plugin configuration file in the `refs/meta/config` branch must be
|
||||
named after the plugin. For example a configuration file for a
|
||||
`default-reviewer` plugin could look like this:
|
||||
|
||||
.default-reviewer.config
|
||||
----
|
||||
[branch "refs/heads/master"]
|
||||
reviewer = Project Owners
|
||||
reviewer = john.doe@example.com
|
||||
[match "file:^.*\.txt"]
|
||||
reviewer = My Info Developers
|
||||
----
|
||||
|
||||
Via the `com.google.gerrit.server.config.PluginConfigFactory` class a
|
||||
plugin can easily access its project specific configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Inject
|
||||
private com.google.gerrit.server.config.PluginConfigFactory cfg;
|
||||
|
||||
[...]
|
||||
|
||||
String[] reviewers = cfg.getProjectPluginConfig(project, "default-reviewer")
|
||||
.getStringList("branch", "refs/heads/master", "reviewer");
|
||||
----
|
||||
|
||||
Project owners can edit the project configuration by fetching the
|
||||
`refs/meta/config` branch, editing the `<plugin-name>.config` file and
|
||||
pushing the commit back.
|
||||
|
||||
[[capabilities]]
|
||||
Plugin Owned Capabilities
|
||||
-------------------------
|
||||
|
@@ -28,6 +28,7 @@ import org.eclipse.jgit.api.AddCommand;
|
||||
import org.eclipse.jgit.api.CheckoutCommand;
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.CommitCommand;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
@@ -178,6 +179,12 @@ public class GitUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void fetch(Git git, String spec) throws GitAPIException {
|
||||
FetchCommand fetch = git.fetch();
|
||||
fetch.setRefSpecs(new RefSpec(spec));
|
||||
fetch.call();
|
||||
}
|
||||
|
||||
public static void checkout(Git git, String name) throws GitAPIException {
|
||||
CheckoutCommand checkout = git.checkout();
|
||||
checkout.setName(name);
|
||||
|
@@ -0,0 +1,102 @@
|
||||
// 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.acceptance.rest.project;
|
||||
|
||||
import static com.google.gerrit.acceptance.git.GitUtil.checkout;
|
||||
import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
|
||||
import static com.google.gerrit.acceptance.git.GitUtil.createProject;
|
||||
import static com.google.gerrit.acceptance.git.GitUtil.fetch;
|
||||
import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.AccountCreator;
|
||||
import com.google.gerrit.acceptance.SshSession;
|
||||
import com.google.gerrit.acceptance.TestAccount;
|
||||
import com.google.gerrit.acceptance.git.PushOneCommit;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ProjectLevelConfigIT extends AbstractDaemonTest {
|
||||
|
||||
@Inject
|
||||
private AccountCreator accounts;
|
||||
|
||||
@Inject
|
||||
private SchemaFactory<ReviewDb> reviewDbProvider;
|
||||
|
||||
@Inject
|
||||
private ProjectCache projectCache;
|
||||
|
||||
private ReviewDb db;
|
||||
private TestAccount admin;
|
||||
private String project;
|
||||
private Git git;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
admin = accounts.admin();
|
||||
initSsh(admin);
|
||||
SshSession sshSession = new SshSession(server, admin);
|
||||
|
||||
project = "p";
|
||||
createProject(sshSession, project, null, true);
|
||||
git = cloneProject(sshSession.getUrl() + "/" + project);
|
||||
fetch(git, GitRepositoryManager.REF_CONFIG + ":refs/heads/config");
|
||||
checkout(git, "refs/heads/config");
|
||||
|
||||
db = reviewDbProvider.open();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessProjectSpecificConfig() throws GitAPIException, IOException {
|
||||
String configName = "test.config";
|
||||
Config cfg = new Config();
|
||||
cfg.setString("s1", null, "k1", "v1");
|
||||
cfg.setString("s2", "ss", "k2", "v2");
|
||||
PushOneCommit push =
|
||||
new PushOneCommit(db, admin.getIdent(), "Create Project Level Config",
|
||||
configName, cfg.toText());
|
||||
push.to(git, GitRepositoryManager.REF_CONFIG);
|
||||
|
||||
ProjectState state = projectCache.get(new Project.NameKey(project));
|
||||
assertEquals(cfg.toText(), state.getConfig(configName).get().toText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExistingConfig() {
|
||||
ProjectState state = projectCache.get(new Project.NameKey(project));
|
||||
assertEquals("", state.getConfig("test.config").get().toText());
|
||||
}
|
||||
}
|
@@ -179,4 +179,27 @@ public class PluginConfigFactory {
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration for the specified plugin that is stored in the
|
||||
* '<plugin-name>.config' file in the 'refs/meta/config' branch of the
|
||||
* specified project.
|
||||
*
|
||||
* @param projectName the name of the project for which the plugin
|
||||
* configuration should be returned
|
||||
* @param pluginName the name of the plugin for which the configuration should
|
||||
* be returned
|
||||
* @return the plugin configuration from the '<plugin-name>.config' file of
|
||||
* the specified project
|
||||
* @throws NoSuchProjectException thrown if the specified project does not
|
||||
* exist
|
||||
*/
|
||||
public Config getProjectPluginConfig(Project.NameKey projectName,
|
||||
String pluginName) throws NoSuchProjectException {
|
||||
ProjectState projectState = projectCache.get(projectName);
|
||||
if (projectState == null) {
|
||||
throw new NoSuchProjectException(projectName);
|
||||
}
|
||||
return projectState.getConfig(pluginName + ".config").get();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,57 @@
|
||||
// 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.server.git;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Configuration file in the projects refs/meta/config branch. */
|
||||
public class ProjectLevelConfig extends VersionedMetaData {
|
||||
private final String fileName;
|
||||
private Config cfg;
|
||||
|
||||
public ProjectLevelConfig(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return GitRepositoryManager.REF_CONFIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
cfg = readConfig(fileName);
|
||||
}
|
||||
|
||||
public Config get() {
|
||||
if (cfg == null) {
|
||||
cfg = new Config();
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSave(CommitBuilder commit) throws IOException,
|
||||
ConfigInvalidException {
|
||||
if (commit.getMessage() == null || "".equals(commit.getMessage())) {
|
||||
commit.setMessage("Updated configuration\n");
|
||||
}
|
||||
saveConfig(fileName, cfg);
|
||||
}
|
||||
}
|
@@ -40,12 +40,14 @@ import com.google.gerrit.server.config.AllProjectsName;
|
||||
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.ProjectLevelConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import com.googlecode.prolog_cafe.compiler.CompileException;
|
||||
import com.googlecode.prolog_cafe.lang.PrologMachineCopy;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
@@ -83,6 +85,7 @@ public class ProjectState {
|
||||
private final List<CommentLinkInfo> commentLinks;
|
||||
|
||||
private final ProjectConfig config;
|
||||
private final Map<String, ProjectLevelConfig> configs;
|
||||
private final Set<AccountGroup.UUID> localOwners;
|
||||
|
||||
/** Prolog rule state. */
|
||||
@@ -121,6 +124,7 @@ public class ProjectState {
|
||||
this.rulesCache = rulesCache;
|
||||
this.commentLinks = commentLinks;
|
||||
this.config = config;
|
||||
this.configs = Maps.newHashMap();
|
||||
this.capabilities = isAllProjects
|
||||
? new CapabilityCollection(config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES))
|
||||
: null;
|
||||
@@ -216,6 +220,29 @@ public class ProjectState {
|
||||
return config;
|
||||
}
|
||||
|
||||
public ProjectLevelConfig getConfig(String fileName) {
|
||||
if (configs.containsKey(fileName)) {
|
||||
return configs.get(fileName);
|
||||
}
|
||||
|
||||
ProjectLevelConfig cfg = new ProjectLevelConfig(fileName);
|
||||
try {
|
||||
Repository git = gitMgr.openRepository(getProject().getNameKey());
|
||||
try {
|
||||
cfg.load(git);
|
||||
} finally {
|
||||
git.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to load " + fileName + " for " + getProject().getName(), e);
|
||||
} catch (ConfigInvalidException e) {
|
||||
log.warn("Failed to load " + fileName + " for " + getProject().getName(), e);
|
||||
}
|
||||
|
||||
configs.put(fileName, cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
public long getMaxObjectSizeLimit() {
|
||||
return config.getMaxObjectSizeLimit();
|
||||
}
|
||||
|
Reference in New Issue
Block a user