<commit_before>package api_v1

import (
	"scal/event_lib"
	"scal/storage"
	"scal/user_lib"
	"testing"
	"time"

	"github.com/globalsign/mgo/bson"
	"github.com/golang/mock/gomock"
	"github.com/ilius/is"
	"github.com/ilius/libgostarcal/utils"
	. "github.com/ilius/ripo"
)

func NewTestHelper(t *testing.T, userEmail string) *TestHelper {
	userAuth, err := utils.GenerateRandomBase64String(16)
	if err != nil {
		panic(err)
	}
	return &TestHelper{
		t:         t,
		userEmail: userEmail,
		userAuth:  userAuth,
	}
}

type TestHelper struct {
	t *testing.T // set in NewTestHelper

	userEmail      string // set in NewTestHelper
	userAuth       string // set in NewTestHelper
	emailConfirmed bool   // set in SetEmailConfirmed

	is           *is.Is                     // set on Start()
	db           storage.Database           // set on Start()
	userModel    *user_lib.UserModel        // set on Start()
	defaultGroup *event_lib.EventGroupModel // set on Start()

	mockControllers []*gomock.Controller // added to in NewRequestMock
}

func (h *TestHelper) Start() {
	var err error
	h.is = is.New(h.t)
	h.db, err = storage.GetDB()
	if err != nil {
		panic(err)
	}
	if h.userEmail != "" {
		h.createUser()
	}
}

func (h *TestHelper) Is() *is.Is {
	return h.is
}

func (h *TestHelper) UserModel() *user_lib.UserModel {
	return h.userModel
}

func (h *TestHelper) SetEmailConfirmed(emailConfirmed bool) *TestHelper {
	h.emailConfirmed = emailConfirmed
	return h
}

func (h *TestHelper) createUser() {
	db := h.db
	email := h.userEmail
	now := time.Now()
	h.userModel = &user_lib.UserModel{
		Email:          email,
		EmailConfirmed: h.emailConfirmed,
		TokenIssuedAt:  &now,
	}

	if testUserMap == nil {
		testUserMap = map[string]*user_lib.UserModel{}
	}
	testUserMap[h.userAuth] = h.userModel

	h.defaultGroup = &event_lib.EventGroupModel{
		Id:         bson.NewObjectId().Hex(),
		Title:      email,
		OwnerEmail: email,
	}
	err := db.Insert(h.defaultGroup)
	if err != nil {
		panic(err)
	}
	h.userModel.DefaultGroupId = &h.defaultGroup.Id

	err = db.Insert(h.userModel)
	if err != nil {
		panic(err)
	}
}

func (h *TestHelper) DefaultGroup() *event_lib.EventGroupModel {
	return h.defaultGroup
}

func (h *TestHelper) CreateGroup(title string) *event_lib.EventGroupModel {
	groupModel := &event_lib.EventGroupModel{}

	groupId := bson.NewObjectId().Hex()
	groupModel.Id = groupId
	groupModel.OwnerEmail = h.userEmail
	groupModel.Title = title
	// groupModel.AddAccessEmails = addAccessEmails
	// groupModel.ReadAccessEmails = readAccessEmails

	err := h.db.Insert(groupModel)
	if err != nil {
		panic(err)
	}
	return groupModel
}

func (h *TestHelper) NewRequestMock(authHeader bool, remoteIp string) (ExtendedRequest, *MockExtendedRequest) {
	ctrl := gomock.NewController(h.t)
	h.mockControllers = append(h.mockControllers, ctrl)
	mockReq := NewMockExtendedRequest(ctrl)
	if authHeader {
		mockReq.EXPECT().Header(gomock.Eq("Authorization")).Return(h.userAuth)
	}
	if remoteIp != "" {
		mockReq.EXPECT().RemoteIP().Return(remoteIp, nil)
	}
	return mockReq, mockReq
}

func (h *TestHelper) FinishMocks() {
	for _, ctrl := range h.mockControllers {
		ctrl.Finish()
	}
	h.mockControllers = nil
}

func (h *TestHelper) deleteUser() {
	if h.userModel == nil {
		return
	}
	db := h.db
	err := db.Remove(h.userModel)
	if err != nil {
		panic(err)
	}
	if h.defaultGroup != nil {
		err := db.Remove(h.defaultGroup)
		if err != nil {
			panic(err)
		}
	}
}

func (h *TestHelper) Finish() {
	testUserMapClear()
	h.deleteUser()
	h.FinishMocks()
}
<commit_msg>TestHelper: set userModel.Id<commit_after>package api_v1

import (
	"scal/event_lib"
	"scal/storage"
	"scal/user_lib"
	"testing"
	"time"

	"github.com/globalsign/mgo/bson"
	"github.com/golang/mock/gomock"
	"github.com/ilius/is"
	"github.com/ilius/libgostarcal/utils"
	. "github.com/ilius/ripo"
)

func NewTestHelper(t *testing.T, userEmail string) *TestHelper {
	userAuth, err := utils.GenerateRandomBase64String(16)
	if err != nil {
		panic(err)
	}
	return &TestHelper{
		t:         t,
		userEmail: userEmail,
		userAuth:  userAuth,
	}
}

type TestHelper struct {
	t *testing.T // set in NewTestHelper

	userEmail      string // set in NewTestHelper
	userAuth       string // set in NewTestHelper
	emailConfirmed bool   // set in SetEmailConfirmed

	is           *is.Is                     // set on Start()
	db           storage.Database           // set on Start()
	userModel    *user_lib.UserModel        // set on Start()
	defaultGroup *event_lib.EventGroupModel // set on Start()

	mockControllers []*gomock.Controller // added to in NewRequestMock
}

func (h *TestHelper) Start() {
	var err error
	h.is = is.New(h.t)
	h.db, err = storage.GetDB()
	if err != nil {
		panic(err)
	}
	if h.userEmail != "" {
		h.createUser()
	}
}

func (h *TestHelper) Is() *is.Is {
	return h.is
}

func (h *TestHelper) UserModel() *user_lib.UserModel {
	return h.userModel
}

func (h *TestHelper) SetEmailConfirmed(emailConfirmed bool) *TestHelper {
	h.emailConfirmed = emailConfirmed
	return h
}

func (h *TestHelper) createUser() {
	db := h.db
	email := h.userEmail
	now := time.Now()
	h.userModel = &user_lib.UserModel{
		Id:             bson.NewObjectId().Hex(),
		Email:          email,
		EmailConfirmed: h.emailConfirmed,
		TokenIssuedAt:  &now,
	}

	if testUserMap == nil {
		testUserMap = map[string]*user_lib.UserModel{}
	}
	testUserMap[h.userAuth] = h.userModel

	h.defaultGroup = &event_lib.EventGroupModel{
		Id:         bson.NewObjectId().Hex(),
		Title:      email,
		OwnerEmail: email,
	}
	err := db.Insert(h.defaultGroup)
	if err != nil {
		panic(err)
	}
	h.userModel.DefaultGroupId = &h.defaultGroup.Id

	err = db.Insert(h.userModel)
	if err != nil {
		panic(err)
	}
}

func (h *TestHelper) DefaultGroup() *event_lib.EventGroupModel {
	return h.defaultGroup
}

func (h *TestHelper) CreateGroup(title string) *event_lib.EventGroupModel {
	groupModel := &event_lib.EventGroupModel{}

	groupId := bson.NewObjectId().Hex()
	groupModel.Id = groupId
	groupModel.OwnerEmail = h.userEmail
	groupModel.Title = title
	// groupModel.AddAccessEmails = addAccessEmails
	// groupModel.ReadAccessEmails = readAccessEmails

	err := h.db.Insert(groupModel)
	if err != nil {
		panic(err)
	}
	return groupModel
}

func (h *TestHelper) NewRequestMock(authHeader bool, remoteIp string) (ExtendedRequest, *MockExtendedRequest) {
	ctrl := gomock.NewController(h.t)
	h.mockControllers = append(h.mockControllers, ctrl)
	mockReq := NewMockExtendedRequest(ctrl)
	if authHeader {
		mockReq.EXPECT().Header(gomock.Eq("Authorization")).Return(h.userAuth)
	}
	if remoteIp != "" {
		mockReq.EXPECT().RemoteIP().Return(remoteIp, nil)
	}
	return mockReq, mockReq
}

func (h *TestHelper) FinishMocks() {
	for _, ctrl := range h.mockControllers {
		ctrl.Finish()
	}
	h.mockControllers = nil
}

func (h *TestHelper) deleteUser() {
	if h.userModel == nil {
		return
	}
	db := h.db
	err := db.Remove(h.userModel)
	if err != nil {
		panic(err)
	}
	if h.defaultGroup != nil {
		err := db.Remove(h.defaultGroup)
		if err != nil {
			panic(err)
		}
	}
}

func (h *TestHelper) Finish() {
	testUserMapClear()
	h.deleteUser()
	h.FinishMocks()
}
<|endoftext|>


<commit_before>package agent

import (
	"encoding/json"
	"fmt"
	"os"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/buildkite/agent/env"
	"github.com/stretchr/testify/assert"
)

func TestPipelineParserParsesYaml(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{
		Filename: "awesome.yml",
		Pipeline: []byte("steps:\n  - label: \"hello ${ENV_VAR_FRIEND}\""),
		Env:      environ}.Parse()

	require.NoError(t, err)
	j, err := json.Marshal(result)
	require.Equal(t, `{"steps":[{"label":"hello \"friend\""}]}`, string(j))
}

func TestPipelineParserSupportsYamlMergesAndAnchors(t *testing.T) {
	complexYAML := `---
base_step: &base_step
  type: script
  agent_query_rules:
    - queue=default

steps:
  - <<: *base_step
    name: ':docker: building image'
    command: docker build .
    agents:
      queue: default`

	result, err := PipelineParser{
		Filename: "awesome.yml",
		Pipeline: []byte(complexYAML)}.Parse()

	require.NoError(t, err)
	j, err := json.Marshal(result)
	require.Equal(t, `{"base_step":{"agent_query_rules":["queue=default"],"type":"script"},"steps":[{"agent_query_rules":["queue=default"],"agents":{"queue":"default"},"command":"docker build .","name":":docker: building image","type":"script"}]}`, string(j))
}

func TestPipelineParserReturnsYamlParsingErrors(t *testing.T) {
	_, err := PipelineParser{Filename: "awesome.yml", Pipeline: []byte("steps: %blah%")}.Parse()
	require.Error(t, err, `Failed to parse awesome.yml: found character that cannot start any token`, fmt.Sprintf("%s", err))
}

func TestPipelineParserReturnsJsonParsingErrors(t *testing.T) {
	_, err := PipelineParser{Filename: "awesome.json", Pipeline: []byte("{")}.Parse()
	require.Error(t, err, `Failed to parse awesome.json: line 1: did not find expected node content`, fmt.Sprintf("%s", err))
}

func TestPipelineParserParsesJson(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{
		Filename: "thing.json",
		Pipeline: []byte("\n\n     \n  { \"foo\": \"bye ${ENV_VAR_FRIEND}\" }\n"),
		Env:      environ}.Parse()

	require.NoError(t, err)
	j, err := json.Marshal(result)
	require.Equal(t, `{"foo":"bye \"friend\""}`, string(j))
}

func TestPipelineParserParsesJsonObjects(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{Pipeline: []byte("\n\n     \n  { \"foo\": \"bye ${ENV_VAR_FRIEND}\" }\n"), Env: environ}.Parse()
	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"foo":"bye \"friend\""}`, string(j))
}

func TestPipelineParserParsesJsonArrays(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{Pipeline: []byte("\n\n     \n  [ { \"foo\": \"bye ${ENV_VAR_FRIEND}\" } ]\n"), Env: environ}.Parse()
	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `[{"foo":"bye \"friend\""}]`, string(j))
}

func TestPipelineParserPreservesBools(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    async: true")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"async":true,"trigger":"hello"}]}`, string(j))
}

func TestPipelineParserPreservesInts(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - label: hello\n    parallelism: 10")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"label":"hello","parallelism":10}]}`, string(j))
}

func TestPipelineParserPreservesNull(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - wait: ~")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"wait":null}]}`, string(j))
}

func TestPipelineParserPreservesFloats(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    llamas: 3.142")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"llamas":3.142,"trigger":"hello"}]}`, string(j))
}

func TestPipelineParserHandlesDates(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    llamas: 2002-08-15T17:18:23.18-06:00")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"llamas":"2002-08-15T17:18:23.18-06:00","trigger":"hello"}]}`, string(j))
}

func TestPipelineParserInterpolatesKeysAsWellAsValues(t *testing.T) {
	var pipeline = `{
		"env": {
			"${FROM_ENV}TEST1": "MyTest",
			"TEST2": "${FROM_ENV}"
		}
	}`

	var decoded struct {
		Env map[string]string `json:"env"`
	}

	environ := env.FromSlice([]string{`FROM_ENV=llamas`})

	result, err := PipelineParser{Pipeline: []byte(pipeline), Env: environ}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, `MyTest`, decoded.Env["llamasTEST1"])
	assert.Equal(t, `llamas`, decoded.Env["TEST2"])
}

func TestPipelineParserLoadsGlobalEnvBlockFirst(t *testing.T) {
	var pipeline = `{
		"env": {
			"TEAM1": "England",
			"TEAM2": "Australia",
			"HEADLINE": "${TEAM1} smashes ${TEAM2} to win the ashes in ${YEAR_FROM_SHELL}!!"
		},
		"steps": [{
			"command": "echo ${HEADLINE}"
		}]
	}`

	var decoded struct {
		Env   map[string]string `json:"env"`
		Steps []struct {
			Command string `json:"command"`
		} `json:"steps"`
	}

	environ := env.FromSlice([]string{`YEAR_FROM_SHELL=1912`})

	result, err := PipelineParser{Pipeline: []byte(pipeline), Env: environ}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, `England`, decoded.Env["TEAM1"])
	assert.Equal(t, `England smashes Australia to win the ashes in 1912!!`, decoded.Env["HEADLINE"])
	assert.Equal(t, `echo England smashes Australia to win the ashes in 1912!!`, decoded.Steps[0].Command)
}

func decodeIntoStruct(into interface{}, from interface{}) error {
	b, err := json.Marshal(from)
	if err != nil {
		return err
	}
	return json.Unmarshal(b, into)
}

func TestPipelineParserLoadsSystemEnvironment(t *testing.T) {
	var pipeline = `{
		"steps": [{
			"command": "echo ${LLAMAS_ROCK?}"
		}]
	}`

	var decoded struct {
		Steps []struct {
			Command string `json:"command"`
		} `json:"steps"`
	}

	_, err := PipelineParser{Pipeline: []byte(pipeline)}.Parse()
	if err == nil {
		t.Fatalf("Expected $LLAMAS_ROCK: not set")
	}

	os.Setenv("LLAMAS_ROCK", "absolutely")
	defer os.Unsetenv("LLAMAS_ROCK")

	result2, err := PipelineParser{Pipeline: []byte(pipeline)}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result2)
	if err != nil {
		t.Fatal(err)
	}

	if decoded.Steps[0].Command != "echo absolutely" {
		t.Fatalf("Unexpected: %q", decoded.Steps[0].Command)
	}
}
<commit_msg>Use assert not require<commit_after>package agent

import (
	"encoding/json"
	"fmt"
	"os"
	"testing"

	"github.com/buildkite/agent/env"
	"github.com/stretchr/testify/assert"
)

func TestPipelineParserParsesYaml(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{
		Filename: "awesome.yml",
		Pipeline: []byte("steps:\n  - label: \"hello ${ENV_VAR_FRIEND}\""),
		Env:      environ}.Parse()

	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"label":"hello \"friend\""}]}`, string(j))
}

func TestPipelineParserSupportsYamlMergesAndAnchors(t *testing.T) {
	complexYAML := `---
base_step: &base_step
  type: script
  agent_query_rules:
    - queue=default

steps:
  - <<: *base_step
    name: ':docker: building image'
    command: docker build .
    agents:
      queue: default`

	result, err := PipelineParser{
		Filename: "awesome.yml",
		Pipeline: []byte(complexYAML)}.Parse()

	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"base_step":{"agent_query_rules":["queue=default"],"type":"script"},"steps":[{"agent_query_rules":["queue=default"],"agents":{"queue":"default"},"command":"docker build .","name":":docker: building image","type":"script"}]}`, string(j))
}

func TestPipelineParserReturnsYamlParsingErrors(t *testing.T) {
	_, err := PipelineParser{Filename: "awesome.yml", Pipeline: []byte("steps: %blah%")}.Parse()
	assert.Error(t, err, `Failed to parse awesome.yml: found character that cannot start any token`, fmt.Sprintf("%s", err))
}

func TestPipelineParserReturnsJsonParsingErrors(t *testing.T) {
	_, err := PipelineParser{Filename: "awesome.json", Pipeline: []byte("{")}.Parse()
	assert.Error(t, err, `Failed to parse awesome.json: line 1: did not find expected node content`, fmt.Sprintf("%s", err))
}

func TestPipelineParserParsesJson(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{
		Filename: "thing.json",
		Pipeline: []byte("\n\n     \n  { \"foo\": \"bye ${ENV_VAR_FRIEND}\" }\n"),
		Env:      environ}.Parse()

	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"foo":"bye \"friend\""}`, string(j))
}

func TestPipelineParserParsesJsonObjects(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{Pipeline: []byte("\n\n     \n  { \"foo\": \"bye ${ENV_VAR_FRIEND}\" }\n"), Env: environ}.Parse()
	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"foo":"bye \"friend\""}`, string(j))
}

func TestPipelineParserParsesJsonArrays(t *testing.T) {
	environ := env.FromSlice([]string{`ENV_VAR_FRIEND="friend"`})

	result, err := PipelineParser{Pipeline: []byte("\n\n     \n  [ { \"foo\": \"bye ${ENV_VAR_FRIEND}\" } ]\n"), Env: environ}.Parse()
	assert.NoError(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `[{"foo":"bye \"friend\""}]`, string(j))
}

func TestPipelineParserPreservesBools(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    async: true")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"async":true,"trigger":"hello"}]}`, string(j))
}

func TestPipelineParserPreservesInts(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - label: hello\n    parallelism: 10")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"label":"hello","parallelism":10}]}`, string(j))
}

func TestPipelineParserPreservesNull(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - wait: ~")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"wait":null}]}`, string(j))
}

func TestPipelineParserPreservesFloats(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    llamas: 3.142")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"llamas":3.142,"trigger":"hello"}]}`, string(j))
}

func TestPipelineParserHandlesDates(t *testing.T) {
	result, err := PipelineParser{Pipeline: []byte("steps:\n  - trigger: hello\n    llamas: 2002-08-15T17:18:23.18-06:00")}.Parse()
	assert.Nil(t, err)
	j, err := json.Marshal(result)
	assert.Equal(t, `{"steps":[{"llamas":"2002-08-15T17:18:23.18-06:00","trigger":"hello"}]}`, string(j))
}

func TestPipelineParserInterpolatesKeysAsWellAsValues(t *testing.T) {
	var pipeline = `{
		"env": {
			"${FROM_ENV}TEST1": "MyTest",
			"TEST2": "${FROM_ENV}"
		}
	}`

	var decoded struct {
		Env map[string]string `json:"env"`
	}

	environ := env.FromSlice([]string{`FROM_ENV=llamas`})

	result, err := PipelineParser{Pipeline: []byte(pipeline), Env: environ}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, `MyTest`, decoded.Env["llamasTEST1"])
	assert.Equal(t, `llamas`, decoded.Env["TEST2"])
}

func TestPipelineParserLoadsGlobalEnvBlockFirst(t *testing.T) {
	var pipeline = `{
		"env": {
			"TEAM1": "England",
			"TEAM2": "Australia",
			"HEADLINE": "${TEAM1} smashes ${TEAM2} to win the ashes in ${YEAR_FROM_SHELL}!!"
		},
		"steps": [{
			"command": "echo ${HEADLINE}"
		}]
	}`

	var decoded struct {
		Env   map[string]string `json:"env"`
		Steps []struct {
			Command string `json:"command"`
		} `json:"steps"`
	}

	environ := env.FromSlice([]string{`YEAR_FROM_SHELL=1912`})

	result, err := PipelineParser{Pipeline: []byte(pipeline), Env: environ}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, `England`, decoded.Env["TEAM1"])
	assert.Equal(t, `England smashes Australia to win the ashes in 1912!!`, decoded.Env["HEADLINE"])
	assert.Equal(t, `echo England smashes Australia to win the ashes in 1912!!`, decoded.Steps[0].Command)
}

func decodeIntoStruct(into interface{}, from interface{}) error {
	b, err := json.Marshal(from)
	if err != nil {
		return err
	}
	return json.Unmarshal(b, into)
}

func TestPipelineParserLoadsSystemEnvironment(t *testing.T) {
	var pipeline = `{
		"steps": [{
			"command": "echo ${LLAMAS_ROCK?}"
		}]
	}`

	var decoded struct {
		Steps []struct {
			Command string `json:"command"`
		} `json:"steps"`
	}

	_, err := PipelineParser{Pipeline: []byte(pipeline)}.Parse()
	if err == nil {
		t.Fatalf("Expected $LLAMAS_ROCK: not set")
	}

	os.Setenv("LLAMAS_ROCK", "absolutely")
	defer os.Unsetenv("LLAMAS_ROCK")

	result2, err := PipelineParser{Pipeline: []byte(pipeline)}.Parse()
	if err != nil {
		t.Fatal(err)
	}

	err = decodeIntoStruct(&decoded, result2)
	if err != nil {
		t.Fatal(err)
	}

	if decoded.Steps[0].Command != "echo absolutely" {
		t.Fatalf("Unexpected: %q", decoded.Steps[0].Command)
	}
}
<|endoftext|>


<commit_before>package intercept_parse

import (
    "encoding/hex"
    "io"
    "log"
    "os"

    "chunkymonkey/proto"
    .   "chunkymonkey/types"
)

// Hex dumps the input to the log
func (p *MessageParser) dumpInput(logPrefix string, reader io.Reader) {
    buf := make([]byte, 16, 16)
    for {
        _, err := io.ReadAtLeast(reader, buf, 1)
        if err != nil {
            return
        }

        hexData := hex.EncodeToString(buf)
        p.printf("Unparsed data: %s", hexData)
    }
}

// Consumes data from reader until an error occurs
func (p *MessageParser) consumeUnrecognizedInput(reader io.Reader) {
    p.printf("Lost packet sync. Ignoring further data.")
    buf := make([]byte, 4096)
    for {
        _, err := io.ReadAtLeast(reader, buf, 1)
        if err != nil {
            return
        }
    }
}

type MessageParser struct {
    logPrefix string
}

func (p *MessageParser) printf(format string, v ...interface{}) {
    log.Printf(p.logPrefix+format, v...)
}

func (p *MessageParser) PacketKeepAlive() {
    // Not logging this packet as it's a bit spammy
}

func (p *MessageParser) PacketChatMessage(message string) {
    p.printf("PacketChatMessage(%s)", message)
}

func (p *MessageParser) PacketRespawn() {
    p.printf("PacketRespawn()")
}

func (p *MessageParser) PacketPlayer(onGround bool) {
    // Not logging this packet as it's a bit spammy
}

func (p *MessageParser) PacketPlayerPosition(position *AbsXyz, stance AbsCoord, onGround bool) {
    p.printf("PacketPlayerPosition(position=%v, stance=%v, onGround=%v)", position, stance, onGround)
}

func (p *MessageParser) PacketPlayerLook(look *LookDegrees, onGround bool) {
    p.printf("PacketPlayerLook(look=%v, onGround=%v)", look, onGround)
}

func (p *MessageParser) PacketPlayerDigging(status DigStatus, blockLoc *BlockXyz, face Face) {
    p.printf("PacketPlayerDigging(status=%v, blockLoc=%v, face=%v)", status, blockLoc, face)
}

func (p *MessageParser) PacketPlayerBlockPlacement(itemId ItemId, blockLoc *BlockXyz, face Face, amount ItemCount, uses ItemUses) {
    p.printf("PacketPlayerBlockPlacement(itemId=%d, blockLoc=%v, face=%d, amount=%d, uses=%d)",
        itemId, blockLoc, face, amount, uses)
}

func (p *MessageParser) PacketHoldingChange(slotId SlotId) {
    p.printf("PacketHoldingChange(slotId=%d)", slotId)
}

func (p *MessageParser) PacketBedUse(flag bool, bedLoc *BlockXyz) {
    p.printf("PacketBedUse(flag=%v, bedLoc=%v)", flag, bedLoc)
}

func (p *MessageParser) PacketEntityAnimation(entityId EntityId, animation EntityAnimation) {
    p.printf("PacketEntityAnimation(entityId=%d, animation=%v)", entityId, animation)
}

func (p *MessageParser) PacketEntityAction(entityId EntityId, action EntityAction) {
    p.printf("PacketEntityAction(entityId=%d, action=%d)",
        entityId, action)
}

func (p *MessageParser) PacketSignUpdate(position *BlockXyz, lines [4]string) {
    p.printf("PacketSignUpdate(position=%v, lines=[%q, %q, %q, %q])",
        position,
        lines[0], lines[1], lines[2], lines[3])
}

func (p *MessageParser) PacketDisconnect(reason string) {
    p.printf("PacketDisconnect(%s)", reason)
}

func (p *MessageParser) ClientPacketLogin(entityId EntityId, mapSeed RandomSeed, dimension DimensionId) {
    p.printf("PacketLogin(entityId=%d, mapSeed=%d, dimension=%d)",
        entityId, mapSeed, dimension)
}

func (p *MessageParser) PacketTimeUpdate(time TimeOfDay) {
    p.printf("PacketTime(time=%d)", time)
}

func (p *MessageParser) PacketEntityEquipment(entityId EntityId, slot SlotId, itemId ItemId, uses ItemUses) {
    p.printf("PacketEntityEquipment(entityId=%d, slot=%d, itemId=%d, uses=%d)",
        entityId, slot, itemId, uses)
}

func (p *MessageParser) PacketSpawnPosition(position *BlockXyz) {
    p.printf("PacketSpawnPosition(position=%v)", position)
}

func (p *MessageParser) PacketUseEntity(user EntityId, target EntityId, leftClick bool) {
    p.printf("PacketUseEntity(user=%d, target=%d, leftClick=%v)", user, target, leftClick)
}

func (p *MessageParser) PacketUpdateHealth(health int16) {
    p.printf("PacketUpdateHealth(health=%d)", health)
}

func (p *MessageParser) PacketNamedEntitySpawn(entityId EntityId, name string, position *AbsIntXyz, look *LookBytes, currentItem ItemId) {
    p.printf("PacketNamedEntitySpawn(entityId=%d, name=%q, position=%v, look=%v, currentItem=%d)",
        entityId, name, position, look, currentItem)
}

func (p *MessageParser) PacketItemSpawn(entityId EntityId, itemId ItemId, count ItemCount, uses ItemUses, location *AbsIntXyz, orientation *OrientationBytes) {
    p.printf("PacketItemSpawn(entityId=%d, itemId=%d, count=%d, uses=%d, location=%v, orientation=%v)",
        entityId, itemId, count, uses, location, orientation)
}

func (p *MessageParser) PacketItemCollect(collectedItem EntityId, collector EntityId) {
    p.printf("PacketItemCollect(collectedItem=%d, collector=%d)",
        collectedItem, collector)
}

func (p *MessageParser) PacketObjectSpawn(entityId EntityId, objType ObjTypeId, position *AbsIntXyz) {
    p.printf("PacketObjectSpawn(entityId=%d, objType=%d, position=%v)",
        entityId, objType, position)
}

func (p *MessageParser) PacketEntitySpawn(entityId EntityId, mobType EntityMobType, position *AbsIntXyz, look *LookBytes, metadata []proto.EntityMetadata) {
    p.printf("PacketEntitySpawn(entityId=%d, mobType=%d, position=%v, look=%v, metadata=%v)",
        entityId, mobType, position, look, metadata)
}

func (p *MessageParser) PacketPaintingSpawn(entityId EntityId, title string, position *BlockXyz, paintingType PaintingTypeId) {
    p.printf("PacketPaintingSpawn(entityId=%d, title=%s, position=%v, paintingType=%d)",
        entityId, title, position, position, paintingType)
}

func (p *MessageParser) PacketUnknown0x1b(field1, field2, field3, field4 float32, field5, field6 bool) {
    p.printf(
        "PacketUnknown0x1b(field1=%v, field2=%v, field3=%v, field4=%v, field5=%v, field6=%v)",
        field1, field2, field3, field4, field5, field6)

}

func (p *MessageParser) PacketEntityVelocity(entityId EntityId, velocity *Velocity) {
    p.printf("PacketEntityVelocity(entityId=%d, velocity=%v)",
        entityId, velocity)
}

func (p *MessageParser) PacketEntityDestroy(entityId EntityId) {
    p.printf("PacketEntityDestroy(entityId=%d)", entityId)
}

func (p *MessageParser) PacketEntity(entityId EntityId) {
    p.printf("PacketEntity(entityId=%d)", entityId)
}

func (p *MessageParser) PacketEntityRelMove(entityId EntityId, movement *RelMove) {
    p.printf("PacketEntityRelMove(entityId=%d, movement=%v)",
        entityId, movement)
}

func (p *MessageParser) PacketEntityLook(entityId EntityId, look *LookBytes) {
    p.printf("PacketEntityLook(entityId=%d, look=%v)",
        entityId, look)
}

func (p *MessageParser) PacketEntityTeleport(entityId EntityId, position *AbsIntXyz, look *LookBytes) {
    p.printf("PacketEntityTeleport(entityId=%d, position=%v, look=%v",
        entityId, position, look)
}

func (p *MessageParser) PacketEntityStatus(entityId EntityId, status EntityStatus) {
    p.printf("PacketEntityStatus(entityId=%d, status=%d",
        entityId, status)
}

func (p *MessageParser) PacketEntityMetadata(entityId EntityId, metadata []proto.EntityMetadata) {
    p.printf("PacketEntityMetadata(entityId=%d, metadata=%v)", entityId, metadata)
}

func (p *MessageParser) PacketPreChunk(position *ChunkXz, mode ChunkLoadMode) {
    p.printf("PacketPreChunk(position=%v, mode=%d)", position, mode)
}

func (p *MessageParser) PacketMapChunk(position *BlockXyz, size *SubChunkSize, data []byte) {
    p.printf("PacketMapChunk(position=%v, size=%v, len(data)=%d)",
        position, size, len(data))
}

func (p *MessageParser) PacketBlockChangeMulti(chunkLoc *ChunkXz, blockCoords []SubChunkXyz, blockTypes []BlockId, blockMetaData []byte) {
    p.printf("PacketBlockChangeMulti(chunkLoc=%v, blockCoords=%v, blockTypes=%v, blockMetaData=%v)",
        chunkLoc, blockCoords, blockTypes, blockMetaData)
}

func (p *MessageParser) PacketBlockChange(blockLoc *BlockXyz, blockType BlockId, blockMetaData byte) {
    p.printf("PacketBlockChange(blockLoc=%v, blockType=%d, blockMetaData=%d)",
        blockLoc, blockType, blockMetaData)
}

func (p *MessageParser) PacketNoteBlockPlay(position *BlockXyz, instrument InstrumentId, pitch NotePitch) {
    p.printf("PacketNoteBlockPlay(position=%v, instrument=%d, pitch=%d)",
        position, instrument, pitch)
}

func (p *MessageParser) PacketExplosion(position *AbsXyz, power float32, blockOffsets []proto.ExplosionOffsetXyz) {
    p.printf("PacketExplosion(position=%v, power=%f, blockOffsets=%v)",
        position, power, blockOffsets)
}

func (p *MessageParser) PacketBedInvalid(field1 byte) {
    p.printf("PacketBedInvalid(field1=%t)", field1)
}

func (p *MessageParser) PacketWindowOpen(windowId WindowId, invTypeId InvTypeId, windowTitle string, numSlots byte) {
    p.printf("PacketWindowOpen(windowId=%d, invTypeId=%d, windowTitle=%q, numSlots=%d)",
        windowId, invTypeId, windowTitle, numSlots)
}

func (p *MessageParser) PacketWindowClose(windowId WindowId) {
    p.printf("PacketWindowClose(windowId=%d)", windowId)
}

func (p *MessageParser) PacketWindowProgressBar(windowId WindowId, prgBarId PrgBarId, value PrgBarValue) {
    p.printf("PacketWindowProgressBar(windowId=%d, prgBarId=%d, value=%d)",
        windowId, prgBarId, value)
}

func (p *MessageParser) PacketWindowTransaction(windowId WindowId, txId TxId, accepted bool) {
    p.printf("PacketWindowTransaction(windowId=%d, txId=%d, accepted=%v)")
}

func (p *MessageParser) PacketWindowClick(windowId WindowId, slot SlotId, rightClick bool, txId TxId, itemId ItemId, amount ItemCount, uses ItemUses) {
    p.printf("PacketWindowClick(windowId=%d, slot=%d, rightClick=%v, txId=%d, itemId=%d, amount=%d, uses=%d)",
        windowId, slot, rightClick, txId, itemId, amount, uses)
}

func (p *MessageParser) PacketWindowSetSlot(windowId WindowId, slot SlotId, itemId ItemId, amount ItemCount, uses ItemUses) {
    p.printf("PacketWindowSetSlot(windowId=%d, slot=%d, itemId=%d, amount=%d, uses=%d)",
        windowId, slot, itemId, amount, uses)
}

func (p *MessageParser) PacketWindowItems(windowId WindowId, items []proto.WindowSlot) {
    p.printf("PacketWindowItems(windowId=%d, items=%v)",
        windowId, items)
}

// Parses messages from the client
func (p *MessageParser) CsParse(reader io.Reader) {
    // If we return, we should consume all input to avoid blocking the pipe
    // we're listening on. TODO Maybe we could just close it?
    defer p.consumeUnrecognizedInput(reader)

    defer func() {
        if err := recover(); err != nil {
            p.printf("Parsing failed: %v", err)
        }
    }()

    p.logPrefix = "(C->S) "

    username, err := proto.ServerReadHandshake(reader)
    if err != nil {
        p.printf("ServerReadHandshake error: %v", err)
        return
    }
    p.printf("ServerReadHandshake(username=%v)", username)

    loginUsername, _, err := proto.ServerReadLogin(reader)
    if err != nil {
        p.printf("ServerReadLogin error: %v", err)
        return
    }
    p.printf("ServerReadLogin(username=%v)", loginUsername)

    for {
        err := proto.ServerReadPacket(reader, p)
        if err != nil {
            if err != os.EOF {
                p.printf("ReceiveLoop failed: %v", err)
            } else {
                p.printf("ReceiveLoop hit EOF")
            }
            return
        }
    }
}

// Parses messages from the server
func (p *MessageParser) ScParse(reader io.Reader) {
    // If we return, we should consume all input to avoid blocking the pipe
    // we're listening on. TODO Maybe we could just close it?
    defer p.consumeUnrecognizedInput(reader)

    defer func() {
        if err := recover(); err != nil {
            p.printf("Parsing failed: %v", err)
        }
    }()

    p.logPrefix = "(S->C) "

    serverId, err := proto.ClientReadHandshake(reader)
    if err != nil {
        p.printf("ClientReadHandshake error: %v", err)
        return
    }
    p.printf("ClientReadHandshake(serverId=%v)", serverId)

    for {
        err := proto.ClientReadPacket(reader, p)
        if err != nil {
            if err != os.EOF {
                p.printf("ReceiveLoop failed: %v", err)
            } else {
                p.printf("ReceiveLoop hit EOF")
            }
            return
        }
    }
}
<commit_msg>Fixed intercept parser logging a little.<commit_after>package intercept_parse

import (
    "encoding/hex"
    "io"
    "log"
    "os"

    "chunkymonkey/proto"
    .   "chunkymonkey/types"
)

// Hex dumps the input to the log
func (p *MessageParser) dumpInput(logPrefix string, reader io.Reader) {
    buf := make([]byte, 16, 16)
    for {
        _, err := io.ReadAtLeast(reader, buf, 1)
        if err != nil {
            return
        }

        hexData := hex.EncodeToString(buf)
        p.printf("Unparsed data: %s", hexData)
    }
}

// Consumes data from reader until an error occurs
func (p *MessageParser) consumeUnrecognizedInput(reader io.Reader) {
    p.printf("Lost packet sync. Ignoring further data.")
    buf := make([]byte, 4096)
    for {
        _, err := io.ReadAtLeast(reader, buf, 1)
        if err != nil {
            return
        }
    }
}

type MessageParser struct {
    logPrefix string
}

func (p *MessageParser) printf(format string, v ...interface{}) {
    log.Printf(p.logPrefix+format, v...)
}

func (p *MessageParser) PacketKeepAlive() {
    // Not logging this packet as it's a bit spammy
}

func (p *MessageParser) PacketChatMessage(message string) {
    p.printf("PacketChatMessage(%q)", message)
}

func (p *MessageParser) PacketRespawn() {
    p.printf("PacketRespawn()")
}

func (p *MessageParser) PacketPlayer(onGround bool) {
    // Not logging this packet as it's a bit spammy
}

func (p *MessageParser) PacketPlayerPosition(position *AbsXyz, stance AbsCoord, onGround bool) {
    p.printf("PacketPlayerPosition(position=%v, stance=%v, onGround=%t)", position, stance, onGround)
}

func (p *MessageParser) PacketPlayerLook(look *LookDegrees, onGround bool) {
    p.printf("PacketPlayerLook(look=%v, onGround=%t)", look, onGround)
}

func (p *MessageParser) PacketPlayerDigging(status DigStatus, blockLoc *BlockXyz, face Face) {
    p.printf("PacketPlayerDigging(status=%v, blockLoc=%v, face=%v)", status, blockLoc, face)
}

func (p *MessageParser) PacketPlayerBlockPlacement(itemId ItemId, blockLoc *BlockXyz, face Face, amount ItemCount, uses ItemUses) {
    p.printf("PacketPlayerBlockPlacement(itemId=%d, blockLoc=%v, face=%d, amount=%d, uses=%d)",
        itemId, blockLoc, face, amount, uses)
}

func (p *MessageParser) PacketHoldingChange(slotId SlotId) {
    p.printf("PacketHoldingChange(slotId=%d)", slotId)
}

func (p *MessageParser) PacketBedUse(flag bool, bedLoc *BlockXyz) {
    p.printf("PacketBedUse(flag=%v, bedLoc=%v)", flag, bedLoc)
}

func (p *MessageParser) PacketEntityAnimation(entityId EntityId, animation EntityAnimation) {
    p.printf("PacketEntityAnimation(entityId=%d, animation=%v)", entityId, animation)
}

func (p *MessageParser) PacketEntityAction(entityId EntityId, action EntityAction) {
    p.printf("PacketEntityAction(entityId=%d, action=%d)",
        entityId, action)
}

func (p *MessageParser) PacketSignUpdate(position *BlockXyz, lines [4]string) {
    p.printf("PacketSignUpdate(position=%v, lines=[%q, %q, %q, %q])",
        position,
        lines[0], lines[1], lines[2], lines[3])
}

func (p *MessageParser) PacketDisconnect(reason string) {
    p.printf("PacketDisconnect(%q)", reason)
}

func (p *MessageParser) ClientPacketLogin(entityId EntityId, mapSeed RandomSeed, dimension DimensionId) {
    p.printf("PacketLogin(entityId=%d, mapSeed=%d, dimension=%d)",
        entityId, mapSeed, dimension)
}

func (p *MessageParser) PacketTimeUpdate(time TimeOfDay) {
    p.printf("PacketTime(time=%d)", time)
}

func (p *MessageParser) PacketEntityEquipment(entityId EntityId, slot SlotId, itemId ItemId, uses ItemUses) {
    p.printf("PacketEntityEquipment(entityId=%d, slot=%d, itemId=%d, uses=%d)",
        entityId, slot, itemId, uses)
}

func (p *MessageParser) PacketSpawnPosition(position *BlockXyz) {
    p.printf("PacketSpawnPosition(position=%v)", position)
}

func (p *MessageParser) PacketUseEntity(user EntityId, target EntityId, leftClick bool) {
    p.printf("PacketUseEntity(user=%d, target=%d, leftClick=%t)", user, target, leftClick)
}

func (p *MessageParser) PacketUpdateHealth(health int16) {
    p.printf("PacketUpdateHealth(health=%d)", health)
}

func (p *MessageParser) PacketNamedEntitySpawn(entityId EntityId, name string, position *AbsIntXyz, look *LookBytes, currentItem ItemId) {
    p.printf("PacketNamedEntitySpawn(entityId=%d, name=%q, position=%v, look=%v, currentItem=%d)",
        entityId, name, position, look, currentItem)
}

func (p *MessageParser) PacketItemSpawn(entityId EntityId, itemId ItemId, count ItemCount, uses ItemUses, location *AbsIntXyz, orientation *OrientationBytes) {
    p.printf("PacketItemSpawn(entityId=%d, itemId=%d, count=%d, uses=%d, location=%v, orientation=%v)",
        entityId, itemId, count, uses, location, orientation)
}

func (p *MessageParser) PacketItemCollect(collectedItem EntityId, collector EntityId) {
    p.printf("PacketItemCollect(collectedItem=%d, collector=%d)",
        collectedItem, collector)
}

func (p *MessageParser) PacketObjectSpawn(entityId EntityId, objType ObjTypeId, position *AbsIntXyz) {
    p.printf("PacketObjectSpawn(entityId=%d, objType=%d, position=%v)",
        entityId, objType, position)
}

func (p *MessageParser) PacketEntitySpawn(entityId EntityId, mobType EntityMobType, position *AbsIntXyz, look *LookBytes, metadata []proto.EntityMetadata) {
    p.printf("PacketEntitySpawn(entityId=%d, mobType=%d, position=%v, look=%v, metadata=%v)",
        entityId, mobType, position, look, metadata)
}

func (p *MessageParser) PacketPaintingSpawn(entityId EntityId, title string, position *BlockXyz, paintingType PaintingTypeId) {
    p.printf("PacketPaintingSpawn(entityId=%d, title=%q, position=%v, paintingType=%d)",
        entityId, title, position, position, paintingType)
}

func (p *MessageParser) PacketUnknown0x1b(field1, field2, field3, field4 float32, field5, field6 bool) {
    p.printf(
        "PacketUnknown0x1b(field1=%v, field2=%v, field3=%v, field4=%v, field5=%v, field6=%v)",
        field1, field2, field3, field4, field5, field6)
}

func (p *MessageParser) PacketEntityVelocity(entityId EntityId, velocity *Velocity) {
    p.printf("PacketEntityVelocity(entityId=%d, velocity=%v)",
        entityId, velocity)
}

func (p *MessageParser) PacketEntityDestroy(entityId EntityId) {
    p.printf("PacketEntityDestroy(entityId=%d)", entityId)
}

func (p *MessageParser) PacketEntity(entityId EntityId) {
    p.printf("PacketEntity(entityId=%d)", entityId)
}

func (p *MessageParser) PacketEntityRelMove(entityId EntityId, movement *RelMove) {
    p.printf("PacketEntityRelMove(entityId=%d, movement=%v)",
        entityId, movement)
}

func (p *MessageParser) PacketEntityLook(entityId EntityId, look *LookBytes) {
    p.printf("PacketEntityLook(entityId=%d, look=%v)",
        entityId, look)
}

func (p *MessageParser) PacketEntityTeleport(entityId EntityId, position *AbsIntXyz, look *LookBytes) {
    p.printf("PacketEntityTeleport(entityId=%d, position=%v, look=%v",
        entityId, position, look)
}

func (p *MessageParser) PacketEntityStatus(entityId EntityId, status EntityStatus) {
    p.printf("PacketEntityStatus(entityId=%d, status=%d",
        entityId, status)
}

func (p *MessageParser) PacketEntityMetadata(entityId EntityId, metadata []proto.EntityMetadata) {
    p.printf("PacketEntityMetadata(entityId=%d, metadata=%v)", entityId, metadata)
}

func (p *MessageParser) PacketPreChunk(position *ChunkXz, mode ChunkLoadMode) {
    p.printf("PacketPreChunk(position=%v, mode=%d)", position, mode)
}

func (p *MessageParser) PacketMapChunk(position *BlockXyz, size *SubChunkSize, data []byte) {
    p.printf("PacketMapChunk(position=%v, size=%v, len(data)=%d)",
        position, size, len(data))
}

func (p *MessageParser) PacketBlockChangeMulti(chunkLoc *ChunkXz, blockCoords []SubChunkXyz, blockTypes []BlockId, blockMetaData []byte) {
    p.printf("PacketBlockChangeMulti(chunkLoc=%v, blockCoords=%v, blockTypes=%v, blockMetaData=%v)",
        chunkLoc, blockCoords, blockTypes, blockMetaData)
}

func (p *MessageParser) PacketBlockChange(blockLoc *BlockXyz, blockType BlockId, blockMetaData byte) {
    p.printf("PacketBlockChange(blockLoc=%v, blockType=%d, blockMetaData=%d)",
        blockLoc, blockType, blockMetaData)
}

func (p *MessageParser) PacketNoteBlockPlay(position *BlockXyz, instrument InstrumentId, pitch NotePitch) {
    p.printf("PacketNoteBlockPlay(position=%v, instrument=%d, pitch=%d)",
        position, instrument, pitch)
}

func (p *MessageParser) PacketExplosion(position *AbsXyz, power float32, blockOffsets []proto.ExplosionOffsetXyz) {
    p.printf("PacketExplosion(position=%v, power=%f, blockOffsets=%v)",
        position, power, blockOffsets)
}

func (p *MessageParser) PacketBedInvalid(field1 byte) {
    p.printf("PacketBedInvalid(field1=%t)", field1)
}

func (p *MessageParser) PacketWindowOpen(windowId WindowId, invTypeId InvTypeId, windowTitle string, numSlots byte) {
    p.printf("PacketWindowOpen(windowId=%d, invTypeId=%d, windowTitle=%q, numSlots=%d)",
        windowId, invTypeId, windowTitle, numSlots)
}

func (p *MessageParser) PacketWindowClose(windowId WindowId) {
    p.printf("PacketWindowClose(windowId=%d)", windowId)
}

func (p *MessageParser) PacketWindowProgressBar(windowId WindowId, prgBarId PrgBarId, value PrgBarValue) {
    p.printf("PacketWindowProgressBar(windowId=%d, prgBarId=%d, value=%d)",
        windowId, prgBarId, value)
}

func (p *MessageParser) PacketWindowTransaction(windowId WindowId, txId TxId, accepted bool) {
    p.printf("PacketWindowTransaction(windowId=%d, txId=%d, accepted=%t)",
        windowId, txId, accepted)
}

func (p *MessageParser) PacketWindowClick(windowId WindowId, slot SlotId, rightClick bool, txId TxId, itemId ItemId, amount ItemCount, uses ItemUses) {
    p.printf("PacketWindowClick(windowId=%d, slot=%d, rightClick=%t, txId=%d, itemId=%d, amount=%d, uses=%d)",
        windowId, slot, rightClick, txId, itemId, amount, uses)
}

func (p *MessageParser) PacketWindowSetSlot(windowId WindowId, slot SlotId, itemId ItemId, amount ItemCount, uses ItemUses) {
    p.printf("PacketWindowSetSlot(windowId=%d, slot=%d, itemId=%d, amount=%d, uses=%d)",
        windowId, slot, itemId, amount, uses)
}

func (p *MessageParser) PacketWindowItems(windowId WindowId, items []proto.WindowSlot) {
    p.printf("PacketWindowItems(windowId=%d, items=%v)",
        windowId, items)
}

// Parses messages from the client
func (p *MessageParser) CsParse(reader io.Reader) {
    // If we return, we should consume all input to avoid blocking the pipe
    // we're listening on. TODO Maybe we could just close it?
    defer p.consumeUnrecognizedInput(reader)

    defer func() {
        if err := recover(); err != nil {
            p.printf("Parsing failed: %v", err)
        }
    }()

    p.logPrefix = "(C->S) "

    username, err := proto.ServerReadHandshake(reader)
    if err != nil {
        p.printf("ServerReadHandshake error: %v", err)
        return
    }
    p.printf("ServerReadHandshake(username=%v)", username)

    loginUsername, _, err := proto.ServerReadLogin(reader)
    if err != nil {
        p.printf("ServerReadLogin error: %v", err)
        return
    }
    p.printf("ServerReadLogin(username=%v)", loginUsername)

    for {
        err := proto.ServerReadPacket(reader, p)
        if err != nil {
            if err != os.EOF {
                p.printf("ReceiveLoop failed: %v", err)
            } else {
                p.printf("ReceiveLoop hit EOF")
            }
            return
        }
    }
}

// Parses messages from the server
func (p *MessageParser) ScParse(reader io.Reader) {
    // If we return, we should consume all input to avoid blocking the pipe
    // we're listening on. TODO Maybe we could just close it?
    defer p.consumeUnrecognizedInput(reader)

    defer func() {
        if err := recover(); err != nil {
            p.printf("Parsing failed: %v", err)
        }
    }()

    p.logPrefix = "(S->C) "

    serverId, err := proto.ClientReadHandshake(reader)
    if err != nil {
        p.printf("ClientReadHandshake error: %v", err)
        return
    }
    p.printf("ClientReadHandshake(serverId=%v)", serverId)

    for {
        err := proto.ClientReadPacket(reader, p)
        if err != nil {
            if err != os.EOF {
                p.printf("ReceiveLoop failed: %v", err)
            } else {
                p.printf("ReceiveLoop hit EOF")
            }
            return
        }
    }
}
<|endoftext|>


<commit_before>package chatwork

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const baseURL = "https://api.chatwork.com/v2/"

type ApiKey string

type Chatwork struct {
	apiKey ApiKey
}

func NewChatwork(apiKey string) *Chatwork {
	c := new(Chatwork)
	c.apiKey = ApiKey(apiKey)
	return c
}

type endpoint string

func (c *Chatwork) post(endpoint endpoint, vs *url.Values) *http.Response {
	reqBody := strings.NewReader(vs.Encode())
	request, requestError := http.NewRequest("POST", string(endpoint), reqBody)
	if requestError != nil {
		log.Fatal(requestError)
	}

	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	request.Header.Add("X-ChatWorkToken", string(c.apiKey))

	httpClient := new(http.Client)
	res, error := httpClient.Do(request)
	if error != nil {
		log.Fatal(error)
	}
	return res
}

func decodeBody(res *http.Response, out interface{}) error {
	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	return decoder.Decode(out)
}

type Text string
type RoomId int64

type CreateMessageRequest struct {
	roomId RoomId
	body   Text
}

func NewCreateMessageRequest(roomId int64, body string) *CreateMessageRequest {
	m := new(CreateMessageRequest)
	m.roomId = RoomId(roomId)
	m.body = Text(body)
	return m
}

func (m *CreateMessageRequest) values() *url.Values {
	vs := url.Values{}
	vs.Add("body", string(m.body))
	return &vs
}

type CreateMessageResponse struct {
	MessageId string `json:"message_id"`
}

func endpointFmt(format string, a ...interface{}) string {
	return fmt.Sprintf(format, a)
}

func (c *Chatwork) CreateMessage(req *CreateMessageRequest) *CreateMessageResponse {
	endpoint := endpoint(baseURL + fmt.Sprintf("rooms/%d/messages", req.roomId))
	httpRes := c.post(endpoint, req.values())

	var res CreateMessageResponse
	if err := decodeBody(httpRes, &res); err != nil {
		log.Fatal(err)
	}
	return &res
}

type UserId int64
type UserIds []UserId

type CreateTaskRequest struct {
	roomId    RoomId
	body      Text
	assignees UserIds
	due       *time.Time
}

func (t *CreateTaskRequest) values() *url.Values {
	vs := url.Values{}
	vs.Add("body", string(t.body))
	vs.Add("to_ids", t.assignees.toString(","))
	if t.due != nil {
		vs.Add("limit", strconv.FormatInt(t.due.Unix(), 10))
	}
	return &vs
}

type CreateTaskResponse struct {
	TaskIds []int64 `json:"task_ids"`
}

func NewCreateTaskRequest(roomId int64, body string, assignees []int64, due *time.Time) *CreateTaskRequest {
	t := new(CreateTaskRequest)
	t.roomId = RoomId(roomId)
	t.body = Text(body)
	t.assignees = make([]UserId, len(assignees))
	for i, a := range assignees {
		t.assignees[i] = UserId(a)
	}
	t.due = due
	return t
}

func (c *Chatwork) CreateTask(req *CreateTaskRequest) *CreateTaskResponse {
	endpoint := endpoint(baseURL + fmt.Sprintf("rooms/%d/tasks", req.roomId))
	httpRes := c.post(endpoint, req.values())

	var res CreateTaskResponse
	if err := decodeBody(httpRes, &res); err != nil {
		log.Fatal(err)
	}
	return &res
}

func (ids UserIds) toString(sep string) string {
	buf := make([]string, len(ids))
	for i, id := range ids {
		buf[i] = strconv.FormatInt(int64(id), 10)
	}
	return strings.Join(buf, sep)
}
<commit_msg>Add new interface chatworkRequest<commit_after>package chatwork

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const baseURL = "https://api.chatwork.com/v2/"

type ApiKey string

type Chatwork struct {
	apiKey ApiKey
}

func NewChatwork(apiKey string) *Chatwork {
	c := new(Chatwork)
	c.apiKey = ApiKey(apiKey)
	return c
}

type endpoint string

type chatworkRequest interface {
	values() *url.Values
}

func (c *Chatwork) post(endpoint endpoint, req chatworkRequest) *http.Response {
	vs := req.values()
	reqBody := strings.NewReader(vs.Encode())
	request, requestError := http.NewRequest("POST", string(endpoint), reqBody)
	if requestError != nil {
		log.Fatal(requestError)
	}

	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	request.Header.Add("X-ChatWorkToken", string(c.apiKey))

	httpClient := new(http.Client)
	res, error := httpClient.Do(request)
	if error != nil {
		log.Fatal(error)
	}
	return res
}

func decodeBody(res *http.Response, out interface{}) error {
	defer res.Body.Close()
	decoder := json.NewDecoder(res.Body)
	return decoder.Decode(out)
}

type Text string
type RoomId int64

type CreateMessageRequest struct {
	roomId RoomId
	body   Text
}

func NewCreateMessageRequest(roomId int64, body string) *CreateMessageRequest {
	m := new(CreateMessageRequest)
	m.roomId = RoomId(roomId)
	m.body = Text(body)
	return m
}

func (m *CreateMessageRequest) values() *url.Values {
	vs := url.Values{}
	vs.Add("body", string(m.body))
	return &vs
}

type CreateMessageResponse struct {
	MessageId string `json:"message_id"`
}

func endpointFmt(format string, a ...interface{}) string {
	return fmt.Sprintf(format, a)
}

func (c *Chatwork) CreateMessage(req *CreateMessageRequest) *CreateMessageResponse {
	endpoint := endpoint(baseURL + fmt.Sprintf("rooms/%d/messages", req.roomId))
	httpRes := c.post(endpoint, req)

	var res CreateMessageResponse
	if err := decodeBody(httpRes, &res); err != nil {
		log.Fatal(err)
	}
	return &res
}

type UserId int64
type UserIds []UserId

type CreateTaskRequest struct {
	roomId    RoomId
	body      Text
	assignees UserIds
	due       *time.Time
}

func (t *CreateTaskRequest) values() *url.Values {
	vs := url.Values{}
	vs.Add("body", string(t.body))
	vs.Add("to_ids", t.assignees.toString(","))
	if t.due != nil {
		vs.Add("limit", strconv.FormatInt(t.due.Unix(), 10))
	}
	return &vs
}

type CreateTaskResponse struct {
	TaskIds []int64 `json:"task_ids"`
}

func NewCreateTaskRequest(roomId int64, body string, assignees []int64, due *time.Time) *CreateTaskRequest {
	t := new(CreateTaskRequest)
	t.roomId = RoomId(roomId)
	t.body = Text(body)
	t.assignees = make([]UserId, len(assignees))
	for i, a := range assignees {
		t.assignees[i] = UserId(a)
	}
	t.due = due
	return t
}

func (c *Chatwork) CreateTask(req *CreateTaskRequest) *CreateTaskResponse {
	endpoint := endpoint(baseURL + fmt.Sprintf("rooms/%d/tasks", req.roomId))
	httpRes := c.post(endpoint, req)

	var res CreateTaskResponse
	if err := decodeBody(httpRes, &res); err != nil {
		log.Fatal(err)
	}
	return &res
}

func (ids UserIds) toString(sep string) string {
	buf := make([]string, len(ids))
	for i, id := range ids {
		buf[i] = strconv.FormatInt(int64(id), 10)
	}
	return strings.Join(buf, sep)
}
<|endoftext|>


<commit_before>/*

The alfred command can be used to manage Go-based Alfred workflows.

The command must be run from a workflow directory, a directory containing a
"workflow" subdirectory. The basename of the workflow directory is the
workflow's filename. A typical layout would look like:

	my-workflow/
		README.md
		LICENSE.txt
		main.go
		workflow/
			info.plist
			icon.png

Installation:

	go install github.com/jason0x43/go-alfred/alfred

Usage:

    alfred [command]

The available commands are:

	build
		Build the workflow executable and output it into the "workflow"
		subdirectory.
	clean
		Delete the compiled workflow executable and the workflow distributable
		package.
	info
		Display information about the workflow.
	link
		Link the "workflow" subdirectory into Alfred's preferences directory,
		installing it.
	pack
		Package the workflow for distribution. This will create a file named
		<filename>.alfredworkflow, where "filename" is the basename of the
		workflow directory.
	release
		Prepare the repo for release.
	unlink
		Unlink the "workflow" subdirectory from Alfred's preferences directory,
		uninstalling it.

*/
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/Masterminds/semver"
	"github.com/jason0x43/go-alfred"
)

var workflowName string
var zipName string
var workflowPath string
var workflowsPath string
var buildDir = "workflow"

type command struct {
	Name string
	Help string
}

var commands = []command{
	command{"build", "build the workflow executable (-a to rebuild libs)"},
	command{"clean", "clean built files"},
	command{"info", "display information about the current workflow"},
	command{"link", "activate this workflow"},
	command{"pack", "create a distributable package"},
	command{"unlink", "deactivate this workflow"},
}

func main() {
	prefsDir := getPrefsDirectory()
	workflowsPath = path.Join(prefsDir, "Alfred.alfredpreferences/workflows")

	if len(os.Args) == 1 {
		println("usage:", os.Args[0], "<command>")
		println()
		println("command may be one of:")
		for _, cmd := range commands {
			fmt.Printf("    %-10s %s\n", cmd.Name, cmd.Help)
		}
		os.Exit(0)
	}

	if len(os.Args) == 2 && os.Args[1] == "-z" {
		for _, cmd := range commands {
			fmt.Printf("%s:%s\n", cmd.Name, cmd.Help)
		}
		os.Exit(0)
	}

	dirExists := func(dir string) bool {
		stat, err := os.Stat(dir)
		return !os.IsNotExist(err) && stat.IsDir()
	}

	if !dirExists(path.Join("workflow")) {
		os.Chdir("..")
		if !dirExists("workflow") {
			println("You're not in a workflow.")
			os.Exit(1)
		}
	}

	workflowPath, _ = filepath.Abs(".")
	workflowName = path.Base(workflowPath)
	zipName = workflowName + ".alfredworkflow"

	switch os.Args[1] {
	case "build":
		build()
	case "clean":
		clean()
	case "info":
		info()
	case "link":
		link()
	case "pack":
		pack()
	case "release":
		release()
	case "unlink":
		unlink()
	default:
		println("Unknown command:", os.Args[1])
	}
}

// getAlfredVersion returns the highest installed version of Alfred. It uses a very naive algorithm.
func getAlfredVersion() string {
	files, _ := ioutil.ReadDir("/Applications")
	name := ""
	for _, file := range files {
		fname := file.Name()
		if strings.HasPrefix(fname, "Alfred ") && fname > name {
			name = fname
			break
		}
	}
	if name != "" {
		name = strings.TrimSuffix(name, ".app")
		parts := strings.Split(name, " ")
		if len(parts) == 2 {
			return parts[1]
		}
	}
	return ""
}

func run(cmd string, args ...string) {
	if output, err := exec.Command(cmd, args...).CombinedOutput(); err != nil {
		println(string(output))
		panic(err)
	}
}

func runIfFile(file, cmd string, args ...string) {
	if _, err := os.Stat(file); err == nil {
		run(cmd, args...)
	}
}

func getPrefsDirectory() string {
	currentUser, _ := user.Current()

	version := getAlfredVersion()
	prefSuffix := ""
	if version != "2" {
		prefSuffix = "-" + version
	}

	prefFile := path.Join(currentUser.HomeDir, "Library", "Preferences",
		"com.runningwithcrayons.Alfred-Preferences"+prefSuffix+".plist")
	preferences := alfred.LoadPlist(prefFile)

	var folder string

	if preferences["syncfolder"] != "" {
		folder = preferences["syncfolder"].(string)
		if strings.HasPrefix(folder, "~/") {
			folder = path.Join(currentUser.HomeDir, folder[2:])
		}
	} else {
		folder = path.Join(currentUser.HomeDir, "Library", "Application Support", "Alfred "+version)
	}

	var info os.FileInfo
	var err error
	if info, err = os.Stat(folder); err != nil {
		panic(err)
	}

	if !info.IsDir() {
		panic(fmt.Errorf("%s is not a directory", folder))
	}

	return folder
}

func loadPreferences() (prefs alfred.Plist) {
	currentUser, _ := user.Current()

	version := getAlfredVersion()
	prefSuffix := ""
	if version != "2" {
		prefSuffix = "-" + version
	}

	prefFile := path.Join(currentUser.HomeDir, "Library", "Preferences",
		"com.runningwithcrayons.Alfred-Preferences"+prefSuffix+".plist")
	return alfred.LoadPlist(prefFile)
}

func build() {
	// use go generate, along with custom build tools, to handle any auxiliary build steps
	run("go", "generate")

	if len(os.Args) > 2 && os.Args[2] == "-a" {
		run("go", "build", "-a", "-ldflags=\"-w\"", "-o", "workflow/"+workflowName)
	} else {
		run("go", "build", "-ldflags=\"-w\"", "-o", "workflow/"+workflowName)
	}
}

func clean() {
	run("rm", "workflow/"+workflowName)
	run("rm", zipName)
}

func getExistingLink() (string, error) {
	dir, err := os.Open(workflowsPath)
	if err != nil {
		return "", err
	}
	defer dir.Close()

	dirs, err := dir.Readdir(-1)
	if err != nil {
		return "", err
	}

	wd, _ := os.Getwd()
	buildPath := path.Join(wd, buildDir)

	for _, dir := range dirs {
		if dir.Mode()&os.ModeSymlink == os.ModeSymlink {
			fullDir := path.Join(workflowsPath, dir.Name())
			link, err := filepath.EvalSymlinks(fullDir)
			if err == nil && link == buildPath {
				return fullDir, nil
			}
		}
	}

	return "", nil
}

func info() {
	width := -15

	printField := func(name, value string) {
		fmt.Printf("%*s %s\n", width, name+":", value)
	}

	printField("Workflows", workflowsPath)

	if link, _ := getExistingLink(); link != "" {
		printField("This workflow", path.Base(link))
	}

	plistFile := path.Join("workflow", "info.plist")
	info := alfred.LoadPlist(plistFile)
	printField("Version", info["version"].(string))
}

func link() {
	existing, err := getExistingLink()
	if err != nil {
		panic(err)
	}

	if existing != "" {
		println("existing link", filepath.Base(existing))
		return
	}

	uuidgen, _ := exec.Command("uuidgen").Output()
	uuid := strings.TrimSpace(string(uuidgen))
	target := path.Join(workflowsPath, "user.workflow."+string(uuid))
	buildPath := path.Join(workflowPath, buildDir)
	run("ln", "-s", buildPath, target)
	println("created link", filepath.Base(target))
}

func pack() {
	pwd, _ := filepath.Abs(".")
	if err := os.Chdir(buildDir); err != nil {
		panic(err)
	}
	zipfile := path.Join("..", zipName)
	run("zip", "-r", zipfile, ".")
	if err := os.Chdir(pwd); err != nil {
		panic(err)
	}
}

func release() {
	plistFile := path.Join("workflow", "info.plist")
	info := alfred.LoadPlist(plistFile)
	var version semver.Version
	var releaseVersion string

	if len(os.Args) > 2 {
		version = *semver.MustParse(os.Args[2])
		releaseVersion = version.String()
	} else {
		version = *semver.MustParse(info["version"].(string))
		if version.Prerelease() != "" {
			releaseVer, _ := version.SetPrerelease("")
			releaseVersion = releaseVer.String()
		} else {
			panic("Workflow version must be a prerelease, or a new version must be specified")
		}
	}

	fmt.Printf("Updating version to %s for release\n", releaseVersion)
	info["version"] = releaseVersion
	alfred.SavePlist(plistFile, info)
	run("git", "commit", "-a", "-m", fmt.Sprintf("Update version to %s for release", releaseVersion))
	run("git", "tag", releaseVersion)
	fmt.Printf("Packaging version %s\n", releaseVersion)
	build()
	pack()

	nextVer, _ := version.IncMinor().SetPrerelease("pre")
	nextVersion := nextVer.String()
	fmt.Printf("Updating version to %s\n", nextVersion)
	info["version"] = nextVersion
	alfred.SavePlist(plistFile, info)
	run("git", "commit", "-a", "-m", fmt.Sprintf("Update version to %s", nextVersion))

	fmt.Printf("Done!\n")
}

func unlink() {
	existing, err := getExistingLink()
	if err != nil {
		panic(err)
	}

	if existing == "" {
		return
	}

	run("rm", existing)
	println("removed link", filepath.Base(existing))
}

func copyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	return err
}

func copyFiles(srcDir, dstDir string) error {
	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		srcPath := path.Join(srcDir, entry.Name())
		dstPath := path.Join(dstDir, entry.Name())

		if entry.IsDir() {
			os.Mkdir(dstPath, 0777)
			if err := copyFiles(srcPath, dstPath); err != nil {
				return err
			}
		} else {
			if err := copyFile(srcPath, dstPath); err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>Cleanup header comment<commit_after>// The alfred command can be used to manage Go-based Alfred workflows.
//
// The command must be run from a workflow directory, a directory containing a
// "workflow" subdirectory. The basename of the workflow directory is the
// workflow's filename. A typical layout would look like:
//
// 	my-workflow/
// 		README.md
// 		LICENSE.txt
// 		main.go
// 		workflow/
// 			info.plist
// 			icon.png
//
// Installation:
//
// 	go install github.com/jason0x43/go-alfred/alfred
//
// Usage:
//
//     alfred [command]
//
// The available commands are:
//
// 	build
// 		Build the workflow executable and output it into the "workflow"
// 		subdirectory.
// 	clean
// 		Delete the compiled workflow executable and the workflow distributable
// 		package.
// 	info
// 		Display information about the workflow.
// 	link
// 		Link the "workflow" subdirectory into Alfred's preferences directory,
// 		installing it.
// 	pack
// 		Package the workflow for distribution. This will create a file named
// 		<filename>.alfredworkflow, where "filename" is the basename of the
// 		workflow directory.
// 	release
// 		Prepare the repo for release.
// 	unlink
// 		Unlink the "workflow" subdirectory from Alfred's preferences directory,
// 		uninstalling it.

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/Masterminds/semver"
	"github.com/jason0x43/go-alfred"
)

var workflowName string
var zipName string
var workflowPath string
var workflowsPath string
var buildDir = "workflow"

type command struct {
	Name string
	Help string
}

var commands = []command{
	command{"build", "build the workflow executable (-a to rebuild libs)"},
	command{"clean", "clean built files"},
	command{"info", "display information about the current workflow"},
	command{"link", "activate this workflow"},
	command{"pack", "create a distributable package"},
	command{"unlink", "deactivate this workflow"},
}

func main() {
	prefsDir := getPrefsDirectory()
	workflowsPath = path.Join(prefsDir, "Alfred.alfredpreferences/workflows")

	if len(os.Args) == 1 {
		println("usage:", os.Args[0], "<command>")
		println()
		println("command may be one of:")
		for _, cmd := range commands {
			fmt.Printf("    %-10s %s\n", cmd.Name, cmd.Help)
		}
		os.Exit(0)
	}

	if len(os.Args) == 2 && os.Args[1] == "-z" {
		for _, cmd := range commands {
			fmt.Printf("%s:%s\n", cmd.Name, cmd.Help)
		}
		os.Exit(0)
	}

	dirExists := func(dir string) bool {
		stat, err := os.Stat(dir)
		return !os.IsNotExist(err) && stat.IsDir()
	}

	if !dirExists(path.Join("workflow")) {
		os.Chdir("..")
		if !dirExists("workflow") {
			println("You're not in a workflow.")
			os.Exit(1)
		}
	}

	workflowPath, _ = filepath.Abs(".")
	workflowName = path.Base(workflowPath)
	zipName = workflowName + ".alfredworkflow"

	switch os.Args[1] {
	case "build":
		build()
	case "clean":
		clean()
	case "info":
		info()
	case "link":
		link()
	case "pack":
		pack()
	case "release":
		release()
	case "unlink":
		unlink()
	default:
		println("Unknown command:", os.Args[1])
	}
}

// getAlfredVersion returns the highest installed version of Alfred. It uses a very naive algorithm.
func getAlfredVersion() string {
	files, _ := ioutil.ReadDir("/Applications")
	name := ""
	for _, file := range files {
		fname := file.Name()
		if strings.HasPrefix(fname, "Alfred ") && fname > name {
			name = fname
			break
		}
	}
	if name != "" {
		name = strings.TrimSuffix(name, ".app")
		parts := strings.Split(name, " ")
		if len(parts) == 2 {
			return parts[1]
		}
	}
	return ""
}

func run(cmd string, args ...string) {
	if output, err := exec.Command(cmd, args...).CombinedOutput(); err != nil {
		println(string(output))
		panic(err)
	}
}

func runIfFile(file, cmd string, args ...string) {
	if _, err := os.Stat(file); err == nil {
		run(cmd, args...)
	}
}

func getPrefsDirectory() string {
	currentUser, _ := user.Current()

	version := getAlfredVersion()
	prefSuffix := ""
	if version != "2" {
		prefSuffix = "-" + version
	}

	prefFile := path.Join(currentUser.HomeDir, "Library", "Preferences",
		"com.runningwithcrayons.Alfred-Preferences"+prefSuffix+".plist")
	preferences := alfred.LoadPlist(prefFile)

	var folder string

	if preferences["syncfolder"] != "" {
		folder = preferences["syncfolder"].(string)
		if strings.HasPrefix(folder, "~/") {
			folder = path.Join(currentUser.HomeDir, folder[2:])
		}
	} else {
		folder = path.Join(currentUser.HomeDir, "Library", "Application Support", "Alfred "+version)
	}

	var info os.FileInfo
	var err error
	if info, err = os.Stat(folder); err != nil {
		panic(err)
	}

	if !info.IsDir() {
		panic(fmt.Errorf("%s is not a directory", folder))
	}

	return folder
}

func loadPreferences() (prefs alfred.Plist) {
	currentUser, _ := user.Current()

	version := getAlfredVersion()
	prefSuffix := ""
	if version != "2" {
		prefSuffix = "-" + version
	}

	prefFile := path.Join(currentUser.HomeDir, "Library", "Preferences",
		"com.runningwithcrayons.Alfred-Preferences"+prefSuffix+".plist")
	return alfred.LoadPlist(prefFile)
}

func build() {
	// use go generate, along with custom build tools, to handle any auxiliary build steps
	run("go", "generate")

	if len(os.Args) > 2 && os.Args[2] == "-a" {
		run("go", "build", "-a", "-ldflags=\"-w\"", "-o", "workflow/"+workflowName)
	} else {
		run("go", "build", "-ldflags=\"-w\"", "-o", "workflow/"+workflowName)
	}
}

func clean() {
	run("rm", "workflow/"+workflowName)
	run("rm", zipName)
}

func getExistingLink() (string, error) {
	dir, err := os.Open(workflowsPath)
	if err != nil {
		return "", err
	}
	defer dir.Close()

	dirs, err := dir.Readdir(-1)
	if err != nil {
		return "", err
	}

	wd, _ := os.Getwd()
	buildPath := path.Join(wd, buildDir)

	for _, dir := range dirs {
		if dir.Mode()&os.ModeSymlink == os.ModeSymlink {
			fullDir := path.Join(workflowsPath, dir.Name())
			link, err := filepath.EvalSymlinks(fullDir)
			if err == nil && link == buildPath {
				return fullDir, nil
			}
		}
	}

	return "", nil
}

func info() {
	width := -15

	printField := func(name, value string) {
		fmt.Printf("%*s %s\n", width, name+":", value)
	}

	printField("Workflows", workflowsPath)

	if link, _ := getExistingLink(); link != "" {
		printField("This workflow", path.Base(link))
	}

	plistFile := path.Join("workflow", "info.plist")
	info := alfred.LoadPlist(plistFile)
	printField("Version", info["version"].(string))
}

func link() {
	existing, err := getExistingLink()
	if err != nil {
		panic(err)
	}

	if existing != "" {
		println("existing link", filepath.Base(existing))
		return
	}

	uuidgen, _ := exec.Command("uuidgen").Output()
	uuid := strings.TrimSpace(string(uuidgen))
	target := path.Join(workflowsPath, "user.workflow."+string(uuid))
	buildPath := path.Join(workflowPath, buildDir)
	run("ln", "-s", buildPath, target)
	println("created link", filepath.Base(target))
}

func pack() {
	pwd, _ := filepath.Abs(".")
	if err := os.Chdir(buildDir); err != nil {
		panic(err)
	}
	zipfile := path.Join("..", zipName)
	run("zip", "-r", zipfile, ".")
	if err := os.Chdir(pwd); err != nil {
		panic(err)
	}
}

func release() {
	plistFile := path.Join("workflow", "info.plist")
	info := alfred.LoadPlist(plistFile)
	var version semver.Version
	var releaseVersion string

	if len(os.Args) > 2 {
		version = *semver.MustParse(os.Args[2])
		releaseVersion = version.String()
	} else {
		version = *semver.MustParse(info["version"].(string))
		if version.Prerelease() != "" {
			releaseVer, _ := version.SetPrerelease("")
			releaseVersion = releaseVer.String()
		} else {
			panic("Workflow version must be a prerelease, or a new version must be specified")
		}
	}

	fmt.Printf("Updating version to %s for release\n", releaseVersion)
	info["version"] = releaseVersion
	alfred.SavePlist(plistFile, info)
	run("git", "commit", "-a", "-m", fmt.Sprintf("Update version to %s for release", releaseVersion))
	run("git", "tag", releaseVersion)
	fmt.Printf("Packaging version %s\n", releaseVersion)
	build()
	pack()

	nextVer, _ := version.IncMinor().SetPrerelease("pre")
	nextVersion := nextVer.String()
	fmt.Printf("Updating version to %s\n", nextVersion)
	info["version"] = nextVersion
	alfred.SavePlist(plistFile, info)
	run("git", "commit", "-a", "-m", fmt.Sprintf("Update version to %s", nextVersion))

	fmt.Printf("Done!\n")
}

func unlink() {
	existing, err := getExistingLink()
	if err != nil {
		panic(err)
	}

	if existing == "" {
		return
	}

	run("rm", existing)
	println("removed link", filepath.Base(existing))
}

func copyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	return err
}

func copyFiles(srcDir, dstDir string) error {
	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		srcPath := path.Join(srcDir, entry.Name())
		dstPath := path.Join(dstDir, entry.Name())

		if entry.IsDir() {
			os.Mkdir(dstPath, 0777)
			if err := copyFiles(srcPath, dstPath); err != nil {
				return err
			}
		} else {
			if err := copyFile(srcPath, dstPath); err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"golang.org/x/net/html"
	curl "github.com/andelf/go-curl"
	"bytes"
	"net/url"
)

type AhrefsService interface {
	SignIn(string, string) string
}

type ahrefsService struct{}

func (ahrefsService) SignIn(email, password string) string {
	easy := curl.EasyInit()
	token := ""
	defer easy.Cleanup()

	fooTest := func(body []byte, userdata interface{}) bool {
		//fmt.Print(string(body))
		root, _ := html.Parse(bytes.NewReader(body))
		element, _ := getElementByName("_token", root)
		if element != nil {
			for _, a := range element.Attr {
				if a.Key == "value" {
					token = a.Val
				}
			}
			//fmt.Print("Token = " + token + "\n")
		}
		return true
	}

	//first call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_SSL_VERIFYPEER, 1)
	easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
	easy.Setopt(curl.OPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
	easy.Setopt(curl.OPT_VERBOSE, 1)
	easy.Setopt(curl.OPT_FOLLOWLOCATION, 1)
	easy.Setopt(curl.OPT_COOKIEJAR, "./cookiejar")
	easy.Setopt(curl.OPT_COOKIEFILE, "./cookiejar")
	easy.Setopt(curl.OPT_NOBODY, 0)
	easy.Perform()

	//second call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_HTTPHEADER, []string{
		"Referer: https://ahrefs.com/user/login",
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
		"Accept-Language: en-US,en;q=0.5",
		"Accept-Encoding: gzip, deflate",
		"Connection: keep-alive",
	})
	easy.Setopt(curl.OPT_POST, 1)
	form := url.Values{}
	form.Add("_token",    token)
	form.Add("email",     email)
	form.Add("password",  password)
	form.Add("return_to", "https://ahrefs.com/")
	postFields := form.Encode()
	fmt.Print(postFields + "\n")
	easy.Setopt(curl.OPT_POSTFIELDSIZE, len(postFields))
	easy.Setopt(curl.OPT_POSTFIELDS, postFields)
	easy.Perform()

	return "true"
}

func NewService() ahrefsService {
	return ahrefsService{}
}

func getElementByName(name string, n *html.Node) (element *html.Node, ok bool) {
	for _, a := range n.Attr {
		if a.Key == "name" && a.Val == name {
			return n, true
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if element, ok = getElementByName(name, c); ok {
			return
		}
	}
	return
}

func getElementId(id string, n *html.Node) (element *html.Node, ok bool) {
	for _, a := range n.Attr {
		if a.Key == "id" && a.Val == id {
			return n, true
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if element, ok = getElementByName(id, c); ok {
			return
		}
	}
	return
}

<commit_msg>using goquery for getting elements from html, removed old code for that issue<commit_after>package service

import (
	"golang.org/x/net/html"
	curl "github.com/andelf/go-curl"
	"bytes"
	"github.com/PuerkitoBio/goquery"
	"net/url"
	"fmt"
)

type AhrefsService interface {
	SignIn(string, string) string
}

type ahrefsService struct{}

func (ahrefsService) SignIn(email, password string) string {
	easy := curl.EasyInit()
	token := ""
	defer easy.Cleanup()

	fooTest := func(body []byte, userdata interface{}) bool {
		data, exists := getToken(body)
		if exists {
			token = data
		}

		return true
	}

	//first call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_SSL_VERIFYPEER, 1)
	easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
	easy.Setopt(curl.OPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
	easy.Setopt(curl.OPT_VERBOSE, 1)
	easy.Setopt(curl.OPT_FOLLOWLOCATION, 1)
	easy.Setopt(curl.OPT_COOKIEJAR, "./cookiejar")
	easy.Setopt(curl.OPT_COOKIEFILE, "./cookiejar")
	easy.Setopt(curl.OPT_NOBODY, 0)
	easy.Perform()

	//second call

	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_HTTPHEADER, []string{
		"Referer: https://ahrefs.com/user/login",
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
		"Accept-Language: en-US,en;q=0.5",
		"Accept-Encoding: gzip, deflate",
		"Connection: keep-alive",
	})
	easy.Setopt(curl.OPT_POST, 1)
	form := url.Values{}
	form.Add("_token",    token)
	form.Add("email",     email)
	form.Add("password",  password)
	form.Add("return_to", "https://ahrefs.com/")
	postFields := form.Encode()
	fmt.Print(postFields + "\n")
	easy.Setopt(curl.OPT_POSTFIELDSIZE, len(postFields))
	easy.Setopt(curl.OPT_POSTFIELDS, postFields)
	easy.Perform()

	return "true"
}

func getToken(body []byte) (string, bool) {
	rootNode, err := html.Parse(bytes.NewReader(body))
	if err != nil {
		return "", false
	}

	meta := goquery.NewDocumentFromNode(rootNode).Find("meta[name=_token]")
	token, exists := meta.Attr("content")
	if exists == false {
		return "", false
	}

	return token, true

}

func NewService() ahrefsService {
	return ahrefsService{}
}
<|endoftext|>


<commit_before>// mouseAnalyzer determines vesicle release events and latencies for our
// mouse NMJ 6 AZ model with two synaptic vesicles each according to the
// second sensor faciliation model (see Ma et al., J. Neurophys, 2014)
package main

import (
	"flag"
	"fmt"
	"github.com/haskelladdict/mbdr/libmbd"
	"log"
	"strconv"
	"strings"
)

var (
	numPulses      int  // number of AP pulses
	energyModel    bool // use the energy model
	sytEnergy      int  // energy of activated synaptotagmin toward vesicle fusion
	yEnergy        int  // energy of activated Y sites toward vesicle fusion
	numActiveSites int  // number of simultaneously active sites required for release
	//ISIValue      int // interstimulus interval
)

func init() {
	flag.IntVar(&numPulses, "p", 1, "number of AP pulses in the model")
	flag.IntVar(&sytEnergy, "s", -1, "energy of active synaptotagmin sites "+
		"(required with -e flag)")
	flag.IntVar(&yEnergy, "y", -1, "energy of active y sites "+
		"(required with -e flag")
	flag.BoolVar(&energyModel, "e", false, "use the energy model instead of "+
		"deterministic model")
	flag.IntVar(&numActiveSites, "n", 0, "number of sites required for activation"+
		"for deterministic model")
	//flag.IntVar(&ISIValue, "i", -1, "energy of active y sites")
}

// usage prints a brief usage information to stdout
func usage() {
	fmt.Println("usage: mouseAnalyzer [options] <binary mcell files>")
	fmt.Println("\noptions:")
	flag.PrintDefaults()
}

// extractSeed attempts to extract the seed from the filename of the provided
// binary mcell data file.
// NOTE: the following filenaming convention is assumed *.<seedIDString>.bin.(gz|bz2)
func extractSeed(fileName string) (int, error) {
	items := strings.Split(fileName, ".")
	if len(items) <= 3 {
		return -1, fmt.Errorf("incorrectly formatted fileName. " +
			"Expected *.<seedIDString>.bin.(gz|bz2)")
	}

	for i := len(items) - 1; i >= 0; i-- {
		if items[i] == "bin" && i >= 1 {
			seed, err := strconv.Atoi(items[i-1])
			if err != nil {
				return -1, err
			}
			return seed, nil
		}
	}
	return -1, fmt.Errorf("Unable to extract seed id from filename ", fileName)
}

// printReleases prints a summary statistic for all released vesicle for a
// given seed
func printReleases(data *libmbd.MCellData, seed int, rel []*ReleaseEvent) {
	timeStep := data.StepSize()
	for _, r := range rel {

		/*
			caContrib, err := determineCaContrib(data, rel)
			if err != nil {
				log.Fatal(err)
			}
		*/
		fmt.Printf("seed : %d   AZ : %d   ves : %d   time : %e", seed, r.azId+1,
			r.vesicleID+1, float64(r.eventIter)*timeStep)
		fmt.Printf("  sensors: [")
		for _, s := range r.sensors {
			fmt.Printf("%d ", s)
		}
		fmt.Printf("]\n")
	}
}

/*
// determineCaContrib determines which Ca channels contributed to the release
// of a particular vesicle.
// NOTE: We try to be as agnostic as we can in terms of the particular
// nomenclature used for naming the channels. However, the expectation is
// that data files tracking Ca binding to vesicles are named
// vesicle_<az>_<1|2>_ca_<ca naming>.<seed>.dat
func determineCaContrib(data *libmbd.MCellData, rel *ReleaseEvent) (map[string]int, error) {

}
*/

// main entry point
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		usage()
		return
	}

	// some sanity checks
	if energyModel && (sytEnergy < 0 || yEnergy < 0) {
		log.Fatal("Please provide a non-negative synaptotagmin and y site energy")
	}

	if !energyModel && numActiveSites == 0 {
		log.Fatal("Please provide a positive count for the number of required active sites")
	}

	// loop over all provided data sets
	for _, fileName := range flag.Args() {

		seed, err := extractSeed(fileName)
		if err != nil {
			log.Fatal(err)
		}

		data, err := libmbd.Read(fileName)
		if err != nil {
			log.Fatal(err)
		}

		err = analyze(data, energyModel, seed, numPulses, numActiveSites, sytEnergy,
			yEnergy)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>Added ability to print out channels contributing to a vesicles release.<commit_after>// mouseAnalyzer determines vesicle release events and latencies for our
// mouse NMJ 6 AZ model with two synaptic vesicles each according to the
// second sensor faciliation model (see Ma et al., J. Neurophys, 2014)
package main

import (
	"flag"
	"fmt"
	"github.com/haskelladdict/mbdr/libmbd"
	"log"
	"strconv"
	"strings"
)

var (
	numPulses      int  // number of AP pulses
	energyModel    bool // use the energy model
	sytEnergy      int  // energy of activated synaptotagmin toward vesicle fusion
	yEnergy        int  // energy of activated Y sites toward vesicle fusion
	numActiveSites int  // number of simultaneously active sites required for release
	//ISIValue      int // interstimulus interval
)

func init() {
	flag.IntVar(&numPulses, "p", 1, "number of AP pulses in the model")
	flag.IntVar(&sytEnergy, "s", -1, "energy of active synaptotagmin sites "+
		"(required with -e flag)")
	flag.IntVar(&yEnergy, "y", -1, "energy of active y sites "+
		"(required with -e flag")
	flag.BoolVar(&energyModel, "e", false, "use the energy model instead of "+
		"deterministic model")
	flag.IntVar(&numActiveSites, "n", 0, "number of sites required for activation"+
		"for deterministic model")
	//flag.IntVar(&ISIValue, "i", -1, "energy of active y sites")
}

// usage prints a brief usage information to stdout
func usage() {
	fmt.Println("usage: mouseAnalyzer [options] <binary mcell files>")
	fmt.Println("\noptions:")
	flag.PrintDefaults()
}

// extractSeed attempts to extract the seed from the filename of the provided
// binary mcell data file.
// NOTE: the following filenaming convention is assumed *.<seedIDString>.bin.(gz|bz2)
func extractSeed(fileName string) (int, error) {
	items := strings.Split(fileName, ".")
	if len(items) <= 3 {
		return -1, fmt.Errorf("incorrectly formatted fileName. " +
			"Expected *.<seedIDString>.bin.(gz|bz2)")
	}

	for i := len(items) - 1; i >= 0; i-- {
		if items[i] == "bin" && i >= 1 {
			seed, err := strconv.Atoi(items[i-1])
			if err != nil {
				return -1, err
			}
			return seed, nil
		}
	}
	return -1, fmt.Errorf("Unable to extract seed id from filename ", fileName)
}

// printReleases prints a summary statistic for all released vesicle for a
// given seed
func printReleases(data *libmbd.MCellData, seed int, rel []*ReleaseEvent) {
	timeStep := data.StepSize()
	for _, r := range rel {

		channels, err := determineCaChanContrib(data, r)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("seed : %d   AZ : %d   ves : %d   time : %e", seed, r.azId+1,
			r.vesicleID+1, float64(r.eventIter)*timeStep)
		fmt.Printf("  sensors: [")
		for _, s := range r.sensors {
			fmt.Printf("%d ", s)
		}
		fmt.Printf("]")
		fmt.Printf("  channels: [")
		for n, c := range channels {
			fmt.Printf("%s : %d  ", n, int(c))
		}
		fmt.Printf("]")
		fmt.Printf("\n")
	}
}

// determineCaContrib determines which Ca channels contributed to the release
// of a particular vesicle.
// NOTE: We try to be as agnostic as we can in terms of the particular
// nomenclature used for naming the channels. However, the expectation is
// that data files tracking Ca binding to vesicles are named
// vesicle_<az>_<1|2>_ca_<ca naming>.<seed>.dat
func determineCaChanContrib(data *libmbd.MCellData, rel *ReleaseEvent) (map[string]float64, error) {
	channels := make(map[string]float64)
	// the az/channel counting is 1 based whereas our internal counting is 0 based
	regexString := fmt.Sprintf("vesicle_%d_%d_ca_.*", rel.azId+1, rel.vesicleID+1)
	counts, err := data.BlockDataByRegex(regexString)
	if err != nil {
		return nil, err
	}
	for k, c := range counts {
		if len(c.Col) != 1 {
			return nil, fmt.Errorf("data set %s has more than the expected 1 column",
				k)
		}
		if c.Col[0][rel.eventIter] > 0 {
			// need to subtract 2 from regexString due to the extra ".*"
			caString, err := extractCaChanName(k, len(regexString)-2)
			if err != nil {
				return nil, err
			}
			channels[caString] = c.Col[0][rel.eventIter]
		}
	}

	return channels, nil
}

// extractCaChanName attempts to extract the name of the calcium channel based
// on the expected data name pattern vesicle_<az>_<1|2>_ca_<ca naming>.<seed>.dat
func extractCaChanName(name string, prefixLength int) (string, error) {
	caName := name[prefixLength:]
	items := strings.Split(caName, ".")
	if len(items) == 0 {
		return "", fmt.Errorf("Could not determine Ca channel name from data set %s", name)
	}
	return items[0], nil
}

// main entry point
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		usage()
		return
	}

	// some sanity checks
	if energyModel && (sytEnergy < 0 || yEnergy < 0) {
		log.Fatal("Please provide a non-negative synaptotagmin and y site energy")
	}

	if !energyModel && numActiveSites == 0 {
		log.Fatal("Please provide a positive count for the number of required active sites")
	}

	// loop over all provided data sets
	for _, fileName := range flag.Args() {

		seed, err := extractSeed(fileName)
		if err != nil {
			log.Fatal(err)
		}

		data, err := libmbd.Read(fileName)
		if err != nil {
			log.Fatal(err)
		}

		err = analyze(data, energyModel, seed, numPulses, numActiveSites, sytEnergy,
			yEnergy)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>// +build ignore

// Cross compile rclone - in go because I hate bash ;-)

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"
)

var (
	// Flags
	debug       = flag.Bool("d", false, "Print commands instead of running them.")
	parallel    = flag.Int("parallel", runtime.NumCPU(), "Number of commands to run in parallel.")
	copyAs      = flag.String("release", "", "Make copies of the releases with this name")
	gitLog      = flag.String("git-log", "", "git log to include as well")
	include     = flag.String("include", "^.*$", "os/arch regexp to include")
	exclude     = flag.String("exclude", "^$", "os/arch regexp to exclude")
	cgo         = flag.Bool("cgo", false, "Use cgo for the build")
	noClean     = flag.Bool("no-clean", false, "Don't clean the build directory before running.")
	tags        = flag.String("tags", "", "Space separated list of build tags")
	compileOnly = flag.Bool("compile-only", false, "Just build the binary, not the zip.")
)

// GOOS/GOARCH pairs we build for
var osarches = []string{
	"windows/386",
	"windows/amd64",
	"darwin/386",
	"darwin/amd64",
	"linux/386",
	"linux/amd64",
	"linux/arm",
	"linux/arm64",
	"linux/mips",
	"linux/mipsle",
	"freebsd/386",
	"freebsd/amd64",
	"freebsd/arm",
	"netbsd/386",
	"netbsd/amd64",
	"netbsd/arm",
	"openbsd/386",
	"openbsd/amd64",
	"plan9/386",
	"plan9/amd64",
	"solaris/amd64",
}

// Special environment flags for a given arch
var archFlags = map[string][]string{
	"386":    {"GO386=387"},
	"mips":   {"GOMIPS=softfloat"},
	"mipsle": {"GOMIPS=softfloat"},
}

// runEnv - run a shell command with env
func runEnv(args, env []string) error {
	if *debug {
		args = append([]string{"echo"}, args...)
	}
	cmd := exec.Command(args[0], args[1:]...)
	if env != nil {
		cmd.Env = append(os.Environ(), env...)
	}
	if *debug {
		log.Printf("args = %v, env = %v\n", args, cmd.Env)
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Print("----------------------------")
		log.Printf("Failed to run %v: %v", args, err)
		log.Printf("Command output was:\n%s", out)
		log.Print("----------------------------")
	}
	return err
}

// run a shell command
func run(args ...string) {
	err := runEnv(args, nil)
	if err != nil {
		log.Fatalf("Exiting after error: %v", err)
	}
}

// chdir or die
func chdir(dir string) {
	err := os.Chdir(dir)
	if err != nil {
		log.Fatalf("Couldn't cd into %q: %v", dir, err)
	}
}

// substitute data from go template file in to file out
func substitute(inFile, outFile string, data interface{}) {
	t, err := template.ParseFiles(inFile)
	if err != nil {
		log.Fatalf("Failed to read template file %q: %v %v", inFile, err)
	}
	out, err := os.Create(outFile)
	if err != nil {
		log.Fatalf("Failed to create output file %q: %v %v", outFile, err)
	}
	defer func() {
		err := out.Close()
		if err != nil {
			log.Fatalf("Failed to close output file %q: %v %v", outFile, err)
		}
	}()
	err = t.Execute(out, data)
	if err != nil {
		log.Fatalf("Failed to substitute template file %q: %v %v", inFile, err)
	}
}

// build the zip package return its name
func buildZip(dir string) string {
	// Now build the zip
	run("cp", "-a", "../MANUAL.txt", filepath.Join(dir, "README.txt"))
	run("cp", "-a", "../MANUAL.html", filepath.Join(dir, "README.html"))
	run("cp", "-a", "../rclone.1", dir)
	if *gitLog != "" {
		run("cp", "-a", *gitLog, dir)
	}
	zip := dir + ".zip"
	run("zip", "-r9", zip, dir)
	return zip
}

// Build .deb and .rpm packages
//
// It returns a list of artifacts it has made
func buildDebAndRpm(dir, version, goarch string) []string {
	// Make internal version number acceptable to .deb and .rpm
	pkgVersion := version[1:]
	pkgVersion = strings.Replace(pkgVersion, "β", "-beta", -1)
	pkgVersion = strings.Replace(pkgVersion, "-", ".", -1)

	// Make nfpm.yaml from the template
	substitute("../bin/nfpm.yaml", path.Join(dir, "nfpm.yaml"), map[string]string{
		"Version": pkgVersion,
		"Arch":    goarch,
	})

	// build them
	var artifacts []string
	for _, pkg := range []string{".deb", ".rpm"} {
		artifact := dir + pkg
		run("bash", "-c", "cd "+dir+" && nfpm -f nfpm.yaml pkg -t ../"+artifact)
		artifacts = append(artifacts, artifact)
	}

	return artifacts
}

// build the binary in dir returning success or failure
func compileArch(version, goos, goarch, dir string) bool {
	log.Printf("Compiling %s/%s", goos, goarch)
	output := filepath.Join(dir, "rclone")
	if goos == "windows" {
		output += ".exe"
	}
	err := os.MkdirAll(dir, 0777)
	if err != nil {
		log.Fatalf("Failed to mkdir: %v", err)
	}
	args := []string{
		"go", "build",
		"--ldflags", "-s -X github.com/rclone/rclone/fs.Version=" + version,
		"-i",
		"-o", output,
		"-tags", *tags,
		"..",
	}
	env := []string{
		"GOOS=" + goos,
		"GOARCH=" + goarch,
	}
	if !*cgo {
		env = append(env, "CGO_ENABLED=0")
	} else {
		env = append(env, "CGO_ENABLED=1")
	}
	if flags, ok := archFlags[goarch]; ok {
		env = append(env, flags...)
	}
	err = runEnv(args, env)
	if err != nil {
		log.Printf("Error compiling %s/%s: %v", goos, goarch, err)
		return false
	}
	if !*compileOnly {
		artifacts := []string{buildZip(dir)}
		// build a .deb and .rpm if appropriate
		if goos == "linux" {
			artifacts = append(artifacts, buildDebAndRpm(dir, version, goarch)...)
		}
		if *copyAs != "" {
			for _, artifact := range artifacts {
				run("ln", artifact, strings.Replace(artifact, "-"+version, "-"+*copyAs, 1))
			}
		}
		// tidy up
		run("rm", "-rf", dir)
	}
	log.Printf("Done compiling %s/%s", goos, goarch)
	return true
}

func compile(version string) {
	start := time.Now()
	wg := new(sync.WaitGroup)
	run := make(chan func(), *parallel)
	for i := 0; i < *parallel; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for f := range run {
				f()
			}
		}()
	}
	includeRe, err := regexp.Compile(*include)
	if err != nil {
		log.Fatalf("Bad -include regexp: %v", err)
	}
	excludeRe, err := regexp.Compile(*exclude)
	if err != nil {
		log.Fatalf("Bad -exclude regexp: %v", err)
	}
	compiled := 0
	var failuresMu sync.Mutex
	var failures []string
	for _, osarch := range osarches {
		if excludeRe.MatchString(osarch) || !includeRe.MatchString(osarch) {
			continue
		}
		parts := strings.Split(osarch, "/")
		if len(parts) != 2 {
			log.Fatalf("Bad osarch %q", osarch)
		}
		goos, goarch := parts[0], parts[1]
		userGoos := goos
		if goos == "darwin" {
			userGoos = "osx"
		}
		dir := filepath.Join("rclone-" + version + "-" + userGoos + "-" + goarch)
		run <- func() {
			if !compileArch(version, goos, goarch, dir) {
				failuresMu.Lock()
				failures = append(failures, goos+"/"+goarch)
				failuresMu.Unlock()
			}
		}
		compiled++
	}
	close(run)
	wg.Wait()
	log.Printf("Compiled %d arches in %v", compiled, time.Since(start))
	if len(failures) > 0 {
		sort.Strings(failures)
		log.Printf("%d compile failures:\n  %s\n", len(failures), strings.Join(failures, "\n  "))
		os.Exit(1)
	}
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		log.Fatalf("Syntax: %s <version>", os.Args[0])
	}
	version := args[0]
	if !*noClean {
		run("rm", "-rf", "build")
		run("mkdir", "build")
	}
	chdir("build")
	err := ioutil.WriteFile("version.txt", []byte(fmt.Sprintf("rclone %s\n", version)), 0666)
	if err != nil {
		log.Fatalf("Couldn't write version.txt: %v", err)
	}
	compile(version)
}
<commit_msg>build: add -trimpath to release build for reproduceable builds<commit_after>// +build ignore

// Cross compile rclone - in go because I hate bash ;-)

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"
)

var (
	// Flags
	debug       = flag.Bool("d", false, "Print commands instead of running them.")
	parallel    = flag.Int("parallel", runtime.NumCPU(), "Number of commands to run in parallel.")
	copyAs      = flag.String("release", "", "Make copies of the releases with this name")
	gitLog      = flag.String("git-log", "", "git log to include as well")
	include     = flag.String("include", "^.*$", "os/arch regexp to include")
	exclude     = flag.String("exclude", "^$", "os/arch regexp to exclude")
	cgo         = flag.Bool("cgo", false, "Use cgo for the build")
	noClean     = flag.Bool("no-clean", false, "Don't clean the build directory before running.")
	tags        = flag.String("tags", "", "Space separated list of build tags")
	compileOnly = flag.Bool("compile-only", false, "Just build the binary, not the zip.")
)

// GOOS/GOARCH pairs we build for
var osarches = []string{
	"windows/386",
	"windows/amd64",
	"darwin/386",
	"darwin/amd64",
	"linux/386",
	"linux/amd64",
	"linux/arm",
	"linux/arm64",
	"linux/mips",
	"linux/mipsle",
	"freebsd/386",
	"freebsd/amd64",
	"freebsd/arm",
	"netbsd/386",
	"netbsd/amd64",
	"netbsd/arm",
	"openbsd/386",
	"openbsd/amd64",
	"plan9/386",
	"plan9/amd64",
	"solaris/amd64",
}

// Special environment flags for a given arch
var archFlags = map[string][]string{
	"386":    {"GO386=387"},
	"mips":   {"GOMIPS=softfloat"},
	"mipsle": {"GOMIPS=softfloat"},
}

// runEnv - run a shell command with env
func runEnv(args, env []string) error {
	if *debug {
		args = append([]string{"echo"}, args...)
	}
	cmd := exec.Command(args[0], args[1:]...)
	if env != nil {
		cmd.Env = append(os.Environ(), env...)
	}
	if *debug {
		log.Printf("args = %v, env = %v\n", args, cmd.Env)
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Print("----------------------------")
		log.Printf("Failed to run %v: %v", args, err)
		log.Printf("Command output was:\n%s", out)
		log.Print("----------------------------")
	}
	return err
}

// run a shell command
func run(args ...string) {
	err := runEnv(args, nil)
	if err != nil {
		log.Fatalf("Exiting after error: %v", err)
	}
}

// chdir or die
func chdir(dir string) {
	err := os.Chdir(dir)
	if err != nil {
		log.Fatalf("Couldn't cd into %q: %v", dir, err)
	}
}

// substitute data from go template file in to file out
func substitute(inFile, outFile string, data interface{}) {
	t, err := template.ParseFiles(inFile)
	if err != nil {
		log.Fatalf("Failed to read template file %q: %v %v", inFile, err)
	}
	out, err := os.Create(outFile)
	if err != nil {
		log.Fatalf("Failed to create output file %q: %v %v", outFile, err)
	}
	defer func() {
		err := out.Close()
		if err != nil {
			log.Fatalf("Failed to close output file %q: %v %v", outFile, err)
		}
	}()
	err = t.Execute(out, data)
	if err != nil {
		log.Fatalf("Failed to substitute template file %q: %v %v", inFile, err)
	}
}

// build the zip package return its name
func buildZip(dir string) string {
	// Now build the zip
	run("cp", "-a", "../MANUAL.txt", filepath.Join(dir, "README.txt"))
	run("cp", "-a", "../MANUAL.html", filepath.Join(dir, "README.html"))
	run("cp", "-a", "../rclone.1", dir)
	if *gitLog != "" {
		run("cp", "-a", *gitLog, dir)
	}
	zip := dir + ".zip"
	run("zip", "-r9", zip, dir)
	return zip
}

// Build .deb and .rpm packages
//
// It returns a list of artifacts it has made
func buildDebAndRpm(dir, version, goarch string) []string {
	// Make internal version number acceptable to .deb and .rpm
	pkgVersion := version[1:]
	pkgVersion = strings.Replace(pkgVersion, "β", "-beta", -1)
	pkgVersion = strings.Replace(pkgVersion, "-", ".", -1)

	// Make nfpm.yaml from the template
	substitute("../bin/nfpm.yaml", path.Join(dir, "nfpm.yaml"), map[string]string{
		"Version": pkgVersion,
		"Arch":    goarch,
	})

	// build them
	var artifacts []string
	for _, pkg := range []string{".deb", ".rpm"} {
		artifact := dir + pkg
		run("bash", "-c", "cd "+dir+" && nfpm -f nfpm.yaml pkg -t ../"+artifact)
		artifacts = append(artifacts, artifact)
	}

	return artifacts
}

// build the binary in dir returning success or failure
func compileArch(version, goos, goarch, dir string) bool {
	log.Printf("Compiling %s/%s", goos, goarch)
	output := filepath.Join(dir, "rclone")
	if goos == "windows" {
		output += ".exe"
	}
	err := os.MkdirAll(dir, 0777)
	if err != nil {
		log.Fatalf("Failed to mkdir: %v", err)
	}
	args := []string{
		"go", "build",
		"--ldflags", "-s -X github.com/rclone/rclone/fs.Version=" + version,
		"-trimpath",
		"-i",
		"-o", output,
		"-tags", *tags,
		"..",
	}
	env := []string{
		"GOOS=" + goos,
		"GOARCH=" + goarch,
	}
	if !*cgo {
		env = append(env, "CGO_ENABLED=0")
	} else {
		env = append(env, "CGO_ENABLED=1")
	}
	if flags, ok := archFlags[goarch]; ok {
		env = append(env, flags...)
	}
	err = runEnv(args, env)
	if err != nil {
		log.Printf("Error compiling %s/%s: %v", goos, goarch, err)
		return false
	}
	if !*compileOnly {
		artifacts := []string{buildZip(dir)}
		// build a .deb and .rpm if appropriate
		if goos == "linux" {
			artifacts = append(artifacts, buildDebAndRpm(dir, version, goarch)...)
		}
		if *copyAs != "" {
			for _, artifact := range artifacts {
				run("ln", artifact, strings.Replace(artifact, "-"+version, "-"+*copyAs, 1))
			}
		}
		// tidy up
		run("rm", "-rf", dir)
	}
	log.Printf("Done compiling %s/%s", goos, goarch)
	return true
}

func compile(version string) {
	start := time.Now()
	wg := new(sync.WaitGroup)
	run := make(chan func(), *parallel)
	for i := 0; i < *parallel; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for f := range run {
				f()
			}
		}()
	}
	includeRe, err := regexp.Compile(*include)
	if err != nil {
		log.Fatalf("Bad -include regexp: %v", err)
	}
	excludeRe, err := regexp.Compile(*exclude)
	if err != nil {
		log.Fatalf("Bad -exclude regexp: %v", err)
	}
	compiled := 0
	var failuresMu sync.Mutex
	var failures []string
	for _, osarch := range osarches {
		if excludeRe.MatchString(osarch) || !includeRe.MatchString(osarch) {
			continue
		}
		parts := strings.Split(osarch, "/")
		if len(parts) != 2 {
			log.Fatalf("Bad osarch %q", osarch)
		}
		goos, goarch := parts[0], parts[1]
		userGoos := goos
		if goos == "darwin" {
			userGoos = "osx"
		}
		dir := filepath.Join("rclone-" + version + "-" + userGoos + "-" + goarch)
		run <- func() {
			if !compileArch(version, goos, goarch, dir) {
				failuresMu.Lock()
				failures = append(failures, goos+"/"+goarch)
				failuresMu.Unlock()
			}
		}
		compiled++
	}
	close(run)
	wg.Wait()
	log.Printf("Compiled %d arches in %v", compiled, time.Since(start))
	if len(failures) > 0 {
		sort.Strings(failures)
		log.Printf("%d compile failures:\n  %s\n", len(failures), strings.Join(failures, "\n  "))
		os.Exit(1)
	}
}

func main() {
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		log.Fatalf("Syntax: %s <version>", os.Args[0])
	}
	version := args[0]
	if !*noClean {
		run("rm", "-rf", "build")
		run("mkdir", "build")
	}
	chdir("build")
	err := ioutil.WriteFile("version.txt", []byte(fmt.Sprintf("rclone %s\n", version)), 0666)
	if err != nil {
		log.Fatalf("Couldn't write version.txt: %v", err)
	}
	compile(version)
}
<|endoftext|>