Skip to content

Commit

Permalink
Merge master into prod, release: v1.1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorbenei committed Feb 23, 2016
2 parents 1adb810 + dc4b46c commit 41bbe94
Show file tree
Hide file tree
Showing 5 changed files with 423 additions and 5 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ For more information check the *How to add support for a new Provider* section.
* handled on the path: `/h/bitbucket-v2/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`
* [Slack](https://slack.com) (both outgoing webhooks & slash commands)
* handled on the path: `/h/slack/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`

Work in progress:

* [Visual Studio Team Services](https://www.visualstudio.com/products/visual-studio-team-services-vs)
* handled on the path: `/h/visualstudio/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`
* [GitLab](https://gitlab.com)
* handled on the path: `/h/gitlab/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`


### GitHub - setup & usage:
Expand Down Expand Up @@ -62,6 +61,23 @@ a [Bitbucket](https://bitbucket.org) *repository*.
That's all, the next time you push code (into your repository) a build will be triggered.


### GitLab - setup & usage:

All you have to do is register your `bitrise-webhooks` URL for
a [GitLab](https://gitlab.com) *project*.

1. Open your *project* on [GitLab.com](https://gitlab.com)
2. Go to `Settings` of the *project*
3. Select `Web Hooks`
4. Specify the `bitrise-webhooks` URL (`.../h/gitlab/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`) in the `URL` field
5. In the *Triggers* section select `Push events`
* Right now `bitrise-webhooks` only supports the *Push events* trigger for
GitLab Webhooks.
6. Click `Add Web Hook`

That's all, the next time you push code (into your repository) a build will be triggered.


### Visual Studio Online / Visual Studio Team Services - setup & usage:

All you have to do is register your `bitrise-webhooks` URL for
Expand Down Expand Up @@ -316,4 +332,4 @@ response provider will be used.

* Re-try handling
* Bitbucket V1 (aka "Services" on the Bitbucket web UI) - not sure whether we should support this,
it'll be deprecated in the future, and we already support the newer, V2 webhooks.
it's already kind of deprecated, and we already support the newer, V2 webhooks.
2 changes: 2 additions & 0 deletions service/hook/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/bitrise-io/bitrise-webhooks/service/hook/bitbucketv2"
hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
"github.com/bitrise-io/bitrise-webhooks/service/hook/github"
"github.com/bitrise-io/bitrise-webhooks/service/hook/gitlab"
"github.com/bitrise-io/bitrise-webhooks/service/hook/slack"
"github.com/bitrise-io/bitrise-webhooks/service/hook/visualstudioteamservices"
"github.com/gorilla/mux"
Expand All @@ -24,6 +25,7 @@ func supportedProviders() map[string]hookCommon.Provider {
"bitbucket-v2": bitbucketv2.HookProvider{},
"slack": slack.HookProvider{},
"visualstudio": visualstudioteamservices.HookProvider{},
"gitlab": gitlab.HookProvider{},
}
}

Expand Down
155 changes: 155 additions & 0 deletions service/hook/gitlab/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package gitlab

// # Infos / notes:
//
// ## Webhook calls
//
// Official API docs: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md
//
// ### Code Push
//
// A code push webhook is sent with the header: `X-Gitlab-Event: Push Hook`.
// Official docs: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md#push-events
//
// GitLab sends push webhooks for every branch separately. Even if you
// push to two different branches at the same time (git push --all) it'll
// trigger two webhook calls, one for each branch.
//
// Commits are grouped in the webhook - if you push more than one commit
// to a single branch it'll be included in a single webhook call, including
// all of the commits.
//
// The latest commit's hash is included as the "checkout_sha" parameter
// in the webhook. As we don't want to trigger build for every commit
// which is related to a single branch we will only handle the commit
// with the hash / id specified as the "checkout_sha".
//

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"

"github.com/bitrise-io/bitrise-webhooks/bitriseapi"
hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
"github.com/bitrise-io/go-utils/httputil"
)

// --------------------------
// --- Webhook Data Model ---

const (
pushEventID = "Push Hook"
)

// CommitModel ...
type CommitModel struct {
CommitHash string `json:"id"`
CommitMessage string `json:"message"`
}

// CodePushEventModel ...
type CodePushEventModel struct {
ObjectKind string `json:"object_kind"`
Ref string `json:"ref"`
CheckoutSHA string `json:"checkout_sha"`
Commits []CommitModel `json:"commits"`
}

// ---------------------------------------
// --- Webhook Provider Implementation ---

// HookProvider ...
type HookProvider struct{}

func detectContentTypeAndEventID(header http.Header) (string, string, error) {
contentType, err := httputil.GetSingleValueFromHeader("Content-Type", header)
if err != nil {
return "", "", fmt.Errorf("Issue with Content-Type Header: %s", err)
}

eventID, err := httputil.GetSingleValueFromHeader("X-Gitlab-Event", header)
if err != nil {
return "", "", fmt.Errorf("Issue with X-Gitlab-Event Header: %s", err)
}

return contentType, eventID, nil
}

func transformCodePushEvent(codePushEvent CodePushEventModel) hookCommon.TransformResultModel {
if !strings.HasPrefix(codePushEvent.Ref, "refs/heads/") {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Ref (%s) is not a head ref", codePushEvent.Ref),
ShouldSkip: true,
}
}
branch := strings.TrimPrefix(codePushEvent.Ref, "refs/heads/")

lastCommit := CommitModel{}
isLastCommitFound := false
for _, aCommit := range codePushEvent.Commits {
if aCommit.CommitHash == codePushEvent.CheckoutSHA {
isLastCommitFound = true
lastCommit = aCommit
break
}
}

if !isLastCommitFound {
return hookCommon.TransformResultModel{
Error: errors.New("The commit specified by 'checkout_sha' was not included in the 'commits' array - no match found"),
}
}

return hookCommon.TransformResultModel{
TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
{
BuildParams: bitriseapi.BuildParamsModel{
CommitHash: lastCommit.CommitHash,
CommitMessage: lastCommit.CommitMessage,
Branch: branch,
},
},
},
}
}

// TransformRequest ...
func (hp HookProvider) TransformRequest(r *http.Request) hookCommon.TransformResultModel {
contentType, eventID, err := detectContentTypeAndEventID(r.Header)
if err != nil {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Issue with Headers: %s", err),
}
}

if contentType != "application/json" {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Content-Type is not supported: %s", contentType),
}
}

if eventID != "Push Hook" {
// Unsupported Event
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Unsupported Webhook event: %s", eventID),
}
}

if r.Body == nil {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Failed to read content of request body: no or empty request body"),
}
}

// code push
var codePushEvent CodePushEventModel
if contentType == "application/json" {
if err := json.NewDecoder(r.Body).Decode(&codePushEvent); err != nil {
return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to parse request body: %s", err)}
}
}
return transformCodePushEvent(codePushEvent)
}
Loading

0 comments on commit 41bbe94

Please sign in to comment.