From 26fd9f6e6c2e53aa0912afad3e8fe31a7ae4d105 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 3 Aug 2011 09:28:26 +0200 Subject: [PATCH] added support for native git post-receive hook --- .../src/main/java/sonia/scm/util/IOUtil.java | 87 +++++ .../src/main/java/sonia/scm/util/Util.java | 4 +- .../sonia/scm/web/GitPostReceiveHook.java | 144 --------- .../java/sonia/scm/web/GitReceiveHook.java | 306 ++++++++++++++++++ .../sonia/scm/web/GitReceivePackFactory.java | 5 +- 5 files changed, 398 insertions(+), 148 deletions(-) delete mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPostReceiveHook.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java diff --git a/scm-core/src/main/java/sonia/scm/util/IOUtil.java b/scm-core/src/main/java/sonia/scm/util/IOUtil.java index cc1d4ccf8b..9c7b3d2972 100644 --- a/scm-core/src/main/java/sonia/scm/util/IOUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/IOUtil.java @@ -89,6 +89,14 @@ public class IOUtil "/opt/csw/bin" }; + /** Field description */ + private static final String[] EXTENSION_SCRIPT_UNIX = { ".sh", ".csh", + ".bsh" }; + + /** Field description */ + private static final String[] EXTENSION_SCRIPT_WINDOWS = { ".bat", ".cmd", + ".exe" }; + /** Field description */ private static final Logger logger = LoggerFactory.getLogger(IOUtil.class.getName()); @@ -582,6 +590,38 @@ public class IOUtil return cmds; } + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param basePath + * + * @since 1.6 + * @return + */ + public static File getScript(String basePath) + { + return getScript(new File(basePath), basePath); + } + + /** + * Method description + * + * + * @param baseFile + * + * @since 1.6 + * @return + */ + public static File getScript(File baseFile) + { + return getScript(baseFile, baseFile.getAbsolutePath()); + } + + //~--- methods -------------------------------------------------------------- + /** * Method description * @@ -637,6 +677,53 @@ public class IOUtil return extensions; } + /** + * Method description + * + * + * @param baseFile + * @param basePath + * + * @since 1.6 + * @return + */ + private static File getScript(File baseFile, String basePath) + { + File script = null; + + if (baseFile.exists()) + { + script = baseFile; + } + else + { + String[] extensions = null; + + if (SystemUtil.isWindows()) + { + extensions = EXTENSION_SCRIPT_WINDOWS; + } + else + { + extensions = EXTENSION_SCRIPT_UNIX; + } + + for (String ext : extensions) + { + File file = new File(basePath.concat(ext)); + + if (file.exists()) + { + script = file; + + break; + } + } + } + + return script; + } + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/util/Util.java b/scm-core/src/main/java/sonia/scm/util/Util.java index 14286adfb0..54dae8a026 100644 --- a/scm-core/src/main/java/sonia/scm/util/Util.java +++ b/scm-core/src/main/java/sonia/scm/util/Util.java @@ -334,10 +334,10 @@ public class Util * * @return */ - public static String nonNull(String value) + public static String nonNull(Object value) { return (value != null) - ? value + ? value.toString() : ""; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPostReceiveHook.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPostReceiveHook.java deleted file mode 100644 index 2579c5f361..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPostReceiveHook.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.web; - -//~--- non-JDK imports -------------------------------------------------------- - -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.transport.PostReceiveHook; -import org.eclipse.jgit.transport.ReceiveCommand; -import org.eclipse.jgit.transport.ReceivePack; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.repository.GitRepositoryHandler; -import sonia.scm.repository.GitRepositoryHookEvent; -import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.File; - -import java.util.Collection; - -/** - * - * @author Sebastian Sdorra - */ -public class GitPostReceiveHook implements PostReceiveHook -{ - - /** the logger for GitPostReceiveHook */ - private static final Logger logger = - LoggerFactory.getLogger(GitPostReceiveHook.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param repositoryManager - */ - public GitPostReceiveHook(RepositoryManager repositoryManager) - { - this.repositoryManager = repositoryManager; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param rpack - * @param receiveCommands - */ - @Override - public void onPostReceive(ReceivePack rpack, - Collection receiveCommands) - { - try - { - for (ReceiveCommand rc : receiveCommands) - { - if (rc.getResult() == ReceiveCommand.Result.OK) - { - ObjectId newId = rc.getNewId(); - ObjectId oldId = null; - - if (isUpdateCommand(rc)) - { - oldId = rc.getOldId(); - } - - File directory = rpack.getRepository().getDirectory(); - String repositoryName = directory.getName(); - GitRepositoryHookEvent e = new GitRepositoryHookEvent(directory, - newId, oldId); - - repositoryManager.fireHookEvent(GitRepositoryHandler.TYPE_NAME, - repositoryName, e); - } - } - } - catch (RepositoryNotFoundException ex) - { - logger.error("repository could not be found", ex); - } - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param rc - * - * @return - */ - private boolean isUpdateCommand(ReceiveCommand rc) - { - return (rc.getType() == ReceiveCommand.Type.UPDATE) - || (rc.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private RepositoryManager repositoryManager; -} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java new file mode 100644 index 0000000000..78a7a96dba --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceiveHook.java @@ -0,0 +1,306 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.transport.PostReceiveHook; +import org.eclipse.jgit.transport.PreReceiveHook; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.transport.ReceivePack; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.io.Command; +import sonia.scm.io.CommandResult; +import sonia.scm.io.SimpleCommand; +import sonia.scm.repository.GitRepositoryHandler; +import sonia.scm.repository.GitRepositoryHookEvent; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryNotFoundException; +import sonia.scm.util.IOUtil; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +import java.util.Collection; + +/** + * + * @author Sebastian Sdorra + */ +public class GitReceiveHook implements PreReceiveHook, PostReceiveHook +{ + + /** Field description */ + public static final String FILE_HOOKDIRECTORY = "hooks"; + + /** Field description */ + public static final String FILE_HOOK_POST_RECEIVE = "post-receive"; + + /** Field description */ + public static final String FILE_HOOK_PRE_RECEIVE = "pre-receive"; + + /** the logger for GitReceiveHook */ + private static final Logger logger = + LoggerFactory.getLogger(GitReceiveHook.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param repositoryManager + */ + public GitReceiveHook(RepositoryManager repositoryManager) + { + this.repositoryManager = repositoryManager; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param rpack + * @param receiveCommands + */ + @Override + public void onPostReceive(ReceivePack rpack, + Collection receiveCommands) + { + onReceive(rpack, receiveCommands, FILE_HOOK_POST_RECEIVE, true); + } + + /** + * Method description + * + * + * + * @param rpack + * @param receiveCommands + */ + @Override + public void onPreReceive(ReceivePack rpack, + Collection receiveCommands) + { + onReceive(rpack, receiveCommands, FILE_HOOK_POST_RECEIVE, false); + } + + /** + * Method description + * + * + * @param hook + * @param oldId + * @param newId + * @param refName + */ + private void executeFileHook(File hook, ObjectId oldId, ObjectId newId, + String refName) + { + final Command cmd = new SimpleCommand(hook.getAbsolutePath(), getId(oldId), + getId(newId), Util.nonNull(refName)); + + try + { + CommandResult result = cmd.execute(); + + if (result.isSuccessfull()) + { + if (logger.isDebugEnabled()) + { + logger.debug("executed file hook successfull"); + + if (logger.isTraceEnabled()) + { + String out = result.getOutput(); + + if (Util.isNotEmpty(out)) + { + logger.trace(out); + } + } + } + } + else if (logger.isErrorEnabled()) + { + logger.error("failed to execute file hook"); + + String out = result.getOutput(); + + if (Util.isNotEmpty(out)) + { + logger.error(out); + } + } + } + catch (IOException ex) + { + logger.error("could not execute file hook", ex); + } + } + + /** + * Method description + * + * + * @param directory + * @param oldId + * @param newId + */ + private void fireHookEvent(File directory, ObjectId oldId, ObjectId newId) + { + try + { + String repositoryName = directory.getName(); + GitRepositoryHookEvent e = new GitRepositoryHookEvent(directory, newId, + oldId); + + repositoryManager.fireHookEvent(GitRepositoryHandler.TYPE_NAME, + repositoryName, e); + } + catch (RepositoryNotFoundException ex) + { + logger.error("repository could not be found", ex); + } + } + + /** + * Method description + * + * + * @param rpack + * @param receiveCommands + * @param hook + * @param fireApiHook + */ + private void onReceive(ReceivePack rpack, + Collection receiveCommands, + String hook, boolean fireApiHook) + { + for (ReceiveCommand rc : receiveCommands) + { + if (rc.getResult() == ReceiveCommand.Result.OK) + { + ObjectId newId = rc.getNewId(); + ObjectId oldId = null; + + if (isUpdateCommand(rc)) + { + oldId = rc.getOldId(); + } + + File directory = rpack.getRepository().getDirectory(); + File hookScript = getHookScript(directory, hook); + + if (hookScript != null) + { + executeFileHook(hookScript, oldId, newId, rc.getRefName()); + } + + if (fireApiHook) + { + fireHookEvent(directory, oldId, newId); + } + } + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param directory + * @param name + * + * @return + */ + private File getHookScript(File directory, String name) + { + File baseFile = + new File(directory, + FILE_HOOKDIRECTORY.concat(File.separator).concat(name)); + + return IOUtil.getScript(baseFile); + } + + /** + * Method description + * + * + * @param objectId + * + * @return + */ + private String getId(ObjectId objectId) + { + String id = Util.EMPTY_STRING; + + if (objectId != null) + { + id = objectId.name(); + } + + return id; + } + + /** + * Method description + * + * + * @param rc + * + * @return + */ + private boolean isUpdateCommand(ReceiveCommand rc) + { + return (rc.getType() == ReceiveCommand.Type.UPDATE) + || (rc.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private RepositoryManager repositoryManager; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java index 7ee529a05a..ed54e5cf24 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java @@ -67,7 +67,7 @@ public class GitReceivePackFactory @Inject public GitReceivePackFactory(RepositoryManager repositoryManager) { - hook = new GitPostReceiveHook(repositoryManager); + hook = new GitReceiveHook(repositoryManager); } //~--- methods -------------------------------------------------------------- @@ -90,6 +90,7 @@ public class GitReceivePackFactory { ReceivePack rpack = defaultFactory.create(request, repository); + rpack.setPreReceiveHook(hook); rpack.setPostReceiveHook(hook); return rpack; @@ -102,5 +103,5 @@ public class GitReceivePackFactory new DefaultReceivePackFactory(); /** Field description */ - private GitPostReceiveHook hook; + private GitReceiveHook hook; }