summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimeon H.K. Fitch <fitch@datamininglab.com>2016-06-03 20:40:37 -0400
committerSimeon H.K. Fitch <fitch@datamininglab.com>2016-06-03 20:40:37 -0400
commit121e2f0dd548176c6a7346da13868117500695d6 (patch)
treee16bd29ec1295c63b0c219260babe769ddca0025
parentc9afc9ed01dbc6b74ecb2734d2cd7ed943b5d643 (diff)
downloadgitblit-121e2f0dd548176c6a7346da13868117500695d6.tar.gz
gitblit-121e2f0dd548176c6a7346da13868117500695d6.zip
Initial implementation of a JetBrains YouTrack hook for GitBlit.
-rw-r--r--src/main/distrib/data/groovy/youtrack-readme.md30
-rw-r--r--src/main/distrib/data/groovy/youtrack.groovy224
2 files changed, 254 insertions, 0 deletions
diff --git a/src/main/distrib/data/groovy/youtrack-readme.md b/src/main/distrib/data/groovy/youtrack-readme.md
new file mode 100644
index 00000000..1a6f9136
--- /dev/null
+++ b/src/main/distrib/data/groovy/youtrack-readme.md
@@ -0,0 +1,30 @@
+# GitBlit YouTrack Receive Hook
+
+GitBlit receive hook for updating referenced YouTrack issues.
+
+This script has only been tested with the cloud hosted YouTrack instance.
+
+## Usage
+
+Due to limited authentication options when using the YouTrack REST API, you have to store a username and password for an account with appropriate permissions for adding comments to any issue. Hopefully in the future YouTrack will support API keys or similar.
+
+1. Update your `gitblit.properties` file with the following entries:
+ * `groovy.customFields = "youtrackProjectID=YouTrack Project ID" ` *(or append to existing setting)*
+ * `youtrack.host = example.myjetbrains.com`
+ * `youtrack.user = ytUser`
+ * `youtrack.pass = insecurep@sswordsRus`
+
+ (But using your own host and credential info).
+
+2. Copy the `youtrack.groovy` script to the `<gitblit-data-dir>/groovy` scripts directory.
+3. In GitBlit, go to a repository, click the *edit* button, then click the *receive* link. In the *post0receive scripts* section you should see `youtrack` as an option. Move it over to the *Selected* column.
+4. At the bottom of this same screen should should be a *custom fields* section with a **YouTrack Project ID** field. Enter the YouTrack Project ID associated with the repository.
+5. When you commit changes, reference YouTrack issues with `#{projectID}-{issueID}` where `{projectID}` is the YouTrack Project ID, and `{issueID}` is the issue number. For example, to references issue `34` in project `fizz`:
+
+ git commit -m'Changed bazinator to fix issue #fizz-34.'
+
+ Multiple issues may be referenced in the same commit message.
+
+## Attribution
+
+Much of this script was cobbled together from the example receive hooks in the official [GitBlit](https://github.com/gitblit/gitblit) distribution.
diff --git a/src/main/distrib/data/groovy/youtrack.groovy b/src/main/distrib/data/groovy/youtrack.groovy
new file mode 100644
index 00000000..c94d8fd1
--- /dev/null
+++ b/src/main/distrib/data/groovy/youtrack.groovy
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2013 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.TeamModel
+import com.gitblit.models.UserModel
+import com.gitblit.utils.JGitUtils
+import java.text.SimpleDateFormat
+import org.eclipse.jgit.lib.Repository
+import org.eclipse.jgit.lib.Config
+import org.eclipse.jgit.transport.ReceiveCommand
+import org.eclipse.jgit.transport.ReceiveCommand.Result
+import org.slf4j.Logger
+
+import org.eclipse.jgit.api.Status;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.IndexDiff;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.patch.FileHeader;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.EmptyTreeIterator;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.util.io.DisabledOutputStream;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.protocol.*;
+import org.apache.http.client.protocol.*;
+import org.apache.http.client.methods.*;
+import org.apache.http.impl.client.*;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.util.EntityUtils;
+
+
+/**
+ * GitBlit Post-Receive Hook for YouTrack
+ *
+ * The purpose of this script is to invoke the YouTrack API and update a case when
+ * push is received based.
+ *
+ * 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.
+ *
+ * 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
+ * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack
+ * user Gitblit User com.gitblit.models.UserModel
+ * commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand>
+ * url Base url for Gitblit String
+ * logger Logs messages to Gitblit org.slf4j.Logger
+ * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger
+ *
+ *
+ * Custom Fileds Used by This script
+ * youtrackProjectID - Project ID in YouTrack
+ *
+ * Make sure to add the following to your gitblit.properties file:
+ * groovy.customFields = "youtrackProjectID=YouTrack Project ID"
+ * youtrack.host = example.myjetbrains.com
+ * youtrack.user = ytUser
+ * youtrack.pass = insecurep@sswordsRus
+ */
+
+// Indicate we have started the script
+logger.info("youtrack hook triggered in ${url} by ${user.username} for ${repository.name}")
+
+Repository r = gitblit.getRepository(repository.name)
+
+// pull custom fields from repository specific values
+def youtrackProjectID = repository.customFields.youtrackProjectID
+
+if(youtrackProjectID == null || youtrackProjectID.length() == 0) return true;
+
+def youtrackHost = gitblit.getString('youtrack.host', 'nohost')
+def bugIdRegex = gitblit.getString('youtrack.commitMessageRegex', "#${youtrackProjectID}-([0-9]+)")
+def youtrackUser = gitblit.getString('youtrack.user', 'nouser')
+def youtrackPass = gitblit.getString('youtrack.pass', 'nopassword')
+
+HttpHost target = new HttpHost(youtrackHost, 80, "http");
+CredentialsProvider credsProvider = new BasicCredentialsProvider();
+credsProvider.setCredentials(
+ new AuthScope(target.getHostName(), target.getPort()),
+ new UsernamePasswordCredentials(youtrackUser, youtrackPass));
+def httpclient = new DefaultHttpClient();
+
+httpclient.setCredentialsProvider(credsProvider);
+
+try {
+
+ AuthCache authCache = new BasicAuthCache();
+ BasicScheme basicAuth = new BasicScheme();
+ authCache.put(target, basicAuth);
+ BasicHttpContext localcontext = new BasicHttpContext();
+ localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
+
+
+ for (command in commands) {
+ for( commit in JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() ) {
+ def bugIds = new java.util.HashSet()
+ def longMsg = commit.getFullMessage()
+ // Grab the second match group and then filter out each numeric ID and add it to array
+ (longMsg =~ bugIdRegex).each{ (it[1] =~ "\\d+").each { bugIds.add(it)} }
+
+ if(bugIds.size() > 0) {
+ def comment = createIssueComment(command, commit)
+
+ logger.debug("Submitting youtrack comment:\n" + comment)
+
+ def encoded = URLEncoder.encode(comment)
+ for(bugId in bugIds ) {
+ def baseURL = "http://${youtrackHost}/youtrack/rest/issue/${youtrackProjectID}-${bugId}/execute?command=&comment=" + encoded
+ def post = new HttpPost(baseURL);
+
+ clientLogger.info("Executing request " + post.getRequestLine() + " to target " + target);
+ def response = httpclient.execute(target, post, localcontext);
+ logger.debug(response.getStatusLine().toString());
+ EntityUtils.consume(response.getEntity());
+ }
+ }
+ }
+ }
+}
+finally {
+ r.close()
+}
+
+def createIssueComment(command, commit) {
+ def commits = [commit] // Borrowed code expects a collection.
+ Repository r = gitblit.getRepository(repository.name)
+ // define the summary and commit urls
+ def repo = repository.name
+ def summaryUrl
+ def commitUrl
+ if (gitblit.getBoolean(Keys.web.mountParameters, true)) {
+ repo = repo.replace('/', gitblit.getString(Keys.web.forwardSlashCharacter, '/')).replace('/', '%2F')
+ summaryUrl = url + "/summary/$repo"
+ commitUrl = url + "/commit/$repo/"
+ } else {
+ summaryUrl = url + "/summary?r=$repo"
+ commitUrl = url + "/commit?r=$repo&h="
+ }
+
+ // construct a simple text summary of the changes contained in the push
+ def commitBreak = '\n'
+ def commitCount = 0
+ def changes = ''
+
+ SimpleDateFormat df = new SimpleDateFormat(gitblit.getString(Keys.web.datetimestampLongFormat, 'EEEE, MMMM d, yyyy h:mm a z'))
+
+ def table = {
+ def shortSha = it.id.name.substring(0, 8)
+ "* [$commitUrl$it.id.name ${shortSha}] by *${it.authorIdent.name}* on ${df.format(JGitUtils.getCommitDate(it))}\n" +
+ " {cut $it.shortMessage}\n{noformat}$it.fullMessage{noformat}{cut}"
+ }
+
+ def ref = command.refName
+ def refType = 'branch'
+ if (ref.startsWith('refs/heads/')) {
+ ref = command.refName.substring('refs/heads/'.length())
+ } else if (ref.startsWith('refs/tags/')) {
+ ref = command.refName.substring('refs/tags/'.length())
+ refType = 'tag'
+ }
+
+ switch (command.type) {
+ case ReceiveCommand.Type.CREATE:
+ // new branch
+ changes += "''new $refType $ref created''\n"
+ changes += commits.collect(table).join(commitBreak)
+ changes += '\n'
+ break
+ case ReceiveCommand.Type.UPDATE:
+ // fast-forward branch commits table
+ changes += "''$ref $refType updated''\n"
+ changes += commits.collect(table).join(commitBreak)
+ changes += '\n'
+ break
+ case ReceiveCommand.Type.UPDATE_NONFASTFORWARD:
+ // non-fast-forward branch commits table
+ changes += "''$ref $refType updated [NON fast-forward]''"
+ changes += commits.collect(table).join(commitBreak)
+ changes += '\n'
+ break
+ case ReceiveCommand.Type.DELETE:
+ // deleted branch/tag
+ changes += "''$ref $refType deleted''"
+ break
+ default:
+ break
+ }
+
+ return "$user.username pushed commits to [$summaryUrl $repository.name]\n$changes"
+}