From 50cb0e3301aaf0f69c7f3826c481babd27b5a578 Mon Sep 17 00:00:00 2001 From: zml Date: Sun, 15 Jun 2025 21:40:59 -0700 Subject: [PATCH] fix(plugin-gradle): Try to locate the git system config without executing any processes While it's not possible to match cgit's behavior exactly, this attempts to read the system gitconfig correcting a regression from 6.21.0 to 6.22.0. Fixes #2404 --- plugin-gradle/CHANGES.md | 2 + .../gradle/spotless/GitRatchetGradle.java | 92 ++++++++++++++++--- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index e35da34bfc..285e9a256e 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Fixed +* Respect system gitconfig when performing git operations ([#2404](https://github.com/diffplug/spotless/issues/2404)) ## [7.0.4] - 2025-05-27 ### Fixed diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java index b2c3adc459..c58f0d39c4 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,14 @@ package com.diffplug.gradle.spotless; import java.io.File; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; @@ -28,20 +32,86 @@ /** Gradle implementation of GitRatchet. */ public class GitRatchetGradle extends GitRatchet { + private static final String[] GIT_EXEC_CANDIDATES = {"git", "git.exe", "git.cmd"}; + static { - preventJGitFromCallingExecutables(); + GitRatchetGradle.redirectJGitExecutions(); } - static void preventJGitFromCallingExecutables() { - SystemReader reader = SystemReader.getInstance(); - SystemReader.setInstance(new DelegatingSystemReader(reader) { + static void redirectJGitExecutions() { + SystemReader existing = SystemReader.getInstance(); + SystemReader.setInstance(new DelegatingSystemReader(existing) { + private AtomicReference systemConfig = new AtomicReference<>(); + + @Override + public StoredConfig getSystemConfig() throws ConfigInvalidException, IOException { + FileBasedConfig c = systemConfig.get(); + if (c == null) { + systemConfig.compareAndSet(null, + this.openSystemConfig(this.getJGitConfig(), FS.DETECTED)); + c = systemConfig.get(); + } + updateAll(c); + return c; + } + + // lifted from SystemReader since it's private + private void updateAll(Config config) throws ConfigInvalidException, IOException { + if (config == null) { + return; + } + + updateAll(config.getBaseConfig()); + if (config instanceof FileBasedConfig) { + FileBasedConfig cfg = (FileBasedConfig) config; + if (cfg.isOutdated()) { + cfg.load(); + } + } + } + @Override - public String getenv(String variable) { - if ("PATH".equals(variable)) { - return ""; - } else { - return super.getenv(variable); + public FileBasedConfig openSystemConfig(final Config parent, final FS fs) { + // cgit logic: https://git.kernel.org/pub/scm/git/git.git/tree/config.c#n1973 - in git_system_config() + // They check the GIT_CONFIG_SYSTEM env var first, then follow up with logic based on compile-time parameters + // We can't replicate this exactly so we'll do the closest approximation that Gradle will allow. + final String systemPath = this.getenv("GIT_CONFIG_SYSTEM"); + if (systemPath != null) { + fs.setGitSystemConfig(new File(systemPath).getAbsoluteFile()); + return super.openSystemConfig(parent, fs); + } + + // match FS.searchPath + File gitExec = null; + final String path = this.getenv("PATH"); + if (path != null) { + outer: for (final String p : path.split(File.pathSeparator)) { + for (final String name : GIT_EXEC_CANDIDATES) { + final File candidate = new File(p, name); + if (candidate.isFile() && candidate.canExecute()) { + gitExec = candidate.getAbsoluteFile(); + break outer; + } + } + } + } + + // Guess at common locations + if (gitExec != null) { + // If git exec is at /bin/git, this returns + File prefix = gitExec.getParentFile().getParentFile(); + + // Then we try to resolve a config + final File systemConfig = new File(prefix, "etc/gitconfig"); + if (systemConfig.exists()) { + fs.setGitSystemConfig(systemConfig); + return super.openSystemConfig(parent, fs); + } } + + // Fallback to the non-prefixed path (this is not the logic that cgit uses, but oh well) + fs.setGitSystemConfig(new File("/etc/gitconfig")); + return super.openSystemConfig(parent, fs); } }); } @@ -70,7 +140,7 @@ public String getHostname() { @Override public String getenv(String variable) { - return reader.getProperty(variable); + return reader.getenv(variable); } @Override