├── .gitignore ├── LICENSE ├── pom.xml ├── src └── main │ └── java │ └── org │ └── sandbox │ └── GitHookInstallMojo.java └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out 3 | *.iml 4 | target -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oleh Lukyrych 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.sandbox 7 | githook-maven-plugin 8 | 1.0.1 9 | maven-plugin 10 | Git Hook Plugin 11 | Maven plugin to configure and install local git hooks 12 | https://github.com/olukyrich/githook-maven-plugin 13 | 14 | 15 | UTF-8 16 | 1.8 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.apache.maven 24 | maven-plugin-api 25 | 3.3.9 26 | 27 | 28 | org.apache.maven.plugin-tools 29 | maven-plugin-annotations 30 | 3.4 31 | provided 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-plugin-plugin 39 | 3.4 40 | 41 | 42 | default-descriptor 43 | 44 | descriptor 45 | 46 | process-classes 47 | 48 | 49 | help-descriptor 50 | 51 | helpmojo 52 | 53 | process-classes 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/org/sandbox/GitHookInstallMojo.java: -------------------------------------------------------------------------------- 1 | package org.sandbox; 2 | 3 | import org.apache.maven.plugin.AbstractMojo; 4 | import org.apache.maven.plugin.MojoExecutionException; 5 | import org.apache.maven.plugin.MojoFailureException; 6 | import org.apache.maven.plugins.annotations.LifecyclePhase; 7 | import org.apache.maven.plugins.annotations.Mojo; 8 | import org.apache.maven.plugins.annotations.Parameter; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.*; 12 | import java.nio.file.attribute.PosixFilePermissions; 13 | import java.util.Map; 14 | 15 | import static java.nio.file.StandardOpenOption.*; 16 | 17 | import java.nio.file.attribute.PosixFilePermission; 18 | import java.util.Arrays; 19 | import java.util.HashSet; 20 | import java.util.stream.Collectors; 21 | 22 | @Mojo(name = "install", defaultPhase = LifecyclePhase.INITIALIZE) 23 | public final class GitHookInstallMojo extends AbstractMojo { 24 | 25 | private static final String SHEBANG = "#!/bin/sh"; 26 | private static final Path HOOK_DIR_PATH = Paths.get(".git/hooks"); 27 | 28 | @Parameter(name = "hooks") 29 | private Map inlineHooks; 30 | @Parameter(name = "resourceHooks") 31 | private Map resourceHooks; 32 | 33 | @Override 34 | public void execute() throws MojoExecutionException, MojoFailureException { 35 | if (!Files.exists(HOOK_DIR_PATH)) { 36 | throw new MojoExecutionException("not a git repository"); 37 | } 38 | this.generateInlineHooks(); 39 | this.generateResourceHooks(); 40 | } 41 | 42 | protected void generateInlineHooks() throws MojoExecutionException { 43 | if (inlineHooks == null) { 44 | return; 45 | } 46 | for (Map.Entry hook : inlineHooks.entrySet()) { 47 | String hookName = hook.getKey(); 48 | getLog().info("Generating " + hookName + " from maven conf"); 49 | generateHookFile(hookName, SHEBANG + '\n' + hook.getValue()); 50 | } 51 | } 52 | 53 | protected void generateResourceHooks() throws MojoExecutionException { 54 | if (resourceHooks == null) { 55 | return; 56 | } 57 | for (Map.Entry hook : resourceHooks.entrySet()) { 58 | String hookName = hook.getKey(); 59 | 60 | Path hookFilePath = Paths.get(hook.getValue()); 61 | Path local = Paths.get(""); 62 | 63 | if (!hookFilePath.toAbsolutePath().startsWith(local.toAbsolutePath())) { 64 | throw new MojoExecutionException("only file inside the project can be used to generate git hooks"); 65 | } 66 | try { 67 | getLog().info("Generating " + hookName + " from " + hookFilePath.toString()); 68 | generateHookFile(hookName, Files.lines(hookFilePath).collect(Collectors.joining("\n"))); 69 | } catch (IOException e) { 70 | throw new MojoExecutionException("could not access hook resource : " + hookFilePath, e); 71 | } 72 | } 73 | } 74 | 75 | protected void generateHookFile(String hookName, String asStringScript) throws MojoExecutionException { 76 | try { 77 | Path path = HOOK_DIR_PATH.resolve(hookName); 78 | Files.write( 79 | path, 80 | asStringScript.getBytes(), 81 | CREATE, TRUNCATE_EXISTING 82 | ); 83 | Files.setPosixFilePermissions( 84 | path, 85 | new HashSet<>(Arrays.asList( 86 | PosixFilePermission.OWNER_EXECUTE, 87 | PosixFilePermission.OWNER_READ, 88 | PosixFilePermission.OWNER_WRITE 89 | )) 90 | ); 91 | } catch (IOException e) { 92 | throw new MojoExecutionException("could not write hook with name : " + hookName, e); 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # githook-maven-plugin 2 | Maven plugin to configure and install local git hooks 3 | 4 | ## Protect your VCS 5 | It's always a good idea to check your changes before committing them: run unit tests, perform the build, etc. However, such check-lists may be easily overlooked, especially in big projects. To get rid of the human factor, they should be somehow forced and automated. The best way is to implement such verification on the project infrastructure level. However, sometimes there's no infrastructure or it doesn't allow to implement that. For the latter there are [git client hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). 6 | 7 | ## Listen to your VCS 8 | Besides pre-commit and pre-push hooks there are some others which allow to handle local VCS events. 9 | 10 | ## Drawbacks of local git hooks 11 | The main disadvantage of this approach is that hooks are kept within .git directory, which shall never come to the remote repository. Thus, each contributor will have to install them manually in his local repository, which may, again, be overlooked. 12 | 13 | ## So why should I use this plugin? 14 | Because it deals with the problem of providing hook configuration to the repository, and automates their installation. 15 | 16 | ## Implementation 17 | The idea is to keep somewhere a mapping between the hook name and the script, for each hook name create a respective file in .git/hooks, containing that script when the project initializes. "Initializes" -- is quite a polymorphic term, but when it's a maven project, then it likely means initial [lifecycle phase](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html). In the majority of cases, it will be enough to map the plugin on "initialize" phase, but you can still [create any other custom execution](https://maven.apache.org/guides/mini/guide-configuring-plugins.html#Using_the_executions_Tag). 18 | 19 | ## Flaws 20 | Obviously, nothing can restrain one from the cloning of repository, and interacting with it without initial build. Also, it's always possible to delete hook files manually. 21 | 22 | ## Usage 23 | The plugin provides the only goal "install". It's mapped on "initialize" phase by default. To use the default flow add these lines to the plugin definition: 24 | ``` 25 | 26 | 27 | 28 | install 29 | 30 | 31 | 32 | ``` 33 | To configure hooks provide the following configuration for the execution: 34 | ``` 35 | 36 | script 37 | ... 38 | 39 | ``` 40 | NOTE: The plugin rewrites hooks. 41 | 42 | ## Usage Example 43 | 44 | Simple usage with inline script : 45 | ``` 46 | 47 | 50 | 4.0.0 51 | org.sandbox 52 | githook-test 53 | 1.0.0 54 | 55 | 56 | 57 | org.sandbox 58 | githook-maven-plugin 59 | 1.0.0 60 | 61 | 62 | 63 | install 64 | 65 | 66 | 67 | 68 | echo running validation build 69 | exec mvn clean install 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | ``` 80 | 81 | External hook files can also been used : 82 | ``` 83 | ... 84 | 85 | 86 | 87 | hooks/pre-push.sh 88 | 89 | 90 | 91 | ... 92 | ``` --------------------------------------------------------------------------------