Skip to content

Commit

Permalink
Merge master into prod, release: v1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
viktorbenei committed Feb 22, 2016
2 parents 95d2aa8 + ee3294a commit b785372
Show file tree
Hide file tree
Showing 6 changed files with 595 additions and 3 deletions.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,33 @@ a [Bitbucket](https://bitbucket.org) *repository*.
Bitbucket Webhooks.
7. Click `Save`

That's all, the next time you push code or create a pull request (if you enabled the related event(s))
a build will be triggered.
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
a [visualstudio.com](https://visualstudio.com) *project* as a `Service Hooks` integration.

You can find an official guide
on [visualstudio.com 's documentations site](https://www.visualstudio.com/en-us/get-started/integrate/service-hooks/webhooks-and-vso-vs).

A short step-by-step guide:

1. Open your *project* on [visualstudio.com](https://visualstudio.com)
2. Go to the *Admin/Control panel* of the *project*
3. Select `Service Hooks`
4. Create a service integration
* In the Service list select the `Web Hooks` option
* Select the `Code pushed` event as the *Trigger*
* In the `Filters` section select the `Repository` you want to integrate
* You can leave the other filters on default
* Click `Next`
* On the `Action` setup form specify the `bitrise-webhooks` URL (`.../h/visualstudio/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`) in the `URL` field
* You can leave every other option on default
7. Click `Finish`

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


### Slack - setup & usage:
Expand Down
1 change: 1 addition & 0 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ workflows:
steps:
- script:
title: "TMP: install Go 1.6.0"
run_if: .IsCI
inputs:
- content: |-
set -x
Expand Down
2 changes: 2 additions & 0 deletions service/hook/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
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/slack"
"github.com/bitrise-io/bitrise-webhooks/service/hook/visualstudioteamservices"
"github.com/gorilla/mux"
)

Expand All @@ -22,6 +23,7 @@ func supportedProviders() map[string]hookCommon.Provider {
"github": github.HookProvider{},
"bitbucket-v2": bitbucketv2.HookProvider{},
"slack": slack.HookProvider{},
"visualstudio": visualstudioteamservices.HookProvider{},
}
}

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

import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"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 ---

// CommitsModel ...
type CommitsModel struct {
CommitID string `json:"commitId"`
Comment string `json:"comment"`
}

// RefUpdatesModel ...
type RefUpdatesModel struct {
Name string `json:"name"`
}

// ResourceModel ...
type ResourceModel struct {
Commits []CommitsModel `json:"commits"`
RefUpdates []RefUpdatesModel `json:"refUpdates"`
}

// CodePushEventModel ...
type CodePushEventModel struct {
SubscriptionID string `json:"subscriptionId"`
EventType string `json:"eventType"`
PublisherID string `json:"publisherId"`
Resource ResourceModel `json:"resource"`
}

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

// HookProvider ...
type HookProvider struct{}

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

return contentType, nil
}

// transformCodePushEvent ...
func transformCodePushEvent(codePushEvent CodePushEventModel) hookCommon.TransformResultModel {
if codePushEvent.PublisherID != "tfs" {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Not a Team Foundation Server notification, can't start a build."),
}
}

if codePushEvent.EventType != "git.push" {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Not a code push event, can't start a build."),
}
}

if codePushEvent.SubscriptionID == "00000000-0000-0000-0000-000000000000" {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Initial (test) event detected, skipping."),
ShouldSkip: true,
}
}

if len(codePushEvent.Resource.RefUpdates) != 1 {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Can't detect branch information (resource.refUpdates is empty), can't start a build."),
}
}

if !strings.HasPrefix(codePushEvent.Resource.RefUpdates[0].Name, "refs/heads/") {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Badly formatted branch detected, can't start a build."),
}
}
branch := strings.TrimPrefix(codePushEvent.Resource.RefUpdates[0].Name, "refs/heads/")

if len(codePushEvent.Resource.Commits) < 1 {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("No 'commits' included in the webhook, can't start a build."),
}
}
// VSO sends separate events for separate branches,
// and commits are in ascending order, by commit date-time
aCommit := codePushEvent.Resource.Commits[0]

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

// TransformRequest ...
func (hp HookProvider) TransformRequest(r *http.Request) hookCommon.TransformResultModel {
contentType, err := detectContentType(r.Header)
if err != nil {
return hookCommon.TransformResultModel{
Error: err,
}
}
matched, err := regexp.MatchString("application/json", contentType)
if err != nil {
return hookCommon.TransformResultModel{
Error: fmt.Errorf("Issue with Header checking: %s", err),
}
}

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

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

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

return transformCodePushEvent(codePushEvent)
}
Loading

0 comments on commit b785372

Please sign in to comment.