<commit_before>package bxmpp

import (
	"crypto/tls"
	"strings"
	"time"

	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/jpillora/backoff"
	"github.com/matterbridge/go-xmpp"
	"github.com/rs/xid"
)

type Bxmpp struct {
	*bridge.Config

	startTime time.Time
	xc        *xmpp.Client
	xmppMap   map[string]string
}

func New(cfg *bridge.Config) bridge.Bridger {
	return &Bxmpp{
		Config:  cfg,
		xmppMap: make(map[string]string),
	}
}

func (b *Bxmpp) Connect() error {
	b.Log.Infof("Connecting %s", b.GetString("Server"))
	if err := b.createXMPP(); err != nil {
		b.Log.Debugf("%#v", err)
		return err
	}

	b.Log.Info("Connection succeeded")
	go b.manageConnection()
	return nil
}

func (b *Bxmpp) Disconnect() error {
	return nil
}

func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
	if channel.Options.Key != "" {
		b.Log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
		b.xc.JoinProtectedMUC(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"), channel.Options.Key, xmpp.NoHistory, 0, nil)
	} else {
		b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
	}
	return nil
}

func (b *Bxmpp) Send(msg config.Message) (string, error) {
	// ignore delete messages
	if msg.Event == config.EventMsgDelete {
		return "", nil
	}
	b.Log.Debugf("=> Receiving %#v", msg)

	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			b.Log.Debugf("=> Sending attachement message %#v", rmsg)
			if _, err := b.xc.Send(xmpp.Chat{
				Type:   "groupchat",
				Remote: rmsg.Channel + "@" + b.GetString("Muc"),
				Text:   rmsg.Username + rmsg.Text,
			}); err != nil {
				b.Log.WithError(err).Error("Unable to send message with share URL.")
			}
		}
		if len(msg.Extra["file"]) > 0 {
			return "", b.handleUploadFile(&msg)
		}
	}

	var msgReplaceID string
	msgID := xid.New().String()
	if msg.ID != "" {
		msgID = msg.ID
		msgReplaceID = msg.ID
	}
	// Post normal message.
	b.Log.Debugf("=> Sending message %#v", msg)
	if _, err := b.xc.Send(xmpp.Chat{
		Type:      "groupchat",
		Remote:    msg.Channel + "@" + b.GetString("Muc"),
		Text:      msg.Username + msg.Text,
		ID:        msgID,
		ReplaceID: msgReplaceID,
	}); err != nil {
		return "", err
	}
	return msgID, nil
}

func (b *Bxmpp) createXMPP() error {
	tc := &tls.Config{
		ServerName:         strings.Split(b.GetString("Server"), ":")[0],
		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
	}
	options := xmpp.Options{
		Host:                         b.GetString("Server"),
		User:                         b.GetString("Jid"),
		Password:                     b.GetString("Password"),
		NoTLS:                        true,
		StartTLS:                     true,
		TLSConfig:                    tc,
		Debug:                        b.GetBool("debug"),
		Logger:                       b.Log.Writer(),
		Session:                      true,
		Status:                       "",
		StatusMessage:                "",
		Resource:                     "",
		InsecureAllowUnencryptedAuth: false,
	}
	var err error
	b.xc, err = options.NewClient()
	return err
}

func (b *Bxmpp) manageConnection() {
	initial := true
	bf := &backoff.Backoff{
		Min:    time.Second,
		Max:    5 * time.Minute,
		Jitter: true,
	}

	// Main connection loop. Each iteration corresponds to a successful
	// connection attempt and the subsequent handling of the connection.
	for {
		if initial {
			initial = false
		} else {
			b.Remote <- config.Message{
				Username: "system",
				Text:     "rejoin",
				Channel:  "",
				Account:  b.Account,
				Event:    config.EventRejoinChannels,
			}
		}

		if err := b.handleXMPP(); err != nil {
			b.Log.WithError(err).Error("Disconnected.")
		}

		// Reconnection loop using an exponential back-off strategy. We
		// only break out of the loop if we have successfully reconnected.
		for {
			d := bf.Duration()
			b.Log.Infof("Reconnecting in %s.", d)
			time.Sleep(d)

			b.Log.Infof("Reconnecting now.")
			if err := b.createXMPP(); err == nil {
				bf.Reset()
				break
			}
			b.Log.Warn("Failed to reconnect.")
		}
	}
}

func (b *Bxmpp) xmppKeepAlive() chan bool {
	done := make(chan bool)
	go func() {
		ticker := time.NewTicker(90 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				b.Log.Debugf("PING")
				if err := b.xc.PingC2S("", ""); err != nil {
					b.Log.Debugf("PING failed %#v", err)
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func (b *Bxmpp) handleXMPP() error {
	b.startTime = time.Now()

	done := b.xmppKeepAlive()
	defer close(done)

	for {
		m, err := b.xc.Recv()
		if err != nil {
			return err
		}

		switch v := m.(type) {
		case xmpp.Chat:
			if v.Type == "groupchat" {
				b.Log.Debugf("== Receiving %#v", v)

				// Skip invalid messages.
				if b.skipMessage(v) {
					continue
				}

				var event string
				if strings.Contains(v.Text, "has set the subject to:") {
					event = config.EventTopicChange
				}

				msgID := v.ID
				if v.ReplaceID != "" {
					msgID = v.ReplaceID
				}
				rmsg := config.Message{
					Username: b.parseNick(v.Remote),
					Text:     v.Text,
					Channel:  b.parseChannel(v.Remote),
					Account:  b.Account,
					UserID:   v.Remote,
					ID:       msgID,
					Event:    event,
				}

				// Check if we have an action event.
				var ok bool
				rmsg.Text, ok = b.replaceAction(rmsg.Text)
				if ok {
					rmsg.Event = config.EventUserAction
				}

				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
				b.Log.Debugf("<= Message is %#v", rmsg)
				b.Remote <- rmsg
			}
		case xmpp.Presence:
			// Do nothing.
		}
	}
}

func (b *Bxmpp) replaceAction(text string) (string, bool) {
	if strings.HasPrefix(text, "/me ") {
		return strings.Replace(text, "/me ", "", -1), true
	}
	return text, false
}

// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
	var urlDesc string

	for _, file := range msg.Extra["file"] {
		fileInfo := file.(config.FileInfo)
		if fileInfo.Comment != "" {
			msg.Text += fileInfo.Comment + ": "
		}
		if fileInfo.URL != "" {
			msg.Text = fileInfo.URL
			if fileInfo.Comment != "" {
				msg.Text = fileInfo.Comment + ": " + fileInfo.URL
				urlDesc = fileInfo.Comment
			}
		}
		if _, err := b.xc.Send(xmpp.Chat{
			Type:   "groupchat",
			Remote: msg.Channel + "@" + b.GetString("Muc"),
			Text:   msg.Username + msg.Text,
		}); err != nil {
			return err
		}

		if fileInfo.URL != "" {
			if _, err := b.xc.SendOOB(xmpp.Chat{
				Type:    "groupchat",
				Remote:  msg.Channel + "@" + b.GetString("Muc"),
				Ooburl:  fileInfo.URL,
				Oobdesc: urlDesc,
			}); err != nil {
				b.Log.WithError(err).Warn("Failed to send share URL.")
			}
		}
	}
	return nil
}

func (b *Bxmpp) parseNick(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) > 0 {
		s = strings.Split(s[1], "/")
		if len(s) == 2 {
			return s[1] // nick
		}
	}
	return ""
}

func (b *Bxmpp) parseChannel(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) >= 2 {
		return s[0] // channel
	}
	return ""
}

// skipMessage skips messages that need to be skipped
func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
	// skip messages from ourselves
	if b.parseNick(message.Remote) == b.GetString("Nick") {
		return true
	}

	// skip empty messages
	if message.Text == "" {
		return true
	}

	// skip subject messages
	if strings.Contains(message.Text, "</subject>") {
		return true
	}

	// do not show subjects on connect #732
	if strings.Contains(message.Text, "has set the subject to:") && time.Since(b.startTime) < time.Second*5 {
		return true
	}

	// skip delayed messages
	return message.Stamp.IsZero()
}
<commit_msg>Revert xmpp to orig behaviour. Closes #844<commit_after>package bxmpp

import (
	"crypto/tls"
	"strings"
	"time"

	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/jpillora/backoff"
	"github.com/matterbridge/go-xmpp"
	"github.com/rs/xid"
)

type Bxmpp struct {
	*bridge.Config

	startTime time.Time
	xc        *xmpp.Client
	xmppMap   map[string]string
}

func New(cfg *bridge.Config) bridge.Bridger {
	return &Bxmpp{
		Config:  cfg,
		xmppMap: make(map[string]string),
	}
}

func (b *Bxmpp) Connect() error {
	b.Log.Infof("Connecting %s", b.GetString("Server"))
	if err := b.createXMPP(); err != nil {
		b.Log.Debugf("%#v", err)
		return err
	}

	b.Log.Info("Connection succeeded")
	go b.manageConnection()
	return nil
}

func (b *Bxmpp) Disconnect() error {
	return nil
}

func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
	if channel.Options.Key != "" {
		b.Log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name)
		b.xc.JoinProtectedMUC(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"), channel.Options.Key, xmpp.NoHistory, 0, nil)
	} else {
		b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
	}
	return nil
}

func (b *Bxmpp) Send(msg config.Message) (string, error) {
	// ignore delete messages
	if msg.Event == config.EventMsgDelete {
		return "", nil
	}
	b.Log.Debugf("=> Receiving %#v", msg)

	// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			b.Log.Debugf("=> Sending attachement message %#v", rmsg)
			if _, err := b.xc.Send(xmpp.Chat{
				Type:   "groupchat",
				Remote: rmsg.Channel + "@" + b.GetString("Muc"),
				Text:   rmsg.Username + rmsg.Text,
			}); err != nil {
				b.Log.WithError(err).Error("Unable to send message with share URL.")
			}
		}
		if len(msg.Extra["file"]) > 0 {
			return "", b.handleUploadFile(&msg)
		}
	}

	var msgReplaceID string
	msgID := xid.New().String()
	if msg.ID != "" {
		msgID = msg.ID
		msgReplaceID = msg.ID
	}
	// Post normal message.
	b.Log.Debugf("=> Sending message %#v", msg)
	if _, err := b.xc.Send(xmpp.Chat{
		Type:      "groupchat",
		Remote:    msg.Channel + "@" + b.GetString("Muc"),
		Text:      msg.Username + msg.Text,
		ID:        msgID,
		ReplaceID: msgReplaceID,
	}); err != nil {
		return "", err
	}
	return msgID, nil
}

func (b *Bxmpp) createXMPP() error {
	tc := &tls.Config{
		ServerName:         strings.Split(b.GetString("Server"), ":")[0],
		InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
	}
	options := xmpp.Options{
		Host:                         b.GetString("Server"),
		User:                         b.GetString("Jid"),
		Password:                     b.GetString("Password"),
		NoTLS:                        true,
		StartTLS:                     true,
		TLSConfig:                    tc,
		Debug:                        b.GetBool("debug"),
		Logger:                       b.Log.Writer(),
		Session:                      true,
		Status:                       "",
		StatusMessage:                "",
		Resource:                     "",
		InsecureAllowUnencryptedAuth: false,
	}
	var err error
	b.xc, err = options.NewClient()
	return err
}

func (b *Bxmpp) manageConnection() {
	initial := true
	bf := &backoff.Backoff{
		Min:    time.Second,
		Max:    5 * time.Minute,
		Jitter: true,
	}

	// Main connection loop. Each iteration corresponds to a successful
	// connection attempt and the subsequent handling of the connection.
	for {
		if initial {
			initial = false
		} else {
			b.Remote <- config.Message{
				Username: "system",
				Text:     "rejoin",
				Channel:  "",
				Account:  b.Account,
				Event:    config.EventRejoinChannels,
			}
		}

		if err := b.handleXMPP(); err != nil {
			b.Log.WithError(err).Error("Disconnected.")
		}

		// Reconnection loop using an exponential back-off strategy. We
		// only break out of the loop if we have successfully reconnected.
		for {
			d := bf.Duration()
			b.Log.Infof("Reconnecting in %s.", d)
			time.Sleep(d)

			b.Log.Infof("Reconnecting now.")
			if err := b.createXMPP(); err == nil {
				bf.Reset()
				break
			}
			b.Log.Warn("Failed to reconnect.")
		}
	}
}

func (b *Bxmpp) xmppKeepAlive() chan bool {
	done := make(chan bool)
	go func() {
		ticker := time.NewTicker(90 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				b.Log.Debugf("PING")
				if err := b.xc.PingC2S("", ""); err != nil {
					b.Log.Debugf("PING failed %#v", err)
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func (b *Bxmpp) handleXMPP() error {
	b.startTime = time.Now()

	done := b.xmppKeepAlive()
	defer close(done)

	for {
		m, err := b.xc.Recv()
		if err != nil {
			return err
		}

		switch v := m.(type) {
		case xmpp.Chat:
			if v.Type == "groupchat" {
				b.Log.Debugf("== Receiving %#v", v)

				// Skip invalid messages.
				if b.skipMessage(v) {
					continue
				}

				var event string
				if strings.Contains(v.Text, "has set the subject to:") {
					event = config.EventTopicChange
				}

				msgID := v.ID
				if v.ReplaceID != "" {
					msgID = v.ReplaceID
				}
				rmsg := config.Message{
					Username: b.parseNick(v.Remote),
					Text:     v.Text,
					Channel:  b.parseChannel(v.Remote),
					Account:  b.Account,
					UserID:   v.Remote,
					ID:       msgID,
					Event:    event,
				}

				// Check if we have an action event.
				var ok bool
				rmsg.Text, ok = b.replaceAction(rmsg.Text)
				if ok {
					rmsg.Event = config.EventUserAction
				}

				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
				b.Log.Debugf("<= Message is %#v", rmsg)
				b.Remote <- rmsg
			}
		case xmpp.Presence:
			// Do nothing.
		}
	}
}

func (b *Bxmpp) replaceAction(text string) (string, bool) {
	if strings.HasPrefix(text, "/me ") {
		return strings.Replace(text, "/me ", "", -1), true
	}
	return text, false
}

// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
	var urlDesc string

	for _, file := range msg.Extra["file"] {
		fileInfo := file.(config.FileInfo)
		if fileInfo.Comment != "" {
			msg.Text += fileInfo.Comment + ": "
		}
		if fileInfo.URL != "" {
			msg.Text = fileInfo.URL
			if fileInfo.Comment != "" {
				msg.Text = fileInfo.Comment + ": " + fileInfo.URL
				urlDesc = fileInfo.Comment
			}
		}
		if _, err := b.xc.Send(xmpp.Chat{
			Type:   "groupchat",
			Remote: msg.Channel + "@" + b.GetString("Muc"),
			Text:   msg.Username + msg.Text,
		}); err != nil {
			return err
		}

		if fileInfo.URL != "" {
			if _, err := b.xc.SendOOB(xmpp.Chat{
				Type:    "groupchat",
				Remote:  msg.Channel + "@" + b.GetString("Muc"),
				Ooburl:  fileInfo.URL,
				Oobdesc: urlDesc,
			}); err != nil {
				b.Log.WithError(err).Warn("Failed to send share URL.")
			}
		}
	}
	return nil
}

func (b *Bxmpp) parseNick(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) > 0 {
		s = strings.Split(s[1], "/")
		if len(s) == 2 {
			return s[1] // nick
		}
	}
	return ""
}

func (b *Bxmpp) parseChannel(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) >= 2 {
		return s[0] // channel
	}
	return ""
}

// skipMessage skips messages that need to be skipped
func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
	// skip messages from ourselves
	if b.parseNick(message.Remote) == b.GetString("Nick") {
		return true
	}

	// skip empty messages
	if message.Text == "" {
		return true
	}

	// skip subject messages
	if strings.Contains(message.Text, "</subject>") {
		return true
	}

	// do not show subjects on connect #732
	if strings.Contains(message.Text, "has set the subject to:") && time.Since(b.startTime) < time.Second*5 {
		return true
	}

	// skip delayed messages
	return !message.Stamp.IsZero()
}
<|endoftext|>


<commit_before>// Copyright 2014 Akamai Technologies, Inc.
// Please see the accompanying LICENSE file for licensing information.
//
// Note: this file is a derivative work of
//
// https://github.com/share/ShareJS/blob/0.6/src/client/ace.coffee
//
// Copyright 2011 Joseph Gentle
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ace

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/gopherjs/gopherjs/js"

	"github.com/mstone/focus/js/alert"
	"github.com/mstone/focus/msg"
	"github.com/mstone/focus/ot"
)

type Lengther interface {
	Length() int
}

type Sender interface {
	Send(msg []byte)
}

type Adapter struct {
	mu       sync.Mutex
	conn     Sender
	session  Lengther
	doc      Document
	state    *ot.Controller
	suppress bool
	fd       int
}

func NewAdapter() *Adapter {
	return &Adapter{
		mu: sync.Mutex{},
	}
}

func (a *Adapter) Suppress(suppress bool) {
	a.suppress = suppress
}

func (a *Adapter) IsSuppressed() bool {
	return a.suppress
}

func (a *Adapter) Send(rev int, hash string, ops ot.Ops) {
	if !a.suppress {
		jsOps, _ := json.Marshal(msg.Msg{
			Cmd:  msg.C_WRITE,
			Fd:   a.fd,
			Rev:  rev,
			Hash: hash,
			Ops:  ops,
		})
		alert.Golang(fmt.Sprintf("sending jsops: %s", jsOps))
		a.conn.Send(jsOps)
	}
}

func (a *Adapter) Recv(ops ot.Ops) {
	a.mu.Lock()
	defer a.mu.Unlock()

	pos := 0
	alert.String(fmt.Sprintf("recv(%s)", ops.String()))

	a.suppress = true
	defer func() {
		a.suppress = false
	}()

	if len(ops) == 0 {
		return
	}
	if len(ops) != 1 || !ops[0].IsWith() {
		alert.String("recv err; ops len != 1 || ops[0] != With(...)")
		panic("bad ops len")
	}
	o := ops[0]

	for _, op := range o.Kids {
		switch {
		case op.IsZero():
			continue
		case op.IsInsert():
			rowcol := NewRowCol(a.doc, pos)
			alert.String(fmt.Sprintf("insert(%d, %q)", pos, op.Body.String()))
			a.doc.Insert(rowcol, ot.AsString([]rune{op.Body.Leaf}))
			pos += op.Len()
			continue
		case op.IsRetain():
			alert.String(fmt.Sprintf("retain(%d)", op.Size))
			pos += op.Size
			continue
		case op.IsDelete():
			startEnd := NewStartEnd(a.doc, pos, pos-op.Size)
			alert.String(fmt.Sprintf("remove(%d, %d)", pos, pos-op.Size))
			a.doc.Remove(startEnd)
		case op.IsWith():
			alert.String("recv err; got inner with op; exiting")
			panic(2)
		}
	}
}

func (a *Adapter) AttachFd(fd int) {
	a.fd = fd
}

func (a *Adapter) AttachEditor(session Lengther, doc Document) {
	a.session = session
	a.doc = doc
	doc.SetOnChange(a.OnChange)
}

func (a *Adapter) AttachSocket(state *ot.Controller, conn Sender) {
	a.state = state
	a.conn = conn
}

// RowCall(...)
//   -> getAllLines() -> Array[Line]
// StartEnd(...)
//   -> RowCol
// NewRange(...)
//   -> getLines(a, b)  -> Array[Line]  (.Get("length").Int())
// On("change")
// Insert()
// Remove()

func (a *Adapter) OnChange(change *js.Object) bool {
	if a.IsSuppressed() {
		alert.String("change SUPPRESSED")
		return true
	}

	a.mu.Lock()
	defer a.mu.Unlock()

	length := a.session.Length()
	alert.String(fmt.Sprintf("session len: %d", length))

	data := change.Get("data")
	alert.JSON(data)

	action := data.Get("action").String()
	textRange := NewRange(a.doc, NewJSStartEnd(data.Get("range")))

	start := textRange.Start()
	end := textRange.End()

	var ops ot.Ops
	switch action {
	case "insertText":
		str := data.Get("text").String()
		oldLen := length - (end - start)
		alert.String(fmt.Sprintf("newInsert(%d, %d, %q)", oldLen, start, str))
		ops = ot.NewInsert(oldLen, start, str)
	case "removeText":
		// BUG(mistone): end is bogus after delete when \n in text...
		// Repro: abc\ndef<<<^H
		text := data.Get("text").String()
		textLen := len(text)
		oldLen := length + textLen
		alert.String(fmt.Sprintf("newDelete(%d, %d, %d)", oldLen, start, textLen))
		ops = ot.NewDelete(oldLen, start, textLen)
	case "insertLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length - numRunes
		ops = ot.NewInsert(oldLen, start, str)
	case "removeLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length + numRunes
		ops = ot.NewDelete(oldLen, start, numRunes)
	}

	ops = ot.Ws(ops)
	alert.String("sending ops")
	alert.Golang(ops)

	go a.state.OnClientWrite(ops)
	return true
}
<commit_msg>Disable noisy client change suppression log line.<commit_after>// Copyright 2014 Akamai Technologies, Inc.
// Please see the accompanying LICENSE file for licensing information.
//
// Note: this file is a derivative work of
//
// https://github.com/share/ShareJS/blob/0.6/src/client/ace.coffee
//
// Copyright 2011 Joseph Gentle
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ace

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/gopherjs/gopherjs/js"

	"github.com/mstone/focus/js/alert"
	"github.com/mstone/focus/msg"
	"github.com/mstone/focus/ot"
)

type Lengther interface {
	Length() int
}

type Sender interface {
	Send(msg []byte)
}

type Adapter struct {
	mu       sync.Mutex
	conn     Sender
	session  Lengther
	doc      Document
	state    *ot.Controller
	suppress bool
	fd       int
}

func NewAdapter() *Adapter {
	return &Adapter{
		mu: sync.Mutex{},
	}
}

func (a *Adapter) Suppress(suppress bool) {
	a.suppress = suppress
}

func (a *Adapter) IsSuppressed() bool {
	return a.suppress
}

func (a *Adapter) Send(rev int, hash string, ops ot.Ops) {
	if !a.suppress {
		jsOps, _ := json.Marshal(msg.Msg{
			Cmd:  msg.C_WRITE,
			Fd:   a.fd,
			Rev:  rev,
			Hash: hash,
			Ops:  ops,
		})
		alert.Golang(fmt.Sprintf("sending jsops: %s", jsOps))
		a.conn.Send(jsOps)
	}
}

func (a *Adapter) Recv(ops ot.Ops) {
	a.mu.Lock()
	defer a.mu.Unlock()

	pos := 0
	alert.String(fmt.Sprintf("recv(%s)", ops.String()))

	a.suppress = true
	defer func() {
		a.suppress = false
	}()

	if len(ops) == 0 {
		return
	}
	if len(ops) != 1 || !ops[0].IsWith() {
		alert.String("recv err; ops len != 1 || ops[0] != With(...)")
		panic("bad ops len")
	}
	o := ops[0]

	for _, op := range o.Kids {
		switch {
		case op.IsZero():
			continue
		case op.IsInsert():
			rowcol := NewRowCol(a.doc, pos)
			alert.String(fmt.Sprintf("insert(%d, %q)", pos, op.Body.String()))
			a.doc.Insert(rowcol, ot.AsString([]rune{op.Body.Leaf}))
			pos += op.Len()
			continue
		case op.IsRetain():
			alert.String(fmt.Sprintf("retain(%d)", op.Size))
			pos += op.Size
			continue
		case op.IsDelete():
			startEnd := NewStartEnd(a.doc, pos, pos-op.Size)
			alert.String(fmt.Sprintf("remove(%d, %d)", pos, pos-op.Size))
			a.doc.Remove(startEnd)
		case op.IsWith():
			alert.String("recv err; got inner with op; exiting")
			panic(2)
		}
	}
}

func (a *Adapter) AttachFd(fd int) {
	a.fd = fd
}

func (a *Adapter) AttachEditor(session Lengther, doc Document) {
	a.session = session
	a.doc = doc
	doc.SetOnChange(a.OnChange)
}

func (a *Adapter) AttachSocket(state *ot.Controller, conn Sender) {
	a.state = state
	a.conn = conn
}

// RowCall(...)
//   -> getAllLines() -> Array[Line]
// StartEnd(...)
//   -> RowCol
// NewRange(...)
//   -> getLines(a, b)  -> Array[Line]  (.Get("length").Int())
// On("change")
// Insert()
// Remove()

func (a *Adapter) OnChange(change *js.Object) bool {
	if a.IsSuppressed() {
		// alert.String("change SUPPRESSED")
		return true
	}

	a.mu.Lock()
	defer a.mu.Unlock()

	length := a.session.Length()
	alert.String(fmt.Sprintf("session len: %d", length))

	data := change.Get("data")
	alert.JSON(data)

	action := data.Get("action").String()
	textRange := NewRange(a.doc, NewJSStartEnd(data.Get("range")))

	start := textRange.Start()
	end := textRange.End()

	var ops ot.Ops
	switch action {
	case "insertText":
		str := data.Get("text").String()
		oldLen := length - (end - start)
		alert.String(fmt.Sprintf("newInsert(%d, %d, %q)", oldLen, start, str))
		ops = ot.NewInsert(oldLen, start, str)
	case "removeText":
		// BUG(mistone): end is bogus after delete when \n in text...
		// Repro: abc\ndef<<<^H
		text := data.Get("text").String()
		textLen := len(text)
		oldLen := length + textLen
		alert.String(fmt.Sprintf("newDelete(%d, %d, %d)", oldLen, start, textLen))
		ops = ot.NewDelete(oldLen, start, textLen)
	case "insertLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length - numRunes
		ops = ot.NewInsert(oldLen, start, str)
	case "removeLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length + numRunes
		ops = ot.NewDelete(oldLen, start, numRunes)
	}

	ops = ot.Ws(ops)
	alert.String("sending ops")
	alert.Golang(ops)

	go a.state.OnClientWrite(ops)
	return true
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI Authors.
//
// 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.

// Package cas provides remote-apis-sdks client with luci integration.
package casclient

import (
	"context"
	"fmt"
	"runtime"
	"strings"

	"github.com/bazelbuild/remote-apis-sdks/go/pkg/cas"
	"github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// New returns luci auth configured Client for RBE-CAS.
func New(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*cas.Client, error) {
	creds, err := perRPCCreds(ctx, instance, opts, readOnly)
	if err != nil {
		return nil, err
	}

	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
		DialOpts:           []grpc.DialOption{grpc.WithPerRPCCredentials(creds)},
	}
	conn, err := client.Dial(ctx, dialParams.Service, dialParams)
	if err != nil {
		return nil, errors.Annotate(err, "failed to dial RBE").Err()
	}

	cl, err := cas.NewClientWithConfig(ctx, conn, instance, DefaultConfig())
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// DefaultConfig returns default CAS client configuration.
func DefaultConfig() cas.ClientConfig {
	cfg := cas.DefaultClientConfig()
	cfg.CompressedBytestreamThreshold = 0 // compress always
	return cfg
}

func perRPCCreds(ctx context.Context, instance string, opts auth.Options, readOnly bool) (credentials.PerRPCCredentials, error) {
	project := strings.Split(instance, "/")[1]
	var role string
	if readOnly {
		role = "cas-read-only"
	} else {
		role = "cas-read-write"
	}

	// Construct auth.Options.
	opts.ActAsServiceAccount = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", role, project)
	opts.ActViaLUCIRealm = fmt.Sprintf("@internal:%s/%s", project, role)
	opts.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"}

	if strings.HasSuffix(project, "-dev") || strings.HasSuffix(project, "-staging") {
		// use dev token server for dev/staging projects.
		opts.TokenServerHost = chromeinfra.TokenServerDevHost
	}

	creds, err := auth.NewAuthenticator(ctx, auth.SilentLogin, opts).PerRPCCredentials()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get PerRPCCredentials").Err()
	}
	return creds, nil
}

// NewLegacy returns luci auth configured legacy Client for RBE.
// In general, NewClient is preferred.
// TODO(crbug.com/1225524): remove this.
func NewLegacy(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*client.Client, error) {
	creds, err := perRPCCreds(ctx, instance, opts, readOnly)
	if err != nil {
		return nil, err
	}
	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
	}

	cl, err := client.NewClient(ctx, instance, dialParams, Options(creds)...)
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// Options returns CAS client options.
func Options(creds credentials.PerRPCCredentials) []client.Opt {
	casConcurrency := runtime.NumCPU() * 2
	if runtime.GOOS == "windows" {
		// This is for better file write performance on Windows (http://b/171672371#comment6).
		casConcurrency = runtime.NumCPU()
	}

	return []client.Opt{
		&client.PerRPCCreds{Creds: creds},
		client.CASConcurrency(casConcurrency),
		client.UtilizeLocality(true),
		&client.TreeSymlinkOpts{Preserved: true, FollowsTarget: false},
		// Set restricted permission for written files.
		client.DirMode(0700),
		client.ExecutableMode(0700),
		client.RegularMode(0600),
		client.CompressedBytestreamThreshold(0),

		// Do not set per RPC timeout.
		client.RPCTimeouts{},
	}
}

// ContextWithMetadata attaches RBE related metadata with tool name to the
// given context.
func ContextWithMetadata(ctx context.Context, toolName string) (context.Context, error) {
	ctx, err := client.ContextWithMetadata(ctx, &client.ContextMetadata{
		ToolName: toolName,
	})
	if err != nil {
		return nil, errors.Annotate(err, "failed to attach metadata").Err()
	}

	m, err := client.GetContextMetadata(ctx)
	if err != nil {
		return nil, errors.Annotate(err, "failed to extract metadata").Err()
	}

	logging.Infof(ctx, "context metadata: %#+v", *m)

	return ctx, nil
}
<commit_msg>isolate: use higher concurrency for large file reads<commit_after>// Copyright 2020 The LUCI Authors.
//
// 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.

// Package cas provides remote-apis-sdks client with luci integration.
package casclient

import (
	"context"
	"fmt"
	"runtime"
	"strings"

	"github.com/bazelbuild/remote-apis-sdks/go/pkg/cas"
	"github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"go.chromium.org/luci/auth"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/hardcoded/chromeinfra"
)

// New returns luci auth configured Client for RBE-CAS.
func New(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*cas.Client, error) {
	creds, err := perRPCCreds(ctx, instance, opts, readOnly)
	if err != nil {
		return nil, err
	}

	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
		DialOpts:           []grpc.DialOption{grpc.WithPerRPCCredentials(creds)},
	}
	conn, err := client.Dial(ctx, dialParams.Service, dialParams)
	if err != nil {
		return nil, errors.Annotate(err, "failed to dial RBE").Err()
	}

	cl, err := cas.NewClientWithConfig(ctx, conn, instance, DefaultConfig())
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// DefaultConfig returns default CAS client configuration.
func DefaultConfig() cas.ClientConfig {
	cfg := cas.DefaultClientConfig()
	cfg.CompressedBytestreamThreshold = 0 // compress always
	cfg.FSLargeConcurrency = runtime.NumCPU()
	return cfg
}

func perRPCCreds(ctx context.Context, instance string, opts auth.Options, readOnly bool) (credentials.PerRPCCredentials, error) {
	project := strings.Split(instance, "/")[1]
	var role string
	if readOnly {
		role = "cas-read-only"
	} else {
		role = "cas-read-write"
	}

	// Construct auth.Options.
	opts.ActAsServiceAccount = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", role, project)
	opts.ActViaLUCIRealm = fmt.Sprintf("@internal:%s/%s", project, role)
	opts.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"}

	if strings.HasSuffix(project, "-dev") || strings.HasSuffix(project, "-staging") {
		// use dev token server for dev/staging projects.
		opts.TokenServerHost = chromeinfra.TokenServerDevHost
	}

	creds, err := auth.NewAuthenticator(ctx, auth.SilentLogin, opts).PerRPCCredentials()
	if err != nil {
		return nil, errors.Annotate(err, "failed to get PerRPCCredentials").Err()
	}
	return creds, nil
}

// NewLegacy returns luci auth configured legacy Client for RBE.
// In general, NewClient is preferred.
// TODO(crbug.com/1225524): remove this.
func NewLegacy(ctx context.Context, instance string, opts auth.Options, readOnly bool) (*client.Client, error) {
	creds, err := perRPCCreds(ctx, instance, opts, readOnly)
	if err != nil {
		return nil, err
	}
	dialParams := client.DialParams{
		Service:            "remotebuildexecution.googleapis.com:443",
		TransportCredsOnly: true,
	}

	cl, err := client.NewClient(ctx, instance, dialParams, Options(creds)...)
	if err != nil {
		return nil, errors.Annotate(err, "failed to create client").Err()
	}
	return cl, nil
}

// Options returns CAS client options.
func Options(creds credentials.PerRPCCredentials) []client.Opt {
	casConcurrency := runtime.NumCPU() * 2
	if runtime.GOOS == "windows" {
		// This is for better file write performance on Windows (http://b/171672371#comment6).
		casConcurrency = runtime.NumCPU()
	}

	return []client.Opt{
		&client.PerRPCCreds{Creds: creds},
		client.CASConcurrency(casConcurrency),
		client.UtilizeLocality(true),
		&client.TreeSymlinkOpts{Preserved: true, FollowsTarget: false},
		// Set restricted permission for written files.
		client.DirMode(0700),
		client.ExecutableMode(0700),
		client.RegularMode(0600),
		client.CompressedBytestreamThreshold(0),

		// Do not set per RPC timeout.
		client.RPCTimeouts{},
	}
}

// ContextWithMetadata attaches RBE related metadata with tool name to the
// given context.
func ContextWithMetadata(ctx context.Context, toolName string) (context.Context, error) {
	ctx, err := client.ContextWithMetadata(ctx, &client.ContextMetadata{
		ToolName: toolName,
	})
	if err != nil {
		return nil, errors.Annotate(err, "failed to attach metadata").Err()
	}

	m, err := client.GetContextMetadata(ctx)
	if err != nil {
		return nil, errors.Annotate(err, "failed to extract metadata").Err()
	}

	logging.Infof(ctx, "context metadata: %#+v", *m)

	return ctx, nil
}
<|endoftext|>


<commit_before>// Copyright © 2016 Mark Bates <mark@markbates.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"errors"
	"fmt"
	"html/template"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
)

var force bool
var skipPop bool
var dbType = "postgres"

var newCmd = &cobra.Command{
	Use:   "new [name]",
	Short: "Creates a new Buffalo application",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must enter a name for your new application.")
		}
		name := args[0]
		pwd, err := os.Getwd()
		if err != nil {
			return err
		}
		rootPath := filepath.Join(pwd, name)

		s, _ := os.Stat(rootPath)
		if s != nil {
			if force {
				os.RemoveAll(rootPath)
			} else {
				return fmt.Errorf("%s already exists! Either delete it or use the -f flag to force.\n", name)
			}
		}

		fmt.Printf("-- ./%s\n", name)
		err = os.MkdirAll(name, 0777)
		if err != nil {
			return err
		}

		err = genNewFiles(name, rootPath)
		if err != nil {
			return err
		}

		err = installDeps(pwd, rootPath)
		if err != nil {
			return err
		}
		return err
	},
}

func installDeps(pwd string, rootPath string) error {
	defer os.Chdir(pwd)
	err := os.Chdir(rootPath)
	if err != nil {
		return err
	}

	cmds := []*exec.Cmd{
		exec.Command("go", "get", "-u", "-v", "github.com/Masterminds/glide"),
		exec.Command("go", "get", "-u", "-v", "github.com/markbates/refresh"),
		exec.Command("go", "get", "-u", "-v", "github.com/markbates/grift"),
		exec.Command("glide", "init", "--non-interactive"),
		exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/refresh"),
		exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/grift"),
		exec.Command("refresh", "init"),
	}

	if !skipPop {
		cmds = append(cmds,
			exec.Command("go", "get", "-u", "-v", "github.com/markbates/pop/soda"),
			exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/pop/"),
			exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/pop/soda"),
			exec.Command("go", "install", "-v", "github.com/markbates/pop/soda"),
			exec.Command("soda", "g", "config", "-t", dbType),
		)
	}

	err = runCommands(cmds...)

	if err != nil {
		return err
	}

	return err
}

func runCommands(cmds ...*exec.Cmd) error {
	for _, cmd := range cmds {
		cmd.Stdin = os.Stdin
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		err := cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func genNewFiles(name, rootPath string) error {
	packagePath := strings.Replace(rootPath, filepath.Join(os.Getenv("GOPATH"), "src")+"/", "", 1)

	data := map[string]interface{}{
		"name":        name,
		"packagePath": packagePath,
		"actionsPath": filepath.Join(packagePath, "actions"),
	}

	for fn, tv := range newTemplates {
		dir := filepath.Dir(fn)
		err := os.MkdirAll(filepath.Join(rootPath, dir), 0777)
		if err != nil {
			return err
		}
		t, err := template.New(fn).Parse(tv)
		if err != nil {
			return err
		}
		fmt.Printf("-- ./%s/%s\n", name, fn)
		f, err := os.Create(filepath.Join(rootPath, fn))
		if err != nil {
			return err
		}
		err = t.Execute(f, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func init() {
	RootCmd.AddCommand(newCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// newCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	newCmd.Flags().BoolVarP(&force, "force", "f", false, "delete and remake if the app already exists")
	newCmd.Flags().BoolVar(&skipPop, "skip-pop", false, "skips add pop/soda to your app")
	newCmd.Flags().StringVar(&dbType, "db-type", "postgres", "specify the type of database you want to use [postgres, mysql, sqlite3]")

}
<commit_msg>deps are hard!<commit_after>// Copyright © 2016 Mark Bates <mark@markbates.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"errors"
	"fmt"
	"html/template"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
)

var force bool
var skipPop bool
var dbType = "postgres"

var newCmd = &cobra.Command{
	Use:   "new [name]",
	Short: "Creates a new Buffalo application",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must enter a name for your new application.")
		}
		name := args[0]
		pwd, err := os.Getwd()
		if err != nil {
			return err
		}
		rootPath := filepath.Join(pwd, name)

		s, _ := os.Stat(rootPath)
		if s != nil {
			if force {
				os.RemoveAll(rootPath)
			} else {
				return fmt.Errorf("%s already exists! Either delete it or use the -f flag to force.\n", name)
			}
		}

		fmt.Printf("-- ./%s\n", name)
		err = os.MkdirAll(name, 0777)
		if err != nil {
			return err
		}

		err = genNewFiles(name, rootPath)
		if err != nil {
			return err
		}

		err = installDeps(pwd, rootPath)
		if err != nil {
			return err
		}
		return err
	},
}

func installDeps(pwd string, rootPath string) error {
	defer os.Chdir(pwd)
	err := os.Chdir(rootPath)
	if err != nil {
		return err
	}

	cmds := []*exec.Cmd{
		exec.Command("go", "get", "-u", "-v", "github.com/Masterminds/glide"),
		exec.Command("glide", "init", "--non-interactive"),
		exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/refresh"),
		exec.Command("go", "install", "-v", "./vendor/github.com/markbates/refresh"),
		exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/grift"),
		exec.Command("go", "install", "-v", "./vendor/github.com/markbates/grift"),
		exec.Command("refresh", "init"),
	}

	if !skipPop {
		cmds = append(cmds,
			exec.Command("glide", "get", "-v", "-u", "--non-interactive", "github.com/markbates/pop/soda"),
			exec.Command("go", "install", "-v", "./vendor/github.com/markbates/pop/soda"),
			exec.Command("soda", "g", "config", "-t", dbType),
		)
	}

	err = runCommands(cmds...)

	if err != nil {
		return err
	}

	return err
}

func runCommands(cmds ...*exec.Cmd) error {
	for _, cmd := range cmds {
		cmd.Stdin = os.Stdin
		cmd.Stderr = os.Stderr
		cmd.Stdout = os.Stdout
		err := cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func genNewFiles(name, rootPath string) error {
	packagePath := strings.Replace(rootPath, filepath.Join(os.Getenv("GOPATH"), "src")+"/", "", 1)

	data := map[string]interface{}{
		"name":        name,
		"packagePath": packagePath,
		"actionsPath": filepath.Join(packagePath, "actions"),
	}

	for fn, tv := range newTemplates {
		dir := filepath.Dir(fn)
		err := os.MkdirAll(filepath.Join(rootPath, dir), 0777)
		if err != nil {
			return err
		}
		t, err := template.New(fn).Parse(tv)
		if err != nil {
			return err
		}
		fmt.Printf("-- ./%s/%s\n", name, fn)
		f, err := os.Create(filepath.Join(rootPath, fn))
		if err != nil {
			return err
		}
		err = t.Execute(f, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func init() {
	RootCmd.AddCommand(newCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// newCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	newCmd.Flags().BoolVarP(&force, "force", "f", false, "delete and remake if the app already exists")
	newCmd.Flags().BoolVar(&skipPop, "skip-pop", false, "skips add pop/soda to your app")
	newCmd.Flags().StringVar(&dbType, "db-type", "postgres", "specify the type of database you want to use [postgres, mysql, sqlite3]")

}
<|endoftext|>


<commit_before>package traytor

import (
	"fmt"
	"image/color"
)

func ExampleImage_String() {
	i := NewImage(2, 2)
	fmt.Printf("%s\n", i)
	// Output:
	// {0, 0, 0}, {0, 0, 0}
	// {0, 0, 0}, {0, 0, 0}
	//
}

func ExampleImage_Add() {
	im1 := NewImage(2, 2)
	for j := 0; j < im1.height; j++ {
		for i := 0; i < im1.width; i++ {
			im1.pixels[i][j].SetColour(4, 5, 6)
		}
	}
	im2 := NewImage(2, 2)
	for j := 0; j < im2.height; j++ {
		for i := 0; i < im2.width; i++ {
			im2.pixels[i][j].SetColour(1, 2, 3)
		}
	}
	im3 := im1.Add(im2)
	fmt.Printf("%s\n", im3)

	// Output:
	// {5, 7, 9}, {5, 7, 9}
	// {5, 7, 9}, {5, 7, 9}
	//
}

func ExampleImage_Bounds() {
	im := NewImage(640, 480)

	bounds := im.Bounds()
	fmt.Printf("%s\n", bounds)

	// Output:
	// (0,0)-(640,480)
	//
}

func ExampleImage_ColorModel() {
	im := NewImage(640, 480)
	if im.ColorModel() == color.RGBAModel {
		fmt.Printf("Model is RGBA\n")
	}

	// Output:
	// Model is RGBA
	//
}

func ExampleImage_At() {
	im := NewImage(2, 2)
	for j := 0; j < im.height; j++ {
		for i := 0; i < im.width; i++ {
			im.pixels[i][j].SetColour(float32(i), float32(j), 0)
		}
	}
	fmt.Printf("%s\n", im.At(0, 0))
	fmt.Printf("%s\n", im.At(1, 0))
	fmt.Printf("%s\n", im.At(0, 1))
	fmt.Printf("%s\n", im.At(1, 1))

	// Output:
	// [0, 0, 0]
	// [255, 0, 0]
	// [0, 255, 0]
	// [255, 255, 0]
	//
}
<commit_msg>add test for saving png<commit_after>package traytor

import (
	"fmt"
	"image/color"
	"image/png"
	"os"
	"testing"
)

func ExampleImage_String() {
	i := NewImage(2, 2)
	fmt.Printf("%s\n", i)
	// Output:
	// {0, 0, 0}, {0, 0, 0}
	// {0, 0, 0}, {0, 0, 0}
	//
}

func ExampleImage_Add() {
	im1 := NewImage(2, 2)
	for j := 0; j < im1.height; j++ {
		for i := 0; i < im1.width; i++ {
			im1.pixels[i][j].SetColour(4, 5, 6)
		}
	}
	im2 := NewImage(2, 2)
	for j := 0; j < im2.height; j++ {
		for i := 0; i < im2.width; i++ {
			im2.pixels[i][j].SetColour(1, 2, 3)
		}
	}
	im3 := im1.Add(im2)
	fmt.Printf("%s\n", im3)

	// Output:
	// {5, 7, 9}, {5, 7, 9}
	// {5, 7, 9}, {5, 7, 9}
	//
}

func ExampleImage_Bounds() {
	im := NewImage(640, 480)

	bounds := im.Bounds()
	fmt.Printf("%s\n", bounds)

	// Output:
	// (0,0)-(640,480)
	//
}

func ExampleImage_ColorModel() {
	im := NewImage(640, 480)
	if im.ColorModel() == color.RGBAModel {
		fmt.Printf("Model is RGBA\n")
	}

	// Output:
	// Model is RGBA
	//
}

func ExampleImage_At() {
	im := NewImage(2, 2)
	for j := 0; j < im.height; j++ {
		for i := 0; i < im.width; i++ {
			im.pixels[i][j].SetColour(float32(i), float32(j), 0)
		}
	}
	fmt.Printf("%s\n", im.At(0, 0))
	fmt.Printf("%s\n", im.At(1, 0))
	fmt.Printf("%s\n", im.At(0, 1))
	fmt.Printf("%s\n", im.At(1, 1))

	// Output:
	// [0, 0, 0]
	// [255, 0, 0]
	// [0, 255, 0]
	// [255, 255, 0]
	//
}

func TestSavePng(t *testing.T) {
	file, err := os.Create("/tmp/test.png")
	if err != nil {
		t.Error(err)
	}

	im := NewImage(20, 20)
	im.pixels[5][5].SetColour(1, 0, 1)

	err = png.Encode(file, im)
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package enemy

import (
	"board"
	"color"
)

type Enemy struct {
	board.Board
	color.Color
}

const (
	Random = iota
	Brutal
)

func NewEnemy(b *board.Board, c color.Color) *Enemy {
	enemy := new(Enemy)
	enemy.Board = *b
	enemy.Color = c
	return enemy
}
<commit_msg>Implement type parser of enemy<commit_after>package enemy

import (
	"board"
	"color"
	"strings"
)

type Enemy struct {
	board.Board
	color.Color
}

type Type int

const (
	Unknown = iota
	Random
	Brutal
)

func NewEnemy(b *board.Board, c color.Color) *Enemy {
	enemy := new(Enemy)
	enemy.Board = *b
	enemy.Color = c
	return enemy
}

func ParseType(s string) Type {
	switch strings.ToLower(s) {
	case "random":
		return Random
	case "brutal":
		return Brutal
	default:
		return Unknown
	}
}
<|endoftext|>


<commit_before>package request

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strconv"

	"github.com/99designs/httpsignatures-go"
	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
	"github.com/opentracing/opentracing-go"
	"github.com/pkg/errors"
	"github.com/remind101/pkg/httpx"
)

// Handlers represents lists of request handlers for each phase in the request
// lifecycle.
type Handlers struct {
	Build            HandlerList
	Sign             HandlerList
	Send             HandlerList
	ValidateResponse HandlerList
	Decode           HandlerList
	DecodeError      HandlerList
	Complete         HandlerList
}

// DefaultHandlers defines a basic request configuration that assumes JSON
// requests and responses.
func DefaultHandlers() Handlers {
	return Handlers{
		Build:            NewHandlerList(JSONBuilder),
		Sign:             NewHandlerList(),
		Send:             NewHandlerList(WithTracing(BaseSender)),
		ValidateResponse: NewHandlerList(),
		Decode:           NewHandlerList(JSONDecoder),
		DecodeError:      NewHandlerList(),
		Complete:         NewHandlerList(),
	}
}

// Copy returns a copy of a Handlers instance.
func (h Handlers) Copy() Handlers {
	return Handlers{
		Build:            h.Build.copy(),
		Sign:             h.Sign.copy(),
		Send:             h.Send.copy(),
		ValidateResponse: h.ValidateResponse.copy(),
		Decode:           h.Decode.copy(),
		DecodeError:      h.DecodeError.copy(),
		Complete:         h.Complete.copy(),
	}
}

// HandlerList manages a list of request handlers.
type HandlerList struct {
	list []Handler
}

// NewHandlerList constructs a new HandlerList with the given handlers.
func NewHandlerList(hh ...Handler) HandlerList {
	return HandlerList{
		list: append([]Handler{}, hh...),
	}
}

// Run calls each request handler in order.
func (hl *HandlerList) Run(r *Request) {
	for _, h := range hl.list {
		h.Fn(r)
	}
}

// Append adds a handler to the end of the list.
func (hl *HandlerList) Append(h Handler) {
	hl.list = append(hl.list, h)
}

// Prepend adds a handler to the front of the list.
func (hl *HandlerList) Prepend(h Handler) {
	hl.list = append([]Handler{h}, hl.list...)
}

// Clear truncates a handler list.
func (hl *HandlerList) Clear() {
	hl.list = []Handler{}
}

func (hl *HandlerList) copy() HandlerList {
	n := HandlerList{}
	if len(hl.list) == 0 {
		return n
	}

	n.list = append(make([]Handler, 0, len(hl.list)), hl.list...)
	return n
}

// Handler defines a request handler.
type Handler struct {
	Name string
	Fn   func(*Request)
}

// BaseSender sends a request using the http.Client.
var BaseSender = Handler{
	Name: "BaseSender",
	Fn: func(r *Request) {
		var err error
		r.HTTPResponse, err = r.HTTPClient.Do(r.HTTPRequest)
		if err != nil {
			handleSendError(r, err)
		}
	},
}

var reStatusCode = regexp.MustCompile(`^(\d{3})`)

func handleSendError(r *Request, err error) {
	// Prevent leaking if an HTTPResponse was returned.
	if r.HTTPResponse != nil {
		r.HTTPResponse.Body.Close()
	}

	// Capture the case where url.Error is returned for error processing
	// response. e.g. 301 without location header comes back as string
	// error and r.HTTPResponse is nil. Other URL redirect errors will
	// comeback in a similar method.
	if e, ok := err.(*url.Error); ok && e.Err != nil {
		if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
			code, _ := strconv.ParseInt(s[1], 10, 64)
			r.HTTPResponse = &http.Response{
				StatusCode: int(code),
				Status:     http.StatusText(int(code)),
				Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
			}
			return
		}
	}

	if r.HTTPResponse == nil {
		// Add a dummy request response object to ensure the HTTPResponse
		// value is consistent.
		r.HTTPResponse = &http.Response{
			StatusCode: int(0),
			Status:     http.StatusText(int(0)),
			Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
		}
	}

	// Catch all other request errors.
	r.Error = errors.Wrap(err, "send request failed")
}

// JSONBuilder adds standard JSON headers to the request and encodes Params
// as JSON to the request body if the request is not a GET.
var JSONBuilder = Handler{
	Name: "JSONBuilder",
	Fn: func(r *Request) {
		r.HTTPRequest.Header.Set("Content-Type", "application/json")
		r.HTTPRequest.Header.Set("Accept", "application/json")

		if r.HTTPRequest.Method != "GET" && r.Params != nil {
			raw, err := json.Marshal(r.Params)
			if err != nil {
				r.Error = err
				return
			}
			r.HTTPRequest.Body = ioutil.NopCloser(bytes.NewReader(raw))
		}
	},
}

// JSONDecoder decodes a response as JSON.
var JSONDecoder = Handler{
	Name: "JSONDecoder",
	Fn: func(r *Request) {
		if r.HTTPResponse == nil {
			return
		}
		if r.HTTPResponse.Body != nil {
			defer r.HTTPResponse.Body.Close()
		}
		if r.Data == nil {
			_, r.Error = io.Copy(ioutil.Discard, r.HTTPResponse.Body)
			return
		}
		r.Error = json.NewDecoder(r.HTTPResponse.Body).Decode(r.Data)
	},
}

// RequestSigner signs requests.
func RequestSigner(id, key string) Handler {
	return Handler{
		Name: "RequestSigner",
		Fn: func(r *Request) {
			r.Error = httpsignatures.DefaultSha256Signer.SignRequest(id, key, r.HTTPRequest)
		},
	}
}

// BasicAuther sets basic auth on a request.
func BasicAuther(username, password string) Handler {
	return Handler{
		Name: "BasicAuther",
		Fn: func(r *Request) {
			r.HTTPRequest.SetBasicAuth(username, password)
		},
	}
}

// HeadersFromContext adds headers with values from the request context.
// This is useful for forwarding headers to upstream services.
func HeadersFromContext(headers ...string) Handler {
	return Handler{
		Name: "HeadersFromContext",
		Fn: func(r *Request) {
			for _, header := range headers {
				r.HTTPRequest.Header.Add(header, httpx.Header(r.HTTPRequest.Context(), header))
			}
		},
	}
}

// RequestLogger dumps the entire request to stdout.
var RequestLogger = Handler{
	Name: "RequestLogger",
	Fn: func(r *Request) {
		b, err := httputil.DumpRequestOut(r.HTTPRequest, true)
		if err != nil {
			fmt.Printf("error dumping request: %s\n", err.Error())
			return
		}
		fmt.Println(string(b))
	},
}

// ResponseLogger dumps the entire response to stdout.
var ResponseLogger = Handler{
	Name: "ResponseLogger",
	Fn: func(r *Request) {
		b, err := httputil.DumpResponse(r.HTTPResponse, true)
		if err != nil {
			fmt.Printf("error dumping response: %s\n", err.Error())
			return
		}
		fmt.Println(string(b))
	},
}

// WithTracing returns a Send Handler that wraps another Send Handler in a trace
// span.
func WithTracing(h Handler) Handler {
	return Handler{
		Name: "TracedSender",
		Fn: func(r *Request) {
			span, ctx := opentracing.StartSpanFromContext(r.HTTPRequest.Context(), "client.request")
			opentracing.GlobalTracer().Inject(
				span.Context(),
				opentracing.HTTPHeaders,
				opentracing.HTTPHeadersCarrier(r.HTTPRequest.Header),
			)
			defer span.Finish()
			r.HTTPRequest = r.HTTPRequest.WithContext(ctx)

			span.SetTag(ext.ResourceName, r.ClientInfo.ServiceName)
			span.SetTag("http.method", r.HTTPRequest.Method)
			span.SetTag("http.url", r.HTTPRequest.URL.Hostname()+r.HTTPRequest.URL.EscapedPath())
			span.SetTag("out.host", r.HTTPRequest.URL.Hostname())
			span.SetTag("out.port", r.HTTPRequest.URL.Port())

			h.Fn(r)

			if r.HTTPResponse != nil {
				span.SetTag("http.status_code", r.HTTPResponse.StatusCode)
			}

			if r.Error != nil {
				span.SetTag(ext.Error, r.Error)
			}
		},
	}
}
<commit_msg>[missing-content-length] add content length when json body is added<commit_after>package request

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strconv"

	"github.com/99designs/httpsignatures-go"
	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
	"github.com/opentracing/opentracing-go"
	"github.com/pkg/errors"
	"github.com/remind101/pkg/httpx"
)

// Handlers represents lists of request handlers for each phase in the request
// lifecycle.
type Handlers struct {
	Build            HandlerList
	Sign             HandlerList
	Send             HandlerList
	ValidateResponse HandlerList
	Decode           HandlerList
	DecodeError      HandlerList
	Complete         HandlerList
}

// DefaultHandlers defines a basic request configuration that assumes JSON
// requests and responses.
func DefaultHandlers() Handlers {
	return Handlers{
		Build:            NewHandlerList(JSONBuilder),
		Sign:             NewHandlerList(),
		Send:             NewHandlerList(WithTracing(BaseSender)),
		ValidateResponse: NewHandlerList(),
		Decode:           NewHandlerList(JSONDecoder),
		DecodeError:      NewHandlerList(),
		Complete:         NewHandlerList(),
	}
}

// Copy returns a copy of a Handlers instance.
func (h Handlers) Copy() Handlers {
	return Handlers{
		Build:            h.Build.copy(),
		Sign:             h.Sign.copy(),
		Send:             h.Send.copy(),
		ValidateResponse: h.ValidateResponse.copy(),
		Decode:           h.Decode.copy(),
		DecodeError:      h.DecodeError.copy(),
		Complete:         h.Complete.copy(),
	}
}

// HandlerList manages a list of request handlers.
type HandlerList struct {
	list []Handler
}

// NewHandlerList constructs a new HandlerList with the given handlers.
func NewHandlerList(hh ...Handler) HandlerList {
	return HandlerList{
		list: append([]Handler{}, hh...),
	}
}

// Run calls each request handler in order.
func (hl *HandlerList) Run(r *Request) {
	for _, h := range hl.list {
		h.Fn(r)
	}
}

// Append adds a handler to the end of the list.
func (hl *HandlerList) Append(h Handler) {
	hl.list = append(hl.list, h)
}

// Prepend adds a handler to the front of the list.
func (hl *HandlerList) Prepend(h Handler) {
	hl.list = append([]Handler{h}, hl.list...)
}

// Clear truncates a handler list.
func (hl *HandlerList) Clear() {
	hl.list = []Handler{}
}

func (hl *HandlerList) copy() HandlerList {
	n := HandlerList{}
	if len(hl.list) == 0 {
		return n
	}

	n.list = append(make([]Handler, 0, len(hl.list)), hl.list...)
	return n
}

// Handler defines a request handler.
type Handler struct {
	Name string
	Fn   func(*Request)
}

// BaseSender sends a request using the http.Client.
var BaseSender = Handler{
	Name: "BaseSender",
	Fn: func(r *Request) {
		var err error
		r.HTTPResponse, err = r.HTTPClient.Do(r.HTTPRequest)
		if err != nil {
			handleSendError(r, err)
		}
	},
}

var reStatusCode = regexp.MustCompile(`^(\d{3})`)

func handleSendError(r *Request, err error) {
	// Prevent leaking if an HTTPResponse was returned.
	if r.HTTPResponse != nil {
		r.HTTPResponse.Body.Close()
	}

	// Capture the case where url.Error is returned for error processing
	// response. e.g. 301 without location header comes back as string
	// error and r.HTTPResponse is nil. Other URL redirect errors will
	// comeback in a similar method.
	if e, ok := err.(*url.Error); ok && e.Err != nil {
		if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
			code, _ := strconv.ParseInt(s[1], 10, 64)
			r.HTTPResponse = &http.Response{
				StatusCode: int(code),
				Status:     http.StatusText(int(code)),
				Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
			}
			return
		}
	}

	if r.HTTPResponse == nil {
		// Add a dummy request response object to ensure the HTTPResponse
		// value is consistent.
		r.HTTPResponse = &http.Response{
			StatusCode: int(0),
			Status:     http.StatusText(int(0)),
			Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
		}
	}

	// Catch all other request errors.
	r.Error = errors.Wrap(err, "send request failed")
}

// JSONBuilder adds standard JSON headers to the request and encodes Params
// as JSON to the request body if the request is not a GET.
var JSONBuilder = Handler{
	Name: "JSONBuilder",
	Fn: func(r *Request) {
		r.HTTPRequest.Header.Set("Content-Type", "application/json")
		r.HTTPRequest.Header.Set("Accept", "application/json")

		if r.HTTPRequest.Method != "GET" && r.Params != nil {
			raw, err := json.Marshal(r.Params)
			if err != nil {
				r.Error = err
				return
			}
			r.HTTPRequest.ContentLength = int64(len(raw))
			r.HTTPRequest.Body = ioutil.NopCloser(bytes.NewReader(raw))
		}
	},
}

// JSONDecoder decodes a response as JSON.
var JSONDecoder = Handler{
	Name: "JSONDecoder",
	Fn: func(r *Request) {
		if r.HTTPResponse == nil {
			return
		}
		if r.HTTPResponse.Body != nil {
			defer r.HTTPResponse.Body.Close()
		}
		if r.Data == nil {
			_, r.Error = io.Copy(ioutil.Discard, r.HTTPResponse.Body)
			return
		}
		r.Error = json.NewDecoder(r.HTTPResponse.Body).Decode(r.Data)
	},
}

// RequestSigner signs requests.
func RequestSigner(id, key string) Handler {
	return Handler{
		Name: "RequestSigner",
		Fn: func(r *Request) {
			r.Error = httpsignatures.DefaultSha256Signer.SignRequest(id, key, r.HTTPRequest)
		},
	}
}

// BasicAuther sets basic auth on a request.
func BasicAuther(username, password string) Handler {
	return Handler{
		Name: "BasicAuther",
		Fn: func(r *Request) {
			r.HTTPRequest.SetBasicAuth(username, password)
		},
	}
}

// HeadersFromContext adds headers with values from the request context.
// This is useful for forwarding headers to upstream services.
func HeadersFromContext(headers ...string) Handler {
	return Handler{
		Name: "HeadersFromContext",
		Fn: func(r *Request) {
			for _, header := range headers {
				r.HTTPRequest.Header.Add(header, httpx.Header(r.HTTPRequest.Context(), header))
			}
		},
	}
}

// RequestLogger dumps the entire request to stdout.
var RequestLogger = Handler{
	Name: "RequestLogger",
	Fn: func(r *Request) {
		b, err := httputil.DumpRequestOut(r.HTTPRequest, true)
		if err != nil {
			fmt.Printf("error dumping request: %s\n", err.Error())
			return
		}
		fmt.Println(string(b))
	},
}

// ResponseLogger dumps the entire response to stdout.
var ResponseLogger = Handler{
	Name: "ResponseLogger",
	Fn: func(r *Request) {
		b, err := httputil.DumpResponse(r.HTTPResponse, true)
		if err != nil {
			fmt.Printf("error dumping response: %s\n", err.Error())
			return
		}
		fmt.Println(string(b))
	},
}

// WithTracing returns a Send Handler that wraps another Send Handler in a trace
// span.
func WithTracing(h Handler) Handler {
	return Handler{
		Name: "TracedSender",
		Fn: func(r *Request) {
			span, ctx := opentracing.StartSpanFromContext(r.HTTPRequest.Context(), "client.request")
			opentracing.GlobalTracer().Inject(
				span.Context(),
				opentracing.HTTPHeaders,
				opentracing.HTTPHeadersCarrier(r.HTTPRequest.Header),
			)
			defer span.Finish()
			r.HTTPRequest = r.HTTPRequest.WithContext(ctx)

			span.SetTag(ext.ResourceName, r.ClientInfo.ServiceName)
			span.SetTag("http.method", r.HTTPRequest.Method)
			span.SetTag("http.url", r.HTTPRequest.URL.Hostname()+r.HTTPRequest.URL.EscapedPath())
			span.SetTag("out.host", r.HTTPRequest.URL.Hostname())
			span.SetTag("out.port", r.HTTPRequest.URL.Port())

			h.Fn(r)

			if r.HTTPResponse != nil {
				span.SetTag("http.status_code", r.HTTPResponse.StatusCode)
			}

			if r.Error != nil {
				span.SetTag(ext.Error, r.Error)
			}
		},
	}
}
<|endoftext|>


<commit_before>package builder

import (
	"log"
	"reflect"
	"strings"

	"github.com/nicksnyder/go-i18n/i18n"
)

// Build FuncMap for template
func (site *SiteBuilder) helpers() map[string]interface{} {
	return map[string]interface{}{
		"urlFor": site.UrlFor,
		"t":      site.Translate,

		"startsWith": StartsWith,
		"mod":        Mod,
		"modBool":    ModBool,
	}
}

// UrlFor returns an URL to an internal page.
func (site *SiteBuilder) UrlFor(dest string) string {
	var result string

	switch dest {
	case KIND_ACTIVITIES, KIND_MEMBERS, KIND_CONTACT, KIND_HOMEPAGE:
		// find uniq node
		nodes := site.nodeBuilder(dest).Nodes()
		if len(nodes) == 0 {
			panic("No node loaded yet")
		}

		if len(nodes) > 1 {
			panic("That method logic is broken, fix it!")
		}

		result = nodes[0].Url

	case KIND_POSTS, KIND_EVENTS:
		// find correct node
		nodes := site.nodeBuilder(dest).Nodes()

		for _, node := range nodes {
			if node.Kind == dest {
				result = node.Url
				break
			}
		}

	default:
		// KIND_PAGE, KIND_POST
		panic("Internal link kind not supported yet")
	}

	return result
}

// Translate translates given sentence.
func (site *SiteBuilder) Translate(sentence string) string {
	T := i18n.MustTfunc(site.site.Lang)

	return T(sentence)
}

// StartsWith returns true if check has prefix.
func StartsWith(check string, prefix string) bool {
	log.Printf("StartsWith(%q, %q)", check, prefix)
	return strings.HasPrefix(check, prefix)
}

// @todo Replace interface{}
func Mod(a, b interface{}) int64 {
	av := reflect.ValueOf(a)
	bv := reflect.ValueOf(b)
	var ai, bi int64

	switch av.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ai = av.Int()
	default:
		panic("Modulo operator can't be used with non integer value")
	}

	switch bv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		bi = bv.Int()
	default:
		panic("Modulo operator can't be used with non integer value")
	}

	if bi == 0 {
		panic("The number can't be divided by zero at modulo operation")
	}

	return ai % bi
}

func ModBool(a, b interface{}) bool {
	return Mod(a, b) == int64(0)
}
<commit_msg>Removes debug log<commit_after>package builder

import (
	"reflect"
	"strings"

	"github.com/nicksnyder/go-i18n/i18n"
)

// Build FuncMap for template
func (site *SiteBuilder) helpers() map[string]interface{} {
	return map[string]interface{}{
		"urlFor": site.UrlFor,
		"t":      site.Translate,

		"startsWith": StartsWith,
		"mod":        Mod,
		"modBool":    ModBool,
	}
}

// UrlFor returns an URL to an internal page.
func (site *SiteBuilder) UrlFor(dest string) string {
	var result string

	switch dest {
	case KIND_ACTIVITIES, KIND_MEMBERS, KIND_CONTACT, KIND_HOMEPAGE:
		// find uniq node
		nodes := site.nodeBuilder(dest).Nodes()
		if len(nodes) == 0 {
			panic("No node loaded yet")
		}

		if len(nodes) > 1 {
			panic("That method logic is broken, fix it!")
		}

		result = nodes[0].Url

	case KIND_POSTS, KIND_EVENTS:
		// find correct node
		nodes := site.nodeBuilder(dest).Nodes()

		for _, node := range nodes {
			if node.Kind == dest {
				result = node.Url
				break
			}
		}

	default:
		// KIND_PAGE, KIND_POST
		panic("Internal link kind not supported yet")
	}

	return result
}

// Translate translates given sentence.
func (site *SiteBuilder) Translate(sentence string) string {
	T := i18n.MustTfunc(site.site.Lang)

	return T(sentence)
}

// StartsWith returns true if check has prefix.
func StartsWith(check string, prefix string) bool {
	return strings.HasPrefix(check, prefix)
}

// @todo Replace interface{}
func Mod(a, b interface{}) int64 {
	av := reflect.ValueOf(a)
	bv := reflect.ValueOf(b)
	var ai, bi int64

	switch av.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ai = av.Int()
	default:
		panic("Modulo operator can't be used with non integer value")
	}

	switch bv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		bi = bv.Int()
	default:
		panic("Modulo operator can't be used with non integer value")
	}

	if bi == 0 {
		panic("The number can't be divided by zero at modulo operation")
	}

	return ai % bi
}

func ModBool(a, b interface{}) bool {
	return Mod(a, b) == int64(0)
}
<|endoftext|>


<commit_before>package buildbox

const (
	Version = "0.2-beta.11"
)
<commit_msg>Bumping to version 0.2-beta.12<commit_after>package buildbox

const (
	Version = "0.2-beta.12"
)
<|endoftext|>


<commit_before>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// CreateBuild - Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
	PullRequestBaseBranch       string            `json:"pull_request_base_branch,omitempty"`
	PullRequestID               int64             `json:"pull_request_id,omitempty"`
	PullRequestRepository       string            `json:"pull_request_repository,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// PullRequest represents a Github PR
type PullRequest struct {
	ID         *string `json:"id,omitempty"`
	Base       *string `json:"base,omitempty"`
	Repository *string `json:"repository,omitempty"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Blocked     *bool                  `json:"blocked,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`
	Source      *string                `json:"source,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`

	// the pull request this build is associated with
	PullRequest *PullRequest `json:"pull_request,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	// Filters the results by builds for the specific commit SHA (full, not shortened). Default is "".
	Commit string `url:"commit,omitempty"`

	ListOptions
}

// Cancel triggers a canel for the tagrget build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#cancel-a-build
func (bs *BuildsService) Cancel(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/cancel", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

// Create - Create a pipeline
//
// buildkite API docs: https://buildkite.com/docs/api/builds#create-a-build
func (bs *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := bs.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (bs *BuildsService) Get(org string, pipeline string, id string) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// Rebuild triggers a rebuild for the target build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#rebuild-a-build
func (bs *BuildsService) Rebuild(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/rebuild", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}
<commit_msg>Added include_retried_jobs for BuildsListOptions<commit_after>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"fmt"
	"time"
)

// BuildsService handles communication with the build related
// methods of the buildkite API.
//
// buildkite API docs: https://buildkite.com/docs/api/builds
type BuildsService struct {
	client *Client
}

// Author of a commit (used in CreateBuild)
type Author struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
}

// CreateBuild - Create a build.
type CreateBuild struct {
	Commit  string `json:"commit"`
	Branch  string `json:"branch"`
	Message string `json:"message"`

	// Optional fields
	Author                      Author            `json:"author,omitempty"`
	Env                         map[string]string `json:"env,omitempty"`
	MetaData                    map[string]string `json:"meta_data,omitempty"`
	IgnorePipelineBranchFilters bool              `json:"ignore_pipeline_branch_filters,omitempty"`
	PullRequestBaseBranch       string            `json:"pull_request_base_branch,omitempty"`
	PullRequestID               int64             `json:"pull_request_id,omitempty"`
	PullRequestRepository       string            `json:"pull_request_repository,omitempty"`
}

// Creator represents who created a build
type Creator struct {
	AvatarURL string     `json:"avatar_url"`
	CreatedAt *Timestamp `json:"created_at"`
	Email     string     `json:"email"`
	ID        string     `json:"id"`
	Name      string     `json:"name"`
}

// PullRequest represents a Github PR
type PullRequest struct {
	ID         *string `json:"id,omitempty"`
	Base       *string `json:"base,omitempty"`
	Repository *string `json:"repository,omitempty"`
}

// Build represents a build which has run in buildkite
type Build struct {
	ID          *string                `json:"id,omitempty"`
	URL         *string                `json:"url,omitempty"`
	WebURL      *string                `json:"web_url,omitempty"`
	Number      *int                   `json:"number,omitempty"`
	State       *string                `json:"state,omitempty"`
	Blocked     *bool                  `json:"blocked,omitempty"`
	Message     *string                `json:"message,omitempty"`
	Commit      *string                `json:"commit,omitempty"`
	Branch      *string                `json:"branch,omitempty"`
	Env         map[string]interface{} `json:"env,omitempty"`
	CreatedAt   *Timestamp             `json:"created_at,omitempty"`
	ScheduledAt *Timestamp             `json:"scheduled_at,omitempty"`
	StartedAt   *Timestamp             `json:"started_at,omitempty"`
	FinishedAt  *Timestamp             `json:"finished_at,omitempty"`
	MetaData    interface{}            `json:"meta_data,omitempty"`
	Creator     *Creator               `json:"creator,omitempty"`
	Source      *string                `json:"source,omitempty"`

	// jobs run during the build
	Jobs []*Job `json:"jobs,omitempty"`

	// the pipeline this build is associated with
	Pipeline *Pipeline `json:"pipeline,omitempty"`

	// the pull request this build is associated with
	PullRequest *PullRequest `json:"pull_request,omitempty"`
}

// BuildsListOptions specifies the optional parameters to the
// BuildsService.List method.
type BuildsListOptions struct {

	// Filters the results by the user who created the build
	Creator string `url:"creator,omitempty"`

	// Filters the results by builds created on or after the given time
	CreatedFrom time.Time `url:"created_from,omitempty"`

	// Filters the results by builds created before the given time
	CreatedTo time.Time `url:"created_to,omitempty"`

	// Filters the results by builds finished on or after the given time
	FinishedFrom time.Time `url:"finished_from,omitempty"`

	// State of builds to list.  Possible values are: running, scheduled, passed,
	// failed, canceled, skipped and not_run. Default is "".
	State []string `url:"state,brackets,omitempty"`

	// Branch filter by the name of the branch. Default is "".
	Branch string `url:"branch,omitempty"`

	// Filters the results by builds for the specific commit SHA (full, not shortened). Default is "".
	Commit string `url:"commit,omitempty"`

	// Include all retried jobs in each build’s jobs list
	IncludeRetriedJobs bool `url:"include_retried_jobs,omitempty"`

	ListOptions
}

// Cancel triggers a canel for the tagrget build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#cancel-a-build
func (bs *BuildsService) Cancel(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/cancel", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

// Create - Create a pipeline
//
// buildkite API docs: https://buildkite.com/docs/api/builds#create-a-build
func (bs *BuildsService) Create(org string, pipeline string, b *CreateBuild) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	req, err := bs.client.NewRequest("POST", u, b)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// Get fetches a build.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#get-a-build
func (bs *BuildsService) Get(org string, pipeline string, id string, opt *BuildsListOptions) (*Build, *Response, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s", org, pipeline, id)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}
	fmt.Println(u)

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	build := new(Build)
	resp, err := bs.client.Do(req, build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, err
}

// List the builds for the current user.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-all-builds
func (bs *BuildsService) List(opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/builds")

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByOrg lists the builds within the specified orginisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-an-organization
func (bs *BuildsService) ListByOrg(org string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/builds", org)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// ListByPipeline lists the builds for a pipeline within the specified originisation.
//
// buildkite API docs: https://buildkite.com/docs/api/builds#list-builds-for-a-pipeline
func (bs *BuildsService) ListByPipeline(org string, pipeline string, opt *BuildsListOptions) ([]Build, *Response, error) {
	var u string

	u = fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds", org, pipeline)

	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := bs.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	orgs := new([]Build)
	resp, err := bs.client.Do(req, orgs)
	if err != nil {
		return nil, resp, err
	}

	return *orgs, resp, err
}

// Rebuild triggers a rebuild for the target build
//
// buildkite API docs: https://buildkite.com/docs/apis/rest-api/builds#rebuild-a-build
func (bs *BuildsService) Rebuild(org, pipeline, build string) (*Build, error) {
	u := fmt.Sprintf("v2/organizations/%s/pipelines/%s/builds/%s/rebuild", org, pipeline, build)
	req, err := bs.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	result := Build{}
	_, err = bs.client.Do(req, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}
<|endoftext|>