|
| 1 | +/* |
| 2 | + * Copyright 2013 gitblit.com. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | +import com.gitblit.GitBlit |
| 17 | +import com.gitblit.Keys |
| 18 | +import com.gitblit.models.RepositoryModel |
| 19 | +import com.gitblit.models.TeamModel |
| 20 | +import com.gitblit.models.UserModel |
| 21 | +import com.gitblit.utils.JGitUtils |
| 22 | +import java.text.SimpleDateFormat |
| 23 | +import org.eclipse.jgit.lib.Repository |
| 24 | +import org.eclipse.jgit.lib.Config |
| 25 | +import org.eclipse.jgit.transport.ReceiveCommand |
| 26 | +import org.eclipse.jgit.transport.ReceiveCommand.Result |
| 27 | +import org.slf4j.Logger |
| 28 | + |
| 29 | +import org.eclipse.jgit.api.Status; |
| 30 | +import org.eclipse.jgit.api.errors.JGitInternalException; |
| 31 | +import org.eclipse.jgit.diff.DiffEntry; |
| 32 | +import org.eclipse.jgit.diff.DiffFormatter; |
| 33 | +import org.eclipse.jgit.diff.RawTextComparator; |
| 34 | +import org.eclipse.jgit.lib.Constants; |
| 35 | +import org.eclipse.jgit.lib.IndexDiff; |
| 36 | +import org.eclipse.jgit.lib.ObjectId; |
| 37 | +import org.eclipse.jgit.patch.FileHeader; |
| 38 | +import org.eclipse.jgit.revwalk.RevWalk; |
| 39 | +import org.eclipse.jgit.revwalk.RevCommit; |
| 40 | +import org.eclipse.jgit.treewalk.FileTreeIterator; |
| 41 | +import org.eclipse.jgit.treewalk.EmptyTreeIterator; |
| 42 | +import org.eclipse.jgit.treewalk.CanonicalTreeParser; |
| 43 | +import org.eclipse.jgit.util.io.DisabledOutputStream; |
| 44 | + |
| 45 | +import java.util.Set; |
| 46 | +import java.util.HashSet; |
| 47 | + |
| 48 | +import org.apache.http.HttpHost; |
| 49 | +import org.apache.http.auth.AuthScope; |
| 50 | +import org.apache.http.auth.UsernamePasswordCredentials; |
| 51 | +import org.apache.http.client.AuthCache; |
| 52 | +import org.apache.http.client.CredentialsProvider; |
| 53 | +import org.apache.http.protocol.*; |
| 54 | +import org.apache.http.client.protocol.*; |
| 55 | +import org.apache.http.client.methods.*; |
| 56 | +import org.apache.http.impl.client.*; |
| 57 | +import org.apache.http.impl.auth.BasicScheme; |
| 58 | +import org.apache.http.util.EntityUtils; |
| 59 | + |
| 60 | + |
| 61 | +/** |
| 62 | + * GitBlit Post-Receive Hook for YouTrack |
| 63 | + * |
| 64 | + * The purpose of this script is to invoke the YouTrack API and update a case when |
| 65 | + * push is received based. |
| 66 | + * |
| 67 | + * The Post-Receive hook is executed AFTER the pushed commits have been applied |
| 68 | + * to the Git repository. This is the appropriate point to trigger an |
| 69 | + * integration build or to send a notification. |
| 70 | + * |
| 71 | + * If you want this hook script to fail and abort all subsequent scripts in the |
| 72 | + * chain, "return false" at the appropriate failure points. |
| 73 | + * |
| 74 | + * Bound Variables: |
| 75 | + * gitblit Gitblit Server com.gitblit.GitBlit |
| 76 | + * repository Gitblit Repository com.gitblit.models.RepositoryModel |
| 77 | + * receivePack JGit Receive Pack org.eclipse.jgit.transport.ReceivePack |
| 78 | + * user Gitblit User com.gitblit.models.UserModel |
| 79 | + * commands JGit commands Collection<org.eclipse.jgit.transport.ReceiveCommand> |
| 80 | + * url Base url for Gitblit String |
| 81 | + * logger Logs messages to Gitblit org.slf4j.Logger |
| 82 | + * clientLogger Logs messages to Git client com.gitblit.utils.ClientLogger |
| 83 | + * |
| 84 | + * |
| 85 | + * Custom Fileds Used by This script |
| 86 | + * youtrackProjectID - Project ID in YouTrack |
| 87 | + * |
| 88 | + * Make sure to add the following to your gitblit.properties file: |
| 89 | + * groovy.customFields = "youtrackProjectID=YouTrack Project ID" |
| 90 | + * youtrack.host = example.myjetbrains.com |
| 91 | + * youtrack.user = ytUser |
| 92 | + * youtrack.pass = insecurep@sswordsRus |
| 93 | + */ |
| 94 | + |
| 95 | +// Indicate we have started the script |
| 96 | +logger.info("youtrack hook triggered in ${url} by ${user.username} for ${repository.name}") |
| 97 | + |
| 98 | +Repository r = gitblit.getRepository(repository.name) |
| 99 | + |
| 100 | +// pull custom fields from repository specific values |
| 101 | +def youtrackProjectID = repository.customFields.youtrackProjectID |
| 102 | + |
| 103 | +if(youtrackProjectID == null || youtrackProjectID.length() == 0) return true; |
| 104 | + |
| 105 | +def youtrackHost = gitblit.getString('youtrack.host', 'nohost') |
| 106 | +def bugIdRegex = gitblit.getString('youtrack.commitMessageRegex', "#${youtrackProjectID}-([0-9]+)") |
| 107 | +def youtrackUser = gitblit.getString('youtrack.user', 'nouser') |
| 108 | +def youtrackPass = gitblit.getString('youtrack.pass', 'nopassword') |
| 109 | + |
| 110 | +HttpHost target = new HttpHost(youtrackHost, 80, "http"); |
| 111 | +CredentialsProvider credsProvider = new BasicCredentialsProvider(); |
| 112 | +credsProvider.setCredentials( |
| 113 | + new AuthScope(target.getHostName(), target.getPort()), |
| 114 | + new UsernamePasswordCredentials(youtrackUser, youtrackPass)); |
| 115 | +def httpclient = new DefaultHttpClient(); |
| 116 | + |
| 117 | +httpclient.setCredentialsProvider(credsProvider); |
| 118 | + |
| 119 | +try { |
| 120 | + |
| 121 | + AuthCache authCache = new BasicAuthCache(); |
| 122 | + BasicScheme basicAuth = new BasicScheme(); |
| 123 | + authCache.put(target, basicAuth); |
| 124 | + BasicHttpContext localcontext = new BasicHttpContext(); |
| 125 | + localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache); |
| 126 | + |
| 127 | + |
| 128 | + for (command in commands) { |
| 129 | + for( commit in JGitUtils.getRevLog(r, command.oldId.name, command.newId.name).reverse() ) { |
| 130 | + def bugIds = new java.util.HashSet() |
| 131 | + def longMsg = commit.getFullMessage() |
| 132 | + // Grab the second match group and then filter out each numeric ID and add it to array |
| 133 | + (longMsg =~ bugIdRegex).each{ (it[1] =~ "\\d+").each { bugIds.add(it)} } |
| 134 | + |
| 135 | + if(bugIds.size() > 0) { |
| 136 | + def comment = createIssueComment(command, commit) |
| 137 | + |
| 138 | + logger.debug("Submitting youtrack comment:\n" + comment) |
| 139 | + |
| 140 | + def encoded = URLEncoder.encode(comment) |
| 141 | + for(bugId in bugIds ) { |
| 142 | + def baseURL = "http://${youtrackHost}/youtrack/rest/issue/${youtrackProjectID}-${bugId}/execute?command=&comment=" + encoded |
| 143 | + def post = new HttpPost(baseURL); |
| 144 | + |
| 145 | + clientLogger.info("Executing request " + post.getRequestLine() + " to target " + target); |
| 146 | + def response = httpclient.execute(target, post, localcontext); |
| 147 | + logger.debug(response.getStatusLine().toString()); |
| 148 | + EntityUtils.consume(response.getEntity()); |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | +} |
| 154 | +finally { |
| 155 | + r.close() |
| 156 | +} |
| 157 | + |
| 158 | +def createIssueComment(command, commit) { |
| 159 | + def commits = [commit] // Borrowed code expects a collection. |
| 160 | + Repository r = gitblit.getRepository(repository.name) |
| 161 | + // define the summary and commit urls |
| 162 | + def repo = repository.name |
| 163 | + def summaryUrl |
| 164 | + def commitUrl |
| 165 | + if (gitblit.getBoolean(Keys.web.mountParameters, true)) { |
| 166 | + repo = repo.replace('/', gitblit.getString(Keys.web.forwardSlashCharacter, '/')).replace('/', '%2F') |
| 167 | + summaryUrl = url + "/summary/$repo" |
| 168 | + commitUrl = url + "/commit/$repo/" |
| 169 | + } else { |
| 170 | + summaryUrl = url + "/summary?r=$repo" |
| 171 | + commitUrl = url + "/commit?r=$repo&h=" |
| 172 | + } |
| 173 | + |
| 174 | + // construct a simple text summary of the changes contained in the push |
| 175 | + def commitBreak = '\n' |
| 176 | + def commitCount = 0 |
| 177 | + def changes = '' |
| 178 | + |
| 179 | + SimpleDateFormat df = new SimpleDateFormat(gitblit.getString(Keys.web.datetimestampLongFormat, 'EEEE, MMMM d, yyyy h:mm a z')) |
| 180 | + |
| 181 | + def table = { |
| 182 | + def shortSha = it.id.name.substring(0, 8) |
| 183 | + "* [$commitUrl$it.id.name ${shortSha}] by *${it.authorIdent.name}* on ${df.format(JGitUtils.getCommitDate(it))}\n" + |
| 184 | + " {cut $it.shortMessage}\n{noformat}$it.fullMessage{noformat}{cut}" |
| 185 | + } |
| 186 | + |
| 187 | + def ref = command.refName |
| 188 | + def refType = 'branch' |
| 189 | + if (ref.startsWith('refs/heads/')) { |
| 190 | + ref = command.refName.substring('refs/heads/'.length()) |
| 191 | + } else if (ref.startsWith('refs/tags/')) { |
| 192 | + ref = command.refName.substring('refs/tags/'.length()) |
| 193 | + refType = 'tag' |
| 194 | + } |
| 195 | + |
| 196 | + switch (command.type) { |
| 197 | + case ReceiveCommand.Type.CREATE: |
| 198 | + // new branch |
| 199 | + changes += "''new $refType $ref created''\n" |
| 200 | + changes += commits.collect(table).join(commitBreak) |
| 201 | + changes += '\n' |
| 202 | + break |
| 203 | + case ReceiveCommand.Type.UPDATE: |
| 204 | + // fast-forward branch commits table |
| 205 | + changes += "''$ref $refType updated''\n" |
| 206 | + changes += commits.collect(table).join(commitBreak) |
| 207 | + changes += '\n' |
| 208 | + break |
| 209 | + case ReceiveCommand.Type.UPDATE_NONFASTFORWARD: |
| 210 | + // non-fast-forward branch commits table |
| 211 | + changes += "''$ref $refType updated [NON fast-forward]''" |
| 212 | + changes += commits.collect(table).join(commitBreak) |
| 213 | + changes += '\n' |
| 214 | + break |
| 215 | + case ReceiveCommand.Type.DELETE: |
| 216 | + // deleted branch/tag |
| 217 | + changes += "''$ref $refType deleted''" |
| 218 | + break |
| 219 | + default: |
| 220 | + break |
| 221 | + } |
| 222 | + |
| 223 | + return "$user.username pushed commits to [$summaryUrl $repository.name]\n$changes" |
| 224 | +} |
0 commit comments