<commit_before>package check

import (
	"fmt"
	"plaid/lexer"
	"plaid/parser"
	"plaid/scope"
	"plaid/types"
	"plaid/vm"
)

type binopsLUT map[string]map[types.Type]map[types.Type]types.Type
type doubleLUT map[types.Type]map[types.Type]types.Type
type singleLUT map[types.Type]types.Type

var defaultBinopsLUT = binopsLUT{
	"+": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
		types.Str: singleLUT{types.Str: types.Str},
	},
	"-": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
	},
	"*": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
	},
	"<": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	"<=": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	">": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	">=": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	"[": doubleLUT{
		types.Str: singleLUT{types.Int: types.Optional{Child: types.Str}},
	},
}

// Check takes an existing abstract syntax tree and performs type checks and
// other correctness checks. It returns a list of any errors that were
// discovered inside the AST
func Check(prog *parser.Program, modules ...*vm.Module) *scope.GlobalScope {
	global := scope.MakeGlobalScope()

	for _, module := range modules {
		global.Import(module)
	}

	checkProgram(global, prog)
	prog.Scope = global
	return global
}

func checkProgram(s scope.Scope, prog *parser.Program) {
	for _, stmt := range prog.Stmts {
		checkStmt(s, stmt)
	}
}

func checkStmt(s scope.Scope, stmt parser.Stmt) {
	switch stmt := stmt.(type) {
	case *parser.IfStmt:
		checkIfStmt(s, stmt)
		break
	case *parser.DeclarationStmt:
		checkDeclarationStmt(s, stmt)
		break
	case *parser.ReturnStmt:
		checkReturnStmt(s, stmt)
		break
	case *parser.ExprStmt:
		checkExprAllowVoid(s, stmt.Expr)
		break
	}
}

func checkStmtBlock(s scope.Scope, block *parser.StmtBlock) {
	for _, stmt := range block.Stmts {
		checkStmt(s, stmt)
	}
}

func checkIfStmt(s scope.Scope, stmt *parser.IfStmt) {
	typ := checkExpr(s, stmt.Cond)
	if typ.Equals(types.Bool) == false {
		addTypeError(s, stmt.Cond.Start(), "condition must resolve to a boolean")
	}

	checkStmtBlock(s, stmt.Clause)
}

func checkDeclarationStmt(s scope.Scope, stmt *parser.DeclarationStmt) {
	name := stmt.Name.Name
	typ := checkExpr(s, stmt.Expr)
	s.NewVariable(name, typ)
}

func checkReturnStmt(s scope.Scope, stmt *parser.ReturnStmt) {
	var ret types.Type = types.Void{}
	if stmt.Expr != nil {
		ret = checkExpr(s, stmt.Expr)
	}

	if s.HasSelfReference() == false {
		addTypeError(s, stmt.Start(), "return statements must be inside a function")
		return
	}

	if s.GetSelfReference().Ret.Equals(ret) || ret.IsError() {
		return
	}

	if s.GetSelfReference().Ret.Equals(types.Void{}) {
		msg := fmt.Sprintf("expected to return nothing, got '%s'", ret)
		addTypeError(s, stmt.Expr.Start(), msg)
		return
	}

	if ret.Equals(types.Void{}) {
		msg := fmt.Sprintf("expected a return type of '%s', got nothing", s.GetSelfReference().Ret)
		addTypeError(s, stmt.Start(), msg)
		return
	}

	msg := fmt.Sprintf("expected to return '%s', got '%s'", s.GetSelfReference().Ret, ret)
	addTypeError(s, stmt.Expr.Start(), msg)
}

func checkExprAllowVoid(s scope.Scope, expr parser.Expr) types.Type {
	var typ types.Type = types.Error{}
	switch expr := expr.(type) {
	case *parser.FunctionExpr:
		typ = checkFunctionExpr(s, expr)
	case *parser.DispatchExpr:
		typ = checkDispatchExpr(s, expr)
	case *parser.AssignExpr:
		typ = checkAssignExpr(s, expr)
	case *parser.BinaryExpr:
		typ = checkBinaryExpr(s, expr, defaultBinopsLUT)
	case *parser.ListExpr:
		typ = checkListExpr(s, expr)
	case *parser.SubscriptExpr:
		typ = checkSubscriptExpr(s, expr, defaultBinopsLUT)
	case *parser.SelfExpr:
		typ = checkSelfExpr(s, expr)
	case *parser.IdentExpr:
		typ = checkIdentExpr(s, expr)
	case *parser.NumberExpr:
		typ = checkNumberExpr(s, expr)
	case *parser.StringExpr:
		typ = checkStringExpr(s, expr)
	case *parser.BooleanExpr:
		typ = checkBooleanExpr(s, expr)
	default:
		addTypeError(s, expr.Start(), "unknown expression type")
	}

	return typ
}

func checkExpr(s scope.Scope, expr parser.Expr) types.Type {
	typ := checkExprAllowVoid(s, expr)

	if typ.Equals(types.Void{}) {
		addTypeError(s, expr.Start(), "cannot use void types in an expression")
		return types.Error{}
	}

	return typ
}

func checkFunctionExpr(s scope.Scope, expr *parser.FunctionExpr) types.Type {
	ret := ConvertTypeNote(expr.Ret)
	params := []types.Type{}
	for _, param := range expr.Params {
		params = append(params, ConvertTypeNote(param.Note))
	}
	tuple := types.Tuple{Children: params}
	self := types.Function{Params: tuple, Ret: ret}

	childScope := scope.MakeLocalScope(s, self)

	for _, param := range expr.Params {
		paramName := param.Name.Name
		paramType := ConvertTypeNote(param.Note)
		childScope.NewVariable(paramName, paramType)
	}

	checkStmtBlock(childScope, expr.Block)
	expr.Scope = childScope
	return self
}

func checkDispatchExpr(s scope.Scope, expr *parser.DispatchExpr) types.Type {
	// Resolve arguments to types
	argTypes := []types.Type{}
	for _, argExpr := range expr.Args {
		argTypes = append(argTypes, checkExpr(s, argExpr))
	}

	// Resolve callee to type
	calleeType := checkExpr(s, expr.Callee)
	calleeFunc, ok := calleeType.(types.Function)
	if ok == false {
		if calleeType.IsError() == false {
			msg := fmt.Sprintf("cannot call function on type '%s'", calleeType)
			addTypeError(s, expr.Start(), msg)
		}

		return types.Error{}
	}

	// Resolve return type
	retType := calleeFunc.Ret

	// Check that the given argument types match the expected parameter types
	totalArgs := len(argTypes)
	totalParams := len(calleeFunc.Params.Children)
	if totalArgs == totalParams {
		for i := 0; i < totalArgs; i++ {
			argType := argTypes[i]
			paramType := calleeFunc.Params.Children[i]

			if argType.IsError() {
				retType = types.Error{}
			} else if argType.Equals(paramType) == false {
				msg := fmt.Sprintf("expected '%s', got '%s'", paramType, argType)
				addTypeError(s, expr.Args[i].Start(), msg)
				retType = types.Error{}
			}
		}
	} else {
		msg := fmt.Sprintf("expected %d arguments, got %d", totalParams, totalArgs)
		addTypeError(s, expr.Start(), msg)
		retType = types.Error{}
	}

	return retType
}

func checkAssignExpr(s scope.Scope, expr *parser.AssignExpr) types.Type {
	name := expr.Left.Name
	leftType := s.GetVariableType(name)
	rightType := checkExpr(s, expr.Right)

	if leftType == nil {
		msg := fmt.Sprintf("'%s' cannot be assigned before it is declared", name)
		addTypeError(s, expr.Start(), msg)
		return types.Error{}
	}

	if leftType.IsError() || rightType.IsError() {
		return types.Error{}
	}

	if leftType.Equals(rightType) == false {
		msg := fmt.Sprintf("'%s' cannot be assigned type '%s'", leftType, rightType)
		addTypeError(s, expr.Right.Start(), msg)
		return types.Error{}
	}

	return leftType
}

func checkBinaryExpr(s scope.Scope, expr *parser.BinaryExpr, lut binopsLUT) types.Type {
	leftType := checkExpr(s, expr.Left)
	rightType := checkExpr(s, expr.Right)

	if leftType.IsError() || rightType.IsError() {
		return types.Error{}
	}

	if operLUT, ok := lut[expr.Oper]; ok {
		if leftLUT, ok := operLUT[leftType]; ok {
			if retType, ok := leftLUT[rightType]; ok {
				return retType
			}
		}

		msg := fmt.Sprintf("operator '%s' does not support %s and %s", expr.Oper, leftType, rightType)
		addTypeError(s, expr.Tok.Loc, msg)
		return types.Error{}
	}

	msg := fmt.Sprintf("unknown infix operator '%s'", expr.Oper)
	addTypeError(s, expr.Tok.Loc, msg)
	return types.Error{}
}

func checkListExpr(s scope.Scope, expr *parser.ListExpr) types.Type {
	var elemTypes []types.Type
	for _, elem := range expr.Elements {
		elemTypes = append(elemTypes, checkExpr(s, elem))
	}

	if len(elemTypes) == 0 {
		msg := "cannot determine type from empty list"
		addTypeError(s, expr.Start(), msg)
		return types.Error{}
	}

	for _, typ := range elemTypes {
		if typ.IsError() {
			return types.Error{}
		}
	}

	var listType types.Type
	for i, typ := range elemTypes {
		if listType == nil {
			listType = typ
			continue
		}

		if listType.Equals(typ) == false {
			msg := fmt.Sprintf("element type %s is not compatible with type %s", typ, listType)
			addTypeError(s, expr.Elements[i].Start(), msg)
			return types.Error{}
		}
	}

	return types.List{Child: listType}
}

func checkSubscriptExpr(s scope.Scope, expr *parser.SubscriptExpr, lut binopsLUT) types.Type {
	listType := checkExpr(s, expr.ListLike)
	indexType := checkExpr(s, expr.Index)

	if listType.IsError() || indexType.IsError() {
		return types.Error{}
	}

	if listType, ok := listType.(types.List); ok {
		return types.Optional{Child: listType.Child}
	}

	if subscriptLUT, ok := lut["["]; ok {
		if listLUT, ok := subscriptLUT[listType]; ok {
			if retType, ok := listLUT[indexType]; ok {
				return retType
			}
		}

		msg := fmt.Sprintf("subscript operator does not support %s[%s]", listType, indexType)
		addTypeError(s, expr.Index.Start(), msg)
		return types.Error{}
	}

	addTypeError(s, expr.Start(), "unknown infix operator '['")
	return types.Error{}
}

func checkSelfExpr(s scope.Scope, expr *parser.SelfExpr) types.Type {
	if s.HasSelfReference() == false {
		addTypeError(s, expr.Start(), "self references must be inside a function")
		return types.Error{}
	}

	return s.GetSelfReference()
}

func checkIdentExpr(s scope.Scope, expr *parser.IdentExpr) types.Type {
	if s.HasVariable(expr.Name) {
		return s.GetVariableType(expr.Name)
	}

	msg := fmt.Sprintf("variable '%s' was used before it was declared", expr.Name)
	addTypeError(s, expr.Start(), msg)
	return types.Error{}
}

func checkNumberExpr(s scope.Scope, expr *parser.NumberExpr) types.Type {
	return types.Int
}

func checkStringExpr(s scope.Scope, expr *parser.StringExpr) types.Type {
	return types.Str
}

func checkBooleanExpr(s scope.Scope, expr *parser.BooleanExpr) types.Type {
	return types.Bool
}

// TypeCheckError combines a source code location with the resulting error message
type TypeCheckError struct {
	Loc     lexer.Loc
	Message string
}

func addTypeError(s scope.Scope, loc lexer.Loc, msg string) {
	err := TypeCheckError{loc, msg}
	s.NewError(err)
}

func (err TypeCheckError) Error() string {
	return fmt.Sprintf("%s %s", err.Loc, err.Message)
}
<commit_msg>change type check syntax to ‘target’.Equals(‘possible’)<commit_after>package check

import (
	"fmt"
	"plaid/lexer"
	"plaid/parser"
	"plaid/scope"
	"plaid/types"
	"plaid/vm"
)

type binopsLUT map[string]map[types.Type]map[types.Type]types.Type
type doubleLUT map[types.Type]map[types.Type]types.Type
type singleLUT map[types.Type]types.Type

var defaultBinopsLUT = binopsLUT{
	"+": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
		types.Str: singleLUT{types.Str: types.Str},
	},
	"-": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
	},
	"*": doubleLUT{
		types.Int: singleLUT{types.Int: types.Int},
	},
	"<": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	"<=": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	">": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	">=": doubleLUT{
		types.Int: singleLUT{types.Int: types.Bool},
	},
	"[": doubleLUT{
		types.Str: singleLUT{types.Int: types.Optional{Child: types.Str}},
	},
}

// Check takes an existing abstract syntax tree and performs type checks and
// other correctness checks. It returns a list of any errors that were
// discovered inside the AST
func Check(prog *parser.Program, modules ...*vm.Module) *scope.GlobalScope {
	global := scope.MakeGlobalScope()

	for _, module := range modules {
		global.Import(module)
	}

	checkProgram(global, prog)
	prog.Scope = global
	return global
}

func checkProgram(s scope.Scope, prog *parser.Program) {
	for _, stmt := range prog.Stmts {
		checkStmt(s, stmt)
	}
}

func checkStmt(s scope.Scope, stmt parser.Stmt) {
	switch stmt := stmt.(type) {
	case *parser.IfStmt:
		checkIfStmt(s, stmt)
		break
	case *parser.DeclarationStmt:
		checkDeclarationStmt(s, stmt)
		break
	case *parser.ReturnStmt:
		checkReturnStmt(s, stmt)
		break
	case *parser.ExprStmt:
		checkExprAllowVoid(s, stmt.Expr)
		break
	}
}

func checkStmtBlock(s scope.Scope, block *parser.StmtBlock) {
	for _, stmt := range block.Stmts {
		checkStmt(s, stmt)
	}
}

func checkIfStmt(s scope.Scope, stmt *parser.IfStmt) {
	typ := checkExpr(s, stmt.Cond)
	if types.Bool.Equals(typ) == false {
		addTypeError(s, stmt.Cond.Start(), "condition must resolve to a boolean")
	}

	checkStmtBlock(s, stmt.Clause)
}

func checkDeclarationStmt(s scope.Scope, stmt *parser.DeclarationStmt) {
	name := stmt.Name.Name
	typ := checkExpr(s, stmt.Expr)
	s.NewVariable(name, typ)
}

func checkReturnStmt(s scope.Scope, stmt *parser.ReturnStmt) {
	var ret types.Type = types.Void{}
	if stmt.Expr != nil {
		ret = checkExpr(s, stmt.Expr)
	}

	if s.HasSelfReference() == false {
		addTypeError(s, stmt.Start(), "return statements must be inside a function")
		return
	}

	if s.GetSelfReference().Ret.Equals(ret) || ret.IsError() {
		return
	}

	if s.GetSelfReference().Ret.Equals(types.Void{}) {
		msg := fmt.Sprintf("expected to return nothing, got '%s'", ret)
		addTypeError(s, stmt.Expr.Start(), msg)
		return
	}

	if (types.Void{}).Equals(ret) {
		msg := fmt.Sprintf("expected a return type of '%s', got nothing", s.GetSelfReference().Ret)
		addTypeError(s, stmt.Start(), msg)
		return
	}

	msg := fmt.Sprintf("expected to return '%s', got '%s'", s.GetSelfReference().Ret, ret)
	addTypeError(s, stmt.Expr.Start(), msg)
}

func checkExprAllowVoid(s scope.Scope, expr parser.Expr) types.Type {
	var typ types.Type = types.Error{}
	switch expr := expr.(type) {
	case *parser.FunctionExpr:
		typ = checkFunctionExpr(s, expr)
	case *parser.DispatchExpr:
		typ = checkDispatchExpr(s, expr)
	case *parser.AssignExpr:
		typ = checkAssignExpr(s, expr)
	case *parser.BinaryExpr:
		typ = checkBinaryExpr(s, expr, defaultBinopsLUT)
	case *parser.ListExpr:
		typ = checkListExpr(s, expr)
	case *parser.SubscriptExpr:
		typ = checkSubscriptExpr(s, expr, defaultBinopsLUT)
	case *parser.SelfExpr:
		typ = checkSelfExpr(s, expr)
	case *parser.IdentExpr:
		typ = checkIdentExpr(s, expr)
	case *parser.NumberExpr:
		typ = checkNumberExpr(s, expr)
	case *parser.StringExpr:
		typ = checkStringExpr(s, expr)
	case *parser.BooleanExpr:
		typ = checkBooleanExpr(s, expr)
	default:
		addTypeError(s, expr.Start(), "unknown expression type")
	}

	return typ
}

func checkExpr(s scope.Scope, expr parser.Expr) types.Type {
	typ := checkExprAllowVoid(s, expr)

	if (types.Void{}).Equals(typ) {
		addTypeError(s, expr.Start(), "cannot use void types in an expression")
		return types.Error{}
	}

	return typ
}

func checkFunctionExpr(s scope.Scope, expr *parser.FunctionExpr) types.Type {
	ret := ConvertTypeNote(expr.Ret)
	params := []types.Type{}
	for _, param := range expr.Params {
		params = append(params, ConvertTypeNote(param.Note))
	}
	tuple := types.Tuple{Children: params}
	self := types.Function{Params: tuple, Ret: ret}

	childScope := scope.MakeLocalScope(s, self)

	for _, param := range expr.Params {
		paramName := param.Name.Name
		paramType := ConvertTypeNote(param.Note)
		childScope.NewVariable(paramName, paramType)
	}

	checkStmtBlock(childScope, expr.Block)
	expr.Scope = childScope
	return self
}

func checkDispatchExpr(s scope.Scope, expr *parser.DispatchExpr) types.Type {
	// Resolve arguments to types
	argTypes := []types.Type{}
	for _, argExpr := range expr.Args {
		argTypes = append(argTypes, checkExpr(s, argExpr))
	}

	// Resolve callee to type
	calleeType := checkExpr(s, expr.Callee)
	calleeFunc, ok := calleeType.(types.Function)
	if ok == false {
		if calleeType.IsError() == false {
			msg := fmt.Sprintf("cannot call function on type '%s'", calleeType)
			addTypeError(s, expr.Start(), msg)
		}

		return types.Error{}
	}

	// Resolve return type
	retType := calleeFunc.Ret

	// Check that the given argument types match the expected parameter types
	totalArgs := len(argTypes)
	totalParams := len(calleeFunc.Params.Children)
	if totalArgs == totalParams {
		for i := 0; i < totalArgs; i++ {
			argType := argTypes[i]
			paramType := calleeFunc.Params.Children[i]

			if argType.IsError() {
				retType = types.Error{}
			} else if paramType.Equals(argType) == false {
				msg := fmt.Sprintf("expected '%s', got '%s'", paramType, argType)
				addTypeError(s, expr.Args[i].Start(), msg)
				retType = types.Error{}
			}
		}
	} else {
		msg := fmt.Sprintf("expected %d arguments, got %d", totalParams, totalArgs)
		addTypeError(s, expr.Start(), msg)
		retType = types.Error{}
	}

	return retType
}

func checkAssignExpr(s scope.Scope, expr *parser.AssignExpr) types.Type {
	name := expr.Left.Name
	leftType := s.GetVariableType(name)
	rightType := checkExpr(s, expr.Right)

	if leftType == nil {
		msg := fmt.Sprintf("'%s' cannot be assigned before it is declared", name)
		addTypeError(s, expr.Start(), msg)
		return types.Error{}
	}

	if leftType.IsError() || rightType.IsError() {
		return types.Error{}
	}

	if leftType.Equals(rightType) == false {
		msg := fmt.Sprintf("'%s' cannot be assigned type '%s'", leftType, rightType)
		addTypeError(s, expr.Right.Start(), msg)
		return types.Error{}
	}

	return leftType
}

func checkBinaryExpr(s scope.Scope, expr *parser.BinaryExpr, lut binopsLUT) types.Type {
	leftType := checkExpr(s, expr.Left)
	rightType := checkExpr(s, expr.Right)

	if leftType.IsError() || rightType.IsError() {
		return types.Error{}
	}

	if operLUT, ok := lut[expr.Oper]; ok {
		if leftLUT, ok := operLUT[leftType]; ok {
			if retType, ok := leftLUT[rightType]; ok {
				return retType
			}
		}

		msg := fmt.Sprintf("operator '%s' does not support %s and %s", expr.Oper, leftType, rightType)
		addTypeError(s, expr.Tok.Loc, msg)
		return types.Error{}
	}

	msg := fmt.Sprintf("unknown infix operator '%s'", expr.Oper)
	addTypeError(s, expr.Tok.Loc, msg)
	return types.Error{}
}

func checkListExpr(s scope.Scope, expr *parser.ListExpr) types.Type {
	var elemTypes []types.Type
	for _, elem := range expr.Elements {
		elemTypes = append(elemTypes, checkExpr(s, elem))
	}

	if len(elemTypes) == 0 {
		msg := "cannot determine type from empty list"
		addTypeError(s, expr.Start(), msg)
		return types.Error{}
	}

	for _, typ := range elemTypes {
		if typ.IsError() {
			return types.Error{}
		}
	}

	var listType types.Type
	for i, typ := range elemTypes {
		if listType == nil {
			listType = typ
			continue
		}

		if listType.Equals(typ) == false {
			msg := fmt.Sprintf("element type %s is not compatible with type %s", typ, listType)
			addTypeError(s, expr.Elements[i].Start(), msg)
			return types.Error{}
		}
	}

	return types.List{Child: listType}
}

func checkSubscriptExpr(s scope.Scope, expr *parser.SubscriptExpr, lut binopsLUT) types.Type {
	listType := checkExpr(s, expr.ListLike)
	indexType := checkExpr(s, expr.Index)

	if listType.IsError() || indexType.IsError() {
		return types.Error{}
	}

	if listType, ok := listType.(types.List); ok {
		return types.Optional{Child: listType.Child}
	}

	if subscriptLUT, ok := lut["["]; ok {
		if listLUT, ok := subscriptLUT[listType]; ok {
			if retType, ok := listLUT[indexType]; ok {
				return retType
			}
		}

		msg := fmt.Sprintf("subscript operator does not support %s[%s]", listType, indexType)
		addTypeError(s, expr.Index.Start(), msg)
		return types.Error{}
	}

	addTypeError(s, expr.Start(), "unknown infix operator '['")
	return types.Error{}
}

func checkSelfExpr(s scope.Scope, expr *parser.SelfExpr) types.Type {
	if s.HasSelfReference() == false {
		addTypeError(s, expr.Start(), "self references must be inside a function")
		return types.Error{}
	}

	return s.GetSelfReference()
}

func checkIdentExpr(s scope.Scope, expr *parser.IdentExpr) types.Type {
	if s.HasVariable(expr.Name) {
		return s.GetVariableType(expr.Name)
	}

	msg := fmt.Sprintf("variable '%s' was used before it was declared", expr.Name)
	addTypeError(s, expr.Start(), msg)
	return types.Error{}
}

func checkNumberExpr(s scope.Scope, expr *parser.NumberExpr) types.Type {
	return types.Int
}

func checkStringExpr(s scope.Scope, expr *parser.StringExpr) types.Type {
	return types.Str
}

func checkBooleanExpr(s scope.Scope, expr *parser.BooleanExpr) types.Type {
	return types.Bool
}

// TypeCheckError combines a source code location with the resulting error message
type TypeCheckError struct {
	Loc     lexer.Loc
	Message string
}

func addTypeError(s scope.Scope, loc lexer.Loc, msg string) {
	err := TypeCheckError{loc, msg}
	s.NewError(err)
}

func (err TypeCheckError) Error() string {
	return fmt.Sprintf("%s %s", err.Loc, err.Message)
}
<|endoftext|>


<commit_before>package check

import (
	"fmt"
)

// Type describes all type structs
type Type interface {
	Equals(other Type) bool
	IsError() bool
	String() string
	isType()
}

// TypeError signals that whatever expression was supposed to produce this type
// had a semantic error that made proper evaluation impossible
type TypeError struct{}

// Equals returns true for every other type
func (te TypeError) Equals(other Type) bool {
	return false
}

// IsError returns true because TypeError is an error
func (te TypeError) IsError() bool  { return true }
func (te TypeError) String() string { return "ERROR" }
func (te TypeError) isType()        {}

// TypeFunction describes mappings of 0+ parameter types to a return type
type TypeFunction struct {
	params TypeTuple
	ret    Type
}

// Equals returns true if another type has an identical structure and identical names
func (tf TypeFunction) Equals(other Type) bool {
	if tf2, ok := other.(TypeFunction); ok {
		return tf.params.Equals(tf2.params) && tf.ret.Equals(tf2.ret)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tf TypeFunction) IsError() bool  { return false }
func (tf TypeFunction) String() string { return fmt.Sprintf("%s => %s", tf.params, tf.ret) }
func (tf TypeFunction) isType()        {}

// TypeTuple describes a group of types
type TypeTuple struct {
	children []Type
}

// Equals returns true if another type has an identical structure and identical names
func (tt TypeTuple) Equals(other Type) bool {
	if tt2, ok := other.(TypeTuple); ok {
		if len(tt.children) != len(tt2.children) {
			return false
		}

		for i, child := range tt.children {
			child2 := tt2.children[i]
			if child.Equals(child2) == false {
				return false
			}
		}

		return true
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tt TypeTuple) IsError() bool  { return false }
func (tt TypeTuple) String() string { return fmt.Sprintf("(%s)", concatTypes(tt.children)) }
func (tt TypeTuple) isType()        {}

// TypeList describes an array of a common type
type TypeList struct {
	child Type
}

// Equals returns true if another type has an identical structure and identical names
func (tl TypeList) Equals(other Type) bool {
	if tl2, ok := other.(TypeList); ok {
		return tl.child.Equals(tl2.child)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tl TypeList) IsError() bool  { return false }
func (tl TypeList) String() string { return fmt.Sprintf("[%s]", tl.child) }
func (tl TypeList) isType()        {}

// TypeOptional describes a type that may resolve to a value or nothing
type TypeOptional struct {
	child Type
}

// Equals returns true if another type has an identical structure and identical names
func (to TypeOptional) Equals(other Type) bool {
	if to2, ok := other.(TypeOptional); ok {
		return to.child.Equals(to2.child)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (to TypeOptional) IsError() bool  { return false }
func (to TypeOptional) String() string { return fmt.Sprintf("%s?", to.child) }
func (to TypeOptional) isType()        {}

// TypeIdent describes a type aliased to an identifier
type TypeIdent struct {
	name string
}

// Equals returns true if another type has an identical structure and identical names
func (ti TypeIdent) Equals(other Type) bool {
	if ti2, ok := other.(TypeIdent); ok {
		return ti.name == ti2.name
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (ti TypeIdent) IsError() bool  { return false }
func (ti TypeIdent) String() string { return ti.name }
func (ti TypeIdent) isType()        {}

func concatTypes(types []Type) string {
	out := ""
	for i, typ := range types {
		if i > 0 {
			out += " "
		}
		out += typ.String()
	}
	return out
}
<commit_msg>handle case where TypeFunction#ret == nil<commit_after>package check

import (
	"fmt"
)

// Type describes all type structs
type Type interface {
	Equals(other Type) bool
	IsError() bool
	String() string
	isType()
}

// TypeError signals that whatever expression was supposed to produce this type
// had a semantic error that made proper evaluation impossible
type TypeError struct{}

// Equals returns true for every other type
func (te TypeError) Equals(other Type) bool {
	return false
}

// IsError returns true because TypeError is an error
func (te TypeError) IsError() bool  { return true }
func (te TypeError) String() string { return "ERROR" }
func (te TypeError) isType()        {}

// TypeFunction describes mappings of 0+ parameter types to a return type
type TypeFunction struct {
	params TypeTuple
	ret    Type
}

// Equals returns true if another type has an identical structure and identical names
func (tf TypeFunction) Equals(other Type) bool {
	if tf2, ok := other.(TypeFunction); ok {
		if tf.ret == nil {
			return tf.params.Equals(tf2.params) && tf.ret == tf2.ret
		}

		return tf.params.Equals(tf2.params) && tf.ret.Equals(tf2.ret)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tf TypeFunction) IsError() bool  { return false }
func (tf TypeFunction) String() string { return fmt.Sprintf("%s => %s", tf.params, tf.ret) }
func (tf TypeFunction) isType()        {}

// TypeTuple describes a group of types
type TypeTuple struct {
	children []Type
}

// Equals returns true if another type has an identical structure and identical names
func (tt TypeTuple) Equals(other Type) bool {
	if tt2, ok := other.(TypeTuple); ok {
		if len(tt.children) != len(tt2.children) {
			return false
		}

		for i, child := range tt.children {
			child2 := tt2.children[i]
			if child.Equals(child2) == false {
				return false
			}
		}

		return true
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tt TypeTuple) IsError() bool  { return false }
func (tt TypeTuple) String() string { return fmt.Sprintf("(%s)", concatTypes(tt.children)) }
func (tt TypeTuple) isType()        {}

// TypeList describes an array of a common type
type TypeList struct {
	child Type
}

// Equals returns true if another type has an identical structure and identical names
func (tl TypeList) Equals(other Type) bool {
	if tl2, ok := other.(TypeList); ok {
		return tl.child.Equals(tl2.child)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (tl TypeList) IsError() bool  { return false }
func (tl TypeList) String() string { return fmt.Sprintf("[%s]", tl.child) }
func (tl TypeList) isType()        {}

// TypeOptional describes a type that may resolve to a value or nothing
type TypeOptional struct {
	child Type
}

// Equals returns true if another type has an identical structure and identical names
func (to TypeOptional) Equals(other Type) bool {
	if to2, ok := other.(TypeOptional); ok {
		return to.child.Equals(to2.child)
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (to TypeOptional) IsError() bool  { return false }
func (to TypeOptional) String() string { return fmt.Sprintf("%s?", to.child) }
func (to TypeOptional) isType()        {}

// TypeIdent describes a type aliased to an identifier
type TypeIdent struct {
	name string
}

// Equals returns true if another type has an identical structure and identical names
func (ti TypeIdent) Equals(other Type) bool {
	if ti2, ok := other.(TypeIdent); ok {
		return ti.name == ti2.name
	}

	return false
}

// IsError returns false because this is a properly resolved type
func (ti TypeIdent) IsError() bool  { return false }
func (ti TypeIdent) String() string { return ti.name }
func (ti TypeIdent) isType()        {}

func concatTypes(types []Type) string {
	out := ""
	for i, typ := range types {
		if i > 0 {
			out += " "
		}
		out += typ.String()
	}
	return out
}
<|endoftext|>


<commit_before>package checks

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

	"github.com/akerl/prospectus/expectations"
)

const (
	prospectusDirName = ".prospectus.d"
)

// TODO: add timber logging
// TODO: add parallelization

// Check defines a single check that is ready for execution
type Check struct {
	Dir      string            `json:"dir"`
	File     string            `json:"file"`
	Name     string            `json:"name"`
	Metadata map[string]string `json:"metadata"`
}

// CheckSet defines a group of Checks
type CheckSet []Check

// Result defines the results of executing a Check
type Result struct {
	Actual   string               `json:"actual"`
	Expected expectations.Wrapper `json:"expected"`
	Check    Check                `json:"check"`
}

// ResultSet defines a group of Results
type ResultSet []Result

type loadCheckInput struct {
	Dir string `json:"dir"`
}

// NewSet returns a CheckSet based on a provided list of directories
func NewSet(relativeDirs []string) (CheckSet, error) {
	var err error

	dirs := make([]string, len(relativeDirs))
	for index, item := range relativeDirs {
		dirs[index], err = filepath.Abs(item)
		if err != nil {
			return CheckSet{}, err
		}
	}

	var cs CheckSet
	for _, item := range dirs {
		newSet, err := newSetFromDir(item)
		if err != nil {
			return CheckSet{}, err
		}
		cs = append(cs, newSet...)
	}

	return cs, nil
}

func newSetFromDir(absoluteDir string) (CheckSet, error) {
	prospectusDir := filepath.Join(absoluteDir, prospectusDirName)

	fileObjs, err := ioutil.ReadDir(prospectusDir)
	if err != nil {
		return CheckSet{}, err
	}

	var cs CheckSet
	for _, fileObj := range fileObjs {
		file := filepath.Join(prospectusDir, fileObj.Name())
		newSet, err := newSetFromFile(absoluteDir, file)
		if err != nil {
			return CheckSet{}, err
		}
		cs = append(cs, newSet...)
	}

	return cs, nil
}

func newSetFromFile(dir, file string) (CheckSet, error) {
	cs := CheckSet{}
	input := loadCheckInput{Dir: dir}
	err := execProspectusFile(file, "load", input, &cs)
	if err != nil {
		return CheckSet{}, err
	}
	for index := range cs {
		cs[index].Dir = dir
		cs[index].File = file
	}
	return cs, nil
}

func execProspectusFile(file, command string, input interface{}, output interface{}) error {
	cmd := exec.Command(file, command)

	inputBytes, err := json.Marshal(input)
	if err != nil {
		return err
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	stdin.Write(inputBytes)
	stdin.Close()

	stdout, err := cmd.Output()
	if err != nil {
		return err
	}

	return json.Unmarshal(stdout, output)
}

// Execute returns the Results from a CheckSet by calling Execute on each Check
func (cs CheckSet) Execute() ResultSet {
	resultSet := make(ResultSet, len(cs))
	for index, item := range cs {
		resultSet[index] = item.Execute()
	}
	return resultSet
}

// Execute runs the Check and returns Results
func (c Check) Execute() Result {
	return execProspectusForResult("execute", c, c)
}

func execProspectusForResult(method string, c Check, input interface{}) Result {
	r := Result{}
	err := execProspectusFile(c.File, method, input, &r)
	if err != nil {
		return Result{
			Actual: "error",
			Expected: expectations.Wrapper{
				Type: "error",
				Data: map[string]string{"msg": fmt.Sprintf("%s error: %s", method, err)},
			},
			Check: c,
		}
	}
	r.Check = c
	return r
}

// String returns the Result as a human-readable string
func (c Check) String() string {
	return fmt.Sprintf(
		"%s::%s",
		c.Dir,
		c.Name,
	)
}

// Changed filters a ResultSet to only Results which do not match
func (rs ResultSet) Changed() ResultSet {
	var newResultSet ResultSet
	for _, item := range rs {
		if !item.Matches() {
			newResultSet = append(newResultSet, item)
		}
	}
	return newResultSet
}

// Matches returns true if the Expected and Actual values of the Result match
func (r Result) Matches() bool {
	return r.Expected.Matches(r.Actual)
}

// JSON returns the ResultsSet as a marshalled JSON string
func (rs ResultSet) JSON() (string, error) {
	data, err := json.MarshalIndent(rs, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// String returns the ResultsSet as a human-readable string
func (rs ResultSet) String() string {
	var b strings.Builder
	for _, item := range rs {
		b.WriteString(item.String())
		b.WriteString("\n")
	}
	return b.String()
}

// String returns the Result as a human-readable string
func (r Result) String() string {
	return fmt.Sprintf(
		"%s: %s / %s",
		r.Check,
		r.Actual,
		r.Expected.String(),
	)
}

// Fix attempts to resolve a mismatched expectation
func (r Result) Fix() Result {
	return execProspectusForResult("fix", r.Check, r)
}
<commit_msg>helper for error results<commit_after>package checks

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

	"github.com/akerl/prospectus/expectations"
)

const (
	prospectusDirName = ".prospectus.d"
)

// TODO: add timber logging
// TODO: add parallelization

// Check defines a single check that is ready for execution
type Check struct {
	Dir      string            `json:"dir"`
	File     string            `json:"file"`
	Name     string            `json:"name"`
	Metadata map[string]string `json:"metadata"`
}

// CheckSet defines a group of Checks
type CheckSet []Check

// Result defines the results of executing a Check
type Result struct {
	Actual   string               `json:"actual"`
	Expected expectations.Wrapper `json:"expected"`
	Check    Check                `json:"check"`
}

// ResultSet defines a group of Results
type ResultSet []Result

type loadCheckInput struct {
	Dir string `json:"dir"`
}

// NewSet returns a CheckSet based on a provided list of directories
func NewSet(relativeDirs []string) (CheckSet, error) {
	var err error

	dirs := make([]string, len(relativeDirs))
	for index, item := range relativeDirs {
		dirs[index], err = filepath.Abs(item)
		if err != nil {
			return CheckSet{}, err
		}
	}

	var cs CheckSet
	for _, item := range dirs {
		newSet, err := newSetFromDir(item)
		if err != nil {
			return CheckSet{}, err
		}
		cs = append(cs, newSet...)
	}

	return cs, nil
}

func newSetFromDir(absoluteDir string) (CheckSet, error) {
	prospectusDir := filepath.Join(absoluteDir, prospectusDirName)

	fileObjs, err := ioutil.ReadDir(prospectusDir)
	if err != nil {
		return CheckSet{}, err
	}

	var cs CheckSet
	for _, fileObj := range fileObjs {
		file := filepath.Join(prospectusDir, fileObj.Name())
		newSet, err := newSetFromFile(absoluteDir, file)
		if err != nil {
			return CheckSet{}, err
		}
		cs = append(cs, newSet...)
	}

	return cs, nil
}

func newSetFromFile(dir, file string) (CheckSet, error) {
	cs := CheckSet{}
	input := loadCheckInput{Dir: dir}
	err := execProspectusFile(file, "load", input, &cs)
	if err != nil {
		return CheckSet{}, fmt.Errorf("Failed loading %s: %s", file, err)
	}
	for index := range cs {
		cs[index].Dir = dir
		cs[index].File = file
	}
	return cs, nil
}

func execProspectusFile(file, command string, input interface{}, output interface{}) error {
	cmd := exec.Command(file, command)

	inputBytes, err := json.Marshal(input)
	if err != nil {
		return err
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	stdin.Write(inputBytes)
	stdin.Close()

	stdout, err := cmd.Output()
	if err != nil {
		return err
	}

	return json.Unmarshal(stdout, output)
}

// Execute returns the Results from a CheckSet by calling Execute on each Check
func (cs CheckSet) Execute() ResultSet {
	resultSet := make(ResultSet, len(cs))
	for index, item := range cs {
		resultSet[index] = item.Execute()
	}
	return resultSet
}

// Execute runs the Check and returns Results
func (c Check) Execute() Result {
	return execProspectusForResult("execute", c, c)
}

func execProspectusForResult(method string, c Check, input interface{}) Result {
	r := Result{}
	err := execProspectusFile(c.File, method, input, &r)
	if err != nil {
		return NewErrorResult(fmt.Sprintf("%s error: %s", method, err), c)
	}
	r.Check = c
	return r
}

// String returns the Result as a human-readable string
func (c Check) String() string {
	return fmt.Sprintf(
		"%s::%s",
		c.Dir,
		c.Name,
	)
}

// Changed filters a ResultSet to only Results which do not match
func (rs ResultSet) Changed() ResultSet {
	var newResultSet ResultSet
	for _, item := range rs {
		if !item.Matches() {
			newResultSet = append(newResultSet, item)
		}
	}
	return newResultSet
}

// Matches returns true if the Expected and Actual values of the Result match
func (r Result) Matches() bool {
	return r.Expected.Matches(r.Actual)
}

// JSON returns the ResultsSet as a marshalled JSON string
func (rs ResultSet) JSON() (string, error) {
	data, err := json.MarshalIndent(rs, "", "  ")
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// String returns the ResultsSet as a human-readable string
func (rs ResultSet) String() string {
	var b strings.Builder
	for _, item := range rs {
		b.WriteString(item.String())
		b.WriteString("\n")
	}
	return b.String()
}

// String returns the Result as a human-readable string
func (r Result) String() string {
	return fmt.Sprintf(
		"%s: %s / %s",
		r.Check,
		r.Actual,
		r.Expected.String(),
	)
}

// Fix attempts to resolve a mismatched expectation
func (r Result) Fix() Result {
	return execProspectusForResult("fix", r.Check, r)
}

// NewErrorResult creates an error result from a given string
func NewErrorResult(msg string, c Check) Result {
	return Result{
		Actual: "error",
		Expected: expectations.Wrapper{
			Type: "error",
			Data: map[string]string{"msg": msg},
		},
		Check: c,
	}
}
<|endoftext|>


<commit_before>// Package github provides an http.Handler implementation that allows Empire to
// handle GitHub Deployments.
package github

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/ejholmes/hookshot"
	"github.com/ejholmes/hookshot/events"
	"github.com/remind101/empire"
	"golang.org/x/net/context"
)

var (
	// DefaultTemplate is a text/template string that will be used to map a
	// deployment event to a docker image to deploy.
	DefaultTemplate = `{{ .Repository.FullName }}:{{ .Deployment.Sha }}`
)

type Options struct {
	// The GitHub secret to ensure that the request was sent from GitHub.
	Secret string

	// If provided, specifies the environments that this Empire instance
	// should handle deployments for.
	Environments []string

	Deployer Deployer
}

func New(e *empire.Empire, opts Options) http.Handler {
	r := hookshot.NewRouter()

	secret := opts.Secret
	r.Handle("deployment", hookshot.Authorize(&DeploymentHandler{Deployer: opts.Deployer, environments: opts.Environments}, secret))
	r.Handle("ping", hookshot.Authorize(http.HandlerFunc(Ping), secret))

	return r
}

// Deployment is an http.Handler for handling the `deployment` event.
type DeploymentHandler struct {
	Deployer
	environments []string
}

func (h *DeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	panic("expected ServeHTTPContext to be called")
}

func (h *DeploymentHandler) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	var p events.Deployment

	if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return nil
	}

	if !currentEnvironment(p.Deployment.Environment, h.environments) {
		w.WriteHeader(http.StatusNoContent)
		fmt.Fprintf(w, "Ignore deployment to environment: %s", p.Deployment.Environment)
		return nil
	}
	if err := h.Deploy(ctx, p, os.Stdout); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return nil
	}

	w.WriteHeader(http.StatusAccepted)
	io.WriteString(w, "Ok\n")
	return nil
}

func currentEnvironment(eventEnv string, environments []string) bool {
	for _, env := range environments {
		if env == eventEnv {
			return true
		}
	}
	return false
}

func Ping(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Ok\n")
}
<commit_msg>Fix panic on ServeHTTP<commit_after>// Package github provides an http.Handler implementation that allows Empire to
// handle GitHub Deployments.
package github

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/ejholmes/hookshot"
	"github.com/ejholmes/hookshot/events"
	"github.com/remind101/empire"
)

var (
	// DefaultTemplate is a text/template string that will be used to map a
	// deployment event to a docker image to deploy.
	DefaultTemplate = `{{ .Repository.FullName }}:{{ .Deployment.Sha }}`
)

type Options struct {
	// The GitHub secret to ensure that the request was sent from GitHub.
	Secret string

	// If provided, specifies the environments that this Empire instance
	// should handle deployments for.
	Environments []string

	Deployer Deployer
}

func New(e *empire.Empire, opts Options) http.Handler {
	r := hookshot.NewRouter()

	secret := opts.Secret
	r.Handle("deployment", hookshot.Authorize(&DeploymentHandler{Deployer: opts.Deployer, environments: opts.Environments}, secret))
	r.Handle("ping", hookshot.Authorize(http.HandlerFunc(Ping), secret))

	return r
}

// Deployment is an http.Handler for handling the `deployment` event.
type DeploymentHandler struct {
	Deployer
	environments []string
}

func (h *DeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var p events.Deployment

	if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if !currentEnvironment(p.Deployment.Environment, h.environments) {
		w.WriteHeader(http.StatusNoContent)
		fmt.Fprintf(w, "Ignore deployment to environment: %s", p.Deployment.Environment)
		return
	}
	if err := h.Deploy(ctx, p, os.Stdout); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusAccepted)
	io.WriteString(w, "Ok\n")
}

func currentEnvironment(eventEnv string, environments []string) bool {
	for _, env := range environments {
		if env == eventEnv {
			return true
		}
	}
	return false
}

func Ping(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Ok\n")
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"time"
)

type Response interface {
	MakeATimestamp()
}

type baseResponse struct {
	Command   string
	Timestamp int64
}

func (r *baseResponse) MakeATimestamp() {
	r.Timestamp = time.Now().UnixNano() / 1e6
}

func Send(r Response, sender func([]byte)) error {
	r.MakeATimestamp()
	serialized, err := json.Marshal(r)
	if err == nil {
		sender(serialized)
	}
	return err
}
<commit_msg>Fix the marshaling of timestamp in response<commit_after>package response

import (
	"encoding/json"
	"time"
)

type Timestamp int64

type baseResponse struct {
	Command   string
	Timestamp Timestamp
}

func (t *Timestamp) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Now().UnixNano() / 1e6)
}

func Send(r interface{}, sender func([]byte)) error {
	serialized, err := json.Marshal(r)
	if err == nil {
		sender(serialized)
	}
	return err
}
<|endoftext|>


<commit_before>package httpservice

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sort"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
	gourdctx "github.com/gourd/kit/context"
	"github.com/gourd/kit/store"
	"golang.org/x/net/context"
)

// jsonEncodeFunc encodes given response into JSON
func jsonEncodeFunc(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
	w.Header().Add("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	err = enc.Encode(response)
	return
}

// jsonErrorEncoder expands given error to StoreError then encode to JSON
func jsonErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
	w.Header().Add("Content-Type", "application/json")

	// quick fix for gokit bad request wrapping problem
	switch err.(type) {
	case httptransport.Error:
		err = err.(httptransport.Error).Err
	}

	serr := store.ExpandError(err)
	log.Printf("error: %#v", serr.ServerMsg)
	json.NewEncoder(w).Encode(serr)
}

// NewJSONService creates a service descriptor
// with defaults for a simple JSON service
func NewJSONService(path string, ep endpoint.Endpoint) *Service {
	return &Service{
		Path:        path,
		Methods:     []string{"GET"},
		Weight:      0,
		Context:     gourdctx.NewEmpty(),
		Endpoint:    ep,
		Middlewares: &Middlewares{},
		EncodeFunc:  jsonEncodeFunc,
		Options: []httptransport.ServerOption{
			httptransport.ServerBefore(gourdctx.UseGorilla),
			httptransport.ServerErrorEncoder(jsonErrorEncoder),
		},
	}
}

// Service contains all parameters needed to call
// httptransport.NewServer
type Service struct {
	Path        string
	Methods     []string
	Weight      int
	Context     context.Context
	Middlewares *Middlewares
	Endpoint    endpoint.Endpoint
	DecodeFunc  httptransport.DecodeRequestFunc
	EncodeFunc  httptransport.EncodeResponseFunc
	Options     []httptransport.ServerOption
}

// Handler returns go-kit http transport server
// of the given definition
func (s Service) Handler() http.Handler {
	ep := s.Middlewares.Chain()(s.Endpoint)
	return httptransport.NewServer(
		s.Context,
		ep,
		s.DecodeFunc,
		s.EncodeFunc,
		s.Options...)
}

// Route add the given service to router with RouterFunc
func (s Service) Route(rtr RouterFunc) error {
	return rtr(s.Path, s.Methods, s.Handler())
}

// ServiceSlice attaches the method of sort.Interface to []*Service, sort in
// increasing order
type ServiceSlice []*Service

// Len implements sort.Interface
func (ss ServiceSlice) Len() int {
	return len(ss)
}

// Less implements sort.Interface
func (ss ServiceSlice) Less(i, j int) bool {
	return ss[i].Weight < ss[j].Weight
}

// Swap implements sort.Interface
func (ss ServiceSlice) Swap(i, j int) {
	ss[i], ss[j] = ss[j], ss[i]
}

// Sort is a short hand for sort.Sort(ServiceSlice)
func (ss ServiceSlice) Sort() {
	sort.Sort(ss)
}

// Services contain a group of named services
type Services map[string]*Service

// Patch takes a patch / patches and apply them to the group
func (services Services) Patch(patches ...ServicesPatch) {
	for _, patch := range patches {
		services = patch(services)
	}
}

// RouterFunc generalize router to route an http.Handler
type RouterFunc func(path string, methods []string, h http.Handler) error

// Each returns a channel that return services by weight order
func (services Services) Each() <-chan *Service {
	out := make(chan *Service)

	// turn services into a slices, sort it
	slice := ServiceSlice(make([]*Service, 0, len(services)))
	for _, s := range services {
		slice = append(slice, s)
	}
	sort.Sort(slice)

	// return *Service in the slice through output channel
	go func(out chan *Service, slice ServiceSlice) {
		defer close(out)
		for _, s := range slice {
			out <- s
		}
	}(out, slice)
	return out
}

// Route routes all services in the group
func (services Services) Route(rtr RouterFunc) (err error) {
	for service := range services.Each() {
		if err = service.Route(rtr); err != nil {
			err = fmt.Errorf("error routing %#v (method: %#v) (%#v)",
				service.Path, service.Methods, err.Error())
			return
		}
	}
	return
}

// ServicesPatch patches all children in a map[string]*Service
type ServicesPatch func(Services) Services
<commit_msg>service/http: Remove groudctx references<commit_after>package httpservice

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sort"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
	"github.com/gourd/kit/store"
	"golang.org/x/net/context"
)

// jsonEncodeFunc encodes given response into JSON
func jsonEncodeFunc(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
	w.Header().Add("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	err = enc.Encode(response)
	return
}

// jsonErrorEncoder expands given error to StoreError then encode to JSON
func jsonErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
	w.Header().Add("Content-Type", "application/json")

	// quick fix for gokit bad request wrapping problem
	switch err.(type) {
	case httptransport.Error:
		err = err.(httptransport.Error).Err
	}

	serr := store.ExpandError(err)
	log.Printf("error: %#v", serr.ServerMsg)
	json.NewEncoder(w).Encode(serr)
}

// NewJSONService creates a service descriptor
// with defaults for a simple JSON service
func NewJSONService(path string, ep endpoint.Endpoint) *Service {
	return &Service{
		Path:        path,
		Methods:     []string{"GET"},
		Weight:      0,
		Context:     context.Background(),
		Endpoint:    ep,
		Middlewares: &Middlewares{},
		EncodeFunc:  jsonEncodeFunc,
		Options: []httptransport.ServerOption{
			httptransport.ServerErrorEncoder(jsonErrorEncoder),
		},
	}
}

// Service contains all parameters needed to call
// httptransport.NewServer
type Service struct {
	Path        string
	Methods     []string
	Weight      int
	Context     context.Context
	Middlewares *Middlewares
	Endpoint    endpoint.Endpoint
	DecodeFunc  httptransport.DecodeRequestFunc
	EncodeFunc  httptransport.EncodeResponseFunc
	Options     []httptransport.ServerOption
}

// Handler returns go-kit http transport server
// of the given definition
func (s Service) Handler() http.Handler {
	ep := s.Middlewares.Chain()(s.Endpoint)
	return httptransport.NewServer(
		s.Context,
		ep,
		s.DecodeFunc,
		s.EncodeFunc,
		s.Options...)
}

// Route add the given service to router with RouterFunc
func (s Service) Route(rtr RouterFunc) error {
	return rtr(s.Path, s.Methods, s.Handler())
}

// ServiceSlice attaches the method of sort.Interface to []*Service, sort in
// increasing order
type ServiceSlice []*Service

// Len implements sort.Interface
func (ss ServiceSlice) Len() int {
	return len(ss)
}

// Less implements sort.Interface
func (ss ServiceSlice) Less(i, j int) bool {
	return ss[i].Weight < ss[j].Weight
}

// Swap implements sort.Interface
func (ss ServiceSlice) Swap(i, j int) {
	ss[i], ss[j] = ss[j], ss[i]
}

// Sort is a short hand for sort.Sort(ServiceSlice)
func (ss ServiceSlice) Sort() {
	sort.Sort(ss)
}

// Services contain a group of named services
type Services map[string]*Service

// Patch takes a patch / patches and apply them to the group
func (services Services) Patch(patches ...ServicesPatch) {
	for _, patch := range patches {
		services = patch(services)
	}
}

// RouterFunc generalize router to route an http.Handler
type RouterFunc func(path string, methods []string, h http.Handler) error

// Each returns a channel that return services by weight order
func (services Services) Each() <-chan *Service {
	out := make(chan *Service)

	// turn services into a slices, sort it
	slice := ServiceSlice(make([]*Service, 0, len(services)))
	for _, s := range services {
		slice = append(slice, s)
	}
	sort.Sort(slice)

	// return *Service in the slice through output channel
	go func(out chan *Service, slice ServiceSlice) {
		defer close(out)
		for _, s := range slice {
			out <- s
		}
	}(out, slice)
	return out
}

// Route routes all services in the group
func (services Services) Route(rtr RouterFunc) (err error) {
	for service := range services.Each() {
		if err = service.Route(rtr); err != nil {
			err = fmt.Errorf("error routing %#v (method: %#v) (%#v)",
				service.Path, service.Methods, err.Error())
			return
		}
	}
	return
}

// ServicesPatch patches all children in a map[string]*Service
type ServicesPatch func(Services) Services
<|endoftext|>


<commit_before>package som

import (
	"container/heap"
	"fmt"
	"sort"
)

// float64Slice is a helper type that allows to sort float64 slices using sort.Interface
// It keeps the track of original indexes in a separate idx slice
type float64Slice struct {
	// implements sort.Interface
	sort.Float64Slice
	// keeps track of original indices
	index []int
}

// override Swap method
func (s float64Slice) Swap(i, j int) {
	s.Float64Slice.Swap(i, j)
	s.index[i], s.index[j] = s.index[j], s.index[i]
}

func newFloat64Slice(n ...float64) *float64Slice {
	// we need to avoid overriding original slice
	fSlice := make([]float64, len(n))
	copy(fSlice, n)
	// initialize float slice
	s := &float64Slice{Float64Slice: sort.Float64Slice(fSlice), index: make([]int, len(n))}
	for i := range s.index {
		s.index[i] = i
	}

	return s
}

// float64HeapItem is an item that can be stored in float64Heap
// float64HeapItem implements Stringer interface for easier debugging
type float64Item struct {
	val   float64
	index int
}

func (f float64Item) Val() float64 {
	return f.val
}

func (f float64Item) String() string {
	return fmt.Sprintf("val: %f, index: %d", f.val, f.index)
}

// float64Heap provides an implementation of min float64 heap
// min heap is a binary that has a max value in its tip/peak
// float64Heap it implements heap.Interface
type float64Heap struct {
	// heap nodes
	items []*float64Item
	// heap size
	size int
	// heap capacity
	cap int
}

// newFloat64Heap initializes new heap and returns it
func newFloat64Heap(cap int, items ...*float64Item) (*float64Heap, error) {
	if cap <= 0 {
		return nil, fmt.Errorf("Invalid capacity supplied: %d\n", cap)
	}

	if cap < len(items) {
		return nil, fmt.Errorf("Items count exceeds capacity: %d\n", cap)
	}

	// pre-allocate heap buffer
	heapItems := make([]*float64Item, cap)
	for i, item := range items {
		heapItems[i] = item
	}
	// init the heap
	h := &float64Heap{
		items: heapItems,
		size:  len(items),
		cap:   cap,
	}
	heap.Init(h)

	return h, nil
}

func (h float64Heap) Len() int           { return h.size }
func (h float64Heap) Less(i, j int) bool { return h.items[i].val > h.items[j].val }
func (h float64Heap) Swap(i, j int)      { h.items[i], h.items[j] = h.items[j], h.items[i] }

func (h *float64Heap) Push(x interface{}) {
	item := x.(*float64Item)
	// if we are at full cap, just replace the peak
	switch h.size {
	case h.cap:
		if item.val < (*h).items[0].val {
			(*h).items[0] = item
			heap.Fix(h, 0)
		}
	default:
		h.items[h.size] = item
		h.size++
	}
}

func (h *float64Heap) Pop() interface{} {
	x := h.items[h.size-1]
	h.size--
	return x
}

func (h float64Heap) Cap() int {
	return h.cap
}
<commit_msg>removed superfluous cap field from float64Heap struct<commit_after>package som

import (
	"container/heap"
	"fmt"
	"sort"
)

// float64Slice is a helper type that allows to sort float64 slices using sort.Interface
// It keeps the track of original indexes in a separate idx slice
type float64Slice struct {
	// implements sort.Interface
	sort.Float64Slice
	// keeps track of original indices
	index []int
}

// override Swap method
func (s float64Slice) Swap(i, j int) {
	s.Float64Slice.Swap(i, j)
	s.index[i], s.index[j] = s.index[j], s.index[i]
}

func newFloat64Slice(n ...float64) *float64Slice {
	// we need to avoid overriding original slice
	fSlice := make([]float64, len(n))
	copy(fSlice, n)
	// initialize float slice
	s := &float64Slice{Float64Slice: sort.Float64Slice(fSlice), index: make([]int, len(n))}
	for i := range s.index {
		s.index[i] = i
	}

	return s
}

// float64HeapItem is an item that can be stored in float64Heap
// float64HeapItem implements Stringer interface for easier debugging
type float64Item struct {
	val   float64
	index int
}

func (f float64Item) Val() float64 {
	return f.val
}

func (f float64Item) String() string {
	return fmt.Sprintf("val: %f, index: %d", f.val, f.index)
}

// float64Heap provides an implementation of min float64 heap
// min heap is a binary that has a max value in its tip/peak
// float64Heap it implements heap.Interface
type float64Heap struct {
	// heap nodes
	items []*float64Item
	// heap size
	size int
}

// newFloat64Heap initializes new heap and returns it
func newFloat64Heap(cap int, items ...*float64Item) (*float64Heap, error) {
	if cap <= 0 {
		return nil, fmt.Errorf("Invalid capacity supplied: %d\n", cap)
	}

	if cap < len(items) {
		return nil, fmt.Errorf("Items count exceeds capacity: %d\n", cap)
	}

	// pre-allocate heap buffer
	heapItems := make([]*float64Item, cap)
	for i, item := range items {
		heapItems[i] = item
	}
	// init the heap
	h := &float64Heap{
		items: heapItems,
		size:  len(items),
	}
	heap.Init(h)

	return h, nil
}

func (h float64Heap) Len() int           { return h.size }
func (h float64Heap) Less(i, j int) bool { return h.items[i].val > h.items[j].val }
func (h float64Heap) Swap(i, j int)      { h.items[i], h.items[j] = h.items[j], h.items[i] }

func (h *float64Heap) Push(x interface{}) {
	item := x.(*float64Item)
	// if we are at full cap, just replace the peak
	switch h.size {
	case len(h.items):
		if item.val < (*h).items[0].val {
			(*h).items[0] = item
			heap.Fix(h, 0)
		}
	default:
		h.items[h.size] = item
		h.size++
	}
}

func (h *float64Heap) Pop() interface{} {
	x := h.items[h.size-1]
	h.size--
	return x
}

func (h float64Heap) Cap() int {
	return len(h.items)
}
<|endoftext|>


<commit_before>package trueskill

import "testing"

func TestSortByRank(t *testing.T) {
	dataIn := []Team{NewEmptyTeam(), NewEmptyTeam(), NewEmptyTeam(),
		NewEmptyTeam()}
	rankIn := []int{4, 3, 1, 1}

	dataOut := []Team{NewEmptyTeam(), NewEmptyTeam(), NewEmptyTeam(),
		NewEmptyTeam()}
	rankOut := []int{1, 1, 3, 4}

	data, rank := sortTeamByRank(dataIn, rankIn)

	if len(data) != len(dataOut) {
		t.Error("Lost data values during sorting. Expected",
			len(dataOut), "got", len(data))
	}

	if len(rank) != len(rankOut) {
		t.Error("Lost rank values during sorting. Expected",
			len(rankOut), "got", len(rank))
	}

	for i, v := range rank {
		if v != rankOut[i] {
			t.Error("Element", i, "of rank is not the same as expected")
		}
	}
}
<commit_msg>Fixed typo.<commit_after>package trueskill

import "testing"

func TestSortByRank(t *testing.T) {
	dataIn := []Team{NewEmptyTeam(), NewEmptyTeam(), NewEmptyTeam(),
		NewEmptyTeam()}
	rankIn := []int{4, 3, 1, 1}

	dataOut := []Team{NewEmptyTeam(), NewEmptyTeam(), NewEmptyTeam(),
		NewEmptyTeam()}
	rankOut := []int{1, 1, 3, 4}

	data, rank := sortTeamsByRank(dataIn, rankIn)

	if len(data) != len(dataOut) {
		t.Error("Lost data values during sorting. Expected",
			len(dataOut), "got", len(data))
	}

	if len(rank) != len(rankOut) {
		t.Error("Lost rank values during sorting. Expected",
			len(rankOut), "got", len(rank))
	}

	for i, v := range rank {
		if v != rankOut[i] {
			t.Error("Element", i, "of rank is not the same as expected")
		}
	}
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	criu "github.com/checkpoint-restore/go-criu/v4/rpc"
	"github.com/opencontainers/runc/libcontainer"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"

	"golang.org/x/sys/unix"
)

var checkpointCommand = cli.Command{
	Name:  "checkpoint",
	Usage: "checkpoint a running container",
	ArgsUsage: `<container-id>

Where "<container-id>" is the name for the instance of the container to be
checkpointed.`,
	Description: `The checkpoint command saves the state of the container instance.`,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
		cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
		cli.StringFlag{Name: "parent-path", Value: "", Usage: "path for previous criu image files in pre-dump"},
		cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
		cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
		cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
		cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
		cli.BoolFlag{Name: "lazy-pages", Usage: "use userfaultfd to lazily restore memory pages"},
		cli.IntFlag{Name: "status-fd", Value: -1, Usage: "criu writes \\0 to this FD once lazy-pages is ready"},
		cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
		cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
		cli.BoolFlag{Name: "pre-dump", Usage: "dump container's memory information only, leave the container running after this"},
		cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'"},
		cli.StringSliceFlag{Name: "empty-ns", Usage: "create a namespace, but don't restore its properties"},
		cli.BoolFlag{Name: "auto-dedup", Usage: "enable auto deduplication of memory images"},
	},
	Action: func(context *cli.Context) error {
		if err := checkArgs(context, 1, exactArgs); err != nil {
			return err
		}
		// XXX: Currently this is untested with rootless containers.
		if os.Geteuid() != 0 || system.RunningInUserNS() {
			logrus.Warn("runc checkpoint is untested with rootless containers")
		}

		container, err := getContainer(context)
		if err != nil {
			return err
		}
		status, err := container.Status()
		if err != nil {
			return err
		}
		if status == libcontainer.Created || status == libcontainer.Stopped {
			fatal(fmt.Errorf("Container cannot be checkpointed in %s state", status.String()))
		}
		options := criuOptions(context)
		if !(options.LeaveRunning || options.PreDump) {
			// destroy container unless we tell CRIU to keep it
			defer destroy(container)
		}
		// these are the mandatory criu options for a container
		setPageServer(context, options)
		setManageCgroupsMode(context, options)
		if err := setEmptyNsMask(context, options); err != nil {
			return err
		}
		return container.Checkpoint(options)
	},
}

func getCheckpointImagePath(context *cli.Context) string {
	imagePath := context.String("image-path")
	if imagePath == "" {
		imagePath = getDefaultImagePath(context)
	}
	return imagePath
}

func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
	// xxx following criu opts are optional
	// The dump image can be sent to a criu page server
	if psOpt := context.String("page-server"); psOpt != "" {
		addressPort := strings.Split(psOpt, ":")
		if len(addressPort) != 2 {
			fatal(errors.New("Use --page-server ADDRESS:PORT to specify page server"))
		}
		portInt, err := strconv.Atoi(addressPort[1])
		if err != nil {
			fatal(errors.New("Invalid port number"))
		}
		options.PageServer = libcontainer.CriuPageServerInfo{
			Address: addressPort[0],
			Port:    int32(portInt),
		}
	}
}

func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
	if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
		switch cgOpt {
		case "soft":
			options.ManageCgroupsMode = criu.CriuCgMode_SOFT
		case "full":
			options.ManageCgroupsMode = criu.CriuCgMode_FULL
		case "strict":
			options.ManageCgroupsMode = criu.CriuCgMode_STRICT
		default:
			fatal(errors.New("Invalid manage cgroups mode"))
		}
	}
}

var namespaceMapping = map[specs.LinuxNamespaceType]int{
	specs.NetworkNamespace: unix.CLONE_NEWNET,
}

func setEmptyNsMask(context *cli.Context, options *libcontainer.CriuOpts) error {
	/* Runc doesn't manage network devices and their configuration */
	nsmask := unix.CLONE_NEWNET

	for _, ns := range context.StringSlice("empty-ns") {
		f, exists := namespaceMapping[specs.LinuxNamespaceType(ns)]
		if !exists {
			return fmt.Errorf("namespace %q is not supported", ns)
		}
		nsmask |= f
	}

	options.EmptyNs = uint32(nsmask)
	return nil
}
<commit_msg>checkpoint: setPageServer: use net.SplitHostPort instead of strings.Split<commit_after>// +build linux

package main

import (
	"errors"
	"fmt"
	"net"
	"os"
	"strconv"

	criu "github.com/checkpoint-restore/go-criu/v4/rpc"
	"github.com/opencontainers/runc/libcontainer"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"golang.org/x/sys/unix"
)

var checkpointCommand = cli.Command{
	Name:  "checkpoint",
	Usage: "checkpoint a running container",
	ArgsUsage: `<container-id>

Where "<container-id>" is the name for the instance of the container to be
checkpointed.`,
	Description: `The checkpoint command saves the state of the container instance.`,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
		cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
		cli.StringFlag{Name: "parent-path", Value: "", Usage: "path for previous criu image files in pre-dump"},
		cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
		cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
		cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
		cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
		cli.BoolFlag{Name: "lazy-pages", Usage: "use userfaultfd to lazily restore memory pages"},
		cli.IntFlag{Name: "status-fd", Value: -1, Usage: "criu writes \\0 to this FD once lazy-pages is ready"},
		cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
		cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
		cli.BoolFlag{Name: "pre-dump", Usage: "dump container's memory information only, leave the container running after this"},
		cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'"},
		cli.StringSliceFlag{Name: "empty-ns", Usage: "create a namespace, but don't restore its properties"},
		cli.BoolFlag{Name: "auto-dedup", Usage: "enable auto deduplication of memory images"},
	},
	Action: func(context *cli.Context) error {
		if err := checkArgs(context, 1, exactArgs); err != nil {
			return err
		}
		// XXX: Currently this is untested with rootless containers.
		if os.Geteuid() != 0 || system.RunningInUserNS() {
			logrus.Warn("runc checkpoint is untested with rootless containers")
		}

		container, err := getContainer(context)
		if err != nil {
			return err
		}
		status, err := container.Status()
		if err != nil {
			return err
		}
		if status == libcontainer.Created || status == libcontainer.Stopped {
			fatal(fmt.Errorf("Container cannot be checkpointed in %s state", status.String()))
		}
		options := criuOptions(context)
		if !(options.LeaveRunning || options.PreDump) {
			// destroy container unless we tell CRIU to keep it
			defer destroy(container)
		}
		// these are the mandatory criu options for a container
		setPageServer(context, options)
		setManageCgroupsMode(context, options)
		if err := setEmptyNsMask(context, options); err != nil {
			return err
		}
		return container.Checkpoint(options)
	},
}

func getCheckpointImagePath(context *cli.Context) string {
	imagePath := context.String("image-path")
	if imagePath == "" {
		imagePath = getDefaultImagePath(context)
	}
	return imagePath
}

func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
	// xxx following criu opts are optional
	// The dump image can be sent to a criu page server
	if psOpt := context.String("page-server"); psOpt != "" {
		address, port, err := net.SplitHostPort(psOpt)

		if err != nil || address == "" || port == "" {
			fatal(errors.New("Use --page-server ADDRESS:PORT to specify page server"))
		}
		portInt, err := strconv.Atoi(port)
		if err != nil {
			fatal(errors.New("Invalid port number"))
		}
		options.PageServer = libcontainer.CriuPageServerInfo{
			Address: address,
			Port:    int32(portInt),
		}
	}
}

func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
	if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
		switch cgOpt {
		case "soft":
			options.ManageCgroupsMode = criu.CriuCgMode_SOFT
		case "full":
			options.ManageCgroupsMode = criu.CriuCgMode_FULL
		case "strict":
			options.ManageCgroupsMode = criu.CriuCgMode_STRICT
		default:
			fatal(errors.New("Invalid manage cgroups mode"))
		}
	}
}

var namespaceMapping = map[specs.LinuxNamespaceType]int{
	specs.NetworkNamespace: unix.CLONE_NEWNET,
}

func setEmptyNsMask(context *cli.Context, options *libcontainer.CriuOpts) error {
	/* Runc doesn't manage network devices and their configuration */
	nsmask := unix.CLONE_NEWNET

	for _, ns := range context.StringSlice("empty-ns") {
		f, exists := namespaceMapping[specs.LinuxNamespaceType(ns)]
		if !exists {
			return fmt.Errorf("namespace %q is not supported", ns)
		}
		nsmask |= f
	}

	options.EmptyNs = uint32(nsmask)
	return nil
}
<|endoftext|>


<commit_before>package perceptron

type Classifier struct {
	weight    Matrix
	extractor Extractor
}

func (c *Classifier) Weight() Matrix {
	return c.weight
}

func (c *Classifier) Classify(instance Instance) (Instance, error) {
	inference := instance.Clone()

	feature, err := c.extractor.Extract(instance)
	if err != nil {
		return nil, err
	}
	inference.SetFeature(feature)

	score, err := c.Weight().Dot(feature)
	if err != nil {
		return nil, err
	}
	inference.SetScore(score)

	if score > 0 {
		inference.SetLabel(1)
	} else {
		inference.SetLabel(-1)
	}

	return inference, nil
}

type Extractor interface {
	Extract(instance Instance) (Vector, error)
}
<commit_msg>"Classifier", which is a binary classifier, requires a feature extractor.<commit_after>package perceptron

type Classifier struct {
	weight    Matrix
	extractor Extractor
}

func NewClassifier(extractor Extractor) *Classifier {
	// TODO: Initialize weight with zero-vector.
	c := &Classifier{
		weight:    nil,
		extractor: extractor,
	}

	return c
}

func (c *Classifier) Weight() Matrix {
	return c.weight
}

func (c *Classifier) Classify(instance Instance) (Instance, error) {
	inference := instance.Clone()

	feature, err := c.extractor.Extract(instance)
	if err != nil {
		return nil, err
	}
	inference.SetFeature(feature)

	score, err := c.Weight().Dot(feature)
	if err != nil {
		return nil, err
	}
	inference.SetScore(score)

	if score > 0 {
		inference.SetLabel(1)
	} else {
		inference.SetLabel(-1)
	}

	return inference, nil
}

type Extractor interface {
	Extract(instance Instance) (Vector, error)
}
<|endoftext|>


<commit_before>package bot

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/datastore"
	"cloud.google.com/go/trace"
	"github.com/nlopes/slack"
	"google.golang.org/api/iterator"
)

type (
	gerritCL struct {
		Project         string `json:"project"`
		ChangeID        string `json:"change_id"`
		Number          int    `json:"_number"`
		Subject         string `json:"subject"`
		Branch          string `json:"branch"`
		CurrentRevision string `json:"current_revision"`
		Revisions       map[string]struct {
			Commit struct {
				Subject string `json:"subject"`
				Message string `json:"message"`
			} `json:"commit"`
		} `json:"revisions"`
	}

	storedCL struct {
		Tweeted   bool      `datastore:"Tweeted,noindex"`
		URL       string    `datastore:"URL,noindex"`
		Message   string    `datastore:"Message,noindex"`
		CrawledAt time.Time `datastore:"CrawledAt"`
	}
)

func (cl *gerritCL) link() string {
	return fmt.Sprintf("https://golang.org/cl/%d/", cl.Number)
}

func (cl *gerritCL) message() string {
	subject := cl.Subject
	if cl.Project != "go" {
		subject = fmt.Sprintf("[%s] %s", cl.Project, subject)
	}

	return subject
}

func (b *Bot) getCLFromDS(ctx context.Context, query *datastore.Query) (*datastore.Key, *storedCL, error) {
	iter := b.dsClient.Run(ctx, query)

	dst := &storedCL{}
	key, err := iter.Next(dst)
	if err != nil && err != iterator.Done {
		b.logf("error while fetching history: %v\n", err)
		return nil, nil, err
	}

	return key, dst, nil
}

func (b *Bot) GetLastSeenCL(ctx context.Context) (int, error) {
	latestCLQuery := datastore.NewQuery("GoCL").
		Order("-CrawledAt").
		Limit(1).
		KeysOnly()

	key, _, err := b.getCLFromDS(ctx, latestCLQuery)
	if err != nil {
		return -1, err
	}
	if key == nil {
		return -1, nil
	}
	return int(key.ID), nil
}

func (b *Bot) wasShown(ctx context.Context, cl gerritCL) (bool, error) {
	key := datastore.IDKey("GoCL", int64(cl.Number), nil)
	query := datastore.NewQuery("GoCL").Ancestor(key)
	key, _, err := b.getCLFromDS(ctx, query)
	return key != nil, err
}

func (b *Bot) saveCL(ctx context.Context, cl gerritCL) error {
	taskKey := datastore.IDKey("GoCL", int64(cl.Number), nil)
	gocl := &storedCL{
		URL:       cl.link(),
		Message:   cl.message(),
		CrawledAt: time.Now(),
	}
	_, err := b.dsClient.Put(ctx, taskKey, gocl)
	return err
}

func (b *Bot) updateCL(ctx context.Context, key *datastore.Key, cl *storedCL) error {
	taskKey := datastore.IDKey("GoCL", key.ID, nil)
	_, err := b.dsClient.Put(ctx, taskKey, cl)
	return err
}

func (b *Bot) processCLList(ctx context.Context, lastID int, span *trace.Span) int {
	req, err := http.NewRequest("GET", b.gerritLink, nil)
	req.Header.Add("User-Agent", "Gophers Slack bot")
	req = req.WithContext(ctx)

	resp, err := b.client.Do(req)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		b.logf("got non-200 code: %d from gerrit api", resp.StatusCode)
		return lastID
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	if len(body) < 4 {
		b.logf("got body: %s\n", string(body))
		return lastID
	}

	// Fix Gerrit adding a random prefix )]}'
	body = body[4:]
	cls := []gerritCL{}
	err = json.Unmarshal(body, &cls)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	foundIdx := len(cls) - 1
	for idx := len(cls) - 1; idx >= 0; idx-- {
		if cls[idx].Number == lastID {
			foundIdx = idx
			break
		}
	}

	pubChannel := b.channels["golang-cls"].slackID
	if pubChannel[:1] == "#" {
		pubChannel = pubChannel[1:]
	}

	pvtChannel := b.channels["golang_cls"].slackID

	for idx := foundIdx - 1; idx >= 0; idx-- {
		cl := cls[idx]

		if shown, err := b.wasShown(ctx, cl); err == nil {
			if shown {
				continue
			}
		} else {
			b.logf("got error: %v\n", err)
			continue
		}

		msg := slack.Attachment{
			Title:     cl.Subject,
			TitleLink: cl.link(),
			Text:      cl.Revisions[cl.CurrentRevision].Commit.Message,
			Footer:    cl.ChangeID,
		}
		params := slack.PostMessageParameters{AsUser: true}
		params.Attachments = append(params.Attachments, msg)

		err = b.saveCL(ctx, cl)
		if err != nil {
			b.logf("got error while saving CL to datastore: %v", err)
			return lastID
		}

		_, _, err = b.slackBotAPI.PostMessageContext(ctx, pvtChannel, fmt.Sprintf("[%d] %s: %s", cl.Number, cl.message(), cl.link()), params)
		if err != nil {
			b.logf("%s\n", err)
			continue
		}

		lastID = cl.Number

		_, _, err = b.slackBotAPI.PostMessageContext(ctx, pubChannel, fmt.Sprintf("[%d] %s: %s", cl.Number, cl.message(), cl.link()), params)
		if err != nil {
			b.logf("%s\n", err)
			continue
		}
	}

	return lastID
}

func shareCL(ctx context.Context, b *Bot, event *slack.MessageEvent) {
	// repeats some earlier work but oh well
	eventText := strings.Trim(strings.ToLower(event.Text), " \n\r")
	eventText = b.trimBot(eventText)

	if !b.specialRestrictions("golang_cls", event) {
		b.logf("share attempt caught: %#v\n", event)

		params := slack.PostMessageParameters{AsUser: true}
		_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, `You are not authorized to share CLs`, params)
		if err != nil {
			b.logf("%s\n", err)
		}
		return
	}

	eventText = strings.Replace(eventText, "share cl", "", -1)
	eventText = strings.Trim(eventText, " \n")

	for _, text := range strings.Fields(eventText) {
		clNumber, err := strconv.ParseInt(text, 10, 64)
		if err != nil {
			b.logf("could not convert string to int: %v from event: %#v\n", err, event)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		key := datastore.IDKey("GoCL", clNumber, nil)
		query := datastore.NewQuery("GoCL").Ancestor(key)
		key, cl, err := b.getCLFromDS(ctx, query)
		if err != nil {
			b.logf("error while retriving CL from the DB: %v\n", err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		if cl.Tweeted {
			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Already tweeted CL %d`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		message := cl.Message + " " + cl.URL
		_, err = b.twitterAPI.PostTweet(message, nil)
		if err != nil {
			b.logf("got error while tweeting CL: %d %#v\n", clNumber, err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		cl.Tweeted = true
		err = b.updateCL(ctx, key, cl)
		if err != nil {
			b.logf("got error while updating CL to datastore: %v", err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not update tweet status for CL %d in the DB`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
		}
	}
}

// MonitorGerrit handles the Gerrit changes
func (b *Bot) MonitorGerrit(duration time.Duration) {
	tk := time.NewTicker(duration)
	defer tk.Stop()

	span := b.traceClient.NewSpan("b.MonitorGerrit")
	ctx := trace.NewContext(context.Background(), span)

	lastID, err := b.GetLastSeenCL(ctx)
	if err != nil {
		b.logf("got error while loading last ID from the datastore: %v\n", err)
		return
	}

	lastID = b.processCLList(ctx, lastID, span)
	span.Finish()
	for range tk.C {
		span = b.traceClient.NewSpan("b.processCLList")
		ctx := trace.NewContext(context.Background(), span)
		lastID = b.processCLList(ctx, lastID, span)
		span.Finish()
	}
}
<commit_msg>Make this message more informative<commit_after>package bot

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/datastore"
	"cloud.google.com/go/trace"
	"github.com/nlopes/slack"
	"google.golang.org/api/iterator"
)

type (
	gerritCL struct {
		Project         string `json:"project"`
		ChangeID        string `json:"change_id"`
		Number          int    `json:"_number"`
		Subject         string `json:"subject"`
		Branch          string `json:"branch"`
		CurrentRevision string `json:"current_revision"`
		Revisions       map[string]struct {
			Commit struct {
				Subject string `json:"subject"`
				Message string `json:"message"`
			} `json:"commit"`
		} `json:"revisions"`
	}

	storedCL struct {
		Tweeted   bool      `datastore:"Tweeted,noindex"`
		URL       string    `datastore:"URL,noindex"`
		Message   string    `datastore:"Message,noindex"`
		CrawledAt time.Time `datastore:"CrawledAt"`
	}
)

func (cl *gerritCL) link() string {
	return fmt.Sprintf("https://golang.org/cl/%d/", cl.Number)
}

func (cl *gerritCL) message() string {
	subject := cl.Subject
	if cl.Project != "go" {
		subject = fmt.Sprintf("[%s] %s", cl.Project, subject)
	}

	return subject
}

func (b *Bot) getCLFromDS(ctx context.Context, query *datastore.Query) (*datastore.Key, *storedCL, error) {
	iter := b.dsClient.Run(ctx, query)

	dst := &storedCL{}
	key, err := iter.Next(dst)
	if err != nil && err != iterator.Done {
		b.logf("error while fetching history: %v\n", err)
		return nil, nil, err
	}

	return key, dst, nil
}

func (b *Bot) GetLastSeenCL(ctx context.Context) (int, error) {
	latestCLQuery := datastore.NewQuery("GoCL").
		Order("-CrawledAt").
		Limit(1).
		KeysOnly()

	key, _, err := b.getCLFromDS(ctx, latestCLQuery)
	if err != nil {
		return -1, err
	}
	if key == nil {
		return -1, nil
	}
	return int(key.ID), nil
}

func (b *Bot) wasShown(ctx context.Context, cl gerritCL) (bool, error) {
	key := datastore.IDKey("GoCL", int64(cl.Number), nil)
	query := datastore.NewQuery("GoCL").Ancestor(key)
	key, _, err := b.getCLFromDS(ctx, query)
	return key != nil, err
}

func (b *Bot) saveCL(ctx context.Context, cl gerritCL) error {
	taskKey := datastore.IDKey("GoCL", int64(cl.Number), nil)
	gocl := &storedCL{
		URL:       cl.link(),
		Message:   cl.message(),
		CrawledAt: time.Now(),
	}
	_, err := b.dsClient.Put(ctx, taskKey, gocl)
	return err
}

func (b *Bot) updateCL(ctx context.Context, key *datastore.Key, cl *storedCL) error {
	taskKey := datastore.IDKey("GoCL", key.ID, nil)
	_, err := b.dsClient.Put(ctx, taskKey, cl)
	return err
}

func (b *Bot) processCLList(ctx context.Context, lastID int, span *trace.Span) int {
	req, err := http.NewRequest("GET", b.gerritLink, nil)
	req.Header.Add("User-Agent", "Gophers Slack bot")
	req = req.WithContext(ctx)

	resp, err := b.client.Do(req)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		b.logf("got non-200 code: %d from gerrit api", resp.StatusCode)
		return lastID
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	if len(body) < 4 {
		b.logf("got body: %s\n", string(body))
		return lastID
	}

	// Fix Gerrit adding a random prefix )]}'
	body = body[4:]
	cls := []gerritCL{}
	err = json.Unmarshal(body, &cls)
	if err != nil {
		b.logf("%s\n", err)
		return lastID
	}

	foundIdx := len(cls) - 1
	for idx := len(cls) - 1; idx >= 0; idx-- {
		if cls[idx].Number == lastID {
			foundIdx = idx
			break
		}
	}

	pubChannel := b.channels["golang-cls"].slackID
	if pubChannel[:1] == "#" {
		pubChannel = pubChannel[1:]
	}

	pvtChannel := b.channels["golang_cls"].slackID

	for idx := foundIdx - 1; idx >= 0; idx-- {
		cl := cls[idx]

		if shown, err := b.wasShown(ctx, cl); err == nil {
			if shown {
				continue
			}
		} else {
			b.logf("got error: %v\n", err)
			continue
		}

		msg := slack.Attachment{
			Title:     cl.Subject,
			TitleLink: cl.link(),
			Text:      cl.Revisions[cl.CurrentRevision].Commit.Message,
			Footer:    cl.ChangeID,
		}
		params := slack.PostMessageParameters{AsUser: true}
		params.Attachments = append(params.Attachments, msg)

		err = b.saveCL(ctx, cl)
		if err != nil {
			b.logf("got error while saving CL to datastore: %v", err)
			return lastID
		}

		_, _, err = b.slackBotAPI.PostMessageContext(ctx, pvtChannel, fmt.Sprintf("[%d] %s: %s", cl.Number, cl.message(), cl.link()), params)
		if err != nil {
			b.logf("Error Posting Message to %s: %s\n", pvtChannel, err)
			continue
		}

		lastID = cl.Number

		_, _, err = b.slackBotAPI.PostMessageContext(ctx, pubChannel, fmt.Sprintf("[%d] %s: %s", cl.Number, cl.message(), cl.link()), params)
		if err != nil {
			b.logf("%s\n", err)
			continue
		}
	}

	return lastID
}

func shareCL(ctx context.Context, b *Bot, event *slack.MessageEvent) {
	// repeats some earlier work but oh well
	eventText := strings.Trim(strings.ToLower(event.Text), " \n\r")
	eventText = b.trimBot(eventText)

	if !b.specialRestrictions("golang_cls", event) {
		b.logf("share attempt caught: %#v\n", event)

		params := slack.PostMessageParameters{AsUser: true}
		_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, `You are not authorized to share CLs`, params)
		if err != nil {
			b.logf("%s\n", err)
		}
		return
	}

	eventText = strings.Replace(eventText, "share cl", "", -1)
	eventText = strings.Trim(eventText, " \n")

	for _, text := range strings.Fields(eventText) {
		clNumber, err := strconv.ParseInt(text, 10, 64)
		if err != nil {
			b.logf("could not convert string to int: %v from event: %#v\n", err, event)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		key := datastore.IDKey("GoCL", clNumber, nil)
		query := datastore.NewQuery("GoCL").Ancestor(key)
		key, cl, err := b.getCLFromDS(ctx, query)
		if err != nil {
			b.logf("error while retriving CL from the DB: %v\n", err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		if cl.Tweeted {
			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Already tweeted CL %d`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		message := cl.Message + " " + cl.URL
		_, err = b.twitterAPI.PostTweet(message, nil)
		if err != nil {
			b.logf("got error while tweeting CL: %d %#v\n", clNumber, err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not share CL %d, please try again`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
			continue
		}

		cl.Tweeted = true
		err = b.updateCL(ctx, key, cl)
		if err != nil {
			b.logf("got error while updating CL to datastore: %v", err)

			params := slack.PostMessageParameters{AsUser: true}
			_, _, err := b.slackBotAPI.PostMessageContext(ctx, event.User, fmt.Sprintf(`Could not update tweet status for CL %d in the DB`, clNumber), params)
			if err != nil {
				b.logf("%s\n", err)
			}
		}
	}
}

// MonitorGerrit handles the Gerrit changes
func (b *Bot) MonitorGerrit(duration time.Duration) {
	tk := time.NewTicker(duration)
	defer tk.Stop()

	span := b.traceClient.NewSpan("b.MonitorGerrit")
	ctx := trace.NewContext(context.Background(), span)

	lastID, err := b.GetLastSeenCL(ctx)
	if err != nil {
		b.logf("got error while loading last ID from the datastore: %v\n", err)
		return
	}

	lastID = b.processCLList(ctx, lastID, span)
	span.Finish()
	for range tk.C {
		span = b.traceClient.NewSpan("b.processCLList")
		ctx := trace.NewContext(context.Background(), span)
		lastID = b.processCLList(ctx, lastID, span)
		span.Finish()
	}
}
<|endoftext|>


<commit_before>package bridge

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/cmdex"
)

// // BitriseRunOrTrigger ...
// func BitriseRunOrTrigger(inventoryPth, configPth, workflowNameOrTriggerPattern string, isUseTrigger bool) error {
// 	logLevel := log.GetLevel().String()
// bitriseCommandToUse := "run"
// if isUseTrigger {
// 	bitriseCommandToUse = "trigger"
// }
// 	args := []string{"--loglevel", logLevel, bitriseCommandToUse, workflowNameOrTriggerPattern, "--path", configPth}
// 	if inventoryPth != "" {
// 		args = append(args, "--inventory", inventoryPth)
// 	}
// 	return cmdex.RunCommand("bitrise", args...)
// }

// CMDBridgeDoBitriseRunOrTrigger ...
func CMDBridgeDoBitriseRunOrTrigger(inventoryPth, configPth, workflowNameOrTriggerPattern string, isUseTrigger bool, workdirPath string) error {
	logLevel := log.GetLevel().String()

	bitriseCommandToUse := "run"
	if isUseTrigger {
		bitriseCommandToUse = "trigger"
	}

	params := fmt.Sprintf("bitrise --loglevel %s %s %s --path %s", logLevel, bitriseCommandToUse, workflowNameOrTriggerPattern, configPth)
	if inventoryPth != "" {
		params = params + fmt.Sprintf(" --inventory %s", inventoryPth)
	}

	args := []string{"-workdir", workdirPath, "-do", params}

	return cmdex.RunCommand("cmd-bridge", args...)
}
<commit_msg>bitrise `--path` replaced with the new, non deprecated `--config` flag<commit_after>package bridge

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/cmdex"
)

// // BitriseRunOrTrigger ...
// func BitriseRunOrTrigger(inventoryPth, configPth, workflowNameOrTriggerPattern string, isUseTrigger bool) error {
// 	logLevel := log.GetLevel().String()
// bitriseCommandToUse := "run"
// if isUseTrigger {
// 	bitriseCommandToUse = "trigger"
// }
// 	args := []string{"--loglevel", logLevel, bitriseCommandToUse, workflowNameOrTriggerPattern, "--path", configPth}
// 	if inventoryPth != "" {
// 		args = append(args, "--inventory", inventoryPth)
// 	}
// 	return cmdex.RunCommand("bitrise", args...)
// }

// CMDBridgeDoBitriseRunOrTrigger ...
func CMDBridgeDoBitriseRunOrTrigger(inventoryPth, configPth, workflowNameOrTriggerPattern string, isUseTrigger bool, workdirPath string) error {
	logLevel := log.GetLevel().String()

	bitriseCommandToUse := "run"
	if isUseTrigger {
		bitriseCommandToUse = "trigger"
	}

	params := fmt.Sprintf("bitrise --loglevel %s %s %s --config %s", logLevel, bitriseCommandToUse, workflowNameOrTriggerPattern, configPth)
	if inventoryPth != "" {
		params = params + fmt.Sprintf(" --inventory %s", inventoryPth)
	}

	args := []string{"-workdir", workdirPath, "-do", params}

	return cmdex.RunCommand("cmd-bridge", args...)
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io/ioutil"
	"regexp"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/shell"
	"github.com/hashicorp/go-version"
)

// The terraform --version output is of the format: Terraform v0.9.5-dev (cad024a5fe131a546936674ef85445215bbc4226+CHANGES)
// where -dev and (commitid+CHANGES) is for custom builds or if TF_LOG is set for debug purposes
var TERRAFORM_VERSION_REGEX = regexp.MustCompile("Terraform (v?[\\d\\.]+)(?:-dev)?(?: .+)?")

// Populate the currently installed version of Terraform into the given terragruntOptions
func PopulateTerraformVersion(terragruntOptions *options.TerragruntOptions) error {
	// Discard all log output to make sure we don't pollute stdout or stderr with this extra call to '--version'
	terragruntOptionsCopy := terragruntOptions.Clone(terragruntOptions.TerragruntConfigPath)
	terragruntOptionsCopy.Writer = ioutil.Discard
	terragruntOptionsCopy.ErrWriter = ioutil.Discard

	output, err := shell.RunTerraformCommandWithOutput(terragruntOptionsCopy, "--version")
	if err != nil {
		return err
	}

	terraformVersion, err := parseTerraformVersion(output.Stdout)
	if err != nil {
		return err
	}

	terragruntOptions.TerraformVersion = terraformVersion
	return nil
}

// Check that the currently installed Terraform version works meets the specified version constraint and return an error
// if it doesn't
func CheckTerraformVersion(constraint string, terragruntOptions *options.TerragruntOptions) error {
	return checkTerraformVersionMeetsConstraint(terragruntOptions.TerraformVersion, constraint)
}

// Check that the currently running Terragrunt version meets the specified version constraint and return an error
// if it doesn't
func CheckTerragruntVersion(constraint string, terragruntOptions *options.TerragruntOptions) error {
	return checkTerragruntVersionMeetsConstraint(terragruntOptions.TerragruntVersion, constraint)
}

// Check that the current version of Terragrunt meets the specified constraint and return an error if it doesn't
func checkTerragruntVersionMeetsConstraint(currentVersion *version.Version, constraint string) error {
	versionConstraint, err := version.NewConstraint(constraint)
	if err != nil {
		return err
	}

	if !versionConstraint.Check(currentVersion) {
		return errors.WithStackTrace(InvalidTerragruntVersion{CurrentVersion: currentVersion, VersionConstraints: versionConstraint})
	}

	return nil
}

// Check that the current version of Terraform meets the specified constraint and return an error if it doesn't
func checkTerraformVersionMeetsConstraint(currentVersion *version.Version, constraint string) error {
	versionConstraint, err := version.NewConstraint(constraint)
	if err != nil {
		return err
	}

	if !versionConstraint.Check(currentVersion) {
		return errors.WithStackTrace(InvalidTerraformVersion{CurrentVersion: currentVersion, VersionConstraints: versionConstraint})
	}

	return nil
}

// Parse the output of the terraform --version command
func parseTerraformVersion(versionCommandOutput string) (*version.Version, error) {
	matches := TERRAFORM_VERSION_REGEX.FindStringSubmatch(versionCommandOutput)

	if len(matches) != 2 {
		return nil, errors.WithStackTrace(InvalidTerraformVersionSyntax(versionCommandOutput))
	}

	return version.NewVersion(matches[1])
}

// Custom error types

type InvalidTerraformVersionSyntax string

func (err InvalidTerraformVersionSyntax) Error() string {
	return fmt.Sprintf("Unable to parse Terraform version output: %s", string(err))
}

type InvalidTerraformVersion struct {
	CurrentVersion     *version.Version
	VersionConstraints version.Constraints
}

type InvalidTerragruntVersion struct {
	CurrentVersion     *version.Version
	VersionConstraints version.Constraints
}

func (err InvalidTerraformVersion) Error() string {
	return fmt.Sprintf("The currently installed version of Terraform (%s) is not compatible with the version Terragrunt requires (%s).", err.CurrentVersion.String(), err.VersionConstraints.String())
}

func (err InvalidTerragruntVersion) Error() string {
	return fmt.Sprintf("The currently installed version of Terragrunt (%s) is not compatible with the version constraint requiring (%s).", err.CurrentVersion.String(), err.VersionConstraints.String())
}
<commit_msg>log terraform version in debug mode<commit_after>package cli

import (
	"fmt"
	"io/ioutil"
	"regexp"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/shell"
	"github.com/hashicorp/go-version"
)

// The terraform --version output is of the format: Terraform v0.9.5-dev (cad024a5fe131a546936674ef85445215bbc4226+CHANGES)
// where -dev and (commitid+CHANGES) is for custom builds or if TF_LOG is set for debug purposes
var TERRAFORM_VERSION_REGEX = regexp.MustCompile("Terraform (v?[\\d\\.]+)(?:-dev)?(?: .+)?")

// Populate the currently installed version of Terraform into the given terragruntOptions
func PopulateTerraformVersion(terragruntOptions *options.TerragruntOptions) error {
	// Discard all log output to make sure we don't pollute stdout or stderr with this extra call to '--version'
	terragruntOptionsCopy := terragruntOptions.Clone(terragruntOptions.TerragruntConfigPath)
	terragruntOptionsCopy.Writer = ioutil.Discard
	terragruntOptionsCopy.ErrWriter = ioutil.Discard

	output, err := shell.RunTerraformCommandWithOutput(terragruntOptionsCopy, "--version")
	if err != nil {
		return err
	}

	terraformVersion, err := parseTerraformVersion(output.Stdout)
	if err != nil {
		return err
	}

	terragruntOptions.TerraformVersion = terraformVersion
	terragruntOptions.Logger.Printf("Terraform version: %s", terraformVersion)
	return nil
}

// Check that the currently installed Terraform version works meets the specified version constraint and return an error
// if it doesn't
func CheckTerraformVersion(constraint string, terragruntOptions *options.TerragruntOptions) error {
	return checkTerraformVersionMeetsConstraint(terragruntOptions.TerraformVersion, constraint)
}

// Check that the currently running Terragrunt version meets the specified version constraint and return an error
// if it doesn't
func CheckTerragruntVersion(constraint string, terragruntOptions *options.TerragruntOptions) error {
	return checkTerragruntVersionMeetsConstraint(terragruntOptions.TerragruntVersion, constraint)
}

// Check that the current version of Terragrunt meets the specified constraint and return an error if it doesn't
func checkTerragruntVersionMeetsConstraint(currentVersion *version.Version, constraint string) error {
	versionConstraint, err := version.NewConstraint(constraint)
	if err != nil {
		return err
	}

	if !versionConstraint.Check(currentVersion) {
		return errors.WithStackTrace(InvalidTerragruntVersion{CurrentVersion: currentVersion, VersionConstraints: versionConstraint})
	}

	return nil
}

// Check that the current version of Terraform meets the specified constraint and return an error if it doesn't
func checkTerraformVersionMeetsConstraint(currentVersion *version.Version, constraint string) error {
	versionConstraint, err := version.NewConstraint(constraint)
	if err != nil {
		return err
	}

	if !versionConstraint.Check(currentVersion) {
		return errors.WithStackTrace(InvalidTerraformVersion{CurrentVersion: currentVersion, VersionConstraints: versionConstraint})
	}

	return nil
}

// Parse the output of the terraform --version command
func parseTerraformVersion(versionCommandOutput string) (*version.Version, error) {
	matches := TERRAFORM_VERSION_REGEX.FindStringSubmatch(versionCommandOutput)

	if len(matches) != 2 {
		return nil, errors.WithStackTrace(InvalidTerraformVersionSyntax(versionCommandOutput))
	}

	return version.NewVersion(matches[1])
}

// Custom error types

type InvalidTerraformVersionSyntax string

func (err InvalidTerraformVersionSyntax) Error() string {
	return fmt.Sprintf("Unable to parse Terraform version output: %s", string(err))
}

type InvalidTerraformVersion struct {
	CurrentVersion     *version.Version
	VersionConstraints version.Constraints
}

type InvalidTerragruntVersion struct {
	CurrentVersion     *version.Version
	VersionConstraints version.Constraints
}

func (err InvalidTerraformVersion) Error() string {
	return fmt.Sprintf("The currently installed version of Terraform (%s) is not compatible with the version Terragrunt requires (%s).", err.CurrentVersion.String(), err.VersionConstraints.String())
}

func (err InvalidTerragruntVersion) Error() string {
	return fmt.Sprintf("The currently installed version of Terragrunt (%s) is not compatible with the version constraint requiring (%s).", err.CurrentVersion.String(), err.VersionConstraints.String())
}
<|endoftext|>