You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GitHook.java 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. * Copyright (C) 2015 Obeo. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.hooks;
  11. import java.io.ByteArrayOutputStream;
  12. import java.io.IOException;
  13. import java.io.OutputStream;
  14. import java.nio.charset.Charset;
  15. import java.util.concurrent.Callable;
  16. import org.eclipse.jgit.api.errors.AbortedByHookException;
  17. import org.eclipse.jgit.lib.Repository;
  18. import org.eclipse.jgit.util.FS;
  19. import org.eclipse.jgit.util.ProcessResult;
  20. import org.eclipse.jgit.util.io.TeeOutputStream;
  21. /**
  22. * Git can fire off custom scripts when certain important actions occur. These
  23. * custom scripts are called "hooks". There are two groups of hooks: client-side
  24. * (that run on local operations such as committing and merging), and
  25. * server-side (that run on network operations such as receiving pushed
  26. * commits). This is the abstract super-class of the different hook
  27. * implementations in JGit.
  28. *
  29. * @param <T>
  30. * the return type which is expected from {@link #call()}
  31. * @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git
  32. * Hooks on the git-scm official site</a>
  33. * @since 5.11
  34. */
  35. public abstract class GitHook<T> implements Callable<T> {
  36. private final Repository repo;
  37. /**
  38. * The output stream to be used by the hook.
  39. */
  40. private final OutputStream outputStream;
  41. /**
  42. * The error stream to be used by the hook.
  43. */
  44. private final OutputStream errorStream;
  45. /**
  46. * Constructor for GitHook.
  47. * <p>
  48. * This constructor will use stderr for the error stream.
  49. * </p>
  50. *
  51. * @param repo
  52. * a {@link org.eclipse.jgit.lib.Repository} object.
  53. * @param outputStream
  54. * The output stream the hook must use. {@code null} is allowed,
  55. * in which case the hook will use {@code System.out}.
  56. */
  57. protected GitHook(Repository repo, OutputStream outputStream) {
  58. this(repo, outputStream, null);
  59. }
  60. /**
  61. * Constructor for GitHook
  62. *
  63. * @param repo
  64. * a {@link org.eclipse.jgit.lib.Repository} object.
  65. * @param outputStream
  66. * The output stream the hook must use. {@code null} is allowed,
  67. * in which case the hook will use {@code System.out}.
  68. * @param errorStream
  69. * The error stream the hook must use. {@code null} is allowed,
  70. * in which case the hook will use {@code System.err}.
  71. */
  72. protected GitHook(Repository repo, OutputStream outputStream,
  73. OutputStream errorStream) {
  74. this.repo = repo;
  75. this.outputStream = outputStream;
  76. this.errorStream = errorStream;
  77. }
  78. /**
  79. * {@inheritDoc}
  80. * <p>
  81. * Run the hook.
  82. */
  83. @Override
  84. public abstract T call() throws IOException, AbortedByHookException;
  85. /**
  86. * Get name of the hook
  87. *
  88. * @return The name of the hook, which must not be {@code null}.
  89. */
  90. public abstract String getHookName();
  91. /**
  92. * Get the repository
  93. *
  94. * @return The repository.
  95. */
  96. protected Repository getRepository() {
  97. return repo;
  98. }
  99. /**
  100. * Override this method when needed to provide relevant parameters to the
  101. * underlying hook script. The default implementation returns an empty
  102. * array.
  103. *
  104. * @return The parameters the hook receives.
  105. */
  106. protected String[] getParameters() {
  107. return new String[0];
  108. }
  109. /**
  110. * Override to provide relevant arguments via stdin to the underlying hook
  111. * script. The default implementation returns {@code null}.
  112. *
  113. * @return The parameters the hook receives.
  114. */
  115. protected String getStdinArgs() {
  116. return null;
  117. }
  118. /**
  119. * Get output stream
  120. *
  121. * @return The output stream the hook must use. Never {@code null},
  122. * {@code System.out} is returned by default.
  123. */
  124. protected OutputStream getOutputStream() {
  125. return outputStream == null ? System.out : outputStream;
  126. }
  127. /**
  128. * Get error stream
  129. *
  130. * @return The error stream the hook must use. Never {@code null},
  131. * {@code System.err} is returned by default.
  132. */
  133. protected OutputStream getErrorStream() {
  134. return errorStream == null ? System.err : errorStream;
  135. }
  136. /**
  137. * Runs the hook, without performing any validity checks.
  138. *
  139. * @throws org.eclipse.jgit.api.errors.AbortedByHookException
  140. * If the underlying hook script exited with non-zero.
  141. * @throws IOException
  142. * if an IO error occurred
  143. */
  144. protected void doRun() throws AbortedByHookException, IOException {
  145. final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
  146. final TeeOutputStream stderrStream = new TeeOutputStream(errorByteArray,
  147. getErrorStream());
  148. Repository repository = getRepository();
  149. FS fs = repository.getFS();
  150. if (fs == null) {
  151. fs = FS.DETECTED;
  152. }
  153. ProcessResult result = fs.runHookIfPresent(repository, getHookName(),
  154. getParameters(), getOutputStream(), stderrStream,
  155. getStdinArgs());
  156. if (result.isExecutedWithError()) {
  157. throw new AbortedByHookException(
  158. new String(errorByteArray.toByteArray(),
  159. Charset.defaultCharset().name()),
  160. getHookName(), result.getExitCode());
  161. }
  162. }
  163. /**
  164. * Check whether a 'native' (i.e. script) hook is installed in the
  165. * repository.
  166. *
  167. * @return whether a native hook script is installed in the repository.
  168. * @since 4.11
  169. */
  170. public boolean isNativeHookPresent() {
  171. FS fs = getRepository().getFS();
  172. if (fs == null) {
  173. fs = FS.DETECTED;
  174. }
  175. return fs.findHook(getRepository(), getHookName()) != null;
  176. }
  177. }