Let JGit read system and user-level config from $SITE/etc/jgit.config
A support for system-level (or user-level) JGit configuration was
suboptimal. Gerrit admins had to use either $HOME/.gitconfig of the user
owning Gerrit process or a system-wide location for global JGit
configuration for all repositories served by a Gerrit site.
Replace the system-wide and user-global JGit configuration to be read from
$SITE/etc/jgit.config.
Change-Id: I5e60e2d4d686b605673fe14665479c3f8cc2a1e2
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 7e9f1e1..67cd0f9 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1577,6 +1577,10 @@
[[core]]
=== Section core
+[NOTE]
+The link:#jgitConfig[etc/jgit.config] file supports configuration of all JGit
+options.
+
[[core.packedGitWindowSize]]core.packedGitWindowSize::
+
Number of bytes of a pack file to load into memory in a single
@@ -4924,6 +4928,19 @@
+
* link:config-themes.html[Themes]
+[[jgitConfig]]
+== File `etc/jgit.config`
+
+Gerrit uses the `$site_path/etc/jgit.config` file instead of the
+system-wide and user-global Git configuration for its runtime JGit
+configuration.
+
+Sample `etc/jgit.config` file:
+----
+[core]
+ trustFolderStat = false
+----
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index 1fd28cc..f406acb 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -37,6 +37,7 @@
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.httpd.raw.StaticModule;
import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
import com.google.gerrit.pgm.util.LogFileCompressor;
@@ -70,6 +71,7 @@
import com.google.gerrit.server.git.GarbageCollectionModule;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
+import com.google.gerrit.server.git.SystemReaderInstaller;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
@@ -263,6 +265,13 @@
} else {
modules.add(new GerritServerConfigModule());
}
+ modules.add(
+ new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(SystemReaderInstaller.class);
+ }
+ });
modules.add(new DropWizardMetricMaker.ApiModule());
return Guice.createInjector(
PRODUCTION, LibModuleLoader.loadModules(cfgInjector, LibModuleType.DB_MODULE));
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index eed307f..98558fb 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -18,6 +18,7 @@
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.Die;
+import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.metrics.DisabledMetricMaker;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
@@ -28,6 +29,7 @@
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
+import com.google.gerrit.server.git.SystemReaderInstaller;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.server.securestore.SecureStoreClassName;
import com.google.inject.AbstractModule;
@@ -106,6 +108,13 @@
});
}
+ modules.add(
+ new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(SystemReaderInstaller.class);
+ }
+ });
Module configModule = new GerritServerConfigModule();
modules.add(configModule);
modules.add(
diff --git a/java/com/google/gerrit/server/config/SitePaths.java b/java/com/google/gerrit/server/config/SitePaths.java
index 47b6336..ee95c6f 100644
--- a/java/com/google/gerrit/server/config/SitePaths.java
+++ b/java/com/google/gerrit/server/config/SitePaths.java
@@ -54,6 +54,8 @@
public final Path secure_config;
public final Path notedb_config;
+ public final Path jgit_config;
+
public final Path ssl_keystore;
public final Path ssh_key;
public final Path ssh_rsa;
@@ -99,6 +101,8 @@
secure_config = etc_dir.resolve("secure.config");
notedb_config = etc_dir.resolve("notedb.config");
+ jgit_config = etc_dir.resolve("jgit.config");
+
ssl_keystore = etc_dir.resolve("keystore");
ssh_key = etc_dir.resolve("ssh_host_key");
ssh_rsa = etc_dir.resolve("ssh_host_rsa_key");
diff --git a/java/com/google/gerrit/server/git/SystemReaderInstaller.java b/java/com/google/gerrit/server/git/SystemReaderInstaller.java
new file mode 100644
index 0000000..520ede4
--- /dev/null
+++ b/java/com/google/gerrit/server/git/SystemReaderInstaller.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2017 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 com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
+
+@Singleton
+public class SystemReaderInstaller implements LifecycleListener {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final SitePaths site;
+
+ @Inject
+ SystemReaderInstaller(SitePaths site) {
+ this.site = site;
+ }
+
+ @Override
+ public void start() {
+ SystemReader.setInstance(customReader());
+ logger.atInfo().log("Set JGit's SystemReader to read system config from %s", site.jgit_config);
+ }
+
+ @Override
+ public void stop() {}
+
+ private SystemReader customReader() {
+ SystemReader current = SystemReader.getInstance();
+
+ FileBasedConfig jgitConfig = new FileBasedConfig(site.jgit_config.toFile(), FS.DETECTED);
+
+ return new SystemReader() {
+ @Override
+ public String getHostname() {
+ return current.getHostname();
+ }
+
+ @Override
+ public String getenv(String variable) {
+ return current.getenv(variable);
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return current.getProperty(key);
+ }
+
+ @Override
+ public FileBasedConfig openUserConfig(Config parent, FS fs) {
+ return current.openSystemConfig(parent, fs);
+ }
+
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return jgitConfig;
+ }
+
+ @Override
+ public long getCurrentTime() {
+ return current.getCurrentTime();
+ }
+
+ @Override
+ public int getTimezone(long when) {
+ return current.getTimezone(when);
+ }
+ };
+ }
+}
diff --git a/javatests/com/google/gerrit/server/git/JGitConfigTest.java b/javatests/com/google/gerrit/server/git/JGitConfigTest.java
new file mode 100644
index 0000000..7cb5a98
--- /dev/null
+++ b/javatests/com/google/gerrit/server/git/JGitConfigTest.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2019 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 static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.config.SitePaths;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class JGitConfigTest {
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private SitePaths site;
+ private Path gitPath;
+
+ @Before
+ public void setUp() throws IOException {
+ site = new SitePaths(temporaryFolder.newFolder().toPath());
+ Files.createDirectories(site.etc_dir);
+ gitPath = Files.createDirectories(site.resolve("git"));
+
+ Files.write(
+ site.jgit_config, "[core]\n trustFolderStat = false\n".getBytes(StandardCharsets.UTF_8));
+ new SystemReaderInstaller(site).start();
+ }
+
+ @Test
+ public void test() throws IOException {
+ try (Repository repo = new FileRepository(gitPath.resolve("foo").toFile())) {
+ assertThat(repo.getConfig().getString("core", null, "trustFolderStat")).isEqualTo("false");
+ }
+ }
+}