|
|
@@ -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" |
|
|
|
} |