-
Notifications
You must be signed in to change notification settings - Fork 489
add git pre push hook #2553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
add git pre push hook #2553
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e6e9a07
add git pre push hook
lazer-dev 6528b0b
spotlessApply
lazer-dev b9f4880
Placeholders for docs.
a790659
maven wrapper support
lazer-dev 525e70e
reinstall support
lazer-dev 259ec38
Modify git-hook docs, with an eye towards a future where there is bot…
2dadeb6
uninstall fix
lazer-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstaller.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| /* | ||
| * Copyright 2025 DiffPlug | ||
| * | ||
| * 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.diffplug.spotless; | ||
|
|
||
| import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
|
||
| import java.io.File; | ||
| import java.io.FileWriter; | ||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
|
|
||
| /** | ||
| * Abstract class responsible for installing a Git pre-push hook in a repository. | ||
| * This class ensures that specific checks and logic are run before a push operation in Git. | ||
| * | ||
| * Subclasses should define specific behavior for hook installation by implementing the required abstract methods. | ||
| */ | ||
| public abstract class GitPrePushHookInstaller { | ||
|
|
||
| /** | ||
| * Logger for recording informational and error messages during the installation process. | ||
| */ | ||
| protected final GitPreHookLogger logger; | ||
|
|
||
| /** | ||
| * The root directory of the Git repository where the hook will be installed. | ||
| */ | ||
| protected final File root; | ||
|
|
||
| /** | ||
| * Constructor to initialize the GitPrePushHookInstaller with a logger and repository root path. | ||
| * | ||
| * @param logger The logger for recording messages. | ||
| * @param root The root directory of the Git repository. | ||
| */ | ||
| public GitPrePushHookInstaller(GitPreHookLogger logger, File root) { | ||
| this.logger = logger; | ||
| this.root = root; | ||
| } | ||
|
|
||
| /** | ||
| * Installs the Git pre-push hook into the repository. | ||
| * | ||
| * <p>This method checks for the following: | ||
| * <ul> | ||
| * <li>Ensures Git is installed and the `.git/config` file exists.</li> | ||
| * <li>Checks if an executor required by the hook is available.</li> | ||
| * <li>Creates and writes the pre-push hook file if it does not exist.</li> | ||
| * <li>Skips installation if the hook is already installed.</li> | ||
| * </ul> | ||
| * If an issue occurs during installation, error messages are logged. | ||
| * | ||
| * @throws Exception if any error occurs during installation. | ||
| */ | ||
| public void install() throws Exception { | ||
| logger.info("Installing git pre-push hook"); | ||
|
|
||
| if (!isGitInstalled()) { | ||
| logger.error("Git not found in root directory"); | ||
| return; | ||
| } | ||
|
|
||
| if (!isExecutorInstalled()) { | ||
| return; | ||
| } | ||
|
|
||
| var hookContent = ""; | ||
| final var gitHookFile = root.toPath().resolve(".git/hooks/pre-push").toFile(); | ||
| if (!gitHookFile.exists()) { | ||
| logger.info("Git pre-push hook not found, creating it"); | ||
| if (!gitHookFile.getParentFile().exists() && !gitHookFile.getParentFile().mkdirs()) { | ||
| logger.error("Failed to create pre-push hook directory"); | ||
| return; | ||
| } | ||
|
|
||
| if (!gitHookFile.createNewFile()) { | ||
| logger.error("Failed to create pre-push hook file"); | ||
| return; | ||
| } | ||
|
|
||
| if (!gitHookFile.setExecutable(true, false)) { | ||
| logger.error("Can not make file executable"); | ||
| return; | ||
| } | ||
|
|
||
| hookContent += "#!/bin/sh\n"; | ||
| } | ||
|
|
||
| if (isGitHookInstalled(gitHookFile)) { | ||
| logger.info("Skipping, git pre-push hook already installed %s", gitHookFile.getAbsolutePath()); | ||
| return; | ||
| } | ||
|
|
||
| hookContent += preHookContent(); | ||
| writeFile(gitHookFile, hookContent); | ||
|
|
||
| logger.info("Git pre-push hook installed successfully to the file %s", gitHookFile.getAbsolutePath()); | ||
| } | ||
|
|
||
| /** | ||
| * Checks if the required executor for performing the desired pre-push actions is installed. | ||
| * | ||
| * @return {@code true} if the executor is installed, {@code false} otherwise. | ||
| */ | ||
| protected abstract boolean isExecutorInstalled(); | ||
|
|
||
| /** | ||
| * Provides the content of the hook that should be inserted into the pre-push script. | ||
| * | ||
| * @return A string representing the content to include in the pre-push script. | ||
| */ | ||
| protected abstract String preHookContent(); | ||
|
|
||
| /** | ||
| * Checks if Git is installed by validating the existence of `.git/config` in the repository root. | ||
| * | ||
| * @return {@code true} if Git is installed, {@code false} otherwise. | ||
| */ | ||
| private boolean isGitInstalled() { | ||
| return root.toPath().resolve(".git/config").toFile().exists(); | ||
| } | ||
|
|
||
| /** | ||
| * Verifies if the pre-push hook file already contains the custom Spotless hook content. | ||
| * | ||
| * @param gitHookFile The file representing the Git hook. | ||
| * @return {@code true} if the hook is already installed, {@code false} otherwise. | ||
| * @throws Exception if an error occurs when reading the file. | ||
| */ | ||
| private boolean isGitHookInstalled(File gitHookFile) throws Exception { | ||
| final var hook = Files.readString(gitHookFile.toPath(), UTF_8); | ||
| return hook.contains("##### SPOTLESS HOOK START #####"); | ||
| } | ||
|
|
||
| /** | ||
| * Writes the specified content into a file. | ||
| * | ||
| * @param file The file to which the content should be written. | ||
| * @param content The content to write into the file. | ||
| * @throws IOException if an error occurs while writing to the file. | ||
| */ | ||
| private void writeFile(File file, String content) throws IOException { | ||
| try (final var writer = new FileWriter(file, UTF_8, true)) { | ||
| writer.write(content); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Generates a pre-push template script that defines the commands to check and apply changes | ||
| * using an executor and Spotless. | ||
| * | ||
| * @param executor The tool to execute the check and apply commands. | ||
| * @param commandCheck The command to check for issues. | ||
| * @param commandApply The command to apply corrections. | ||
| * @return A string template representing the Spotless Git pre-push hook content. | ||
| */ | ||
| protected String preHookTemplate(String executor, String commandCheck, String commandApply) { | ||
| var spotlessHook = "\n"; | ||
| spotlessHook += "\n##### SPOTLESS HOOK START #####"; | ||
| spotlessHook += "\nSPOTLESS_EXECUTOR=" + executor; | ||
| spotlessHook += "\nif ! $SPOTLESS_EXECUTOR " + commandCheck + " ; then"; | ||
| spotlessHook += "\n echo 1>&2 \"spotless found problems, running " + commandApply + "; commit the result and re-push\""; | ||
| spotlessHook += "\n $SPOTLESS_EXECUTOR " + commandApply; | ||
| spotlessHook += "\n exit 1"; | ||
| spotlessHook += "\nfi"; | ||
| spotlessHook += "\n##### SPOTLESS HOOK END #####"; | ||
| spotlessHook += "\n\n"; | ||
| return spotlessHook; | ||
| } | ||
|
|
||
| public interface GitPreHookLogger { | ||
| void info(String format, Object... arguments); | ||
|
|
||
| void error(String format, Object... arguments); | ||
| } | ||
| } | ||
57 changes: 57 additions & 0 deletions
57
lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerGradle.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* | ||
| * Copyright 2025 DiffPlug | ||
| * | ||
| * 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.diffplug.spotless; | ||
|
|
||
| import java.io.File; | ||
|
|
||
| /** | ||
| * Implementation of {@link GitPrePushHookInstaller} specifically for Gradle-based projects. | ||
| * This class installs a Git pre-push hook that uses Gradle's `gradlew` executable to check and apply Spotless formatting. | ||
| */ | ||
| public class GitPrePushHookInstallerGradle extends GitPrePushHookInstaller { | ||
|
|
||
| /** | ||
| * The Gradle wrapper file (`gradlew`) located in the root directory of the project. | ||
| */ | ||
| private final File gradlew; | ||
|
|
||
| public GitPrePushHookInstallerGradle(GitPreHookLogger logger, File root) { | ||
| super(logger, root); | ||
| this.gradlew = root.toPath().resolve("gradlew").toFile(); | ||
| } | ||
|
|
||
| /** | ||
| * Checks if the Gradle wrapper (`gradlew`) is present in the root directory. | ||
| * This ensures that the executor used for formatting (`spotlessCheck` and `spotlessApply`) is available. | ||
| * | ||
| * @return {@code true} if the Gradle wrapper is found, {@code false} otherwise. | ||
| * An error is logged if the wrapper is not found. | ||
| */ | ||
| @Override | ||
| protected boolean isExecutorInstalled() { | ||
| if (gradlew.exists()) { | ||
| return true; | ||
| } | ||
|
|
||
| logger.error("Failed to find gradlew in root directory"); | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| protected String preHookContent() { | ||
| return preHookTemplate(gradlew.getAbsolutePath(), "spotlessCheck", "spotlessApply"); | ||
| } | ||
| } |
47 changes: 47 additions & 0 deletions
47
lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerMaven.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /* | ||
| * Copyright 2025 DiffPlug | ||
| * | ||
| * 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.diffplug.spotless; | ||
|
|
||
| import java.io.File; | ||
|
|
||
| /** | ||
| * Implementation of {@link GitPrePushHookInstaller} specifically for Maven-based projects. | ||
| * This class installs a Git pre-push hook that uses Maven to check and apply Spotless formatting. | ||
| */ | ||
| public class GitPrePushHookInstallerMaven extends GitPrePushHookInstaller { | ||
|
|
||
| public GitPrePushHookInstallerMaven(GitPreHookLogger logger, File root) { | ||
| super(logger, root); | ||
| } | ||
|
|
||
| /** | ||
| * Confirms that Maven is installed and available for use. | ||
| * | ||
| * <p>This method assumes that if this code is running, then Maven is already properly installed and configured, | ||
| * so it always returns {@code true}. | ||
| * | ||
| * @return {@code true}, indicating that Maven is available. | ||
| */ | ||
| @Override | ||
| protected boolean isExecutorInstalled() { | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| protected String preHookContent() { | ||
| return preHookTemplate("mvn", "spotless:check", "spotless:apply"); | ||
lazer-dev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.