<commit_before>package library

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
)

type Environment struct {
	Catalogs
	Halos
	MemoDir string

}

//////////////
// Catalogs //
//////////////

type Catalogs struct {
	snapMin int
	names [][]string
}

func (cat *Catalogs) Blocks() int {
	return len(cat.names[0])
}

func (cat *Catalogs) Catalog(snap, block int) string {
	return cat.names[snap + cat.snapMin][block]
}

func (cat *Catalogs) InitGotetra(
	format string, snapMin, snapMax int, blockMins, blockMaxes []int,
) error {
	if len(blockMins) != 3 {
		return fmt.Errorf(
			"'BlockMins' had %d elements, but 3 are required for " +
			"gotetra catalogs.", len(blockMins),
		)
	}

	cat.names = make([][]string, snapMax - snapMin)
	cat.snapMin = snapMin

	for snap := snapMin; snap < snapMax; snap++ {
		for x := blockMins[0]; x < blockMaxes[0]; x++ {
			for y := blockMins[1]; y < blockMaxes[1]; y++ {
				for z := blockMins[2]; z < blockMaxes[2]; z++ {

					fname := fmt.Sprintf(format, snap, x, y, z)
					_, err := os.Stat(fname)
					if err != nil { return err }

					cat.names[snap - snapMin] = append(
						cat.names[snap - snapMin], fname,
					)
				}
			}
		}
	}

	return nil
}

func (cat *Catalogs) InitLGadget2(
	format string, snapMin, snapMax int, blockMins, blockMaxes []int,
) error {
	panic("NYI")
}

///////////
// Halos //
///////////

type Halos struct {
	snapMin int
	names []string
}

func (h *Halos) HaloCatalog(snap int) string {
	return h.names[snap - h.snapMin]
}

func (h *Halos) InitRockstar(dir string, snapMin, snapMax int) error {
	infos, err := ioutil.ReadDir(dir)
	if err != nil { return err }

	h.names = []string{}
	for i := range infos {
		h.names = append(h.names, path.Join(dir, infos[i].Name()))
	}

	if len(h.names) < snapMax - snapMin + 1 {
		return fmt.Errorf(
			"There are %d files in the 'HaloDir' directory, %s, but " +
			"'SnapMin' = %d and 'SnapMax' = %d.",
			len(h.names), dir, snapMin, snapMax,
		)
	}
	h.names = h.names[len(h.names) - (snapMax - snapMin + 1):]

	return nil
}<commit_msg>Changend env.go to reflect new package name.<commit_after>package env

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
)

type Environment struct {
	Catalogs
	Halos
	MemoDir string

}

//////////////
// Catalogs //
//////////////

type Catalogs struct {
	snapMin int
	names [][]string
}

func (cat *Catalogs) Blocks() int {
	return len(cat.names[0])
}

func (cat *Catalogs) Catalog(snap, block int) string {
	return cat.names[snap + cat.snapMin][block]
}

func (cat *Catalogs) InitGotetra(
	format string, snapMin, snapMax int, blockMins, blockMaxes []int,
) error {
	if len(blockMins) != 3 {
		return fmt.Errorf(
			"'BlockMins' had %d elements, but 3 are required for " +
			"gotetra catalogs.", len(blockMins),
		)
	}

	cat.names = make([][]string, snapMax - snapMin)
	cat.snapMin = snapMin

	for snap := snapMin; snap < snapMax; snap++ {
		for x := blockMins[0]; x < blockMaxes[0]; x++ {
			for y := blockMins[1]; y < blockMaxes[1]; y++ {
				for z := blockMins[2]; z < blockMaxes[2]; z++ {

					fname := fmt.Sprintf(format, snap, x, y, z)
					_, err := os.Stat(fname)
					if err != nil { return err }

					cat.names[snap - snapMin] = append(
						cat.names[snap - snapMin], fname,
					)
				}
			}
		}
	}

	return nil
}

func (cat *Catalogs) InitLGadget2(
	format string, snapMin, snapMax int, blockMins, blockMaxes []int,
) error {
	panic("NYI")
}

///////////
// Halos //
///////////

type Halos struct {
	snapMin int
	names []string
}

func (h *Halos) HaloCatalog(snap int) string {
	return h.names[snap - h.snapMin]
}

func (h *Halos) InitRockstar(dir string, snapMin, snapMax int) error {
	infos, err := ioutil.ReadDir(dir)
	if err != nil { return err }

	h.names = []string{}
	for i := range infos {
		h.names = append(h.names, path.Join(dir, infos[i].Name()))
	}

	if len(h.names) < snapMax - snapMin + 1 {
		return fmt.Errorf(
			"There are %d files in the 'HaloDir' directory, %s, but " +
			"'SnapMin' = %d and 'SnapMax' = %d.",
			len(h.names), dir, snapMin, snapMax,
		)
	}
	h.names = h.names[len(h.names) - (snapMax - snapMin + 1):]

	return nil
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"sort"
	"strings"

	"github.com/stts-se/rbg2p"
)

var l = log.New(os.Stderr, "", 0)

func print(orth string, transes []string) {

}

type transResult struct {
	orth    string
	transes []string
	result  bool
}

func transcribe(ruleSet rbg2p.RuleSet, orth string) transResult {
	transes, err := ruleSet.Apply(orth)
	if err != nil {
		l.Printf("Couldn't transcribe '%s' : %s", orth, err)
		return transResult{orth: orth, transes: transes, result: false}
	}
	return transResult{orth: orth, transes: transes, result: true}
}

var removeStressAndBoundaries = regexp.MustCompile("[.\"%!~] *")

func cleanTransForDiff(t string) string {
	var res = t
	res = removeStressAndBoundaries.ReplaceAllString(res, "")
	res = strings.Replace(res, "'", "", -1)
	return res
}

func cleanTransForIJDiff(t string) string {
	var res = t
	res = strings.Replace(res, " i ", " j ", -1)
	return res
}

func compareForDiff(old []string, new []string) (string, bool) {
	for i, s := range old {
		old[i] = cleanTransForDiff(s)
	}
	for i, s := range new {
		new[i] = cleanTransForDiff(s)
	}
	var oldIJ = []string{}
	var newIJ = []string{}
	for _, s := range old {
		oldIJ = append(oldIJ, cleanTransForIJDiff(s))
	}
	for _, s := range new {
		newIJ = append(newIJ, cleanTransForIJDiff(s))
	}
	if reflect.DeepEqual(old, new) {
		return "ALL EQ", true
	} else if old[0] == new[0] {
		return "#1 EQ", false
		// } else if reflect.DeepEqual(oldIJ, newIJ) {
		// 	return "ALL EQ IJ", false
		// } else if oldIJ[0] == newIJ[0] {
		// 	return "#1 EQ IJ", false
	} else {
		return "DIFF", false
	}
}

func main() {

	var f = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	var force = f.Bool("force", false, "print transcriptions even if errors are found (default: false)")
	var test = f.Bool("test", false, "test g2p against input file; orth <tab> trans (default: false)")
	var ssFile = f.String("symbolset", "", "use specified symbol set file for validating the symbols in the g2p rule set (default: none; overrides the g2p rule file's symbolset, if any)")
	var help = f.Bool("help", false, "print help message")

	var usage = `go run g2p.go <FLAGS> <G2P RULE FILE> <WORDS (FILES OR LIST OF WORDS)> (optional)

FLAGS:
   -force      bool    print transcriptions even if errors are found (default: false)
   -test       bool    test g2p against input file; orth <tab> trans (default: false)
   -symbolset  string  use specified symbol set file for validating the symbols in the g2p rule set (default: none)
   -help       bool    print help message`

	f.Usage = func() {
		l.Printf(usage)
	}

	var args = os.Args
	if strings.HasSuffix(args[0], "g2p") {
		args = args[1:] // remove first argument if it's the program name
	}
	err := f.Parse(args)
	if err != nil {
		os.Exit(1)
	}

	args = f.Args()

	if *help {
		l.Println(usage)
		os.Exit(1)
	}

	if len(args) < 1 {
		l.Println(usage)
		os.Exit(1)
	}

	g2pFile := args[0]
	ruleSet, err := rbg2p.LoadFile(g2pFile)
	if err != nil {
		l.Printf("couldn't load rule file %s : %s", g2pFile, err)
		os.Exit(1)
	}

	if *ssFile != "" {
		phonemeSet, err := rbg2p.LoadPhonemeSetFile(*ssFile, ruleSet.PhonemeDelimiter)
		if err != nil {
			l.Printf("couldn't load symbol set : %s", err)
			os.Exit(1)
		}
		ruleSet.PhonemeSet = phonemeSet
	}

	haltingError := false
	result := ruleSet.Test()
	for _, e := range result.Errors {
		l.Printf("ERROR: %v\n", e)
	}
	l.Printf("%d ERROR(S) FOR %s\n", len(result.Errors), g2pFile)
	for _, e := range result.Warnings {
		l.Printf("WARNING: %v\n", e)
	}
	l.Printf("%d WARNING(S) FOR %s\n", len(result.Warnings), g2pFile)
	if len(result.Errors) > 0 {
		haltingError = true
	}
	if len(result.FailedTests) > 0 {
		for _, e := range result.FailedTests {
			l.Printf("FAILED TEST: %v\n", e)
		}
		l.Printf("%d OF %d TESTS FAILED FOR %s\n", len(result.FailedTests), len(ruleSet.Tests), g2pFile)
		haltingError = true
	} else {
		l.Printf("ALL %d TESTS PASSED FOR %s\n", len(ruleSet.Tests), g2pFile)
	}

	if haltingError && !*force {
		os.Exit(1)
	}

	nTotal := 0
	nErrs := 0
	nTrans := 0
	nTests := 0
	testRes := make(map[string]int)
	if *test {
		fmt.Println("ORTH\tDIFFTAG\tNEW TRANSES\tOLD TRANSES")
	}
	for i := 1; i < len(args); i++ {
		s := args[i]
		if _, err := os.Stat(s); os.IsNotExist(err) {
			nTotal = nTotal + 1
			res := transcribe(ruleSet, strings.ToLower(s))
			if res.result || *force {
				nTrans = nTrans + 1
				fmt.Printf("%s\t%s\n", s, strings.Join(res.transes, "\t"))
			}
			if !res.result {
				nErrs = nErrs + 1
			}
		} else {
			fh, err := os.Open(s)
			defer fh.Close()
			if err != nil {
				l.Println(err)
				os.Exit(1)
			}
			sc := bufio.NewScanner(fh)
			for sc.Scan() {
				if err := sc.Err(); err != nil {
					l.Println(err)
					os.Exit(1)
				}
				nTotal = nTotal + 1
				line := sc.Text()
				fs := strings.Split(line, "\t")
				o, refTranses := fs[0], fs[1:]
				res := transcribe(ruleSet, strings.ToLower(o))
				if res.result || *force {
					nTrans = nTrans + 1
					if *test {
						nTests++
						info, _ := compareForDiff(res.transes, refTranses)
						testRes[info]++
						outFs := []string{res.orth, info, strings.Join(res.transes, " # "), strings.Join(refTranses, "#")}
						fmt.Println(strings.Join(outFs, "\t"))
					} else {
						print(res.orth, res.transes)
					}
				}
				if !res.result {
					nErrs = nErrs + 1
				}
			}
		}
	}
	l.Printf("%-18s: % 7d", "TOTAL WORDS", nTotal)
	l.Printf("%-18s: % 7d", "ERRORS", nErrs)
	l.Printf("%-18s: % 7d", "TRANSCRIBED", nTrans)
	if *test {
		l.Printf("%-18s: % 7d", "TESTED", nTests)
		var keys []string
		for k := range testRes {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, tag := range keys {
			freq := testRes[tag]
			s := " > TEST " + tag
			l.Printf("%-18s: % 7d", s, freq)
		}
	}
}
<commit_msg>g2p script now ignores comment lines (#)<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"sort"
	"strings"

	"github.com/stts-se/rbg2p"
)

var l = log.New(os.Stderr, "", 0)

func print(orth string, transes []string) {

}

type transResult struct {
	orth    string
	transes []string
	result  bool
}

func transcribe(ruleSet rbg2p.RuleSet, orth string) transResult {
	transes, err := ruleSet.Apply(orth)
	if err != nil {
		l.Printf("Couldn't transcribe '%s' : %s", orth, err)
		return transResult{orth: orth, transes: transes, result: false}
	}
	return transResult{orth: orth, transes: transes, result: true}
}

var removeStressAndBoundaries = regexp.MustCompile("[.\"%!~] *")

func cleanTransForDiff(t string) string {
	var res = t
	res = removeStressAndBoundaries.ReplaceAllString(res, "")
	res = strings.Replace(res, "'", "", -1)
	return res
}

func cleanTransForIJDiff(t string) string {
	var res = t
	res = strings.Replace(res, " i ", " j ", -1)
	return res
}

func compareForDiff(old []string, new []string) (string, bool) {
	for i, s := range old {
		old[i] = cleanTransForDiff(s)
	}
	for i, s := range new {
		new[i] = cleanTransForDiff(s)
	}
	var oldIJ = []string{}
	var newIJ = []string{}
	for _, s := range old {
		oldIJ = append(oldIJ, cleanTransForIJDiff(s))
	}
	for _, s := range new {
		newIJ = append(newIJ, cleanTransForIJDiff(s))
	}
	if reflect.DeepEqual(old, new) {
		return "ALL EQ", true
	} else if old[0] == new[0] {
		return "#1 EQ", false
		// } else if reflect.DeepEqual(oldIJ, newIJ) {
		// 	return "ALL EQ IJ", false
		// } else if oldIJ[0] == newIJ[0] {
		// 	return "#1 EQ IJ", false
	} else {
		return "DIFF", false
	}
}

func main() {

	var f = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	var force = f.Bool("force", false, "print transcriptions even if errors are found (default: false)")
	var test = f.Bool("test", false, "test g2p against input file; orth <tab> trans (default: false)")
	var ssFile = f.String("symbolset", "", "use specified symbol set file for validating the symbols in the g2p rule set (default: none; overrides the g2p rule file's symbolset, if any)")
	var help = f.Bool("help", false, "print help message")

	var usage = `go run g2p.go <FLAGS> <G2P RULE FILE> <WORDS (FILES OR LIST OF WORDS)> (optional)

FLAGS:
   -force      bool    print transcriptions even if errors are found (default: false)
   -test       bool    test g2p against input file; orth <tab> trans (default: false)
   -symbolset  string  use specified symbol set file for validating the symbols in the g2p rule set (default: none)
   -help       bool    print help message`

	f.Usage = func() {
		l.Printf(usage)
	}

	var args = os.Args
	if strings.HasSuffix(args[0], "g2p") {
		args = args[1:] // remove first argument if it's the program name
	}
	err := f.Parse(args)
	if err != nil {
		os.Exit(1)
	}

	args = f.Args()

	if *help {
		l.Println(usage)
		os.Exit(1)
	}

	if len(args) < 1 {
		l.Println(usage)
		os.Exit(1)
	}

	g2pFile := args[0]
	ruleSet, err := rbg2p.LoadFile(g2pFile)
	if err != nil {
		l.Printf("couldn't load rule file %s : %s", g2pFile, err)
		os.Exit(1)
	}

	if *ssFile != "" {
		phonemeSet, err := rbg2p.LoadPhonemeSetFile(*ssFile, ruleSet.PhonemeDelimiter)
		if err != nil {
			l.Printf("couldn't load symbol set : %s", err)
			os.Exit(1)
		}
		ruleSet.PhonemeSet = phonemeSet
	}

	haltingError := false
	result := ruleSet.Test()
	for _, e := range result.Errors {
		l.Printf("ERROR: %v\n", e)
	}
	l.Printf("%d ERROR(S) FOR %s\n", len(result.Errors), g2pFile)
	for _, e := range result.Warnings {
		l.Printf("WARNING: %v\n", e)
	}
	l.Printf("%d WARNING(S) FOR %s\n", len(result.Warnings), g2pFile)
	if len(result.Errors) > 0 {
		haltingError = true
	}
	if len(result.FailedTests) > 0 {
		for _, e := range result.FailedTests {
			l.Printf("FAILED TEST: %v\n", e)
		}
		l.Printf("%d OF %d TESTS FAILED FOR %s\n", len(result.FailedTests), len(ruleSet.Tests), g2pFile)
		haltingError = true
	} else {
		l.Printf("ALL %d TESTS PASSED FOR %s\n", len(ruleSet.Tests), g2pFile)
	}

	if haltingError && !*force {
		os.Exit(1)
	}

	nTotal := 0
	nErrs := 0
	nTrans := 0
	nTests := 0
	testRes := make(map[string]int)
	if *test {
		fmt.Println("ORTH\tDIFFTAG\tNEW TRANSES\tOLD TRANSES")
	}
	for i := 1; i < len(args); i++ {
		s := args[i]
		if _, err := os.Stat(s); os.IsNotExist(err) {
			nTotal = nTotal + 1
			res := transcribe(ruleSet, strings.ToLower(s))
			if res.result || *force {
				nTrans = nTrans + 1
				fmt.Printf("%s\t%s\n", s, strings.Join(res.transes, "\t"))
			}
			if !res.result {
				nErrs = nErrs + 1
			}
		} else {
			fh, err := os.Open(s)
			defer fh.Close()
			if err != nil {
				l.Println(err)
				os.Exit(1)
			}
			sc := bufio.NewScanner(fh)
			for sc.Scan() {
				if err := sc.Err(); err != nil {
					l.Println(err)
					os.Exit(1)
				}
				nTotal = nTotal + 1
				line := sc.Text()
				if strings.HasPrefix(strings.TrimSpace(line), "#") {
					l.Println("Skipping line " + line)
					continue
				}
				fs := strings.Split(line, "\t")
				o, refTranses := fs[0], fs[1:]
				res := transcribe(ruleSet, strings.ToLower(o))
				if res.result || *force {
					nTrans = nTrans + 1
					if *test {
						nTests++
						info, _ := compareForDiff(res.transes, refTranses)
						testRes[info]++
						outFs := []string{res.orth, info, strings.Join(res.transes, " # "), strings.Join(refTranses, "#")}
						fmt.Println(strings.Join(outFs, "\t"))
					} else {
						print(res.orth, res.transes)
					}
				}
				if !res.result {
					nErrs = nErrs + 1
				}
			}
		}
	}
	l.Printf("%-18s: % 7d", "TOTAL WORDS", nTotal)
	l.Printf("%-18s: % 7d", "ERRORS", nErrs)
	l.Printf("%-18s: % 7d", "TRANSCRIBED", nTrans)
	if *test {
		l.Printf("%-18s: % 7d", "TESTED", nTests)
		var keys []string
		for k := range testRes {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, tag := range keys {
			freq := testRes[tag]
			s := " > TEST " + tag
			l.Printf("%-18s: % 7d", s, freq)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	"fmt"
	"go/format"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/google/go-github/github"
	"github.com/weppos/publicsuffix-go/publicsuffix"
)

const (
	goSrc = `// This file is automatically generated
// Run "go run cmd/gen/gen.go" to update the list.

package publicsuffix

const defaultListVersion = "PSL version {{.VersionSHA}} ({{.VersionDate}})"

func init() {
	r := [{{len .Rules}}]Rule{
		{{range $r := .Rules}} \
		{ {{$r.Type}}, "{{$r.Value}}", {{$r.Length}}, {{$r.Private}} },
		{{end}}
	}
	DefaultList.rules = r[:]
}

`
)

var (
	goTemplate = template.Must(template.New("").Parse(cont(goSrc)))
)

// https://github.com/golang/go/issues/9969
func cont(s string) string {
	return strings.Replace(s, "\\\n", "", -1)
}

func main() {
	sha, datetime := extractHeadInfo()

	resp, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/publicsuffix/list/%s/public_suffix_list.dat", sha))
	if err != nil {
		fatal(err)
	}
	defer resp.Body.Close()

	list := publicsuffix.NewList()
	rules, err := list.Load(resp.Body, nil)
	if err != nil {
		fatal(err)
	}

	data := struct {
		VersionSHA  string
		VersionDate string
		Rules       []publicsuffix.Rule
	}{
		sha[:6],
		datetime.Format(time.ANSIC),
		rules,
	}

	buf := new(bytes.Buffer)
	err = goTemplate.Execute(buf, &data)
	if err != nil {
		fatal(err)
	}

	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		fatal(err)
	}
	_, err = os.Stdout.Write(formatted)
	//_, err = os.Stdout.Write(buf.Bytes())
}

func extractHeadInfo() (sha string, datetime time.Time) {
	client := github.NewClient(nil)

	commits, _, err := client.Repositories.ListCommits(context.Background(), "publicsuffix", "list", nil)
	if err != nil {
		fatal(err)
	}

	lastCommit := commits[0]
	return lastCommit.GetSHA(), lastCommit.GetCommit().GetCommitter().GetDate()
}

func fatal(err error) {
	fmt.Println(err)
	os.Exit(1)
}
<commit_msg>Do not build gen<commit_after>// +build ignore

package main

import (
	"bytes"
	"context"
	"fmt"
	"go/format"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/google/go-github/github"
	"github.com/weppos/publicsuffix-go/publicsuffix"
)

const (
	goSrc = `// This file is automatically generated
// Run "go run cmd/gen/gen.go" to update the list.

package publicsuffix

const defaultListVersion = "PSL version {{.VersionSHA}} ({{.VersionDate}})"

func init() {
	r := [{{len .Rules}}]Rule{
		{{range $r := .Rules}} \
		{ {{$r.Type}}, "{{$r.Value}}", {{$r.Length}}, {{$r.Private}} },
		{{end}}
	}
	DefaultList.rules = r[:]
}

`
)

var (
	goTemplate = template.Must(template.New("").Parse(cont(goSrc)))
)

// https://github.com/golang/go/issues/9969
func cont(s string) string {
	return strings.Replace(s, "\\\n", "", -1)
}

func main() {
	sha, datetime := extractHeadInfo()

	resp, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/publicsuffix/list/%s/public_suffix_list.dat", sha))
	if err != nil {
		fatal(err)
	}
	defer resp.Body.Close()

	list := publicsuffix.NewList()
	rules, err := list.Load(resp.Body, nil)
	if err != nil {
		fatal(err)
	}

	data := struct {
		VersionSHA  string
		VersionDate string
		Rules       []publicsuffix.Rule
	}{
		sha[:6],
		datetime.Format(time.ANSIC),
		rules,
	}

	buf := new(bytes.Buffer)
	err = goTemplate.Execute(buf, &data)
	if err != nil {
		fatal(err)
	}

	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		fatal(err)
	}
	_, err = os.Stdout.Write(formatted)
	//_, err = os.Stdout.Write(buf.Bytes())
}

func extractHeadInfo() (sha string, datetime time.Time) {
	client := github.NewClient(nil)

	commits, _, err := client.Repositories.ListCommits(context.Background(), "publicsuffix", "list", nil)
	if err != nil {
		fatal(err)
	}

	lastCommit := commits[0]
	return lastCommit.GetSHA(), lastCommit.GetCommit().GetCommitter().GetDate()
}

func fatal(err error) {
	fmt.Println(err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/whalebrew/whalebrew/packages"
	"path"
	"strings"
)

func init() {
	RootCmd.AddCommand(installCommand)
}

var installCommand = &cobra.Command{
	Use:   "install [image name]",
	Short: "Install a package",
	Long:  `install a Docker image as a Whalebrew package in your install path.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		pm := packages.NewPackageManager("/usr/local/bin")
		imageName := args[0]
		packageName := imageName
		if strings.Contains(packageName, "/") {
			packageName = strings.SplitN(packageName, "/", 2)[1]
		}
		err := pm.Install(imageName, packageName)
		if err != nil {
			return err
		}
		fmt.Printf("🐳  Installed %s to %s\n", imageName, path.Join(pm.InstallPath, packageName))
		return nil
	},
}
<commit_msg>Pull images when installing them<commit_after>package cmd

import (
	"context"
	"fmt"
	"github.com/docker/docker/client"
	"github.com/spf13/cobra"
	"github.com/whalebrew/whalebrew/packages"
	"os"
	"os/exec"
	"path"
	"strings"
)

func init() {
	RootCmd.AddCommand(installCommand)
}

var installCommand = &cobra.Command{
	Use:   "install [image name]",
	Short: "Install a package",
	Long:  `install a Docker image as a Whalebrew package in your install path.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		imageName := args[0]

		cli, err := client.NewEnvClient()
		if err != nil {
			return err
		}
		img, _, err := cli.ImageInspectWithRaw(context.Background(), imageName)
		if err != nil {
			if client.IsErrNotFound(err) {
				fmt.Printf("Unable to find image '%s' locally\n", imageName)
				if err = pullImage(imageName); err != nil {
					return err
				}
				// retry
				img, _, err = cli.ImageInspectWithRaw(context.Background(), imageName)
				if err != nil {
					return err
				}
			} else {
				return err
			}
		}
		if img.ContainerConfig.Entrypoint == nil {
			return fmt.Errorf("The image '%s' is not compatible with Whalebrew: it does not have an entrypoint.", imageName)
		}

		pm := packages.NewPackageManager("/usr/local/bin")
		packageName := imageName
		if strings.Contains(packageName, "/") {
			packageName = strings.SplitN(packageName, "/", 2)[1]
		}
		err = pm.Install(imageName, packageName)
		if err != nil {
			return err
		}
		fmt.Printf("🐳  Installed %s to %s\n", imageName, path.Join(pm.InstallPath, packageName))
		return nil
	},
}

func pullImage(image string) error {
	c := exec.Command("docker", "pull", image)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	return c.Run()
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/open-falcon/falcon-plus/g"
	"github.com/spf13/cobra"
)

var Monitor = &cobra.Command{
	Use:   "tail [Module ...]",
	Short: "Display an Open-Falcon module's log",
	Long: `
Display the log of the specified Open-Falcon module.
A module represents a single node in a cluster.
Modules:
  ` + strings.Join(g.AllModulesInOrder, " "),
	RunE: monitor,
}

func monitor(c *cobra.Command, args []string) error {
	if len(args) != 1 {
		return c.Usage()
	}
	moduleName := args[0]
	err := g.ModuleExists(moduleName)
	if err != nil {
		fmt.Println(err)
		fmt.Println("** start failed **")
		return nil //g.Command_EX_ERR
	}

	logPath := g.LogPath(moduleName)
	cmd := exec.Command("tail", "-f", logPath)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	dir, _ := os.Getwd()
	cmd.Dir = dir
	err = cmd.Run()
	if err != nil {
		fmt.Println("** tail failed **")
		return nil //g.Command_EX_ERR
	}
	return nil //0
}
<commit_msg>Redesign of `monitor` command<commit_after>package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/open-falcon/falcon-plus/g"
	"github.com/spf13/cobra"
)

var Monitor = &cobra.Command{
	Use:   "monitor [Module ...]",
	Short: "Display an Open-Falcon module's log",
	Long: `
Display the log of the specified Open-Falcon module.
A module represents a single node in a cluster.
Modules:
  ` + strings.Join(g.AllModulesInOrder, " "),
	RunE: monitor,
}

func monitor(c *cobra.Command, args []string) error {
	if len(args) != 1 {
		return c.Usage()
	}
	moduleName := args[0]
	if !g.HasModule(moduleName) {
		return fmt.Errorf("%s doesn't exist\n", moduleName)
	}

	logPath := g.LogPath(moduleName)
	cmd := exec.Command("tail", "-f", logPath)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}
<|endoftext|>


<commit_before>package cmd

import "github.com/spf13/cobra"

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Display the version of mbt",
	Run: func(cmd *cobra.Command, args []string) {
		println("0.14.1")
	},
}
<commit_msg>Bump version - v0.14.2<commit_after>package cmd

import "github.com/spf13/cobra"

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Display the version of mbt",
	Run: func(cmd *cobra.Command, args []string) {
		println("0.14.2")
	},
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 简单的辅助功能命令行工具。
package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/issue9/cmdopt"

	"github.com/issue9/web/internal/cmd/build"
	"github.com/issue9/web/internal/cmd/create"
	"github.com/issue9/web/internal/cmd/version"
	"github.com/issue9/web/internal/cmd/watch"
)

var opt *cmdopt.CmdOpt

func main() {
	opt = cmdopt.New(os.Stdout, flag.ExitOnError, usage)

	version.Init(opt)
	build.Init(opt)
	create.Init(opt)
	watch.Init(opt)

	if err := opt.Exec(os.Args[1:]); err != nil {
		panic(err)
	}
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `web 命令是 github.com/issue9/web 框架提供的辅助工具。

目前支持以下子命令：%s
详情可以通过 web help [subcommand] 进行查看。
`, strings.Join(opt.Commands(), ","))

	return err
}
<commit_msg>修正未注册 help 命令的错误<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 简单的辅助功能命令行工具。
package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/issue9/cmdopt"

	"github.com/issue9/web/internal/cmd/build"
	"github.com/issue9/web/internal/cmd/create"
	"github.com/issue9/web/internal/cmd/version"
	"github.com/issue9/web/internal/cmd/watch"
)

var opt *cmdopt.CmdOpt

func main() {
	opt = cmdopt.New(os.Stdout, flag.ExitOnError, usage)

	opt.Help("help")
	version.Init(opt)
	build.Init(opt)
	create.Init(opt)
	watch.Init(opt)

	if err := opt.Exec(os.Args[1:]); err != nil {
		panic(err)
	}
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `web 命令是 github.com/issue9/web 框架提供的辅助工具。

目前支持以下子命令：%s
详情可以通过 web help [subcommand] 进行查看。
`, strings.Join(opt.Commands(), ","))

	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package aws

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/golang/glog"
)

const scaleToZeroSupported = false

type asgCache struct {
	registeredAsgs     []*asgInformation
	instanceToAsg      map[AwsRef]*Asg
	notInRegisteredAsg map[AwsRef]bool
	mutex              sync.Mutex
	service            autoScalingWrapper
	interrupt          chan struct{}
}

func newASGCache(service autoScalingWrapper) (*asgCache, error) {
	registry := &asgCache{
		registeredAsgs:     make([]*asgInformation, 0),
		service:            service,
		instanceToAsg:      make(map[AwsRef]*Asg),
		notInRegisteredAsg: make(map[AwsRef]bool),
		interrupt:          make(chan struct{}),
	}
	go wait.Until(func() {
		registry.mutex.Lock()
		defer registry.mutex.Unlock()
		if err := registry.regenerate(); err != nil {
			glog.Errorf("Error while regenerating Asg cache: %v", err)
		}
	}, time.Hour, registry.interrupt)

	return registry, nil
}

// Register ASG. Returns true if the ASG was registered.
func (m *asgCache) Register(asg *Asg) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	for i := range m.registeredAsgs {
		if existing := m.registeredAsgs[i].config; existing.AwsRef == asg.AwsRef {
			if reflect.DeepEqual(existing, asg) {
				return false
			}
			m.registeredAsgs[i].config = asg
			glog.V(4).Infof("Updated ASG %s", asg.AwsRef.Name)
			m.invalidateUnownedInstanceCache()
			return true
		}
	}

	glog.V(1).Infof("Registering ASG %s", asg.AwsRef.Name)
	m.registeredAsgs = append(m.registeredAsgs, &asgInformation{
		config: asg,
	})
	m.invalidateUnownedInstanceCache()
	return true
}

// Unregister ASG. Returns true if the ASG was unregistered.
func (m *asgCache) Unregister(asg *Asg) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	updated := make([]*asgInformation, 0, len(m.registeredAsgs))
	changed := false
	for _, existing := range m.registeredAsgs {
		if existing.config.AwsRef == asg.AwsRef {
			glog.V(1).Infof("Unregistered ASG %s", asg.AwsRef.Name)
			changed = true
			continue
		}
		updated = append(updated, existing)
	}
	m.registeredAsgs = updated
	return changed
}

func (m *asgCache) get() []*asgInformation {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	return m.registeredAsgs
}

// FindForInstance returns AsgConfig of the given Instance
func (m *asgCache) FindForInstance(instance *AwsRef) (*Asg, error) {
	// TODO(negz): Prevent this calling describe ASGs too often.
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if m.notInRegisteredAsg[*instance] {
		// We already know we don't own this instance. Return early and avoid
		// additional calls to describe ASGs.
		return nil, nil
	}

	if config, found := m.instanceToAsg[*instance]; found {
		return config, nil
	}
	if err := m.regenerate(); err != nil {
		return nil, fmt.Errorf("Error while looking for ASG for instance %+v, error: %v", *instance, err)
	}
	if config, found := m.instanceToAsg[*instance]; found {
		return config, nil
	}

	m.notInRegisteredAsg[*instance] = true
	return nil, nil
}

func (m *asgCache) invalidateUnownedInstanceCache() {
	glog.V(4).Info("Invalidating unowned instance cache")
	m.notInRegisteredAsg = make(map[AwsRef]bool)
}

func (m *asgCache) regenerate() error {
	newCache := make(map[AwsRef]*Asg)

	names := make([]string, len(m.registeredAsgs))
	configs := make(map[string]*Asg)
	for i, asg := range m.registeredAsgs {
		names[i] = asg.config.Name
		configs[asg.config.Name] = asg.config
	}

	glog.V(4).Infof("Regenerating instance to ASG map for ASGs: %v", names)
	groups, err := m.service.getAutoscalingGroupsByNames(names)
	if err != nil {
		return err
	}
	for _, group := range groups {
		for _, instance := range group.Instances {
			ref := AwsRef{Name: aws.StringValue(instance.InstanceId)}
			newCache[ref] = configs[aws.StringValue(group.AutoScalingGroupName)]
		}
	}

	m.instanceToAsg = newCache
	return nil
}

// Cleanup closes the channel to signal the go routine to stop that is handling the cache
func (m *asgCache) Cleanup() {
	close(m.interrupt)
}
<commit_msg>Allow scaling to zero on AWS<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package aws

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/golang/glog"
)

const scaleToZeroSupported = true

type asgCache struct {
	registeredAsgs     []*asgInformation
	instanceToAsg      map[AwsRef]*Asg
	notInRegisteredAsg map[AwsRef]bool
	mutex              sync.Mutex
	service            autoScalingWrapper
	interrupt          chan struct{}
}

func newASGCache(service autoScalingWrapper) (*asgCache, error) {
	registry := &asgCache{
		registeredAsgs:     make([]*asgInformation, 0),
		service:            service,
		instanceToAsg:      make(map[AwsRef]*Asg),
		notInRegisteredAsg: make(map[AwsRef]bool),
		interrupt:          make(chan struct{}),
	}
	go wait.Until(func() {
		registry.mutex.Lock()
		defer registry.mutex.Unlock()
		if err := registry.regenerate(); err != nil {
			glog.Errorf("Error while regenerating Asg cache: %v", err)
		}
	}, time.Hour, registry.interrupt)

	return registry, nil
}

// Register ASG. Returns true if the ASG was registered.
func (m *asgCache) Register(asg *Asg) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	for i := range m.registeredAsgs {
		if existing := m.registeredAsgs[i].config; existing.AwsRef == asg.AwsRef {
			if reflect.DeepEqual(existing, asg) {
				return false
			}
			m.registeredAsgs[i].config = asg
			glog.V(4).Infof("Updated ASG %s", asg.AwsRef.Name)
			m.invalidateUnownedInstanceCache()
			return true
		}
	}

	glog.V(1).Infof("Registering ASG %s", asg.AwsRef.Name)
	m.registeredAsgs = append(m.registeredAsgs, &asgInformation{
		config: asg,
	})
	m.invalidateUnownedInstanceCache()
	return true
}

// Unregister ASG. Returns true if the ASG was unregistered.
func (m *asgCache) Unregister(asg *Asg) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	updated := make([]*asgInformation, 0, len(m.registeredAsgs))
	changed := false
	for _, existing := range m.registeredAsgs {
		if existing.config.AwsRef == asg.AwsRef {
			glog.V(1).Infof("Unregistered ASG %s", asg.AwsRef.Name)
			changed = true
			continue
		}
		updated = append(updated, existing)
	}
	m.registeredAsgs = updated
	return changed
}

func (m *asgCache) get() []*asgInformation {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	return m.registeredAsgs
}

// FindForInstance returns AsgConfig of the given Instance
func (m *asgCache) FindForInstance(instance *AwsRef) (*Asg, error) {
	// TODO(negz): Prevent this calling describe ASGs too often.
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if m.notInRegisteredAsg[*instance] {
		// We already know we don't own this instance. Return early and avoid
		// additional calls to describe ASGs.
		return nil, nil
	}

	if config, found := m.instanceToAsg[*instance]; found {
		return config, nil
	}
	if err := m.regenerate(); err != nil {
		return nil, fmt.Errorf("Error while looking for ASG for instance %+v, error: %v", *instance, err)
	}
	if config, found := m.instanceToAsg[*instance]; found {
		return config, nil
	}

	m.notInRegisteredAsg[*instance] = true
	return nil, nil
}

func (m *asgCache) invalidateUnownedInstanceCache() {
	glog.V(4).Info("Invalidating unowned instance cache")
	m.notInRegisteredAsg = make(map[AwsRef]bool)
}

func (m *asgCache) regenerate() error {
	newCache := make(map[AwsRef]*Asg)

	names := make([]string, len(m.registeredAsgs))
	configs := make(map[string]*Asg)
	for i, asg := range m.registeredAsgs {
		names[i] = asg.config.Name
		configs[asg.config.Name] = asg.config
	}

	glog.V(4).Infof("Regenerating instance to ASG map for ASGs: %v", names)
	groups, err := m.service.getAutoscalingGroupsByNames(names)
	if err != nil {
		return err
	}
	for _, group := range groups {
		for _, instance := range group.Instances {
			ref := AwsRef{Name: aws.StringValue(instance.InstanceId)}
			newCache[ref] = configs[aws.StringValue(group.AutoScalingGroupName)]
		}
	}

	m.instanceToAsg = newCache
	return nil
}

// Cleanup closes the channel to signal the go routine to stop that is handling the cache
func (m *asgCache) Cleanup() {
	close(m.interrupt)
}
<|endoftext|>


<commit_before>package codec

import (
	context "golang.org/x/net/context"
	"go.rpc/gorpc"
)

type ServerCodec interface {
	ReadRequestHeader(context.Context, *gorpc.Request) error
	ReadRequestBody(context.Context, interface{}) error
	// WriteResponse must be safe for concurrent use by multiple goroutines.
	WriteResponse(context.Context, *gorpc.Response, interface{}) error

	Close() error
}

type ClientCodec interface {
	// WriteRequest must be safe for concurrent use by multiple goroutines.
	WriteRequest(context.Context, *gorpc.Request, interface{}) error
	ReadResponseHeader(context.Context, *gorpc.Response) error
	ReadResponseBody(context.Context, interface{}) error

	Close() error
}<commit_msg>构建rpc request 及 response<commit_after>package codec

import (
	context "golang.org/x/net/context"
	"github.com/zhaowanda/go.rpc/gorpc"
)

type ServerCodec interface {
	ReadRequestHeader(context.Context, *gorpc.Request) error
	ReadRequestBody(context.Context, interface{}) error
	// WriteResponse must be safe for concurrent use by multiple goroutines.
	WriteResponse(context.Context, *gorpc.Response, interface{}) error

	Close() error
}

type ClientCodec interface {
	// WriteRequest must be safe for concurrent use by multiple goroutines.
	WriteRequest(context.Context, *gorpc.Request, interface{}) error
	ReadResponseHeader(context.Context, *gorpc.Response) error
	ReadResponseBody(context.Context, interface{}) error

	Close() error
}<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"strings"

	"github.com/hashicorp/vault/builtin/logical/ssh"
	"github.com/hashicorp/vault/meta"
	"github.com/mitchellh/mapstructure"
)

// SSHCommand is a Command that establishes a SSH connection
// with target by generating a dynamic key
type SSHCommand struct {
	meta.Meta
}

// Structure to hold the fields returned when asked for a credential from SSHh backend.
type SSHCredentialResp struct {
	KeyType  string `mapstructure:"key_type"`
	Key      string `mapstructure:"key"`
	Username string `mapstructure:"username"`
	IP       string `mapstructure:"ip"`
	Port     string `mapstructure:"port"`
}

func (c *SSHCommand) Run(args []string) int {
	var role, mountPoint, format, userKnownHostsFile, strictHostKeyChecking string
	var noExec bool
	var sshCmdArgs []string
	var sshDynamicKeyFileName string
	flags := c.Meta.FlagSet("ssh", meta.FlagSetDefault)
	flags.StringVar(&strictHostKeyChecking, "strict-host-key-checking", "ask", "")
	flags.StringVar(&userKnownHostsFile, "user-known-hosts-file", "~/.ssh/known_hosts", "")
	flags.StringVar(&format, "format", "table", "")
	flags.StringVar(&role, "role", "", "")
	flags.StringVar(&mountPoint, "mount-point", "ssh", "")
	flags.BoolVar(&noExec, "no-exec", false, "")

	flags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := flags.Parse(args); err != nil {
		return 1
	}
	args = flags.Args()
	if len(args) < 1 {
		c.Ui.Error("ssh expects at least one argument")
		return 1
	}

	client, err := c.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// split the parameter username@ip
	input := strings.Split(args[0], "@")
	var username string
	var ipAddr string

	// If only IP is mentioned and username is skipped, assume username to
	// be the current username. Vault SSH role's default username could have
	// been used, but in order to retain the consistency with SSH command,
	// current username is employed.
	if len(input) == 1 {
		u, err := user.Current()
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error fetching username: %s", err))
			return 1
		}
		username = u.Username
		ipAddr = input[0]
	} else if len(input) == 2 {
		username = input[0]
		ipAddr = input[1]
	} else {
		c.Ui.Error(fmt.Sprintf("Invalid parameter: %s", args[0]))
		return 1
	}

	// Resolving domain names to IP address on the client side.
	// Vault only deals with IP addresses.
	ip, err := net.ResolveIPAddr("ip", ipAddr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error resolving IP Address: %s", err))
		return 1
	}

	// Credentials are generated only against a registered role. If user
	// does not specify a role with the SSH command, then lookup API is used
	// to fetch all the roles with which this IP is associated. If there is
	// only one role associated with it, use it to establish the connection.
	if role == "" {
		role, err = c.defaultRole(mountPoint, ip.String())
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error choosing role: %s", err))
			return 1
		}
		// Print the default role chosen so that user knows the role name
		// if something doesn't work. If the role chosen is not allowed to
		// be used by the user (ACL enforcement), then user should see an
		// error message accordingly.
		c.Ui.Output(fmt.Sprintf("Vault SSH: Role: %s", role))
	}

	data := map[string]interface{}{
		"username": username,
		"ip":       ip.String(),
	}

	keySecret, err := client.SSHWithMountPoint(mountPoint).Credential(role, data)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error getting key for SSH session:%s", err))
		return 1
	}

	// if no-exec was chosen, just print out the secret and return.
	if noExec {
		return OutputSecret(c.Ui, format, keySecret)
	}

	// Port comes back as a json.Number which mapstructure doesn't like, so convert it
	if keySecret.Data["port"] != nil {
		keySecret.Data["port"] = keySecret.Data["port"].(json.Number).String()
	}
	var resp SSHCredentialResp
	if err := mapstructure.Decode(keySecret.Data, &resp); err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing the credential response:%s", err))
		return 1
	}

	if resp.KeyType == ssh.KeyTypeDynamic {
		if len(resp.Key) == 0 {
			c.Ui.Error(fmt.Sprintf("Invalid key"))
			return 1
		}
		sshDynamicKeyFileName = fmt.Sprintf("vault_ssh_%s_%s", username, ip.String())
		err = ioutil.WriteFile(sshDynamicKeyFileName, []byte(resp.Key), 0600)
		sshCmdArgs = append(sshCmdArgs, []string{"-i", sshDynamicKeyFileName}...)

	} else if resp.KeyType == ssh.KeyTypeOTP {
		// Check if the application 'sshpass' is installed in the client machine.
		// If it is then, use it to automate typing in OTP to the prompt. Unfortunately,
		// it was not possible to automate it without a third-party application, with
		// only the Go libraries.
		// Feel free to try and remove this dependency.
		sshpassPath, err := exec.LookPath("sshpass")
		if err == nil {
			sshCmdArgs = append(sshCmdArgs, []string{"-p", string(resp.Key), "ssh", "-o UserKnownHostsFile=" + userKnownHostsFile, "-o StrictHostKeyChecking=" + strictHostKeyChecking, "-p", resp.Port, username + "@" + ip.String()}...)
			sshCmd := exec.Command(sshpassPath, sshCmdArgs...)
			sshCmd.Stdin = os.Stdin
			sshCmd.Stdout = os.Stdout
			err = sshCmd.Run()
			if err != nil {
				c.Ui.Error(fmt.Sprintf("Failed to establish SSH connection:%s", err))
			}
			return 0
		}
		c.Ui.Output("OTP for the session is " + resp.Key)
		c.Ui.Output("[Note: Install 'sshpass' to automate typing in OTP]")
	}
	sshCmdArgs = append(sshCmdArgs, []string{"-o UserKnownHostsFile=" + userKnownHostsFile, "-o StrictHostKeyChecking=" + strictHostKeyChecking, "-p", resp.Port, username + "@" + ip.String()}...)

	sshCmd := exec.Command("ssh", sshCmdArgs...)
	sshCmd.Stdin = os.Stdin
	sshCmd.Stdout = os.Stdout

	// Running the command as a separate command. The reason for using exec.Command instead
	// of using crypto/ssh package is that, this way, user can have the same feeling of
	// connecting to remote hosts with the ssh command. Package crypto/ssh did not have a way
	// to establish an independent session like this.
	err = sshCmd.Run()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error while running ssh command:%s", err))
	}

	// Delete the temporary key file generated by the command.
	if resp.KeyType == ssh.KeyTypeDynamic {
		// Ignoring the error from the below call since it is not a security
		// issue if the deletion of file is not successful. User is authorized
		// to have this secret.
		os.Remove(sshDynamicKeyFileName)
	}

	// If the session established was longer than the lease expiry, the secret
	// might have been revoked already. If not, then revoke it. Since the key
	// file is deleted and since user doesn't know the credential anymore, there
	// is not point in Vault maintaining this secret anymore. Every time the command
	// is run, a fresh credential is generated anyways.
	err = client.Sys().Revoke(keySecret.LeaseID)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error revoking the key: %s", err))
	}

	return 0
}

// If user did not provide the role with which SSH connection has
// to be established and if there is only one role associated with
// the IP, it is used by default.
func (c *SSHCommand) defaultRole(mountPoint, ip string) (string, error) {
	data := map[string]interface{}{
		"ip": ip,
	}
	client, err := c.Client()
	if err != nil {
		return "", err
	}
	secret, err := client.Logical().Write(mountPoint+"/lookup", data)
	if err != nil {
		return "", fmt.Errorf("Error finding roles for IP %s: %s", ip, err)

	}
	if secret == nil {
		return "", fmt.Errorf("Error finding roles for IP %s: %s", ip, err)
	}

	if secret.Data["roles"] == nil {
		return "", fmt.Errorf("No matching roles found for IP %s", ip)
	}

	if len(secret.Data["roles"].([]interface{})) == 1 {
		return secret.Data["roles"].([]interface{})[0].(string), nil
	} else {
		var roleNames string
		for _, item := range secret.Data["roles"].([]interface{}) {
			roleNames += item.(string) + ", "
		}
		roleNames = strings.TrimRight(roleNames, ", ")
		return "", fmt.Errorf("Roles:[%s]"+`
		Multiple roles are registered for this IP.
		Select a role using '-role' option.
		Note that all roles may not be permitted, based on ACLs.`, roleNames)
	}
}

func (c *SSHCommand) Synopsis() string {
	return "Initiate a SSH session"
}

func (c *SSHCommand) Help() string {
	helpText := `
Usage: vault ssh [options] username@ip

  Establishes an SSH connection with the target machine.

  This command generates a key and uses it to establish an SSH
  connection with the target machine. This operation requires
  that SSH backend is mounted and at least one 'role' be registed
  with vault at priori.

  For setting up SSH backends with one-time-passwords, installation
  of agent in target machines is required.
  See [https://github.com/hashicorp/vault-ssh-agent]

General Options:
` + meta.GeneralOptionsUsage() + `
SSH Options:

	-role				Role to be used to create the key.
					Each IP is associated with a role. To see the associated
					roles with IP, use "lookup" endpoint. If you are certain
					that there is only one role associated with the IP, you can
					skip mentioning the role. It will be chosen by default.  If
					there are no roles associated with the IP, register the
					CIDR block of that IP using the "roles/" endpoint.

	-no-exec			Shows the credentials but does not establish connection.

	-mount-point			Mount point of SSH backend. If the backend is mounted at
					'ssh', which is the default as well, this parameter can be
					skipped.

	-format				If no-exec option is enabled, then the credentials will be
					printed out and SSH connection will not be established. The
					format of the output can be 'json' or 'table'. JSON output
					is useful when writing scripts. Default is 'table'.

	-strict-host-key-checking	This option corresponds to StrictHostKeyChecking of SSH configuration.
					If 'sshpass' is employed to enable automated login, then if host key
					is not "known" to the client, 'vault ssh' command will fail. Set this
					option to "no" to bypass the host key checking. Defaults to "ask".

	-user-known-hosts-file		This option corresponds to UserKnownHostsFile of SSH configuration.
					Assigns the file to use for storing the host keys. If this option is
					set to "/dev/null" along with "-strict-host-key-checking=no", both
					warnings and host key checking can be avoided while establishing the
					connection. Defaults to "~/.ssh/known_hosts".
`
	return strings.TrimSpace(helpText)
}
<commit_msg>Supplying strictHostKeyChecking and userKnownHostsFile from env vars<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"strings"

	"github.com/hashicorp/vault/builtin/logical/ssh"
	"github.com/hashicorp/vault/meta"
	"github.com/mitchellh/mapstructure"
)

// SSHCommand is a Command that establishes a SSH connection
// with target by generating a dynamic key
type SSHCommand struct {
	meta.Meta
}

// Structure to hold the fields returned when asked for a credential from SSHh backend.
type SSHCredentialResp struct {
	KeyType  string `mapstructure:"key_type"`
	Key      string `mapstructure:"key"`
	Username string `mapstructure:"username"`
	IP       string `mapstructure:"ip"`
	Port     string `mapstructure:"port"`
}

func (c *SSHCommand) Run(args []string) int {
	var role, mountPoint, format, userKnownHostsFile, strictHostKeyChecking string
	var noExec bool
	var sshCmdArgs []string
	var sshDynamicKeyFileName string
	flags := c.Meta.FlagSet("ssh", meta.FlagSetDefault)
	flags.StringVar(&strictHostKeyChecking, "strict-host-key-checking", "ask", "")
	flags.StringVar(&userKnownHostsFile, "user-known-hosts-file", "~/.ssh/known_hosts", "")
	flags.StringVar(&format, "format", "table", "")
	flags.StringVar(&role, "role", "", "")
	flags.StringVar(&mountPoint, "mount-point", "ssh", "")
	flags.BoolVar(&noExec, "no-exec", false, "")

	flags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := flags.Parse(args); err != nil {
		return 1
	}

	if os.Getenv("VAULT_STRICT_HOST_KEY_CHECKING") != "" {
		strictHostKeyChecking = os.Getenv("VAULT_STRICT_HOST_KEY_CHECKING")
	}

	if os.Getenv("VAULT_USER_KNOWN_HOSTS_FILE") != "" {
		userKnownHostsFile = os.Getenv("VAULT_USER_KNOWN_HOSTS_FILE")
	}

	args = flags.Args()
	if len(args) < 1 {
		c.Ui.Error("ssh expects at least one argument")
		return 1
	}

	client, err := c.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// split the parameter username@ip
	input := strings.Split(args[0], "@")
	var username string
	var ipAddr string

	// If only IP is mentioned and username is skipped, assume username to
	// be the current username. Vault SSH role's default username could have
	// been used, but in order to retain the consistency with SSH command,
	// current username is employed.
	if len(input) == 1 {
		u, err := user.Current()
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error fetching username: %s", err))
			return 1
		}
		username = u.Username
		ipAddr = input[0]
	} else if len(input) == 2 {
		username = input[0]
		ipAddr = input[1]
	} else {
		c.Ui.Error(fmt.Sprintf("Invalid parameter: %s", args[0]))
		return 1
	}

	// Resolving domain names to IP address on the client side.
	// Vault only deals with IP addresses.
	ip, err := net.ResolveIPAddr("ip", ipAddr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error resolving IP Address: %s", err))
		return 1
	}

	// Credentials are generated only against a registered role. If user
	// does not specify a role with the SSH command, then lookup API is used
	// to fetch all the roles with which this IP is associated. If there is
	// only one role associated with it, use it to establish the connection.
	if role == "" {
		role, err = c.defaultRole(mountPoint, ip.String())
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error choosing role: %s", err))
			return 1
		}
		// Print the default role chosen so that user knows the role name
		// if something doesn't work. If the role chosen is not allowed to
		// be used by the user (ACL enforcement), then user should see an
		// error message accordingly.
		c.Ui.Output(fmt.Sprintf("Vault SSH: Role: %s", role))
	}

	data := map[string]interface{}{
		"username": username,
		"ip":       ip.String(),
	}

	keySecret, err := client.SSHWithMountPoint(mountPoint).Credential(role, data)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error getting key for SSH session:%s", err))
		return 1
	}

	// if no-exec was chosen, just print out the secret and return.
	if noExec {
		return OutputSecret(c.Ui, format, keySecret)
	}

	// Port comes back as a json.Number which mapstructure doesn't like, so convert it
	if keySecret.Data["port"] != nil {
		keySecret.Data["port"] = keySecret.Data["port"].(json.Number).String()
	}
	var resp SSHCredentialResp
	if err := mapstructure.Decode(keySecret.Data, &resp); err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing the credential response:%s", err))
		return 1
	}

	if resp.KeyType == ssh.KeyTypeDynamic {
		if len(resp.Key) == 0 {
			c.Ui.Error(fmt.Sprintf("Invalid key"))
			return 1
		}
		sshDynamicKeyFileName = fmt.Sprintf("vault_ssh_%s_%s", username, ip.String())
		err = ioutil.WriteFile(sshDynamicKeyFileName, []byte(resp.Key), 0600)
		sshCmdArgs = append(sshCmdArgs, []string{"-i", sshDynamicKeyFileName}...)

	} else if resp.KeyType == ssh.KeyTypeOTP {
		// Check if the application 'sshpass' is installed in the client machine.
		// If it is then, use it to automate typing in OTP to the prompt. Unfortunately,
		// it was not possible to automate it without a third-party application, with
		// only the Go libraries.
		// Feel free to try and remove this dependency.
		sshpassPath, err := exec.LookPath("sshpass")
		if err == nil {
			sshCmdArgs = append(sshCmdArgs, []string{"-p", string(resp.Key), "ssh", "-o UserKnownHostsFile=" + userKnownHostsFile, "-o StrictHostKeyChecking=" + strictHostKeyChecking, "-p", resp.Port, username + "@" + ip.String()}...)
			sshCmd := exec.Command(sshpassPath, sshCmdArgs...)
			sshCmd.Stdin = os.Stdin
			sshCmd.Stdout = os.Stdout
			err = sshCmd.Run()
			if err != nil {
				c.Ui.Error(fmt.Sprintf("Failed to establish SSH connection:%s", err))
			}
			return 0
		}
		c.Ui.Output("OTP for the session is " + resp.Key)
		c.Ui.Output("[Note: Install 'sshpass' to automate typing in OTP]")
	}
	sshCmdArgs = append(sshCmdArgs, []string{"-o UserKnownHostsFile=" + userKnownHostsFile, "-o StrictHostKeyChecking=" + strictHostKeyChecking, "-p", resp.Port, username + "@" + ip.String()}...)

	sshCmd := exec.Command("ssh", sshCmdArgs...)
	sshCmd.Stdin = os.Stdin
	sshCmd.Stdout = os.Stdout

	// Running the command as a separate command. The reason for using exec.Command instead
	// of using crypto/ssh package is that, this way, user can have the same feeling of
	// connecting to remote hosts with the ssh command. Package crypto/ssh did not have a way
	// to establish an independent session like this.
	err = sshCmd.Run()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error while running ssh command:%s", err))
	}

	// Delete the temporary key file generated by the command.
	if resp.KeyType == ssh.KeyTypeDynamic {
		// Ignoring the error from the below call since it is not a security
		// issue if the deletion of file is not successful. User is authorized
		// to have this secret.
		os.Remove(sshDynamicKeyFileName)
	}

	// If the session established was longer than the lease expiry, the secret
	// might have been revoked already. If not, then revoke it. Since the key
	// file is deleted and since user doesn't know the credential anymore, there
	// is not point in Vault maintaining this secret anymore. Every time the command
	// is run, a fresh credential is generated anyways.
	err = client.Sys().Revoke(keySecret.LeaseID)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error revoking the key: %s", err))
	}

	return 0
}

// If user did not provide the role with which SSH connection has
// to be established and if there is only one role associated with
// the IP, it is used by default.
func (c *SSHCommand) defaultRole(mountPoint, ip string) (string, error) {
	data := map[string]interface{}{
		"ip": ip,
	}
	client, err := c.Client()
	if err != nil {
		return "", err
	}
	secret, err := client.Logical().Write(mountPoint+"/lookup", data)
	if err != nil {
		return "", fmt.Errorf("Error finding roles for IP %s: %s", ip, err)

	}
	if secret == nil {
		return "", fmt.Errorf("Error finding roles for IP %s: %s", ip, err)
	}

	if secret.Data["roles"] == nil {
		return "", fmt.Errorf("No matching roles found for IP %s", ip)
	}

	if len(secret.Data["roles"].([]interface{})) == 1 {
		return secret.Data["roles"].([]interface{})[0].(string), nil
	} else {
		var roleNames string
		for _, item := range secret.Data["roles"].([]interface{}) {
			roleNames += item.(string) + ", "
		}
		roleNames = strings.TrimRight(roleNames, ", ")
		return "", fmt.Errorf("Roles:[%s]"+`
		Multiple roles are registered for this IP.
		Select a role using '-role' option.
		Note that all roles may not be permitted, based on ACLs.`, roleNames)
	}
}

func (c *SSHCommand) Synopsis() string {
	return "Initiate a SSH session"
}

func (c *SSHCommand) Help() string {
	helpText := `
Usage: vault ssh [options] username@ip

  Establishes an SSH connection with the target machine.

  This command generates a key and uses it to establish an SSH
  connection with the target machine. This operation requires
  that SSH backend is mounted and at least one 'role' be registed
  with vault at priori.

  For setting up SSH backends with one-time-passwords, installation
  of agent in target machines is required.
  See [https://github.com/hashicorp/vault-ssh-agent]

General Options:
` + meta.GeneralOptionsUsage() + `
SSH Options:

	-role				Role to be used to create the key.
					Each IP is associated with a role. To see the associated
					roles with IP, use "lookup" endpoint. If you are certain
					that there is only one role associated with the IP, you can
					skip mentioning the role. It will be chosen by default.  If
					there are no roles associated with the IP, register the
					CIDR block of that IP using the "roles/" endpoint.

	-no-exec			Shows the credentials but does not establish connection.

	-mount-point			Mount point of SSH backend. If the backend is mounted at
					'ssh', which is the default as well, this parameter can be
					skipped.

	-format				If no-exec option is enabled, then the credentials will be
					printed out and SSH connection will not be established. The
					format of the output can be 'json' or 'table'. JSON output
					is useful when writing scripts. Default is 'table'.

	-strict-host-key-checking	This option corresponds to StrictHostKeyChecking of SSH configuration.
					If 'sshpass' is employed to enable automated login, then if host key
					is not "known" to the client, 'vault ssh' command will fail. Set this
					option to "no" to bypass the host key checking. Defaults to "ask".
					Can also be specified with VAULT_STRICT_HOST_KEY_CHECKING environment
					variable.

	-user-known-hosts-file		This option corresponds to UserKnownHostsFile of SSH configuration.
					Assigns the file to use for storing the host keys. If this option is
					set to "/dev/null" along with "-strict-host-key-checking=no", both
					warnings and host key checking can be avoided while establishing the
					connection. Defaults to "~/.ssh/known_hosts". Can also be specified
					with VAULT_USER_KNOWN_HOSTS_FILE environment variable.
`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cachingfs_test

import (
	"io/ioutil"
	"log"
	"strings"
	"testing"
	"time"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/samples/cachingfs"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestHelloFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type CachingFSTest struct {
	mfs *fuse.MountedFileSystem
}

var _ TearDownInterface = &CachingFSTest{}

func (t *CachingFSTest) setUp(
	lookupEntryTimeout time.Duration,
	getattrTimeout time.Duration) {
	var err error

	// Set up a temporary directory for mounting.
	mountPoint, err := ioutil.TempDir("", "caching_fs_test")
	AssertEq(nil, err)

	// Create a file system.
	fs, err := cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout)
	AssertEq(nil, err)

	// Mount it.
	t.mfs, err = fuse.Mount(mountPoint, fs)
	AssertEq(nil, err)

	err = t.mfs.WaitForReady(context.Background())
	AssertEq(nil, err)
}

func (t *CachingFSTest) TearDown() {
	// Was the file system mounted?
	if t.mfs == nil {
		return
	}

	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := t.mfs.Unmount()
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		panic("MountedFileSystem.Unmount: " + err.Error())
	}

	if err := t.mfs.Join(context.Background()); err != nil {
		panic("MountedFileSystem.Join: " + err.Error())
	}
}

////////////////////////////////////////////////////////////////////////
// Basics
////////////////////////////////////////////////////////////////////////

type BasicsTest struct {
	NoCachingTest
}

func init() { RegisterTestSuite(&BasicsTest{}) }

func (t *BasicsTest) StatNonexistent_Root() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatNonexistent_Dir() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatFoo() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatDir() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatBar() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// No caching
////////////////////////////////////////////////////////////////////////

type NoCachingTest struct {
	CachingFSTest
}

var _ SetUpInterface = &NoCachingTest{}

func init() { RegisterTestSuite(&NoCachingTest{}) }

func (t *NoCachingTest) SetUp(ti *TestInfo) {
	const (
		lookupEntryTimeout = 0
		getattrTimeout     = 0
	)

	t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout)
}

func (t *NoCachingTest) StatStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatRenumberStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatMtimeStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatRenumberMtimeStat() {
	AssertTrue(false, "TODO")
}
<commit_msg>Fixed duplicate test methods.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cachingfs_test

import (
	"io/ioutil"
	"log"
	"strings"
	"testing"
	"time"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/samples/cachingfs"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestHelloFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type CachingFSTest struct {
	mfs *fuse.MountedFileSystem
}

var _ TearDownInterface = &CachingFSTest{}

func (t *CachingFSTest) setUp(
	lookupEntryTimeout time.Duration,
	getattrTimeout time.Duration) {
	var err error

	// Set up a temporary directory for mounting.
	mountPoint, err := ioutil.TempDir("", "caching_fs_test")
	AssertEq(nil, err)

	// Create a file system.
	fs, err := cachingfs.NewCachingFS(lookupEntryTimeout, getattrTimeout)
	AssertEq(nil, err)

	// Mount it.
	t.mfs, err = fuse.Mount(mountPoint, fs)
	AssertEq(nil, err)

	err = t.mfs.WaitForReady(context.Background())
	AssertEq(nil, err)
}

func (t *CachingFSTest) TearDown() {
	// Was the file system mounted?
	if t.mfs == nil {
		return
	}

	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := t.mfs.Unmount()
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		panic("MountedFileSystem.Unmount: " + err.Error())
	}

	if err := t.mfs.Join(context.Background()); err != nil {
		panic("MountedFileSystem.Join: " + err.Error())
	}
}

////////////////////////////////////////////////////////////////////////
// Basics
////////////////////////////////////////////////////////////////////////

type BasicsTest struct {
	CachingFSTest
}

var _ SetUpInterface = &BasicsTest{}

func init() { RegisterTestSuite(&BasicsTest{}) }

func (t *BasicsTest) SetUp(ti *TestInfo) {
	const (
		lookupEntryTimeout = 0
		getattrTimeout     = 0
	)

	t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout)
}

func (t *BasicsTest) StatNonexistent_Root() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatNonexistent_Dir() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatFoo() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatDir() {
	AssertTrue(false, "TODO")
}

func (t *BasicsTest) StatBar() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// No caching
////////////////////////////////////////////////////////////////////////

type NoCachingTest struct {
	CachingFSTest
}

var _ SetUpInterface = &NoCachingTest{}

func init() { RegisterTestSuite(&NoCachingTest{}) }

func (t *NoCachingTest) SetUp(ti *TestInfo) {
	const (
		lookupEntryTimeout = 0
		getattrTimeout     = 0
	)

	t.CachingFSTest.setUp(lookupEntryTimeout, getattrTimeout)
}

func (t *NoCachingTest) StatStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatRenumberStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatMtimeStat() {
	AssertTrue(false, "TODO")
}

func (t *NoCachingTest) StatRenumberMtimeStat() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package runner

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/dockpit/exec"
	"github.com/dockpit/pit/config"
	"github.com/dockpit/pit/contract"
	"github.com/dockpit/state"
)

func NewDefault(out io.Writer) *Default {
	return &Default{out}
}

// Default runner starts and shutsdown the test subject
// for every new test case, this is slow but guarantees
// that internal states are clean and doesn't require
// the developer to implement logic for handling dropping
// state connections
type Default struct {
	out io.Writer
}

func (d *Default) Name() string { return "default" }

func (d *Default) RunOne(conf config.C, p *contract.Pair, sm *state.Manager, subject *url.URL, docker *url.URL) (reerr error) {
	t := p.GenerateTest()

	//map states in contract
	states := map[string]string{}
	for pname, g := range p.Given {
		states[pname] = g.Name

	}

	//add default states if given doesn't give one
	for _, pc := range conf.ProviderConfigs() {
		if _, ok := states[pc.Name()]; !ok {
			states[pc.Name()] = pc.DefaultState()
		}
	}

	//actually start the states
	for pname, sname := range states {
		_, err := sm.Start(pname, sname)
		if err != nil {
			return err
		}
	}

	//stop states
	defer func() {
		for pname, sname := range states {
			err := sm.Stop(pname, sname)
			if err != nil {
				reerr = err
				return
			}
		}
	}()

	//start subject
	tcmd := exec.Command(conf.RunConfig().Cmd[0], conf.RunConfig().Cmd[1:]...)
	tcmd.Stdout = d.out
	tcmd.Stderr = d.out

	err := tcmd.StartWithTimeout(conf.RunConfig().ReadyTimeout, conf.RunConfig().ReadyExp)
	if err != nil {
		return err
	}

	//stop subject, @todo this to be configurable
	defer tcmd.StopWithTimeout(time.Second * 2)

	//execute the actual test
	err = t(subject.String(), docker.String(), http.DefaultClient, conf)
	if err != nil {
		return err
	}

	return nil
}

func (d *Default) Run(conf config.C, c contract.C, sm *state.Manager, subject *url.URL, docker *url.URL) error {
	res, err := c.Resources()
	if err != nil {
		return err
	}

	//loop over each resource in the contract
	for _, r := range res {
		fmt.Fprintf(d.out, "Resource '%s'", r.Pattern())

		acs, err := r.Actions()
		if err != nil {
			return err
		}

		//loop over each action
		for _, a := range acs {

			//for each test
			for _, p := range a.Pairs() {
				err = d.RunOne(conf, p, sm, subject, docker)
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}
<commit_msg>added some extra logging to test logic<commit_after>package runner

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/dockpit/exec"
	"github.com/dockpit/pit/config"
	"github.com/dockpit/pit/contract"
	"github.com/dockpit/state"
)

func NewDefault(out io.Writer) *Default {
	return &Default{out}
}

// Default runner starts and shutsdown the test subject
// for every new test case, this is slow but guarantees
// that internal states are clean and doesn't require
// the developer to implement logic for handling dropping
// state connections
type Default struct {
	out io.Writer
}

func (d *Default) Name() string { return "default" }

func (d *Default) RunOne(conf config.C, p *contract.Pair, sm *state.Manager, subject *url.URL, docker *url.URL) (reerr error) {
	t := p.GenerateTest()

	//map states in contract
	states := map[string]string{}
	for pname, g := range p.Given {
		states[pname] = g.Name

	}

	//add default states if given doesn't give one
	for _, pc := range conf.ProviderConfigs() {
		if _, ok := states[pc.Name()]; !ok {
			states[pc.Name()] = pc.DefaultState()
		}
	}

	//actually start the states
	for pname, sname := range states {
		_, err := sm.Start(pname, sname)
		if err != nil {
			return err
		}
	}

	//stop states
	defer func() {
		for pname, sname := range states {
			err := sm.Stop(pname, sname)
			if err != nil {
				reerr = err
				return
			}
		}
	}()

	//start subject
	tcmd := exec.Command(conf.RunConfig().Cmd[0], conf.RunConfig().Cmd[1:]...)
	tcmd.Stdout = d.out
	tcmd.Stderr = d.out

	err := tcmd.StartWithTimeout(conf.RunConfig().ReadyTimeout, conf.RunConfig().ReadyExp)
	if err != nil {
		return err
	}

	//stop subject, @todo this to be configurable
	defer tcmd.StopWithTimeout(time.Second * 2)

	//execute the actual test
	err = t(subject.String(), docker.String(), http.DefaultClient, conf)
	if err != nil {
		return err
	}

	return nil
}

func (d *Default) Run(conf config.C, c contract.C, sm *state.Manager, subject *url.URL, docker *url.URL) error {
	res, err := c.Resources()
	if err != nil {
		return err
	}

	//loop over each resource in the contract
	for _, r := range res {
		fmt.Fprintf(d.out, "%s\n", r.Pattern())

		acs, err := r.Actions()
		if err != nil {
			return err
		}

		//loop over each action
		for _, a := range acs {
			fmt.Fprintf(d.out, "%s %s\n", a.Method(), r.Pattern())

			//for each test
			for _, p := range a.Pairs() {
				err = d.RunOne(conf, p, sm, subject, docker)
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package harvest

import (
	"encoding/json"
	"time"
)

type HarvestDate struct {
	time.Time
}

func (harvestDate *HarvestDate) UnmarshalJSON(input []byte) (err error) {
	dateString := string(input)
	if dateString == "null" {

	} else if len(dateString) == 12 {
		harvestDate.Time, err = time.Parse("\"2006-01-02\"", dateString)
	} else {
		err = json.Unmarshal(input, &harvestDate.Time)
	}

	return
}
<commit_msg>simplify harvest date unmarshal logic<commit_after>package harvest

import (
	"encoding/json"
	"time"
)

type HarvestDate struct {
	time.Time
}

func (harvestDate *HarvestDate) UnmarshalJSON(input []byte) (err error) {
	dateString := string(input)

	if len(dateString) == 12 {
		harvestDate.Time, err = time.Parse("\"2006-01-02\"", dateString)
	} else if dateString != "null" {
		err = json.Unmarshal(input, &harvestDate.Time)
	}

	return
}
<|endoftext|>


<commit_before>package helpers

import (
	"time"

	. "github.com/onsi/gomega"

	"github.com/cloudfoundry-incubator/cf-test-helpers/runner"
)

const CURL_TIMEOUT = 30 * time.Second

// Gets an app's endpoint with the specified path
func AppUri(appName, path string) string {
	config := LoadConfig()
	appsDomain := config.AppsDomain

	return config.Protocol() + appName + "." + appsDomain + path
}

// Gets an app's root endpoint
func AppRootUri(appName string) string {
	return AppUri(appName, "/")
}

// Curls an app's endpoint and exit successfully before the specified timeout
func CurlAppWithTimeout(appName, path string, timeout time.Duration) string {
	uri := AppUri(appName, path)
	curlCmd := runner.Curl(uri)
	runner.NewCmdRunner(curlCmd, timeout).Run()
	Expect(string(curlCmd.Err.Contents())).To(HaveLen(0))
	return string(curlCmd.Out.Contents())
}

// Curls an app's endpoint and exit successfully before the default timeout
func CurlApp(appName, path string) string {
	return CurlAppWithTimeout(appName, path, CURL_TIMEOUT)
}

// Curls an app's root endpoint and exit successfully before the default timeout
func CurlAppRoot(appName string) string {
	return CurlApp(appName, "/")
}

// Returns a function that curls an app's root endpoint and exit successfully before the default timeout
func CurlingAppRoot(appName string) func() string {
	return func() string {
		return CurlAppRoot(appName)
	}
}
<commit_msg>When curling, use -k when skipsslvalidation is set<commit_after>package helpers

import (
	"time"

	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/runner"
)

const CURL_TIMEOUT = 30 * time.Second

// Gets an app's endpoint with the specified path
func AppUri(appName, path string) string {
	config := LoadConfig()
	appsDomain := config.AppsDomain

	return config.Protocol() + appName + "." + appsDomain + path
}

// Gets an app's root endpoint
func AppRootUri(appName string) string {
	return AppUri(appName, "/")
}

// Curls an app's endpoint and exit successfully before the specified timeout
func CurlAppWithTimeout(appName, path string, timeout time.Duration) string {
	config := LoadConfig()
	uri := AppUri(appName, path)

	var curlCmd *gexec.Session
	if config.SkipSSLValidation {
		curlCmd = runner.Curl("-k", uri)
	} else {
		curlCmd = runner.Curl(uri)
	}

	runner.NewCmdRunner(curlCmd, timeout).Run()
	Expect(string(curlCmd.Err.Contents())).To(HaveLen(0))
	return string(curlCmd.Out.Contents())
}

// Curls an app's endpoint and exit successfully before the default timeout
func CurlApp(appName, path string) string {
	return CurlAppWithTimeout(appName, path, CURL_TIMEOUT)
}

// Curls an app's root endpoint and exit successfully before the default timeout
func CurlAppRoot(appName string) string {
	return CurlApp(appName, "/")
}

// Returns a function that curls an app's root endpoint and exit successfully before the default timeout
func CurlingAppRoot(appName string) func() string {
	return func() string {
		return CurlAppRoot(appName)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/blang/semver/v4"
)

var sleep = time.Second
var issueRE = regexp.MustCompile(`#(\d+)\b`)
var verTmpl = `package main

import (
	"strings"

	"github.com/blang/semver/v4"
)

func getVersion() semver.Version {
	sv, err := semver.Parse(strings.TrimPrefix(version, "v"))
	if err == nil {
		if commit != "" {
			sv.Build = []string{commit}
		}
		return sv
	}
	return semver.Version{
		Major: {{ .Major }},
		Minor: {{ .Minor }},
		Patch: {{ .Patch }},
		Pre: []semver.PRVersion{
			{VersionStr: "git"},
		},
		Build: []string{"HEAD"},
	}
}
`

const logo = `
   __     _    _ _      _ _   ___   ___
 /'_ '\ /'_'\ ( '_'\  /'_' )/',__)/',__)
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
'\__  |'\___/'| ,__/''\__,_)(____/(____/
( )_) |       | |
 \___/'       (_)
`

func main() {
	fmt.Println(logo)
	fmt.Println()
	fmt.Println("🌟 Preparing a new gopass release.")
	fmt.Println("☝  Checking pre-conditions ...")
	// - check that workdir is clean
	if !isGitClean() {
		panic("❌ git is dirty")
	}
	fmt.Println("✅ git is clean")
	// - check out master
	if err := gitCoMaster(); err != nil {
		panic(err)
	}
	fmt.Println("✅ Switched to master branch")
	// - pull from origin
	if err := gitPom(); err != nil {
		panic(err)
	}
	fmt.Println("✅ Fetched changes for master")
	// - check that workdir is clean
	if !isGitClean() {
		panic("git is dirty")
	}
	fmt.Println("✅ git is still clean")

	prevVer, nextVer := getVersions()

	fmt.Println()
	fmt.Printf("✅ New version will be: %s\n", nextVer.String())
	fmt.Println()
	fmt.Println("❓ Do you want to continue? (press any key to continue or Ctrl+C to abort)")
	fmt.Scanln()

	// - update VERSION
	if err := writeVersion(nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Wrote VERSION")
	time.Sleep(sleep)
	// - update version.go
	if err := writeVersionGo(nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Wrote version.go")
	time.Sleep(sleep)
	// - update CHANGELOG.md
	if err := writeChangelog(prevVer, nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Updated CHANGELOG.md")
	time.Sleep(sleep)

	// - create PR
	//   git checkout -b release/vX.Y.Z
	if err := gitCoRel(nextVer); err != nil {
		panic(err)
	}
	fmt.Printf("✅ Created branch release/v%s\n", nextVer.String())
	time.Sleep(sleep)

	// commit changes
	if err := gitCommit(nextVer); err != nil {
		panic(err)
	}
	fmt.Printf("✅ Committed changes to release/v%s\n", nextVer.String())
	time.Sleep(sleep)

	// tag
	if err := gitTag(nextVer); err != nil {
		panic(err)
	}
	fmt.Printf("✅ Tagged v%s\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Println("🏁 Preparation finished")
	time.Sleep(sleep)

	fmt.Printf("⚠ Prepared release of gopass %s.\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Printf("⚠ Run 'git push <remote> release/v%s' to push this branch and open a PR against gopasspw/gopass master.\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Printf("⚠ Get the PR merged and run 'git push origin v%s' to kick off the release process.\n", nextVer.String())
	time.Sleep(sleep)
	fmt.Println()

	fmt.Println("💎🙌 Done 🚀🚀🚀🚀🚀🚀")
}

func getVersions() (semver.Version, semver.Version) {
	nextVerFlag := ""
	if len(os.Args) > 1 {
		nextVerFlag = strings.TrimSpace(strings.TrimPrefix(os.Args[1], "v"))
	}
	prevVerFlag := ""
	if len(os.Args) > 2 {
		prevVerFlag = strings.TrimSpace(strings.TrimPrefix(os.Args[2], "v"))
	}

	// obtain the last tagged version from git
	gitVer, err := gitVersion()
	if err != nil {
		panic(err)
	}

	// read the version file to get the last committed version
	vfVer, err := versionFile()
	if err != nil {
		panic(err)
	}

	prevVer := gitVer
	if prevVerFlag != "" {
		prevVer = semver.MustParse(prevVerFlag)
	}

	if gitVer.NE(vfVer) {
		fmt.Printf("git version: %q != VERSION: %q\n", gitVer.String(), vfVer.String())
		if prevVerFlag == "" && len(vfVer.Pre) < 1 {
			usage()
			panic("version mismatch")
		}
	}

	nextVer := prevVer
	if nextVerFlag != "" {
		nextVer = semver.MustParse(nextVerFlag)
		if nextVer.LTE(prevVer) {
			usage()
			panic("next version must be greather than the previous version")
		}
	} else {
		nextVer.IncrementPatch()
		if len(vfVer.Pre) > 0 {
			nextVer = vfVer
			nextVer.Pre = nil
		}
	}

	fmt.Printf(`☝ Version overview
  Git (latest tag):  %q
  VERSION:           %q
  Next version flag: %q
  Prev version flag: %q

Will use
  Previous: %q
  Next:     %q
`,
		gitVer,
		vfVer,
		prevVerFlag,
		nextVerFlag,
		prevVer,
		nextVer)

	return prevVer, nextVer
}

func gitCoMaster() error {
	cmd := exec.Command("git", "checkout", "master")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitPom() error {
	cmd := exec.Command("git", "pull", "origin", "master")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitCoRel(v semver.Version) error {
	cmd := exec.Command("git", "checkout", "-b", "release/v"+v.String())
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitCommit(v semver.Version) error {
	cmd := exec.Command("git", "add", "CHANGELOG.md", "VERSION", "version.go")
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}
	cmd = exec.Command("git", "commit", "-s", "-m", "Tag v"+v.String(), "-m", "RELEASE_NOTES=n/a")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitTag(v semver.Version) error {
	cmd := exec.Command("git", "tag", "v"+v.String())
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func writeChangelog(prev, next semver.Version) error {
	cl, err := changelogEntries(prev)
	if err != nil {
		panic(err)
	}

	// prepend the new changelog entries by first writing the
	// new content in a new file ...
	fh, err := os.Create("CHANGELOG.new")
	if err != nil {
		return err
	}
	defer fh.Close()

	fmt.Fprintf(fh, "## %s / %s\n\n", next.String(), time.Now().UTC().Format("2006-01-02"))
	for _, e := range cl {
		fmt.Fprint(fh, "* ")
		fmt.Fprintln(fh, e)
	}
	fmt.Fprintln(fh)

	ofh, err := os.Open("CHANGELOG.md")
	if err != nil {
		return err
	}
	defer ofh.Close()

	// then appending any existing content from the old file and ...
	if _, err := io.Copy(fh, ofh); err != nil {
		return err
	}

	// renaming the new file to the old file
	return os.Rename("CHANGELOG.new", "CHANGELOG.md")
}

func writeVersion(v semver.Version) error {
	return ioutil.WriteFile("VERSION", []byte(v.String()+"\n"), 0644)
}

type tplPayload struct {
	Major uint64
	Minor uint64
	Patch uint64
}

func writeVersionGo(v semver.Version) error {
	tmpl, err := template.New("version").Parse(verTmpl)
	if err != nil {
		return err
	}
	fh, err := os.Create("version.go")
	if err != nil {
		return err
	}
	defer fh.Close()
	return tmpl.Execute(fh, tplPayload{
		Major: v.Major,
		Minor: v.Minor,
		Patch: v.Patch,
	})
}

func isGitClean() bool {
	buf, err := exec.Command("git", "diff", "--stat").CombinedOutput()
	if err != nil {
		panic(err)
	}
	return strings.TrimSpace(string(buf)) == ""
}

func versionFile() (semver.Version, error) {
	buf, err := ioutil.ReadFile("VERSION")
	if err != nil {
		return semver.Version{}, err
	}
	return semver.Parse(strings.TrimSpace(string(buf)))
}

func gitVersion() (semver.Version, error) {
	buf, err := exec.Command("git", "tag", "--sort=version:refname").CombinedOutput()
	if err != nil {
		return semver.Version{}, err
	}
	lines := strings.Split(strings.TrimSpace(string(buf)), "\n")
	if len(lines) < 1 {
		return semver.Version{}, fmt.Errorf("no output")
	}
	return semver.Parse(strings.TrimPrefix(lines[len(lines)-1], "v"))
}

func changelogEntries(since semver.Version) ([]string, error) {
	gitSep := "@@@GIT-SEP@@@"
	gitDelim := "@@@GIT-DELIM@@@"
	// full hash - subject - body
	// note: we don't use the hash at the moment
	prettyFormat := gitSep + "%H" + gitDelim + "%s" + gitDelim + "%b" + gitSep
	args := []string{
		"log",
		"v" + since.String() + "..HEAD",
		"--pretty=" + prettyFormat,
	}
	buf, err := exec.Command("git", args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("failed to run git %+v with error %w: %s", args, err, string(buf))
	}

	notes := make([]string, 0, 10)
	commits := strings.Split(string(buf), gitSep)
	for _, commit := range commits {
		commit := strings.TrimSpace(commit)
		if commit == "" {
			continue
		}
		p := strings.Split(commit, gitDelim)
		if len(p) < 3 {
			// invalid entry, shouldn't happen
			continue
		}

		issues := []string{}
		if m := issueRE.FindStringSubmatch(strings.TrimSpace(p[1])); len(m) > 1 {
			issues = append(issues, m[1])
		}

		for _, line := range strings.Split(p[2], "\n") {
			line := strings.TrimSpace(line)

			if m := issueRE.FindStringSubmatch(line); len(m) > 1 {
				issues = append(issues, m[1])
			}

			if !strings.HasPrefix(line, "RELEASE_NOTES=") {
				continue
			}
			p := strings.Split(line, "=")
			if len(p) < 2 {
				continue
			}
			val := p[1]
			if strings.ToLower(val) == "n/a" {
				continue
			}
			if len(issues) > 0 {
				val += " (#" + strings.Join(issues, ", #") + ")"
			}
			notes = append(notes, val)
		}
	}

	sort.Strings(notes)
	return notes, nil
}

func usage() {
	fmt.Printf("Usage: %s [next version] [prev version]\n", "go run helpers/release/main.go")
}
<commit_msg>Do not tag during release preparation (#1785)<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/blang/semver/v4"
)

var sleep = time.Second
var issueRE = regexp.MustCompile(`#(\d+)\b`)
var verTmpl = `package main

import (
	"strings"

	"github.com/blang/semver/v4"
)

func getVersion() semver.Version {
	sv, err := semver.Parse(strings.TrimPrefix(version, "v"))
	if err == nil {
		if commit != "" {
			sv.Build = []string{commit}
		}
		return sv
	}
	return semver.Version{
		Major: {{ .Major }},
		Minor: {{ .Minor }},
		Patch: {{ .Patch }},
		Pre: []semver.PRVersion{
			{VersionStr: "git"},
		},
		Build: []string{"HEAD"},
	}
}
`

const logo = `
   __     _    _ _      _ _   ___   ___
 /'_ '\ /'_'\ ( '_'\  /'_' )/',__)/',__)
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
'\__  |'\___/'| ,__/''\__,_)(____/(____/
( )_) |       | |
 \___/'       (_)
`

func main() {
	fmt.Println(logo)
	fmt.Println()
	fmt.Println("🌟 Preparing a new gopass release.")
	fmt.Println("☝  Checking pre-conditions ...")
	// - check that workdir is clean
	if !isGitClean() {
		panic("❌ git is dirty")
	}
	fmt.Println("✅ git is clean")
	// - check out master
	if err := gitCoMaster(); err != nil {
		panic(err)
	}
	fmt.Println("✅ Switched to master branch")
	// - pull from origin
	if err := gitPom(); err != nil {
		panic(err)
	}
	fmt.Println("✅ Fetched changes for master")
	// - check that workdir is clean
	if !isGitClean() {
		panic("git is dirty")
	}
	fmt.Println("✅ git is still clean")

	prevVer, nextVer := getVersions()

	fmt.Println()
	fmt.Printf("✅ New version will be: %s\n", nextVer.String())
	fmt.Println()
	fmt.Println("❓ Do you want to continue? (press any key to continue or Ctrl+C to abort)")
	fmt.Scanln()

	// - update VERSION
	if err := writeVersion(nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Wrote VERSION")
	time.Sleep(sleep)
	// - update version.go
	if err := writeVersionGo(nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Wrote version.go")
	time.Sleep(sleep)
	// - update CHANGELOG.md
	if err := writeChangelog(prevVer, nextVer); err != nil {
		panic(err)
	}
	fmt.Println("✅ Updated CHANGELOG.md")
	time.Sleep(sleep)

	// - create PR
	//   git checkout -b release/vX.Y.Z
	if err := gitCoRel(nextVer); err != nil {
		panic(err)
	}
	fmt.Printf("✅ Created branch release/v%s\n", nextVer.String())
	time.Sleep(sleep)

	// commit changes
	if err := gitCommit(nextVer); err != nil {
		panic(err)
	}
	fmt.Printf("✅ Committed changes to release/v%s\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Println("🏁 Preparation finished")
	time.Sleep(sleep)

	fmt.Printf("⚠ Prepared release of gopass %s.\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Printf("⚠ Run 'git push <remote> release/v%s' to push this branch and open a PR against gopasspw/gopass master.\n", nextVer.String())
	time.Sleep(sleep)

	fmt.Printf("⚠ Get the PR merged and run 'git tag v%s && git push origin v%s' to kick off the release process.\n", nextVer.String(), nextVer.String())
	time.Sleep(sleep)
	fmt.Println()

	fmt.Println("💎🙌 Done 🚀🚀🚀🚀🚀🚀")
}

func getVersions() (semver.Version, semver.Version) {
	nextVerFlag := ""
	if len(os.Args) > 1 {
		nextVerFlag = strings.TrimSpace(strings.TrimPrefix(os.Args[1], "v"))
	}
	prevVerFlag := ""
	if len(os.Args) > 2 {
		prevVerFlag = strings.TrimSpace(strings.TrimPrefix(os.Args[2], "v"))
	}

	// obtain the last tagged version from git
	gitVer, err := gitVersion()
	if err != nil {
		panic(err)
	}

	// read the version file to get the last committed version
	vfVer, err := versionFile()
	if err != nil {
		panic(err)
	}

	prevVer := gitVer
	if prevVerFlag != "" {
		prevVer = semver.MustParse(prevVerFlag)
	}

	if gitVer.NE(vfVer) {
		fmt.Printf("git version: %q != VERSION: %q\n", gitVer.String(), vfVer.String())
		if prevVerFlag == "" && len(vfVer.Pre) < 1 {
			usage()
			panic("version mismatch")
		}
	}

	nextVer := prevVer
	if nextVerFlag != "" {
		nextVer = semver.MustParse(nextVerFlag)
		if nextVer.LTE(prevVer) {
			usage()
			panic("next version must be greather than the previous version")
		}
	} else {
		nextVer.IncrementPatch()
		if len(vfVer.Pre) > 0 {
			nextVer = vfVer
			nextVer.Pre = nil
		}
	}

	fmt.Printf(`☝ Version overview
  Git (latest tag):  %q
  VERSION:           %q
  Next version flag: %q
  Prev version flag: %q

Will use
  Previous: %q
  Next:     %q
`,
		gitVer,
		vfVer,
		prevVerFlag,
		nextVerFlag,
		prevVer,
		nextVer)

	return prevVer, nextVer
}

func gitCoMaster() error {
	cmd := exec.Command("git", "checkout", "master")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitPom() error {
	cmd := exec.Command("git", "pull", "origin", "master")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitCoRel(v semver.Version) error {
	cmd := exec.Command("git", "checkout", "-b", "release/v"+v.String())
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func gitCommit(v semver.Version) error {
	cmd := exec.Command("git", "add", "CHANGELOG.md", "VERSION", "version.go")
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}
	cmd = exec.Command("git", "commit", "-s", "-m", "Tag v"+v.String(), "-m", "RELEASE_NOTES=n/a")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func writeChangelog(prev, next semver.Version) error {
	cl, err := changelogEntries(prev)
	if err != nil {
		panic(err)
	}

	// prepend the new changelog entries by first writing the
	// new content in a new file ...
	fh, err := os.Create("CHANGELOG.new")
	if err != nil {
		return err
	}
	defer fh.Close()

	fmt.Fprintf(fh, "## %s / %s\n\n", next.String(), time.Now().UTC().Format("2006-01-02"))
	for _, e := range cl {
		fmt.Fprint(fh, "* ")
		fmt.Fprintln(fh, e)
	}
	fmt.Fprintln(fh)

	ofh, err := os.Open("CHANGELOG.md")
	if err != nil {
		return err
	}
	defer ofh.Close()

	// then appending any existing content from the old file and ...
	if _, err := io.Copy(fh, ofh); err != nil {
		return err
	}

	// renaming the new file to the old file
	return os.Rename("CHANGELOG.new", "CHANGELOG.md")
}

func writeVersion(v semver.Version) error {
	return ioutil.WriteFile("VERSION", []byte(v.String()+"\n"), 0644)
}

type tplPayload struct {
	Major uint64
	Minor uint64
	Patch uint64
}

func writeVersionGo(v semver.Version) error {
	tmpl, err := template.New("version").Parse(verTmpl)
	if err != nil {
		return err
	}
	fh, err := os.Create("version.go")
	if err != nil {
		return err
	}
	defer fh.Close()
	return tmpl.Execute(fh, tplPayload{
		Major: v.Major,
		Minor: v.Minor,
		Patch: v.Patch,
	})
}

func isGitClean() bool {
	buf, err := exec.Command("git", "diff", "--stat").CombinedOutput()
	if err != nil {
		panic(err)
	}
	return strings.TrimSpace(string(buf)) == ""
}

func versionFile() (semver.Version, error) {
	buf, err := ioutil.ReadFile("VERSION")
	if err != nil {
		return semver.Version{}, err
	}
	return semver.Parse(strings.TrimSpace(string(buf)))
}

func gitVersion() (semver.Version, error) {
	buf, err := exec.Command("git", "tag", "--sort=version:refname").CombinedOutput()
	if err != nil {
		return semver.Version{}, err
	}
	lines := strings.Split(strings.TrimSpace(string(buf)), "\n")
	if len(lines) < 1 {
		return semver.Version{}, fmt.Errorf("no output")
	}
	return semver.Parse(strings.TrimPrefix(lines[len(lines)-1], "v"))
}

func changelogEntries(since semver.Version) ([]string, error) {
	gitSep := "@@@GIT-SEP@@@"
	gitDelim := "@@@GIT-DELIM@@@"
	// full hash - subject - body
	// note: we don't use the hash at the moment
	prettyFormat := gitSep + "%H" + gitDelim + "%s" + gitDelim + "%b" + gitSep
	args := []string{
		"log",
		"v" + since.String() + "..HEAD",
		"--pretty=" + prettyFormat,
	}
	buf, err := exec.Command("git", args...).CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("failed to run git %+v with error %w: %s", args, err, string(buf))
	}

	notes := make([]string, 0, 10)
	commits := strings.Split(string(buf), gitSep)
	for _, commit := range commits {
		commit := strings.TrimSpace(commit)
		if commit == "" {
			continue
		}
		p := strings.Split(commit, gitDelim)
		if len(p) < 3 {
			// invalid entry, shouldn't happen
			continue
		}

		issues := []string{}
		if m := issueRE.FindStringSubmatch(strings.TrimSpace(p[1])); len(m) > 1 {
			issues = append(issues, m[1])
		}

		for _, line := range strings.Split(p[2], "\n") {
			line := strings.TrimSpace(line)

			if m := issueRE.FindStringSubmatch(line); len(m) > 1 {
				issues = append(issues, m[1])
			}

			if !strings.HasPrefix(line, "RELEASE_NOTES=") {
				continue
			}
			p := strings.Split(line, "=")
			if len(p) < 2 {
				continue
			}
			val := p[1]
			if strings.ToLower(val) == "n/a" {
				continue
			}
			if len(issues) > 0 {
				val += " (#" + strings.Join(issues, ", #") + ")"
			}
			notes = append(notes, val)
		}
	}

	sort.Strings(notes)
	return notes, nil
}

func usage() {
	fmt.Printf("Usage: %s [next version] [prev version]\n", "go run helpers/release/main.go")
}
<|endoftext|>


<commit_before>package hermes

import(
	"testing"
	"strconv"
)

func TestChecksum(t *testing.T){
	global_contents := "jhfvjhdfjhfjjhjhdfvjvcvfjh"
	want := "_68f3caf439065824dcf75651c202e9f7c28ebf07" //expected checksum result
	file := HermesFile{}
	file.contents = global_contents;
	got := file.GenerateFileChecksum()
	if want != got {
		t.Errorf("GenerateFileChecksum() failed expected %v got %v", want, got)
	}
}

func TestFileName(t *testing.T){
	file := HermesFile{}
	file_id := 23;
	fake_checksum :="_abba"
	want :=  "Hermes_"+strconv.Itoa(file_ID)+"_abba" //expected file name result
	file.GenerateFileName(file_ID, fake_checksum)
	got := file.name
	if got != want {
		t.Errorf("GenerateFileName(%v, \"abba\") failed expected %v got %v", file_ID, want, got)
	}
	file_ID = 4;
	want = "Hermes_0"+strconv.Itoa(file_ID)+"_abba" //expected file name result
	file.GenerateFileName(file_ID, fake_checksum)
	got = file.name
	if got != want {
		t.Errorf("GenerateFileName(%v, \"abba\") failed expected %v got %v", file_ID, want, got)
	}
}
<commit_msg>Update hermes/file_gen_test.go<commit_after>package hermes

import(
	"testing"
	"strconv"
)

func TestChecksum(t *testing.T){
	global_contents := "jhfvjhdfjhfjjhjhdfvjvcvfjh"
	want := "_68f3caf439065824dcf75651c202e9f7c28ebf07"  //expected checksum result
	file := HermesFile{}
	file.contents = global_contents;
	got := file.GenerateFileChecksum()
	if want != got {
		t.Errorf("GenerateFileChecksum() failed expected %v got %v", want, got)
	}
}

func TestFileName(t *testing.T){
	file := HermesFile{}
	file_id := 23;
	fake_checksum :="_abba"
	want :=  "Hermes_"+strconv.Itoa(file_ID)+"_abba" //expected file name result
	file.GenerateFileName(file_ID, fake_checksum)
	got := file.name
	if got != want {
		t.Errorf("GenerateFileName(%v, \"abba\") failed expected %v got %v", file_ID, want, got)
	}
	file_ID = 4;
	want = "Hermes_0"+strconv.Itoa(file_ID)+"_abba" //expected file name result
	file.GenerateFileName(file_ID, fake_checksum)
	got = file.name
	if got != want {
		t.Errorf("GenerateFileName(%v, \"abba\") failed expected %v got %v", file_ID, want, got)
	}
}
<|endoftext|>