1 /* 2 * Copyright (C) 2015 Obeo. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 package org.eclipse.jgit.hooks; 44 45 import static java.nio.charset.StandardCharsets.UTF_8; 46 47 import java.io.ByteArrayOutputStream; 48 import java.io.IOException; 49 import java.io.PrintStream; 50 import java.io.UnsupportedEncodingException; 51 import java.util.concurrent.Callable; 52 53 import org.eclipse.jgit.api.errors.AbortedByHookException; 54 import org.eclipse.jgit.lib.Repository; 55 import org.eclipse.jgit.util.FS; 56 import org.eclipse.jgit.util.ProcessResult; 57 58 /** 59 * Git can fire off custom scripts when certain important actions occur. These 60 * custom scripts are called "hooks". There are two groups of hooks: client-side 61 * (that run on local operations such as committing and merging), and 62 * server-side (that run on network operations such as receiving pushed 63 * commits). This is the abstract super-class of the different hook 64 * implementations in JGit. 65 * 66 * @param <T> 67 * the return type which is expected from {@link #call()} 68 * @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git 69 * Hooks on the git-scm official site</a> 70 * @since 4.0 71 */ 72 abstract class GitHook<T> implements Callable<T> { 73 74 private final Repository repo; 75 76 /** 77 * The output stream to be used by the hook. 78 */ 79 protected final PrintStream outputStream; 80 81 /** 82 * Constructor for GitHook 83 * 84 * @param repo 85 * a {@link org.eclipse.jgit.lib.Repository} object. 86 * @param outputStream 87 * The output stream the hook must use. {@code null} is allowed, 88 * in which case the hook will use {@code System.out}. 89 */ 90 protected GitHook(Repository repo, PrintStream outputStream) { 91 this.repo = repo; 92 this.outputStream = outputStream; 93 } 94 95 /** 96 * {@inheritDoc} 97 * <p> 98 * Run the hook. 99 */ 100 @Override 101 public abstract T call() throws IOException, AbortedByHookException; 102 103 /** 104 * Get name of the hook 105 * 106 * @return The name of the hook, which must not be {@code null}. 107 */ 108 public abstract String getHookName(); 109 110 /** 111 * Get the repository 112 * 113 * @return The repository. 114 */ 115 protected Repository getRepository() { 116 return repo; 117 } 118 119 /** 120 * Override this method when needed to provide relevant parameters to the 121 * underlying hook script. The default implementation returns an empty 122 * array. 123 * 124 * @return The parameters the hook receives. 125 */ 126 protected String[] getParameters() { 127 return new String[0]; 128 } 129 130 /** 131 * Override to provide relevant arguments via stdin to the underlying hook 132 * script. The default implementation returns {@code null}. 133 * 134 * @return The parameters the hook receives. 135 */ 136 protected String getStdinArgs() { 137 return null; 138 } 139 140 /** 141 * Get output stream 142 * 143 * @return The output stream the hook must use. Never {@code null}, 144 * {@code System.out} is returned by default. 145 */ 146 protected PrintStream getOutputStream() { 147 return outputStream == null ? System.out : outputStream; 148 } 149 150 /** 151 * Runs the hook, without performing any validity checks. 152 * 153 * @throws org.eclipse.jgit.api.errors.AbortedByHookException 154 * If the underlying hook script exited with non-zero. 155 */ 156 protected void doRun() throws AbortedByHookException { 157 final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream(); 158 PrintStream hookErrRedirect = null; 159 try { 160 hookErrRedirect = new PrintStream(errorByteArray, false, 161 UTF_8.name()); 162 } catch (UnsupportedEncodingException e) { 163 // UTF-8 is guaranteed to be available 164 } 165 ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(), 166 getHookName(), getParameters(), getOutputStream(), 167 hookErrRedirect, getStdinArgs()); 168 if (result.isExecutedWithError()) { 169 throw new AbortedByHookException( 170 new String(errorByteArray.toByteArray(), UTF_8), 171 getHookName(), result.getExitCode()); 172 } 173 } 174 175 /** 176 * Check whether a 'native' (i.e. script) hook is installed in the 177 * repository. 178 * 179 * @return whether a native hook script is installed in the repository. 180 * @since 4.11 181 */ 182 public boolean isNativeHookPresent() { 183 return FS.DETECTED.findHook(getRepository(), getHookName()) != null; 184 } 185 186 }