Browse Source

Groovy push hooks

tags/v0.8.0
James Moger 12 years ago
parent
commit
fa54bec1d9

+ 5
- 0
.classpath View File

@@ -97,5 +97,10 @@
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/wicket-extensions-1.4.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="ext/groovy-all-1.8.4.jar" sourcepath="ext/groovy-all-1.8.4-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/groovy-all-1.8.4-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 18
- 2
build.xml View File

@@ -186,6 +186,14 @@
<param name="docs.output.dir" value="${project.deploy.dir}/docs" />
</antcall>
<!-- Copy the sample Groovy hook scripts -->
<mkdir dir="${project.deploy.dir}/groovy" />
<copy todir="${project.deploy.dir}/groovy">
<fileset dir="${basedir}/groovy">
<include name="**/*" />
</fileset>
</copy>
<!-- Create Zip deployment -->
<zip destfile="${distribution.zipfile}">
<fileset dir="${project.deploy.dir}">
@@ -343,7 +351,15 @@
<antcall target="buildDocs" inheritall="true" inheritrefs="true">
<param name="docs.output.dir" value="${project.war.dir}/WEB-INF/docs" />
</antcall>
<!-- Copy the sample Groovy hook scripts -->
<mkdir dir="${project.war.dir}/WEB-INF/groovy" />
<copy todir="${project.war.dir}/WEB-INF/groovy">
<fileset dir="${basedir}/groovy">
<include name="**/*" />
</fileset>
</copy>
<!-- Build the WAR web.xml from the prototype web.xml and gitblit.properties -->
<java classpath="${project.build.dir}" classname="com.gitblit.build.BuildWebXml">
<classpath refid="master-classpath" />
@@ -922,4 +938,4 @@
<delete dir="${project.war.dir}" />
<delete dir="${project.deploy.dir}" />
</target>
</project>
</project>

+ 56
- 0
distrib/gitblit.properties View File

@@ -28,6 +28,51 @@ git.searchRepositoriesSubfolders = true
# SINCE 0.5.0
git.enableGitServlet = true
#
# Groovy Integration
#
# Location of Groovy scripts to use for Pre and Post receive hooks.
# Use forward slashes even on Windows!!
# e.g. c:/groovy
#
# SINCE 0.8.0
groovy.scriptsFolder = groovy
# Scripts to execute on Pre-Receive.
#
# These scripts execute after an incoming push has been parsed and validated
# but BEFORE the changes are applied to the repository. You might reject a
# push in this script based on the repository and branch the push is attempting
# to change.
#
# NOTE:
# These scripts are only executed when pushing to *Gitblit*, not to other Git
# tooling you may be using. Also note that these scripts are shared between
# repositories. These are NOT repository-specific scripts! Within the script
# you may customize the control-flow for a specific repository by checking the
# *repository* variable.
#
# SPACE-DELIMITED
# SINCE 0.8.0
groovy.preReceiveScripts =
# Scripts to execute on Post-Receive.
#
# These scripts execute AFTER an incoming push has been applied to a repository.
# You might trigger a continuous-integration build here or send a notification.
#
# NOTE:
# These scripts are only executed when pushing to *Gitblit*, not to other Git
# tooling you may be using. Also note that these scripts are shared between
# repositories. These are NOT repository-specific scripts! Within the script
# you may customize the control-flow for a specific repository by checking the
# *repository* variable.
#
# SPACE-DELIMITED
# SINCE 0.8.0
groovy.postReceiveScripts =
#
# Authentication Settings
#
@@ -385,6 +430,17 @@ mail.fromAddress =
# SINCE 0.6.0
mail.adminAddresses =
# List of email addresses for sending commit email notifications.
#
# This key currently requires use of the sendemail.groovy hook script.
# If you set sendemail.groovy in *groovy.postReceiveScripts* then email
# notifications for all repositories (regardless of access restrictions)
# will be sent to these addresses.
#
# SPACE-DELIMITED
# SINCE 0.8.0
mail.mailingLists =
#
# Federation Settings
# SINCE 0.6.0

+ 1
- 0
docs/00_index.mkd View File

@@ -66,6 +66,7 @@ Administrators can create and manage all repositories, user accounts, and teams
### Integration with Your Infrastructure
- Groovy push hook scripts
- Pluggable user service mechanism for custom authentication, authorization, and user management
- Rich RSS feeds
- JSON-based RPC mechanism

+ 3
- 2
docs/01_features.mkd View File

@@ -11,6 +11,7 @@
- RSS/JSON RPC interface
- Java/Swing Gitblit Manager tool
- Gitweb inspired web UI
- Groovy pre- and post- push hook scripts, per-repository or globally for all repositories
- Administrators may create, edit, rename, or delete repositories through the web UI or RPC interface
- Administrators may create, edit, rename, or delete users through the web UI or RPC interface
- Administrators may create, edit, rename, or delete teams through the web UI or RPC interface
@@ -40,10 +41,10 @@
## Limitations
- HTTP/HTTPS are the only supported Git protocols
- Access controls are not path-based, they are repository-based
- Built-in access controls are not path-based, they are repository-based.
- Only Administrators can create, rename or delete repositories
- Only Administrators can create, modify or delete users
- Commit hooks are not supported
- Only Administrators can create, modify or delete teams
- Native Git may be needed to periodically run git-gc as [JGit][jgit] does not fully support the git-gc featureset.
### Caveats

+ 26
- 7
docs/01_setup.mkd View File

@@ -3,9 +3,11 @@
1. Download [Gitblit WAR %VERSION%](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) to the webapps folder of your servlet container.
2. You may have to manually extract the WAR (zip file) to a folder within your webapps folder.
3. Copy the `WEB-INF/users.conf` file to a location outside the webapps folder that is accessible by your servlet container.
Optionally copy the example hook scripts in `WEB-INF/groovy` to a location outside the webapps folder that is accesible by your servlet container.
4. The Gitblit webapp is configured through its `web.xml` file.
Open `web.xml` in your favorite text editor and make sure to review and set:
- &lt;context-parameter&gt; *git.repositoryFolder* (set the full path to your repositories folder)
- &lt;context-parameter&gt; *groovy.scriptsFolder* (set the full path to your Groovy hook scripts folder)
- &lt;context-parameter&gt; *realm.userService* (set the full path to `users.conf`)
5. You may have to restart your servlet container.
6. Open your browser to <http://localhost/gitblit> or whatever the url should be.
@@ -19,6 +21,7 @@ Open `web.xml` in your favorite text editor and make sure to review and set:
2. The server itself is configured through a simple text file.
Open `gitblit.properties` in your favorite text editor and make sure to review and set:
- *git.repositoryFolder* (path may be relative or absolute)
- *groovy.scriptsFolder* (path may be relative or absolute)
- *server.tempFolder* (path may be relative or absolute)
- *server.httpPort* and *server.httpsPort*
- *server.httpBindInterface* and *server.httpsBindInterface*
@@ -115,12 +118,13 @@ Backup your `web.properties` file (if you have one, these are the setting overri
2. Backup your `users.properties` file *(if it is located in the Gitblit GO folder)*
OR
Backup your `users.conf` file *(if it is located in the Gitblit GO folder)*
3. Unzip Gitblit GO to a new folder
4. Overwrite the `gitblit.properties` file with your backup
5. Overwrite the `users.properties` file with your backup *(if it was located in the Gitblit GO folder)*
3. Backup your Groovy hook scripts
4. Unzip Gitblit GO to a new folder
5. Overwrite the `gitblit.properties` file with your backup
6. Overwrite the `users.properties` file with your backup *(if it was located in the Gitblit GO folder)*
OR
Overwrite the `users.conf` file with your backup *(if it was located in the Gitblit GO folder)*
6. Review and optionally apply any new settings as indicated in the [release log](releases.html).
7. Review and optionally apply any new settings as indicated in the [release log](releases.html).
#### Upgrading Windows Service
You may need to delete your old service definition and install a new one depending on what has changed in the release.
@@ -197,10 +201,10 @@ The `users.conf` file allows flexibility for adding new fields to a UserModel ob
### Administering Users (users.properties, Gitblit v0.5.0 - v0.7.0)
All users are stored in the `users.properties` file or in the file you specified in `gitblit.properties`. Your file extension must be *.properties* in order to use this user service.
The format of `users.properties` follows Jetty's convention for HashRealms:
The format of `users.properties` loosely follows Jetty's convention for HashRealms:
username,password,role1,role2,role3...
@teamname,!username1,!username2,!username3,repository1,repository2,repository3...
username=password,role1,role2,role3...
@teamname=!username1,!username2,!username3,repository1,repository2,repository3...
#### Usernames
Usernames must be unique and are case-insensitive.
@@ -220,6 +224,21 @@ You may use your own custom *com.gitblit.IUserService* implementation by specify
Your user service class must be on Gitblit's classpath and must have a public default constructor.
Please see the following interface definition [com.gitblit.IUserService](https://github.com/gitblit/gitblit/blob/master/src/com/gitblit/IUserService.java).
## Groovy Hook Scripts
*SINCE 0.8.0*
The preferred hook mechanism is Groovy. This mechanism only executes when pushing to Gitblit, not when pushing to some other Git tooling in your stack.
The Groovy hook mechanism allows for dynamic extension of Gitblit to execute custom tasks on receiving and processing push events. The scripts run within the context of your Gitblit instance and therefore have access to Gitblit's internals at runtime.
Your Groovy scripts should be stored in the *groovy.scriptsFolder* as specified in `gitblit.properties` or `web.xml`.
Scripts must be explicitly specified to be executed. A script can be run on *all repositories* by adding the script file name to *groovy.preReceiveScripts* or *groovy.postReceiveScripts* in `gitblit.properties` or `web.xml`. Alternatively, you may specify per-repository scripts in the repository settings. Global/shared scripts are executed first in their listed order, followed by per-repository scripts in their listed order.
Some primitive sample scripts are included in the GO and WAR distributions to show you how you can tap into Gitblit with the provided bound variables.
Hook contributions and improvements are welcome.
## Client Setup and Configuration
### Https with Self-Signed Certificates
You must tell Git/JGit not to verify the self-signed certificate in order to perform any remote Git operations.

+ 1
- 0
docs/04_design.mkd View File

@@ -35,6 +35,7 @@ The following dependencies are automatically downloaded by Gitblit GO (or alread
- [jdom](http://www.jdom.org) (Apache-style JDOM license)
- [google-gson](http://code.google.com/google-gson) (Apache 2.0)
- [javamail](http://kenai.com/projects/javamail) (CDDL-1.0, BSD, GPL-2.0, GNU-Classpath)
- [Groovy](http://groovy.codehaus.org) (Apache 2.0)
### Other Build Dependencies
- [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)

+ 7
- 1
docs/04_releases.mkd View File

@@ -3,6 +3,13 @@
### Current Release
**%VERSION%** ([go](http://code.google.com/p/gitblit/downloads/detail?name=%GO%) | [war](http://code.google.com/p/gitblit/downloads/detail?name=%WAR%) | [express](http://code.google.com/p/gitblit/downloads/detail?name=%EXPRESS%) | [fedclient](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%) | [manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) | [api](http://code.google.com/p/gitblit/downloads/detail?name=%API%)) based on [%JGIT%][jgit] &nbsp; *released %BUILDDATE%*
- added: Groovy 1.8.4 and sample pre- and post- push Groovy hook scripts. Hook scripts can be set per-repository or globally for all repositories.
Unfortunately this adds another 6 MB to the 8MB Gitblit package, but it allows for a *very* powerful, flexible, platform-independent hook script mechanism.
**New:** *groovy.scriptsFolder = groovy*
**New:** *groovy.preReceiveScripts =*
**New:** *groovy.postReceiveScripts =*
- added: New key for mailing lists. This is _currently_ used in conjunction with the example *sendemail.groovy* post-receive hook script.
**New:** *mail.mailingLists =*
- added: new default user service implementation: com.gitblit.ConfigUserService (users.conf)
This user service implementation allows for serialization and deserialization of more sophisticated Gitblit User objects and will open the door for more advanced Gitblit features. For upgrading installations, a `users.conf` file will automatically be created for you from your existing `users.properties` file on your first launch of Gitblit. You will have to manually set *realm.userService=users.conf* to switch to the new user service. The original `users.properties` file and it's corresponding implementation are deprecated.
**New:** *realm.userService = users.conf*
@@ -21,7 +28,6 @@ This user service implementation allows for serialization and deserialization of
- improved: empty repositories now link to the *empty repository* page which gives some direction to the user for the next step in using Gitblit. This page displays the primary push/clone url of the repository and gives sample syntax for the git command-line client. (issue 31)
- improved: unit testing framework has been migrated to JUnit4 syntax and the test suite has been redesigned to run all unit tests, including rpc, federation, and git push/clone tests
### Older Releases
**0.7.0** ([go](http://code.google.com/p/gitblit/downloads/detail?name=gitblit-0.7.0.zip) | [war](http://code.google.com/p/gitblit/downloads/detail?name=gitblit-0.7.0.war) | [fedclient](http://code.google.com/p/gitblit/downloads/detail?name=fedclient-0.7.0.zip) | [manager](http://code.google.com/p/gitblit/downloads/detail?name=manager-0.7.0.zip) | [api](http://code.google.com/p/gitblit/downloads/detail?name=gbapi-0.7.0.zip)) based on [JGit 1.1.0 (201109151100-r)][jgit] &nbsp; *released 2011-11-11*

+ 4
- 1
docs/05_roadmap.mkd View File

@@ -37,7 +37,10 @@ This list is volatile.
### IDEAS
* Gitblit: consider user-subscribed email notifications for a repository branch
* Gitblit: implement branch permission controls as Groovy pre-receive script.
*Maintain permissions text file similar to a gitolite configuration file or svn authz file.*
* Gitblit: consider user-subscribed email notifications for a repository branch as a built-in feature.
*There is a sample Groovy post-receive hook script which can send emails.*
* Gitblit: aggregate RSS feeds by tag or subfolder
* Gitblit: Consider creating more Git model objects and exposing them via the JSON RPC interface to allow inspection/retrieval of Git commits, Git trees, etc from Gitblit.
* Gitblit: Stronger ticgit integration (issue 8)

+ 88
- 0
groovy/blockpush.groovy View File

@@ -0,0 +1,88 @@
/*
* Copyright 2011 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.
*/
import java.text.MessageFormat;
import com.gitblit.GitBlit
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
/**
* Sample Gitblit Pre-Receive Hook: blockpush
*
* This script could and perhaps should be further developed to provide
* a full repository-branch permissions system similar to gitolite or gitosis.
*
* The Pre-Receive hook is executed after an incoming push has been parsed,
* validated, and objects have been written but BEFORE the refs are updated.
* This is the appropriate point to block a push for some reason.
*
* This script is only executed when pushing to *Gitblit*, not to other Git
* tooling you may be using.
*
* If this script is specified in *groovy.preReceiveScripts* of gitblit.properties
* or web.xml then it will be executed by any repository when it receives a
* push. If you choose to share your script then you may have to consider
* tailoring control-flow based on repository access restrictions.
*
* Scripts may also be specified per-repository in the repository settings page.
* Shared scripts will be excluded from this list of available scripts.
*
* This script is dynamically reloaded and it is executed within it's own
* exception handler so it will not crash another script nor crash Gitblit.
*
* If you want this hook script to fail and abort all subsequent scripts in the
* chain, "return false" at the appropriate failure points.
*
* Bound Variables:
* gitblit Gitblit Server com.gitblit.GitBlit
* repository Gitblit Repository com.gitblit.models.RepositoryModel
* user Gitblit User com.gitblit.models.UserModel
* commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand>
* url Base url for Gitblit String
* log Logger instance org.slf4j.Logger
*
*/
// Indicate we have started the script
logger.info("blockpush hook triggered by ${user.username} for ${repository.name}: checking ${commands.size} commands")
/*
* Example rejection of pushes to the master branch of example.git
*/
def blocked = false
switch (repository.name) {
case "ex@mple.git":
for (ReceiveCommand command : commands) {
def updatedRef = command.refName
if (updatedRef.equals("refs/heads/master")) {
// to reject a command set it's result to anything other than Result.NOT_ATTEMPTED
command.setResult(Result.REJECTED_OTHER_REASON, "You are not permitted to write to ${repository.name}:${updatedRef}")
blocked = true
}
}
break
default:
break
}
if (blocked) {
// return false to break the push hook chain
return false
}

+ 71
- 0
groovy/jenkins.groovy View File

@@ -0,0 +1,71 @@
/*
* Copyright 2011 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.
*/
import com.gitblit.GitBlit
import com.gitblit.Keys
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import com.gitblit.utils.JGitUtils
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
/**
* Sample Gitblit Post-Receive Hook: jenkins
*
* The Post-Receive hook is executed AFTER the pushed commits have been applied
* to the Git repository. This is the appropriate point to trigger an
* integration build or to send a notification.
*
* This script is only executed when pushing to *Gitblit*, not to other Git
* tooling you may be using.
*
* If this script is specified in *groovy.postReceiveScripts* of gitblit.properties
* or web.xml then it will be executed by any repository when it receives a
* push. If you choose to share your script then you may have to consider
* tailoring control-flow based on repository access restrictions.
*
* Scripts may also be specified per-repository in the repository settings page.
* Shared scripts will be excluded from this list of available scripts.
*
* This script is dynamically reloaded and it is executed within it's own
* exception handler so it will not crash another script nor crash Gitblit.
*
* Bound Variables:
* gitblit Gitblit Server com.gitblit.GitBlit
* repository Gitblit Repository com.gitblit.models.RepositoryModel
* user Gitblit User com.gitblit.models.UserModel
* commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand>
* url Base url for Gitblit String
* logger Logger instance org.slf4j.Logger
*
*/
// Indicate we have started the script
logger.info("jenkins hook triggered by ${user.username} for ${repository.name}")
// This script requires Jenkins Git plugin 1.1.14 or later
// http://kohsuke.org/2011/12/01/polling-must-die-triggering-jenkins-builds-from-a-git-hook/
// define your jenkins url here or set groovy.jenkinsServer in
// gitblit.properties or web.xml
def jenkinsUrl = gitblit.getString("groovy.jenkinsServer", "http://yourserver/jenkins")
// define the trigger url
def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=" + url + "/git/" + repository.name
// trigger the build
new URL(triggerUrl).getContent()

+ 134
- 0
groovy/sendemail.groovy View File

@@ -0,0 +1,134 @@
/*
* Copyright 2011 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.
*/
import com.gitblit.GitBlit
import com.gitblit.Keys
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import com.gitblit.utils.JGitUtils
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.lib.Config
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
/**
* Sample Gitblit Post-Receive Hook: sendemail
*
* The Post-Receive hook is executed AFTER the pushed commits have been applied
* to the Git repository. This is the appropriate point to trigger an
* integration build or to send a notification.
*
* This script is only executed when pushing to *Gitblit*, not to other Git
* tooling you may be using.
*
* If this script is specified in *groovy.postReceiveScripts* of gitblit.properties
* or web.xml then it will be executed by any repository when it receives a
* push. If you choose to share your script then you may have to consider
* tailoring control-flow based on repository access restrictions.
*
* Scripts may also be specified per-repository in the repository settings page.
* Shared scripts will be excluded from this list of available scripts.
*
* This script is dynamically reloaded and it is executed within it's own
* exception handler so it will not crash another script nor crash Gitblit.
*
* If you want this hook script to fail and abort all subsequent scripts in the
* chain, "return false" at the appropriate failure points.
*
* Bound Variables:
* gitblit Gitblit Server com.gitblit.GitBlit
* repository Gitblit Repository com.gitblit.models.RepositoryModel
* user Gitblit User com.gitblit.models.UserModel
* commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand>
* url Base url for Gitblit String
* logger Logger instance org.slf4j.Logger
*
*/
// Indicate we have started the script
logger.info("sendemail hook triggered by ${user.username} for ${repository.name}")
/*
* Primitive example email notification with example repository-specific checks.
* This requires the mail settings to be properly configured in Gitblit.
*/
Repository r = gitblit.getRepository(repository.name)
// reuse some existing repository config settings, if available
Config config = r.getConfig()
def mailinglist = config.getString("hooks", null, "mailinglist")
def emailprefix = config.getString("hooks", null, "emailprefix")
// set default values
def toAddresses = []
if (emailprefix == null)
emailprefix = "[Gitblit]"
if (mailinglist != null) {
def addrs = mailinglist.split("(,|\\s)")
toAddresses.addAll(addrs)
}
// add all mailing lists defined in gitblit.properties or web.xml
toAddresses.addAll(gitblit.getStrings(Keys.mail.mailingLists))
// special custom cases
switch(repository.name) {
case "ex@mple.git":
toAddresses.add "dev-team@somewhere.com"
toAddresses.add "qa-team@somewhere.com"
break
default:
break
}
// get the create/update commits from the repository to build message content
def commits = []
for (ReceiveCommand command:commands) {
switch (command.type) {
case ReceiveCommand.Type.UPDATE:
case ReceiveCommand.Type.CREATE:
RevCommit commit = JGitUtils.getCommit(r, command.newId.name)
commits.add(commit)
break
default:
break
}
}
// close the repository reference
r.close()
// build a link to the summary page, either mounted or parameterized
def summaryUrl
if (gitblit.getBoolean(Keys.web.mountParameters, true))
summaryUrl = url + "/summary/" + repository.name.replace("/", gitblit.getString(Keys.web.forwardSlashCharacter, "/"))
else
summaryUrl = url + "/summary?r=" + repository.name
// create a simple commits table
def table = commits.collect { it.id.name[0..8] + " " + it.authorIdent.name.padRight(20, " ") + it.shortMessage }.join("\n")
// create the message body
def msg = """${user.username} pushed ${commits.size} commits to ${repository.name}
${summaryUrl}
${table}"""
// tell Gitblit to send the message (Gitblit filters duplicate addresses)
gitblit.notifyUsers("${emailprefix} ${user.username} pushed ${commits.size} commits => ${repository.name}", msg, toAddresses)

+ 41
- 2
src/com/gitblit/GitBlit.java View File

@@ -557,6 +557,7 @@ public class GitBlit implements ServletContextListener {
public boolean setRepositoryTeams(RepositoryModel repository, List<String> repositoryTeams) {
return userService.setTeamnamesForRepositoryRole(repository.name, repositoryTeams);
}
/**
* Updates the TeamModel object for the specified name.
*
@@ -564,7 +565,8 @@ public class GitBlit implements ServletContextListener {
* @param team
* @param isCreate
*/
public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) throws GitBlitException {
public void updateTeamModel(String teamname, TeamModel team, boolean isCreate)
throws GitBlitException {
if (!teamname.equalsIgnoreCase(team.name)) {
if (userService.getTeamModel(team.name) != null) {
throw new GitBlitException(MessageFormat.format(
@@ -576,7 +578,7 @@ public class GitBlit implements ServletContextListener {
throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!");
}
}
/**
* Delete the team object with the specified teamname
*
@@ -725,6 +727,10 @@ public class GitBlit implements ServletContextListener {
"gitblit", null, "federationSets")));
model.isFederated = getConfig(config, "isFederated", false);
model.origin = config.getString("remote", "origin", "url");
model.preReceiveScripts = new ArrayList<String>(Arrays.asList(config.getStringList(
"gitblit", null, "preReceiveScript")));
model.postReceiveScripts = new ArrayList<String>(Arrays.asList(config.getStringList(
"gitblit", null, "postReceiveScript")));
}
r.close();
return model;
@@ -944,6 +950,8 @@ public class GitBlit implements ServletContextListener {
config.setString("gitblit", null, "federationStrategy",
repository.federationStrategy.name());
config.setBoolean("gitblit", null, "isFederated", repository.isFederated);
config.setStringList("gitblit", null, "preReceiveScript", repository.preReceiveScripts);
config.setStringList("gitblit", null, "postReceiveScript", repository.postReceiveScripts);
try {
config.save();
} catch (IOException e) {
@@ -1426,6 +1434,37 @@ public class GitBlit implements ServletContextListener {
}
}
/**
* Notify users by email of something.
*
* @param subject
* @param message
* @param toAddresses
*/
public void notifyUsers(String subject, String message, ArrayList<String> toAddresses) {
this.notifyUsers(subject, message, toAddresses.toArray(new String[0]));
}
/**
* Notify users by email of something.
*
* @param subject
* @param message
* @param toAddresses
*/
public void notifyUsers(String subject, String message, String... toAddresses) {
try {
Message mail = mailExecutor.createMessage(toAddresses);
if (mail != null) {
mail.setSubject(subject);
mail.setText(message);
mailExecutor.queue(mail);
}
} catch (MessagingException e) {
logger.error("Messaging error", e);
}
}
/**
* Returns the descriptions/comments of the Gitblit config settings.
*

+ 267
- 0
src/com/gitblit/GitServlet.java View File

@@ -15,11 +15,48 @@
*/
package com.gitblit;
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PostReceiveHook;
import org.eclipse.jgit.transport.PreReceiveHook;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
/**
* The GitServlet exists to force configuration of the JGit GitServlet based on
* the Gitblit settings from either gitblit.properties or from context
* parameters in the web.xml file.
*
* It also implements and registers the Groovy hook mechanism.
*
* Access to this servlet is protected by the GitFilter.
*
* @author James Moger
@@ -29,6 +66,8 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet {
private static final long serialVersionUID = 1L;
private GroovyScriptEngine gse;
/**
* Configure the servlet from Gitblit's configuration.
*/
@@ -41,4 +80,232 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet {
}
return super.getInitParameter(name);
}
@Override
public void init(ServletConfig config) throws ServletException {
String groovyRoot = GitBlit.getString(Keys.groovy.scriptsFolder, "groovy");
try {
gse = new GroovyScriptEngine(groovyRoot);
} catch (IOException e) {
throw new ServletException("Failed to instantiate Groovy Script Engine!", e);
}
// set the Gitblit receive hook
setReceivePackFactory(new DefaultReceivePackFactory() {
@Override
public ReceivePack create(HttpServletRequest req, Repository db)
throws ServiceNotEnabledException, ServiceNotAuthorizedException {
ReceivePack rp = super.create(req, db);
GitblitReceiveHook hook = new GitblitReceiveHook();
hook.gitblitUrl = HttpUtils.getGitblitURL(req);
rp.setPreReceiveHook(hook);
rp.setPostReceiveHook(hook);
return rp;
}
});
super.init(config);
}
/**
* The Gitblit receive hook allows for special processing on push events.
* That might include rejecting writes to specific branches or executing a
* script.
*
* @author James Moger
*
*/
private class GitblitReceiveHook implements PreReceiveHook, PostReceiveHook {
protected final Logger logger = LoggerFactory.getLogger(GitblitReceiveHook.class);
protected String gitblitUrl;
/**
* Instrumentation point where the incoming push event has been parsed,
* validated, objects created BUT refs have not been updated. You might
* use this to enforce a branch-write permissions model.
*/
@Override
public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
List<String> scripts = GitBlit.getStrings(Keys.groovy.preReceiveScripts);
RepositoryModel repository = getRepositoryModel(rp);
scripts.addAll(repository.preReceiveScripts);
UserModel user = getUserModel(rp);
runGroovy(repository, user, commands, scripts);
for (ReceiveCommand cmd : commands) {
if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) {
logger.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId()
.getName(), cmd.getResult(), cmd.getMessage()));
}
}
// Experimental
// runNativeScript(rp, "hooks/pre-receive", commands);
}
/**
* Instrumentation point where the incoming push has been applied to the
* repository. This is the point where we would trigger a Jenkins build
* or send an email.
*/
@Override
public void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
if (commands.size() == 0) {
logger.info("skipping post-receive hooks, no refs created, updated, or removed");
return;
}
List<String> scripts = GitBlit.getStrings(Keys.groovy.postReceiveScripts);
RepositoryModel repository = getRepositoryModel(rp);
scripts.addAll(repository.postReceiveScripts);
UserModel user = getUserModel(rp);
runGroovy(repository, user, commands, scripts);
// Experimental
// runNativeScript(rp, "hooks/post-receive", commands);
}
/**
* Returns the RepositoryModel for the repository we are pushing into.
*
* @param rp
* @return a RepositoryModel
*/
protected RepositoryModel getRepositoryModel(ReceivePack rp) {
Repository repository = rp.getRepository();
String rootPath = GitBlit.getRepositoriesFolder().getAbsolutePath();
String repositoryName = repository.getDirectory().getAbsolutePath();
repositoryName = repositoryName.substring(rootPath.length() + 1);
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
return model;
}
/**
* Returns the UserModel for the user pushing the changes.
*
* @param rp
* @return a UserModel
*/
protected UserModel getUserModel(ReceivePack rp) {
PersonIdent person = rp.getRefLogIdent();
UserModel user = GitBlit.self().getUserModel(person.getName());
if (user == null) {
// anonymous push, create a temporary usermodel
user = new UserModel(person.getName());
}
return user;
}
/**
* Runs the specified Groovy hook scripts.
*
* @param repository
* @param user
* @param commands
* @param scripts
*/
protected void runGroovy(RepositoryModel repository, UserModel user,
Collection<ReceiveCommand> commands, List<String> scripts) {
if (scripts == null || scripts.size() == 0) {
// no Groovy scripts to execute
return;
}
Binding binding = new Binding();
binding.setVariable("gitblit", GitBlit.self());
binding.setVariable("repository", repository);
binding.setVariable("user", user);
binding.setVariable("commands", commands);
binding.setVariable("url", gitblitUrl);
binding.setVariable("logger", logger);
for (String script : scripts) {
if (StringUtils.isEmpty(script)) {
continue;
}
try {
Object result = gse.run(script, binding);
if (result instanceof Boolean) {
if (!((Boolean) result)) {
logger.error(MessageFormat.format(
"Groovy script {0} has failed! Hook scripts aborted.", script));
break;
}
}
} catch (Exception e) {
logger.error(
MessageFormat.format("Failed to execute Groovy script {0}", script), e);
}
}
}
/**
* Runs the native push hook script.
*
* http://book.git-scm.com/5_git_hooks.html
* http://longair.net/blog/2011/04/09/missing-git-hooks-documentation/
*
* @param rp
* @param script
* @param commands
*/
@SuppressWarnings("unused")
protected void runNativeScript(ReceivePack rp, String script,
Collection<ReceiveCommand> commands) {
Repository repository = rp.getRepository();
File scriptFile = new File(repository.getDirectory(), script);
int resultCode = 0;
if (scriptFile.exists()) {
try {
logger.debug("executing " + scriptFile);
Process process = Runtime.getRuntime().exec(scriptFile.getAbsolutePath(), null,
repository.getDirectory());
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
process.getOutputStream()));
for (ReceiveCommand command : commands) {
switch (command.getType()) {
case UPDATE:
// updating a ref
writer.append(MessageFormat.format("{0} {1} {2}\n", command.getOldId()
.getName(), command.getNewId().getName(), command.getRefName()));
break;
case CREATE:
// new ref
// oldrev hard-coded to 40? weird.
writer.append(MessageFormat.format("40 {0} {1}\n", command.getNewId()
.getName(), command.getRefName()));
break;
}
}
resultCode = process.waitFor();
// read and buffer stdin
// this is supposed to be piped back to the git client.
// not sure how to do that right now.
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}
logger.debug(sb.toString());
} catch (Throwable e) {
resultCode = -1;
logger.error(
MessageFormat.format("Failed to execute {0}",
scriptFile.getAbsolutePath()), e);
}
}
// reject push
if (resultCode != 0) {
for (ReceiveCommand command : commands) {
command.setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
"Native script {0} rejected push or failed",
scriptFile.getAbsolutePath()));
}
}
}
}
}

+ 18
- 4
src/com/gitblit/MailExecutor.java View File

@@ -15,6 +15,7 @@
*/
package com.gitblit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
@@ -24,6 +25,7 @@ import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;
import javax.mail.Authenticator;
import javax.mail.Message;
@@ -152,11 +154,23 @@ public class MailExecutor implements Runnable {
InternetAddress from = new InternetAddress(fromAddress, "Gitblit");
message.setFrom(from);
InternetAddress[] tos = new InternetAddress[toAddresses.size()];
for (int i = 0; i < toAddresses.size(); i++) {
tos[i] = new InternetAddress(toAddresses.get(i));
Set<String> uniques = new HashSet<String>(toAddresses);
Pattern validEmail = Pattern
.compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");
List<InternetAddress> tos = new ArrayList<InternetAddress>();
for (String address : uniques) {
if (StringUtils.isEmpty(address)) {
continue;
}
if (validEmail.matcher(address).find()) {
try {
tos.add(new InternetAddress(address));
} catch (Throwable t) {
}
}
}
message.setRecipients(Message.RecipientType.TO, tos);
message.setRecipients(Message.RecipientType.TO,
tos.toArray(new InternetAddress[tos.size()]));
message.setSentDate(new Date());
} catch (Exception e) {
logger.error("Failed to properly create message", e);

+ 7
- 1
src/com/gitblit/build/Build.java View File

@@ -89,6 +89,7 @@ public class Build {
downloadFromApache(MavenObject.JDOM, BuildType.RUNTIME);
downloadFromApache(MavenObject.GSON, BuildType.RUNTIME);
downloadFromApache(MavenObject.MAIL, BuildType.RUNTIME);
downloadFromApache(MavenObject.GROOVY, BuildType.RUNTIME);
downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.RUNTIME);
@@ -114,7 +115,8 @@ public class Build {
downloadFromApache(MavenObject.JDOM, BuildType.COMPILETIME);
downloadFromApache(MavenObject.GSON, BuildType.COMPILETIME);
downloadFromApache(MavenObject.MAIL, BuildType.COMPILETIME);
downloadFromApache(MavenObject.GROOVY, BuildType.COMPILETIME);
downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME);
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.COMPILETIME);
@@ -495,6 +497,10 @@ public class Build {
"1.4.3", 462000, 642000, 0, "8154bf8d666e6db154c548dc31a8d512c273f5ee",
"5875e2729de83a4e46391f8f979ec8bd03810c10", null);
public static final MavenObject GROOVY = new MavenObject("groovy", "org/codehaus/groovy", "groovy-all",
"1.8.4", 6143000, 2290000, 4608000, "b5e7c2a5e6af43ccccf643ad656d6fe4b773be2b",
"a527e83e5c715540108d8f2b86ca19a3c9c78ac1", "8e5da5584fff57b14adbb4ee25cca09f5a00b013");
public final String name;
public final String group;
public final String artifact;

+ 2
- 0
src/com/gitblit/models/RepositoryModel.java View File

@@ -55,6 +55,8 @@ public class RepositoryModel implements Serializable, Comparable<RepositoryModel
public String frequency;
public String origin;
public String size;
public List<String> preReceiveScripts;
public List<String> postReceiveScripts;
public RepositoryModel() {
this("", "", "", new Date(0));

+ 4
- 0
test-gitblit.properties View File

@@ -5,6 +5,9 @@
git.repositoriesFolder = git
git.searchRepositoriesSubfolders = true
git.enableGitServlet = true
groovy.scriptsFolder = groovy
groovy.preReceiveScripts = blockpush.groovy
groovy.postReceiveScripts = sendemail.groovy jenkins.groovy
web.authenticateViewPages = false
web.authenticateAdminPages = true
web.allowCookieAuthentication = true
@@ -59,6 +62,7 @@ mail.username =
mail.password =
mail.fromAddress =
mail.adminAddresses =
mail.mailingLists = x@test.com y@test.com z@test.com
federation.name = Unit Test
federation.passphrase = Unit Testing
federation.allowProposals = false

Loading…
Cancel
Save