From 819efdc8c5ea633e9be1e744ade8b197d21a1962 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Sat, 12 Apr 2014 11:31:03 -0400
Subject: [PATCH] Allow ReceiveHook extensions

---
 src/site/setup_plugins.mkd                            |   36 +++++++++++
 src/main/java/com/gitblit/git/GitblitReceivePack.java |   51 +++++++++++++++--
 src/main/java/com/gitblit/extensions/ReceiveHook.java |   53 +++++++++++++++++
 3 files changed, 133 insertions(+), 7 deletions(-)

diff --git a/src/main/java/com/gitblit/extensions/ReceiveHook.java b/src/main/java/com/gitblit/extensions/ReceiveHook.java
new file mode 100644
index 0000000..00dcb3e
--- /dev/null
+++ b/src/main/java/com/gitblit/extensions/ReceiveHook.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * 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.gitblit.extensions;
+
+import java.util.Collection;
+
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+import ro.fortsoft.pf4j.ExtensionPoint;
+
+import com.gitblit.git.GitblitReceivePack;
+
+/**
+ * Extension point for plugins to process commits on Pre- and Post- Receive.
+ *
+ * @author James Moger
+ *
+ */
+public abstract class ReceiveHook implements ExtensionPoint {
+
+	/**
+	 * Called BEFORE received ref update commands have been written to the
+	 * repository.  This allows extensions to process or reject incoming pushes
+	 * using whatever logic may be appropriate.
+	 *
+	 * @param receivePack
+	 * @param commands
+	 */
+	public abstract void onPreReceive(GitblitReceivePack receivePack, Collection<ReceiveCommand> commands);
+
+	/**
+	 * Called AFTER received ref update commands have been written to the
+	 * repository.  This allows extensions to send notifications or trigger
+	 * continuous integration systems.
+	 *
+	 * @param receivePack
+	 * @param commands
+	 */
+	public abstract void onPostReceive(GitblitReceivePack receivePack, Collection<ReceiveCommand> commands);
+}
diff --git a/src/main/java/com/gitblit/git/GitblitReceivePack.java b/src/main/java/com/gitblit/git/GitblitReceivePack.java
index 73da3d3..0cc4198 100644
--- a/src/main/java/com/gitblit/git/GitblitReceivePack.java
+++ b/src/main/java/com/gitblit/git/GitblitReceivePack.java
@@ -47,6 +47,7 @@
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
 import com.gitblit.client.Translation;
+import com.gitblit.extensions.ReceiveHook;
 import com.gitblit.manager.IGitblit;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
@@ -156,6 +157,14 @@
 	 */
 	@Override
 	public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
+
+		if (commands.size() == 0) {
+			// no receive commands to process
+			// this can happen if receive pack subclasses intercept and filter
+			// the commands
+			LOGGER.debug("skipping pre-receive processing, no refs created, updated, or removed");
+			return;
+		}
 
 		if (repository.isMirror) {
 			// repository is a mirror
@@ -276,6 +285,15 @@
 			}
 		}
 
+		// call pre-receive plugins
+		for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) {
+			try {
+				hook.onPreReceive(this, commands);
+			} catch (Exception e) {
+				LOGGER.error("Failed to execute extension", e);
+			}
+		}
+
 		Set<String> scripts = new LinkedHashSet<String>();
 		scripts.addAll(gitblit.getPreReceiveScriptsInherited(repository));
 		if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) {
@@ -298,7 +316,7 @@
 	@Override
 	public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
 		if (commands.size() == 0) {
-			LOGGER.debug("skipping post-receive hooks, no refs created, updated, or removed");
+			LOGGER.debug("skipping post-receive processing, no refs created, updated, or removed");
 			return;
 		}
 
@@ -379,6 +397,15 @@
 			}
 		}
 
+		// call post-receive plugins
+		for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) {
+			try {
+				hook.onPostReceive(this, commands);
+			} catch (Exception e) {
+				LOGGER.error("Failed to execute extension", e);
+			}
+		}
+
 		// run Groovy hook scripts
 		Set<String> scripts = new LinkedHashSet<String>();
 		scripts.addAll(gitblit.getPostReceiveScriptsInherited(repository));
@@ -434,7 +461,7 @@
 		this.gitblitUrl = url;
 	}
 
-	protected void sendRejection(final ReceiveCommand cmd, final String why, Object... objects) {
+	public void sendRejection(final ReceiveCommand cmd, final String why, Object... objects) {
 		String text;
 		if (ArrayUtils.isEmpty(objects)) {
 			text = why;
@@ -445,15 +472,15 @@
 		LOGGER.error(text + " (" + user.username + ")");
 	}
 
-	protected void sendHeader(String msg, Object... objects) {
+	public void sendHeader(String msg, Object... objects) {
 		sendInfo("--> ", msg, objects);
 	}
 
-	protected void sendInfo(String msg, Object... objects) {
+	public void sendInfo(String msg, Object... objects) {
 		sendInfo("    ", msg, objects);
 	}
 
-	protected void sendInfo(String prefix, String msg, Object... objects) {
+	private void sendInfo(String prefix, String msg, Object... objects) {
 		String text;
 		if (ArrayUtils.isEmpty(objects)) {
 			text = msg;
@@ -467,7 +494,7 @@
 		}
 	}
 
-	protected void sendError(String msg, Object... objects) {
+	public void sendError(String msg, Object... objects) {
 		String text;
 		if (ArrayUtils.isEmpty(objects)) {
 			text = msg;
@@ -532,4 +559,16 @@
 			}
 		}
 	}
+
+	public IGitblit getGitblit() {
+		return gitblit;
+	}
+
+	public RepositoryModel getRepositoryModel() {
+		return repository;
+	}
+
+	public UserModel getUserModel() {
+		return user;
+	}
 }
diff --git a/src/site/setup_plugins.mkd b/src/site/setup_plugins.mkd
index b609a68..b956cc1 100644
--- a/src/site/setup_plugins.mkd
+++ b/src/site/setup_plugins.mkd
@@ -54,13 +54,47 @@
 
 ### Extension Point: SSH DispatchCommand
 
-You can provide your own custom SSH commands by extending the DispatchCommand.
+You can provide your own custom SSH commands by extending the *DispatchCommand* class.
 
 For some examples of how to do this, please see:
 
 [gitblit-cookbook-plugin (Maven project)](https://dev.gitblit.com/summary/gitblit-cookbook-plugin.git)
 [gitblit-powertools-plugin (Ant/Moxie project)](https://dev.gitblit.com/summary/gitblit-powertools-plugin.git)
 
+### Extension Point: Pre- and Post- Receive Hook
+
+You can provide your own custom pre and/or post receive hooks by extending the *ReceiveHook* class.
+
+```java
+import com.gitblit.extensions.ReceiveHook;
+import ro.fortsoft.pf4j.Extension;
+
+@Extension
+public class MyHook extends ReceiveHook {
+
+    @Override
+    public void onPreReceive(GitblitReceivePack receivePack, Collection<ReceiveCommand> commands) {
+        RepositoryModel repository = receivePack.getRepositoryModel();
+        UserModel user = receivePack.getUserModel();
+        receivePack.sendInfo("Hi {0}, I see {1} commands for {2} onPreReceive",
+                        user.getDisplayName(),
+                        commands.size(),
+                        repository.name);
+    }
+
+    @Override
+    public void onPostReceive(GitblitReceivePack receivePack, Collection<ReceiveCommand> commands) {
+        RepositoryModel repository = receivePack.getRepositoryModel();
+        UserModel user = receivePack.getUserModel();
+        receivePack.sendInfo("Hi {0}, I see {1} commands for {2} onPostReceive",
+                        user.getDisplayName(),
+                        commands.size(),
+                        repository.name);
+    }
+}
+
+```
+
 ### Mac OSX Fonts
 
 Gitblit's core SSH commands and those in the *powertools* plugin rely on use of ANSI border characters to provide a pretty presentation of data.  Unfortunately, the fonts provided by Apple - while very nice - don't work well with ANSI border characters.  The following public domain fixed-width, fixed-point, bitmapped fonts work very nicely.  I find the 6x12 font with a line spacing of ~0.8 to be quite acceptable.

--
Gitblit v1.9.1