<commit_before>package main

import (
	"github.com/kisielk/whisper-go/whisper"
	pickle "github.com/kisielk/og-rek"

	"net/http"
	"encoding/json"
	"path/filepath"
	"strings"
	"strconv"
	"fmt"
)

var config = struct {
	WhisperData	string
}{
	WhisperData: "/var/lib/carbon/whisper",
	//WhisperData: "..",
}

type WhisperFetchResponse struct {
	Name		string		`json:"name"`
	StartTime	uint32		`json:"startTime"`
	StopTime	uint32		`json:"stopTime"`
	StepTime	uint32		`json:"stepTime"`
	Values		[]float64	`json:"values"`
}

type WhisperGlobResponse struct {
	Name		string		`json:"name"`
	Paths		[]string	`json:"paths"`
}

func findHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /metrics/find/?local=1&format=pickle&query=general.hadoop.lhr4.ha201jobtracker-01.jobtracker.NonHeapMemoryUsage.committed HTTP/1.1
//	http://localhost:8080/metrics/find/?query=test
	req.ParseForm()
	glob := req.FormValue("query")
	format := req.FormValue("format")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	/* things to glob:
	 * - carbon.relays  -> carbon.relays
	 * - carbon.re      -> carbon.relays, carbon.rewhatever
	 * - implicit * at the end of each query
	 * - match is either dir or .wsp file
	 * (this is less featureful than original carbon)
	 */
	path := config.WhisperData + "/" + strings.Replace(glob, ".", "/", -1) + "*"
	files, err := filepath.Glob(path)
	if err != nil {
		files = make([]string, 0)
	}

	leafs := make([]bool, len(files))
	for i, p := range files {
		p = p[len(config.WhisperData + "/"):]
		if strings.HasSuffix(p, ".wsp") {
			p = p[:len(p) - 4]
			leafs[i] = true
		} else {
			leafs[i] = false
		}
		files[i] = strings.Replace(p, "/", ".", -1)
	}

	if format == "json" {
		response := WhisperGlobResponse {
			Name:		glob,
			Paths:		make([]string, 0),
		}
		for _, p := range files {
			response.Paths = append(response.Paths, p)
		}
		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", glob, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		// [{'metric_path': 'metric', 'intervals': [(x,y)], 'isLeaf': True},]
		var metrics []map[string]interface{}
		var m map[string]interface{}

		for i, p := range files {
			m = make(map[string]interface{})
			m["metric_path"] = p
			// m["intervals"] = dunno how to do a tuple here
			m["isLeaf"] = leafs[i]
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}
	fmt.Printf("served %d points\n", len(files))
	return
}

func fetchHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /render/?target=general.me.1.percent_time_active.pfnredis&format=pickle&from=1396008021&until=1396022421 HTTP/1.1
//	http://localhost:8080/render/?target=testmetric&format=json&from=1395961200&until=1395961800
	req.ParseForm()
	metric := req.FormValue("target")
	format := req.FormValue("format")
	from := req.FormValue("from")
	until := req.FormValue("until")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	path := config.WhisperData + "/" + strings.Replace(metric, ".", "/", -1) + ".wsp"
	w, err := whisper.Open(path)
	if err != nil {
		fmt.Printf("failed to open %s: %s\n", path, err)
		w = nil
	}

	i, err := strconv.Atoi(from)
	var fromTime uint32
	if err != nil {
		fmt.Printf("fromTime (%s) invalid: %s\n", from, err)
		if w != nil {
			w.Close()
		}
		w = nil
	} else {
		fromTime = uint32(i)
	}
	i, err = strconv.Atoi(until)
	var untilTime uint32
	if err != nil {
		fmt.Printf("untilTime (%s) invalid: %s\n", from, err)
		if w != nil {
			w.Close()
		}
		w = nil
	} else {
		untilTime = uint32(i)
	}

	if (w != nil) {
		defer w.Close()
	}

	var interval whisper.Interval
	var points []whisper.Point
	if w != nil {
		interval, points, err = w.FetchUntil(fromTime, untilTime)
		if err != nil {
			fmt.Printf("failed to getch points from %s: %s\n", path, err)
			return
		}
	}

	if format == "json" {
		var response WhisperFetchResponse
		if w != nil {
			response = WhisperFetchResponse {
				Name:		metric,
				StartTime:	interval.FromTimestamp,
				StopTime:	interval.UntilTimestamp,
				StepTime:	interval.Step,
			}
			for _, p := range points {
				response.Values = append(response.Values, p.Value)
			}
		}

		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", path, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		//[{'start': 1396271100, 'step': 60, 'name': 'metric',
		//'values': [9.0, 19.0, None], 'end': 1396273140}
		var metrics []map[string]interface{}
		var m map[string]interface{}

		if w != nil {
			m = make(map[string]interface{})
			m["start"] = interval.FromTimestamp
			m["step"] = interval.Step
			m["end"] = interval.UntilTimestamp
			m["name"] = metric

			values := make([]interface{}, len(points))
			for i, p := range points {
				values[i] = p.Value
			}
			m["values"] = values
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}

	fmt.Printf("served %d points\n", len(points))
	return
}

func main() {
	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", fetchHandler)

	http.ListenAndServe(":8080", nil)
}
<commit_msg>make (info) output more useful<commit_after>package main

import (
	"github.com/kisielk/whisper-go/whisper"
	pickle "github.com/kisielk/og-rek"

	"net/http"
	"encoding/json"
	"path/filepath"
	"strings"
	"strconv"
	"fmt"
)

var config = struct {
	WhisperData	string
}{
	WhisperData: "/var/lib/carbon/whisper",
	//WhisperData: "..",
}

type WhisperFetchResponse struct {
	Name		string		`json:"name"`
	StartTime	uint32		`json:"startTime"`
	StopTime	uint32		`json:"stopTime"`
	StepTime	uint32		`json:"stepTime"`
	Values		[]float64	`json:"values"`
}

type WhisperGlobResponse struct {
	Name		string		`json:"name"`
	Paths		[]string	`json:"paths"`
}

func findHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /metrics/find/?local=1&format=pickle&query=general.hadoop.lhr4.ha201jobtracker-01.jobtracker.NonHeapMemoryUsage.committed HTTP/1.1
//	http://localhost:8080/metrics/find/?query=test
	req.ParseForm()
	glob := req.FormValue("query")
	format := req.FormValue("format")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	/* things to glob:
	 * - carbon.relays  -> carbon.relays
	 * - carbon.re      -> carbon.relays, carbon.rewhatever
	 * - implicit * at the end of each query
	 * - match is either dir or .wsp file
	 * (this is less featureful than original carbon)
	 */
	path := config.WhisperData + "/" + strings.Replace(glob, ".", "/", -1) + "*"
	files, err := filepath.Glob(path)
	if err != nil {
		files = make([]string, 0)
	}

	leafs := make([]bool, len(files))
	for i, p := range files {
		p = p[len(config.WhisperData + "/"):]
		if strings.HasSuffix(p, ".wsp") {
			p = p[:len(p) - 4]
			leafs[i] = true
		} else {
			leafs[i] = false
		}
		files[i] = strings.Replace(p, "/", ".", -1)
	}

	if format == "json" {
		response := WhisperGlobResponse {
			Name:		glob,
			Paths:		make([]string, 0),
		}
		for _, p := range files {
			response.Paths = append(response.Paths, p)
		}
		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", glob, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		// [{'metric_path': 'metric', 'intervals': [(x,y)], 'isLeaf': True},]
		var metrics []map[string]interface{}
		var m map[string]interface{}

		for i, p := range files {
			m = make(map[string]interface{})
			m["metric_path"] = p
			// m["intervals"] = dunno how to do a tuple here
			m["isLeaf"] = leafs[i]
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}
	fmt.Printf("find: %d hits for %s\n", len(files), glob)
	return
}

func fetchHandler(wr http.ResponseWriter, req *http.Request) {
//	GET /render/?target=general.me.1.percent_time_active.pfnredis&format=pickle&from=1396008021&until=1396022421 HTTP/1.1
//	http://localhost:8080/render/?target=testmetric&format=json&from=1395961200&until=1395961800
	req.ParseForm()
	metric := req.FormValue("target")
	format := req.FormValue("format")
	from := req.FormValue("from")
	until := req.FormValue("until")

	if format != "json" && format != "pickle" {
		fmt.Printf("dropping invalid uri (format=%s): %s\n",
				format, req.URL.RequestURI())
		return
	}

	path := config.WhisperData + "/" + strings.Replace(metric, ".", "/", -1) + ".wsp"
	w, err := whisper.Open(path)
	if err != nil {
		// the FE/carbonzipper often requests metrics we don't have
		//fmt.Printf("failed to open %s: %s\n", path, err)
		w = nil
	}

	i, err := strconv.Atoi(from)
	var fromTime uint32
	if err != nil {
		fmt.Printf("fromTime (%s) invalid: %s\n", from, err)
		if w != nil {
			w.Close()
		}
		w = nil
	} else {
		fromTime = uint32(i)
	}
	i, err = strconv.Atoi(until)
	var untilTime uint32
	if err != nil {
		fmt.Printf("untilTime (%s) invalid: %s\n", from, err)
		if w != nil {
			w.Close()
		}
		w = nil
	} else {
		untilTime = uint32(i)
	}

	if (w != nil) {
		defer w.Close()
	}

	var interval whisper.Interval
	var points []whisper.Point
	if w != nil {
		interval, points, err = w.FetchUntil(fromTime, untilTime)
		if err != nil {
			fmt.Printf("failed to fetch points from %s: %s\n", path, err)
			return
		}
	}

	if format == "json" {
		var response WhisperFetchResponse
		if w != nil {
			response = WhisperFetchResponse {
				Name:		metric,
				StartTime:	interval.FromTimestamp,
				StopTime:	interval.UntilTimestamp,
				StepTime:	interval.Step,
			}
			for _, p := range points {
				response.Values = append(response.Values, p.Value)
			}
		}

		b, err := json.Marshal(response)
		if err != nil {
			fmt.Printf("failed to create JSON data for %s: %s\n", path, err)
			return
		}
		wr.Write(b)
	} else if format == "pickle" {
		//[{'start': 1396271100, 'step': 60, 'name': 'metric',
		//'values': [9.0, 19.0, None], 'end': 1396273140}
		var metrics []map[string]interface{}
		var m map[string]interface{}

		if w != nil {
			m = make(map[string]interface{})
			m["start"] = interval.FromTimestamp
			m["step"] = interval.Step
			m["end"] = interval.UntilTimestamp
			m["name"] = metric

			values := make([]interface{}, len(points))
			for i, p := range points {
				values[i] = p.Value
			}
			m["values"] = values
			metrics = append(metrics, m)
		}

		wr.Header().Set("Content-Type", "application/pickle")
		pEnc := pickle.NewEncoder(wr)
		pEnc.Encode(metrics)
	}

	fmt.Printf("rendered %d points for %s\n", len(points), metric)
	return
}

func main() {
	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", fetchHandler)

	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
	"runtime"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/yaml.v2"
)

type Data struct {
	commentDuration int `json:"commentDuration"`
	submitDuration  int `json:"submitDuration"`
	prID            int `json:"prID"`
}

type Config struct {
	AccessToken string `yaml:"AccessToken"` //mapping of the name.
	Search      struct {
		User string `yaml:"User"`
		Repo string `yaml:"Repo"`
	} `yaml:"Search"`
	TimeLine struct {
		Start string `yaml:"Start"`
		End   string `yaml:"End"`
	} `yaml:"TimeLine"`
}

type agent struct {
	config      Config
	startTime   time.Time
	endTime     time.Time
	client      *github.Client
	currentTime time.Time
	file        *os.File
}

func New() *agent {

	a := &agent{}
	source, err := ioutil.ReadFile("config.yaml")
	check(err)

	err = yaml.Unmarshal(source, &(a.config))
	check(err)

	fmt.Printf("Your token value: %#v\n", a.config.AccessToken)

	a.startTime, err = time.Parse("2006-01-02", a.config.TimeLine.Start)
	check(err)

	a.endTime, err = time.Parse("2006-01-02", a.config.TimeLine.End)
	check(err)

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: a.config.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	a.client = github.NewClient(tc)

	currentTime := time.Now()
	currentTime.Format("2006-01-02")
	a.currentTime = currentTime

	a.file, err = os.Create("/Users/Rishi/.gvm/pkgsets/go1.4/global/src/github.com/rishiloyola/pr-stats/github.json")
	check(err)

	return a
}

func main() {

	var (
		breakLoop bool
		wg        sync.WaitGroup
	)

	a := New()
	opt := &github.PullRequestListOptions{
		ListOptions: github.ListOptions{PerPage: 100},
		State:       "all",
	}
	breakLoop = false

	for {
		prs, resp, err := a.client.PullRequests.List(a.config.Search.User, a.config.Search.Repo, opt)

		if _, ok := err.(*github.RateLimitError); ok {
			log.Println("Hit rate limit. Try after an hour.")
		} else {
			check(err)
		}

		for i := 0; i < len(prs); i++ {
			current := *prs[i].CreatedAt

			if current.Before(a.startTime) {
				if current.Before(a.endTime) {
					breakLoop = true
					break
					fmt.Println("break")
				}
				wg.Add(1)

				go a.process(current, prs[i].Number, prs[i].ClosedAt, prs[i].MergedAt, getName(prs[i].User), getName(prs[i].Assignee), prs[i].State, &wg)
				time.Sleep(1000 * time.Millisecond)
			}
		}

		if resp.NextPage == 0 {
			break
		}

		opt.ListOptions.Page = resp.NextPage

		if breakLoop {
			break
		}
	}

	wg.Wait()
	defer a.file.Close()
}

func (a *agent) process(createdAt time.Time, id *int, closedAt *time.Time, mergedAt *time.Time, user string, assignee string, status *string, wg *sync.WaitGroup) {

	var (
		commentCreatedAt time.Time
		commentDuration  time.Duration
		submitDuration   time.Duration
	)

	optComments := &github.IssueListCommentsOptions{
		ListOptions: github.ListOptions{PerPage: 2},
	}
	comments, _, err := a.client.Issues.ListComments(a.config.Search.User, a.config.Search.Repo, *id, optComments)
	// optLabels := &github.ListOptions{
	// 	Page: 1,
	// 	PerPage: 10,
	// }
	// labels, _, err := a.client.Issues.ListLabelsByIssue(a.config.Search.User, a.config.Search.Repo, *id, optLabels)

	if _, ok := err.(*github.RateLimitError); ok {
		log.Println("hit rate limit. Try after an hour.")
		os.Exit(0)
	} else {
		check(err)
	}

	if len(comments) > 0 {
		commentCreatedAt = *comments[0].CreatedAt
		commentDuration = commentCreatedAt.Sub(createdAt)

		if closedAt != nil {
			submitDuration = closedAt.Sub(commentCreatedAt)
		} else if mergedAt != nil {
			submitDuration = mergedAt.Sub(commentCreatedAt)
		} else {
			if a.endTime.Sub(commentCreatedAt) > 0 {
				submitDuration = a.currentTime.Sub(commentCreatedAt)
			} else {
				submitDuration = commentCreatedAt.Sub(a.endTime)
			} //negative
		}

	} else {

		if closedAt != nil {
			commentDuration = 0
			submitDuration = closedAt.Sub(createdAt)
		} else if mergedAt != nil {
			commentDuration = 0
			submitDuration = mergedAt.Sub(createdAt)
		} else {
			commentDuration = a.currentTime.Sub(createdAt)
			submitDuration = 0
		}

	}

	_commentDuration := strconv.Itoa(int(commentDuration.Hours() / 24))
	_submitDuration := strconv.Itoa(int(submitDuration.Hours() / 24))
	_prID := strconv.Itoa(*id)
	created_on := strings.Split(createdAt.String(), " ")

	message := `{` +
		`"commentDuration": ` + _commentDuration +
		`, "submitDuration": ` + _submitDuration +
		`, "prID": "` + _prID +
		`", "status": "` + (*status) +
		`", "assignee": "` + assignee +
		`", "createdBy": "` + user +
		`", "created_on": "` + created_on[0] +
		`"}`

	_, err = a.file.Write([]byte(message + "\n"))
	check(err)

	fmt.Println(message)

	wg.Done()
}

func getName(user *github.User) string {

	if user != nil {
		return *user.Login
	} else {
		return "null"
	}

}

func check(err error) {
	if err != nil {
		pc, _, line, _ := runtime.Caller(1)
    	message := fmt.Sprintf("[error] %s[%d] %s", runtime.FuncForPC(pc).Name(), line, err.Error())
    	log.Println(message)
    	os.Exit(0)
	}
}
<commit_msg>Remove the labels feature<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
	"runtime"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"gopkg.in/yaml.v2"
)

type Data struct {
	commentDuration int `json:"commentDuration"`
	submitDuration  int `json:"submitDuration"`
	prID            int `json:"prID"`
}

type Config struct {
	AccessToken string `yaml:"AccessToken"` //mapping of the name.
	Search      struct {
		User string `yaml:"User"`
		Repo string `yaml:"Repo"`
	} `yaml:"Search"`
	TimeLine struct {
		Start string `yaml:"Start"`
		End   string `yaml:"End"`
	} `yaml:"TimeLine"`
}

type agent struct {
	config      Config
	startTime   time.Time
	endTime     time.Time
	client      *github.Client
	currentTime time.Time
	file        *os.File
}

func New() *agent {

	a := &agent{}
	source, err := ioutil.ReadFile("config.yaml")
	check(err)

	err = yaml.Unmarshal(source, &(a.config))
	check(err)

	fmt.Printf("Your token value: %#v\n", a.config.AccessToken)

	a.startTime, err = time.Parse("2006-01-02", a.config.TimeLine.Start)
	check(err)

	a.endTime, err = time.Parse("2006-01-02", a.config.TimeLine.End)
	check(err)

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: a.config.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	a.client = github.NewClient(tc)

	currentTime := time.Now()
	currentTime.Format("2006-01-02")
	a.currentTime = currentTime

	a.file, err = os.Create("/Users/Rishi/.gvm/pkgsets/go1.4/global/src/github.com/rishiloyola/pr-stats/github.json")
	check(err)

	return a
}

func main() {

	var (
		breakLoop bool
		wg        sync.WaitGroup
	)

	a := New()
	opt := &github.PullRequestListOptions{
		ListOptions: github.ListOptions{PerPage: 100},
		State:       "all",
	}
	breakLoop = false

	for {
		prs, resp, err := a.client.PullRequests.List(a.config.Search.User, a.config.Search.Repo, opt)

		if _, ok := err.(*github.RateLimitError); ok {
			log.Println("Hit rate limit. Try after an hour.")
		} else {
			check(err)
		}

		for i := 0; i < len(prs); i++ {
			current := *prs[i].CreatedAt

			if current.Before(a.startTime) {
				if current.Before(a.endTime) {
					breakLoop = true
					break
					fmt.Println("break")
				}
				wg.Add(1)

				go a.process(current, prs[i].Number, prs[i].ClosedAt, prs[i].MergedAt, getName(prs[i].User), getName(prs[i].Assignee), prs[i].State, &wg)
				time.Sleep(1000 * time.Millisecond)
			}
		}

		if resp.NextPage == 0 {
			break
		}

		opt.ListOptions.Page = resp.NextPage

		if breakLoop {
			break
		}
	}

	wg.Wait()
	defer a.file.Close()
}

func (a *agent) process(createdAt time.Time, id *int, closedAt *time.Time, mergedAt *time.Time, user string, assignee string, status *string, wg *sync.WaitGroup) {

	var (
		commentCreatedAt time.Time
		commentDuration  time.Duration
		submitDuration   time.Duration
	)

	optComments := &github.IssueListCommentsOptions{
		ListOptions: github.ListOptions{PerPage: 2},
	}
	comments, _, err := a.client.Issues.ListComments(a.config.Search.User, a.config.Search.Repo, *id, optComments)

	if _, ok := err.(*github.RateLimitError); ok {
		log.Println("hit rate limit. Try after an hour.")
		os.Exit(0)
	} else {
		check(err)
	}

	if len(comments) > 0 {
		commentCreatedAt = *comments[0].CreatedAt
		commentDuration = commentCreatedAt.Sub(createdAt)

		if closedAt != nil {
			submitDuration = closedAt.Sub(commentCreatedAt)
		} else if mergedAt != nil {
			submitDuration = mergedAt.Sub(commentCreatedAt)
		} else {
			if a.endTime.Sub(commentCreatedAt) > 0 {
				submitDuration = a.currentTime.Sub(commentCreatedAt)
			} else {
				submitDuration = commentCreatedAt.Sub(a.endTime)
			} //negative
		}

	} else {

		if closedAt != nil {
			commentDuration = 0
			submitDuration = closedAt.Sub(createdAt)
		} else if mergedAt != nil {
			commentDuration = 0
			submitDuration = mergedAt.Sub(createdAt)
		} else {
			commentDuration = a.currentTime.Sub(createdAt)
			submitDuration = 0
		}

	}

	_commentDuration := strconv.Itoa(int(commentDuration.Hours() / 24))
	_submitDuration := strconv.Itoa(int(submitDuration.Hours() / 24))
	_prID := strconv.Itoa(*id)
	created_on := strings.Split(createdAt.String(), " ")

	message := `{` +
		`"commentDuration": ` + _commentDuration +
		`, "submitDuration": ` + _submitDuration +
		`, "prID": "` + _prID +
		`", "status": "` + (*status) +
		`", "assignee": "` + assignee +
		`", "createdBy": "` + user +
		`", "created_on": "` + created_on[0] +
		`"}`

	_, err = a.file.Write([]byte(message + "\n"))
	check(err)

	fmt.Println(message)

	wg.Done()
}

func getName(user *github.User) string {

	if user != nil {
		return *user.Login
	} else {
		return "null"
	}

}

func check(err error) {
	if err != nil {
		pc, _, line, _ := runtime.Caller(1)
    	message := fmt.Sprintf("[error] %s[%d] %s", runtime.FuncForPC(pc).Name(), line, err.Error())
    	log.Println(message)
    	os.Exit(0)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/urfave/cli"
)

var (
	noop    = false
	build   = ""
	version = "dev-build"
)

func readInBytes(ch chan []byte, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanBytes)
	for scanner.Scan() {
		b := scanner.Bytes()
		ch <- b
		if tee {
			fmt.Printf("%s", b)
		}
	}
	close(ch)
}

func readInLines(ch chan string, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		ch <- scanner.Text()
		if tee {
			fmt.Println(scanner.Text())
		}
	}
	close(ch)
}

func writeTemp(byteCh chan []byte) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile")

	w := bufio.NewWriter(tmp)
	for b := range byteCh {
		n, err := w.Write(b)
		failOnError(err, "error writing to tmpfile")
	}
	w.Flush()

	return tmp.Name()
}

func handleUsageError(c *cli.Context, err error, _ bool) error {
	fmt.Fprintf(c.App.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
	cli.ShowAppHelp(c)
	return cli.NewExitError("", 1)
}

func printFullVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v, build %v\n", c.App.Name, c.App.Version, build)
}

func main() {
	cli.VersionPrinter = printFullVersion

	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.OnUsageError = handleUsageError
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "comment",
			Usage: "Initial comment for snippet",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
		cli.StringFlag{
			Name:  "filetype",
			Usage: "Specify filetype for syntax highlighting",
		},
		cli.BoolFlag{
			Name:  "list",
			Usage: "List team channel names",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "stream, s",
			Usage: "Stream messages to Slack continuously instead of uploading a single snippet",
		},
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
		cli.StringFlag{
			Name:  "username, u",
			Usage: "Stream messages as given bot user. Defaults to auth user",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.Bool("configure") {
			configureOA()
			os.Exit(0)
		}

		configPath, exists := getConfigPath()
		if !exists {
			exitErr(fmt.Errorf("missing config file at %s\nuse --configure to create", configPath))
		}
		config := ReadConfig(configPath)

		team, channel, err := config.parseChannelOpt(c.String("channel"))
		failOnError(err)

		noop = c.Bool("noop")
		username := c.String("username")
		fileName := c.String("filename")
		fileType := c.String("filetype")
		fileComment := c.String("comment")

		token := config.Teams[team]
		if token == "" {
			exitErr(fmt.Errorf("no such team: %s", team))
		}

		InitAPI(token)
		slackcat := newSlackcat(username, channel)

		if c.Bool("list") {
			fmt.Println("channels:")
			for _, n := range listChannels() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("groups:")
			for _, n := range listGroups() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("ims:")
			for _, n := range listIms() {
				fmt.Printf("  %s\n", n)
			}
			os.Exit(0)
		}

		if len(c.Args()) > 0 {
			if c.Bool("stream") {
				output("filepath provided, ignoring stream option")
			}
			filePath := c.Args()[0]
			if fileName == "" {
				fileName = filepath.Base(filePath)
			}
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}

		if c.Bool("stream") {
			lineCh := make(chan string)
			go readInLines(lineCh, c.Bool("tee"))
			slackcat.stream(lineCh)
		} else {
			byteCh := make(chan []byte)
			go readInBytes(byteCh, c.Bool("tee"))
			filePath := writeTemp(byteCh)
			defer os.Remove(filePath)
			fmt.Println(filePath)
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}
	}

	app.Run(os.Args)

}
<commit_msg>refactor readin methods to stdinscanner<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/urfave/cli"
)

var (
	noop    = false
	build   = ""
	version = "dev-build"
)

type StdinScanner struct {
	*bufio.Scanner
	tee bool
}

func NewStdinScanner(tee bool) *StdinScanner {
	return &StdinScanner{bufio.NewScanner(os.Stdin), tee}
}

func (s *StdinScanner) StreamBytes() chan []byte {
	ch := make(chan []byte)
	s.Split(bufio.ScanBytes)
	go func() {
		for s.Scan() {
			b := s.Bytes()
			ch <- b
			if s.tee {
				fmt.Printf("%s", b)
			}
		}
		close(ch)
	}()
	return ch
}

func (s *StdinScanner) StreamLines() chan string {
	ch := make(chan string)
	s.Split(bufio.ScanLines)
	go func() {
		for s.Scan() {
			ch <- s.Text()
			if s.tee {
				fmt.Println(s.Text())
			}
		}
		close(ch)
	}()
	return ch
}

func writeTemp(byteCh chan []byte) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile")

	w := bufio.NewWriter(tmp)
	for b := range byteCh {
		_, err := w.Write(b)
		failOnError(err, "error writing to tmpfile")
	}
	w.Flush()

	return tmp.Name()
}

func handleUsageError(c *cli.Context, err error, _ bool) error {
	fmt.Fprintf(c.App.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
	cli.ShowAppHelp(c)
	return cli.NewExitError("", 1)
}

func printFullVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v, build %v\n", c.App.Name, c.App.Version, build)
}

func main() {
	cli.VersionPrinter = printFullVersion

	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.OnUsageError = handleUsageError
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "comment",
			Usage: "Initial comment for snippet",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
		cli.StringFlag{
			Name:  "filetype",
			Usage: "Specify filetype for syntax highlighting",
		},
		cli.BoolFlag{
			Name:  "list",
			Usage: "List team channel names",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "stream, s",
			Usage: "Stream messages to Slack continuously instead of uploading a single snippet",
		},
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
		cli.StringFlag{
			Name:  "username, u",
			Usage: "Stream messages as given bot user. Defaults to auth user",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.Bool("configure") {
			configureOA()
			os.Exit(0)
		}

		configPath, exists := getConfigPath()
		if !exists {
			exitErr(fmt.Errorf("missing config file at %s\nuse --configure to create", configPath))
		}
		config := ReadConfig(configPath)

		team, channel, err := config.parseChannelOpt(c.String("channel"))
		failOnError(err)

		noop = c.Bool("noop")
		username := c.String("username")
		fileName := c.String("filename")
		fileType := c.String("filetype")
		fileComment := c.String("comment")

		token := config.Teams[team]
		if token == "" {
			exitErr(fmt.Errorf("no such team: %s", team))
		}

		InitAPI(token)
		slackcat := newSlackcat(username, channel)

		if c.Bool("list") {
			fmt.Println("channels:")
			for _, n := range listChannels() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("groups:")
			for _, n := range listGroups() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("ims:")
			for _, n := range listIms() {
				fmt.Printf("  %s\n", n)
			}
			os.Exit(0)
		}

		if len(c.Args()) > 0 {
			if c.Bool("stream") {
				output("filepath provided, ignoring stream option")
			}
			filePath := c.Args()[0]
			if fileName == "" {
				fileName = filepath.Base(filePath)
			}
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}

		scanner := NewStdinScanner(c.Bool("tee"))

		if c.Bool("stream") {
			slackcat.stream(scanner.StreamLines())
		} else {
			filePath := writeTemp(scanner.StreamBytes())
			defer os.Remove(filePath)
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}
	}

	app.Run(os.Args)

}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	"io/ioutil"
)

var DB = make(map[string]string)

func NewApp() *gin.Engine {

	r := gin.Default()

	r.LoadHTMLGlob("templates/index.tmpl")
  r.Static("/static", "./static")

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "users/index.tmpl", gin.H{
			"pubTo": "Users",
		})
	})

	r.GET("/slides.md", func(c *gin.Context) {
		body, err := ioutil.ReadFile("initial-slides.md")
		if err != nil {
			panic(err)
		}
    ioutil.WriteFile("slides.md", body, 0644)
		c.String(200, string(body))
	})

	r.PUT("/slides.md", func(c *gin.Context) {
    body, _ := ioutil.ReadAll(c.Request.Body);
    ioutil.WriteFile("slides.md", body, 0644)
		c.String(200, "")
	})

	return r

}

func main() {
	r := NewApp()
	r.Run(":8080")
}
<commit_msg>Almost working<commit_after>package main

import (
	"fmt"
	"github.com/atrox/haikunatorgo"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"os"
)

func NewApp() *gin.Engine {

	r := gin.Default()

	store := sessions.NewCookieStore([]byte("secret"))
	r.Use(sessions.Sessions("mysession", store))

	r.LoadHTMLGlob("templates/index.tmpl")
	r.Static("/static", "./static")

	r.GET("/", func(c *gin.Context) {
		haikunator := haikunator.NewHaikunator()
		haikunator.TokenLength = 0
		name := haikunator.Haikunate()
		path := fmt.Sprintf("%s.md", name)
		session := sessions.Default(c)
		session.Set("name", path)
		session.Save()
		c.HTML(200, "users/index.tmpl", gin.H{
			"pubTo": path,
		})
	})

	r.GET("/slides.md", func(c *gin.Context) {
		session := sessions.Default(c)
		val := session.Get("name")
		path, ok := val.(string)
		if !ok {
			panic("unlucky")
		}
		if _, err := os.Stat(path); err != nil {
			body, err := ioutil.ReadFile("initial-slides.md")
			if err != nil {
				panic(err)
			}
			ioutil.WriteFile(path, body, 0644)
		}

		body, err := ioutil.ReadFile(path)
		if err != nil {
			panic(err)
		}
		c.String(200, string(body))
	})

	r.PUT("/slides.md", func(c *gin.Context) {
		session := sessions.Default(c)
		val := session.Get("name")
		path, ok := val.(string)
		if !ok {
			panic("unlucky")
		}
		body, _ := ioutil.ReadAll(c.Request.Body)
		ioutil.WriteFile(path, body, 0644)
		c.String(200, "")
	})

	return r

}

func main() {
	r := NewApp()
	r.Run(":8080")
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.8.0-beta"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<commit_msg>update to 0.8.0 (#199)<commit_after>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.8.0"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/pelletier/go-toml"
)

func setColor(c string) {
	if c == "red" {
		color.Set(color.FgRed)
	} else if c == "yellow" {
		color.Set(color.FgYellow)
	} else if c == "green" {
		color.Set(color.FgGreen)
	} else if c == "purple" {
		color.Set(color.FgMagenta)
	}
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		config, err := toml.LoadFile(home + "/.trelloclirc")
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.Get("app_key").(string)
		token = config.Get("token").(string)
		username = config.Get("username").(string)
	}

	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	for _, board := range boards {
		if board.Closed {
			continue
		}

		fmt.Printf("# %v (%v)\n\n", board.Name, board.ShortUrl)

		lists, err := board.Lists()
		if err != nil {
			log.Fatal(err)
		}

		for _, list := range lists {
			cards, _ := list.Cards()
			if len(cards) > 0 {
				fmt.Printf("## %s\n\n", list.Name)

				for _, card := range cards {
					for _, label := range card.Labels {
						setColor(label.Color)
						break
					}

					if card.Due != "" {
						t, _ := time.Parse(time.RFC3339Nano, card.Due)
						fmt.Printf("* %s %s\n", t.Format("[2006-01-02]"), card.Name)
					} else {
						fmt.Printf("* %s\n", card.Name)
					}

					color.Unset()
				}
			}

			fmt.Println()
		}

		fmt.Println()
	}
}
<commit_msg>Refactoring<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/VojtechVitek/go-trello"
	"github.com/fatih/color"
	"github.com/mitchellh/go-homedir"
	"github.com/pelletier/go-toml"
)

func setColor(c string) {
	if c == "red" {
		color.Set(color.FgRed)
	} else if c == "yellow" {
		color.Set(color.FgYellow)
	} else if c == "green" {
		color.Set(color.FgGreen)
	} else if c == "purple" {
		color.Set(color.FgMagenta)
	}
}

func showBoards(boards []trello.Board) error {
	for _, board := range boards {
		if board.Closed {
			continue
		}

		fmt.Printf("# %v (%v)\n\n", board.Name, board.ShortUrl)

		lists, err := board.Lists()
		if err != nil {
			return err
		}

		showLists(lists)

		fmt.Println()
	}

	return nil
}

func showLists(lists []trello.List) {
	for _, list := range lists {
		cards, _ := list.Cards()
		if len(cards) > 0 {
			fmt.Printf("## %s\n\n", list.Name)

			showCards(cards)
		}

		fmt.Println()
	}
}

func showCards(cards []trello.Card) {
	for _, card := range cards {
		for _, label := range card.Labels {
			setColor(label.Color)
			break
		}

		if card.Due != "" {
			t, _ := time.Parse(time.RFC3339Nano, card.Due)
			fmt.Printf("* %s %s\n", t.Format("[2006-01-02]"), card.Name)
		} else {
			fmt.Printf("* %s\n", card.Name)
		}

		color.Unset()
	}
}

func getBoards(appKey string, token string, username string) []trello.Board {
	trello, err := trello.NewAuthClient(appKey, &token)
	if err != nil {
		log.Fatal(err)
	}

	user, err := trello.Member(username)
	if err != nil {
		log.Fatal(err)
	}

	boards, err := user.Boards()
	if err != nil {
		log.Fatal(err)
	}

	return boards
}

func main() {
	appKey := os.Getenv("TRELLO_APP_KEY")
	token := os.Getenv("TRELLO_TOKEN")
	username := os.Getenv("TRELLO_USER")

	if appKey == "" || token == "" || username == "" {
		home, err := homedir.Dir()
		if err != nil {
			log.Fatal(err)
		}

		config, err := toml.LoadFile(home + "/.trelloclirc")
		if err != nil {
			log.Fatal(err)
		}

		appKey = config.Get("app_key").(string)
		token = config.Get("token").(string)
		username = config.Get("username").(string)
	}

	boards := getBoards(appKey, token, username)

	err := showBoards(boards)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/krasin/cobra"
	"github.com/krasin/stl"
)

const sliceThreshold = 0.001

var (
	scaleX  float64
	coordX  float64
	coordY  float64
	coordZ  float64
	outPath string
)

func fail(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func openIn(files []string) (io.ReadCloser, error) {
	if len(files) == 0 {
		return os.Stdin, nil
	}
	if len(files) > 1 {
		return nil, errors.New("multiple input files are not supported yet")
	}
	return os.Open(files[0])
}

func openOut(path string) (io.WriteCloser, error) {
	if path == "" {
		return os.Stdout, nil
	}
	return os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
}

func info(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}
	fmt.Printf("Triangles: %d\n", len(t))
	min, max := stl.BoundingBox(t)
	fmt.Printf("Bounding box: %v - %v\n", min, max)
}

func scale(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()

	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}
	for i := range t {
		tr := &t[i]
		for j := 0; j < 3; j++ {
			for k := 0; k < 3; k++ {
				tr.V[j][k] = float32(scaleX * float64(tr.V[j][k]))
			}
		}
	}
	if err := stl.Write(w, t); err != nil {
		fail("Failed to write STL file:", err)
	}
}

func slice(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}

	// by default, slice with XY plane at z = 0
	si := 2
	sx := 0
	sy := 0
	var sv float32
	cnt := 0

	for i, v := range []float64{coordX, coordY, coordZ} {
		if v != 0 {
			si, sx, sy = i, (i+1)%3, (i+2)%3
			sv = float32(v)
			cnt++
		}
	}
	if cnt > 1 {
		fail("More than one coord is specified: x: %f, y: %f, z: %f", coordX, coordY, coordZ)
	}

	min, max := stl.BoundingBox(t)
	eps := (max[si] - min[si]) * sliceThreshold

	less := func(p stl.Point) bool { return p[si] < sv-eps }
	more := func(p stl.Point) bool { return p[si] > sv+eps }
	eq := func(p stl.Point) bool { return !less(p) && !more(p) }

	// SVG file will have units of 0.01 mm, and the input STL file is treated as mm.
	pmm := func(v float32) int { return int(v * 100) }
	width := pmm(max[sx] - min[sx])
	height := pmm(max[sy] - min[sy])

	// Write SVG header
	fmt.Fprintln(w, `<?xml version="1.0" encoding="UTF-8" standalone="no"?>`)
	fmt.Fprintln(w, `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`)
	fmt.Fprintf(w, `<svg width="%fmm" height="%fmm" version="1.1" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">`,
		float64(width)/100, float64(height)/100, width, height)
	fmt.Fprintln(w)
	fmt.Fprintln(w, `<g fill="gray" stroke="black" stroke-width="10">`)

	xx := func(v float32) int { return pmm(v - min[sx]) }
	yy := func(v float32) int { return pmm(v - min[sy]) }
	pxy := func(p stl.Point) string { return fmt.Sprintf("%d,%d", xx(p[sx]), yy(p[sy])) }

	for _, tr := range t {
		// For each triangle, we have the options: skip, draw a line, draw the triangle.

		// First, let's detect the triangles to skip.
		if less(tr.V[0]) && less(tr.V[1]) && less(tr.V[2]) {
			fmt.Fprintf(w, "<!-- skip a triangle; it's below: %v -->\n", tr.V)
			continue
		}
		if more(tr.V[0]) && more(tr.V[1]) && more(tr.V[2]) {
			fmt.Fprintf(w, "<!-- skip a triangle; it's above: %v -->\n", tr.V)
			continue
		}

		// Now, check if we need to draw the whole triangle
		if eq(tr.V[0]) && eq(tr.V[1]) && eq(tr.V[2]) {
			fmt.Fprintf(w, "<path d=\"M%s L%s L%s L%s\"/>\n", pxy(tr.V[0]), pxy(tr.V[1]), pxy(tr.V[2]), pxy(tr.V[0]))
			continue
		}

		// OK, it's the line. Two cases: line is a triangle side, or it's not.
		// First, let's check if it's a triangle side.
		was := false
		for i := 0; i < 3; i++ {
			j := (i + 1) % 3
			if eq(tr.V[i]) && eq(tr.V[j]) {
				fmt.Fprintf(w, "<path d='M%s L%s' />\n", pxy(tr.V[i]), pxy(tr.V[j]))
				was = true
				break
			}
		}
		if was {
			continue
		}

		// The line is not a triangle side.
		fmt.Fprintf(w, "<!-- here be a line -->\n")
	}

	// Write SVG footer
	fmt.Fprintln(w, "</g>")
	fmt.Fprintln(w, "</svg>")
}

func main() {
	rootCmd := &cobra.Command{
		Use:   "steel",
		Short: "A tool to tinker with STL files",
		Long:  "Command-line processor for STL files",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("Steel -- a tool to tinker with STL files.")
			cmd.Usage()
		},
	}

	infoCmd := &cobra.Command{
		Use:   "info [STL file]",
		Short: "STL file info",
		Long: `info displays STL metrics, such as the number of triangles, bounding box, etc.
If no STL file is specified, it will read from stdin`,
		Run: info,
	}
	rootCmd.AddCommand(infoCmd)

	scaleCmd := &cobra.Command{
		Use:   "scale [STL file]",
		Short: "Scale mesh",
		Long: `scale multiplies all mesh vertices coordinates by the specified amount.
If no STL file is specified, it will read from stdin.`,
		Run: scale,
	}
	scaleCmd.Flags().Float64VarP(&scaleX, "x", "x", 1, "Scale factor")
	scaleCmd.Flags().StringVarP(&outPath, "output", "o", "", "Output STL file. By default, it's stdout.")
	rootCmd.AddCommand(scaleCmd)

	sliceCmd := &cobra.Command{
		Use:   "slice [STL file]",
		Short: "Slice mesh by a plane to SVG",
		Long: `slice mesh by a specified plane and render to SVG graphics.
If no STL file is specified, it will read from stdin.`,
		Run: slice,
	}
	sliceCmd.Flags().StringVarP(&outPath, "output", "o", "", "Output SVG file. By default, it's stdout.")
	sliceCmd.Flags().Float64VarP(&coordX, "x", "x", 0, "If specified, it will slice with YZ plane at specified x.")
	sliceCmd.Flags().Float64VarP(&coordY, "y", "y", 0, "If specified, it will slice with XZ plane at specified y.")
	sliceCmd.Flags().Float64VarP(&coordZ, "z", "z", 0, "If specified, it will slice with XY plane at specified z.")
	rootCmd.AddCommand(sliceCmd)

	rootCmd.Execute()
}
<commit_msg>steel slice: draw lines, which are not triangle sides. Add -v parameter to control verbosity. If verbose, skipped triangles leave traces in SVG comments.<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/krasin/cobra"
	"github.com/krasin/stl"
)

const sliceThreshold = 0.001

var (
	scaleX  float64
	coordX  float64
	coordY  float64
	coordZ  float64
	outPath string
	verbose bool
)

func fail(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func openIn(files []string) (io.ReadCloser, error) {
	if len(files) == 0 {
		return os.Stdin, nil
	}
	if len(files) > 1 {
		return nil, errors.New("multiple input files are not supported yet")
	}
	return os.Open(files[0])
}

func openOut(path string) (io.WriteCloser, error) {
	if path == "" {
		return os.Stdout, nil
	}
	return os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
}

func info(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}
	fmt.Printf("Triangles: %d\n", len(t))
	min, max := stl.BoundingBox(t)
	fmt.Printf("Bounding box: %v - %v\n", min, max)
}

func scale(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()

	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}
	for i := range t {
		tr := &t[i]
		for j := 0; j < 3; j++ {
			for k := 0; k < 3; k++ {
				tr.V[j][k] = float32(scaleX * float64(tr.V[j][k]))
			}
		}
	}
	if err := stl.Write(w, t); err != nil {
		fail("Failed to write STL file:", err)
	}
}

func slice(cmd *cobra.Command, args []string) {
	r, err := openIn(args)
	if err != nil {
		fail(err)
	}
	defer r.Close()
	w, err := openOut(outPath)
	if err != nil {
		fail(err)
	}
	defer w.Close()
	t, err := stl.Read(r)
	if err != nil {
		fail("Failed to read STL file:", err)
	}

	// by default, slice with XY plane at z = 0
	si := 2
	sx := 0
	sy := 0
	var sv float32
	cnt := 0

	for i, v := range []float64{coordX, coordY, coordZ} {
		if v != 0 {
			si, sx, sy = i, (i+1)%3, (i+2)%3
			sv = float32(v)
			cnt++
		}
	}
	if cnt > 1 {
		fail("More than one coord is specified: x: %f, y: %f, z: %f", coordX, coordY, coordZ)
	}

	min, max := stl.BoundingBox(t)
	eps := (max[si] - min[si]) * sliceThreshold

	less := func(p stl.Point) bool { return p[si] < sv-eps }
	more := func(p stl.Point) bool { return p[si] > sv+eps }
	eq := func(p stl.Point) bool { return !less(p) && !more(p) }
	intersect := func(p0, p1 stl.Point) (res stl.Point) {
		alpha := (sv - p0[si]) / (p1[si] - p0[si])
		for i := 0; i < 3; i++ {
			res[i] = p0[i] + alpha*(p1[i]-p0[i])
		}
		return
	}

	// SVG file will have units of 0.01 mm, and the input STL file is treated as mm.
	pmm := func(v float32) int { return int(v * 100) }
	width := pmm(max[sx] - min[sx])
	height := pmm(max[sy] - min[sy])

	// Write SVG header
	fmt.Fprintln(w, `<?xml version="1.0" encoding="UTF-8" standalone="no"?>`)
	fmt.Fprintln(w, `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`)
	fmt.Fprintf(w, `<svg width="%fmm" height="%fmm" version="1.1" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">`,
		float64(width)/100, float64(height)/100, width, height)
	fmt.Fprintln(w)
	fmt.Fprintln(w, `<g fill="gray" stroke="black" stroke-width="10">`)

	xx := func(v float32) int { return pmm(v - min[sx]) }
	yy := func(v float32) int { return pmm(v - min[sy]) }
	pxy := func(p stl.Point) string { return fmt.Sprintf("%d,%d", xx(p[sx]), yy(p[sy])) }

	for _, tr := range t {
		// For each triangle, we have the options: skip, draw a line, draw the triangle.

		// First, let's detect the triangles to skip.
		if less(tr.V[0]) && less(tr.V[1]) && less(tr.V[2]) {
			if verbose {
				fmt.Fprintf(w, "<!-- skip a triangle; it's below: %v -->\n", tr.V)
			}
			continue
		}
		if more(tr.V[0]) && more(tr.V[1]) && more(tr.V[2]) {
			if verbose {
				fmt.Fprintf(w, "<!-- skip a triangle; it's above: %v -->\n", tr.V)
			}
			continue
		}

		// Now, check if we need to draw the whole triangle
		if eq(tr.V[0]) && eq(tr.V[1]) && eq(tr.V[2]) {
			fmt.Fprintf(w, "<path d=\"M%s L%s L%s L%s\"/>\n", pxy(tr.V[0]), pxy(tr.V[1]), pxy(tr.V[2]), pxy(tr.V[0]))
			continue
		}

		// OK, it's the line. Two cases: line is a triangle side, or it's not.
		was := false
		for i := 0; i < 3; i++ {
			j := (i + 1) % 3
			k := (i + 2) % 3
			// First, let's check if it's a triangle side.
			if eq(tr.V[i]) && eq(tr.V[j]) {
				fmt.Fprintf(w, "<path d='M%s L%s' />\n", pxy(tr.V[i]), pxy(tr.V[j]))
				was = true
				break
			}
			if less(tr.V[i]) && less(tr.V[j]) || more(tr.V[i]) && more(tr.V[j]) {
				// Since this triangle is known to intersect the slice plane, the k'th vertex is on the other side.
				// and we need to find intersection points on i-k and j-k triangle sides.
				p0 := intersect(tr.V[i], tr.V[k])
				p1 := intersect(tr.V[j], tr.V[k])
				fmt.Fprintf(w, "<path d='M%s L%s' />\n", pxy(p0), pxy(p1))
				was = true
			}
		}
		if was {
			continue
		}

		// Just a dot
		if verbose {
			fmt.Fprintf(w, "<!-- it's just a dot: %v -->\n", tr)
		}
	}

	// Write SVG footer
	fmt.Fprintln(w, "</g>")
	fmt.Fprintln(w, "</svg>")
}

func main() {
	rootCmd := &cobra.Command{
		Use:   "steel",
		Short: "A tool to tinker with STL files",
		Long:  "Command-line processor for STL files",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("Steel -- a tool to tinker with STL files.")
			cmd.Usage()
		},
	}

	infoCmd := &cobra.Command{
		Use:   "info [STL file]",
		Short: "STL file info",
		Long: `info displays STL metrics, such as the number of triangles, bounding box, etc.
If no STL file is specified, it will read from stdin`,
		Run: info,
	}
	rootCmd.AddCommand(infoCmd)

	scaleCmd := &cobra.Command{
		Use:   "scale [STL file]",
		Short: "Scale mesh",
		Long: `scale multiplies all mesh vertices coordinates by the specified amount.
If no STL file is specified, it will read from stdin.`,
		Run: scale,
	}
	scaleCmd.Flags().Float64VarP(&scaleX, "x", "x", 1, "Scale factor")
	scaleCmd.Flags().StringVarP(&outPath, "output", "o", "", "Output STL file. By default, it's stdout.")
	rootCmd.AddCommand(scaleCmd)

	sliceCmd := &cobra.Command{
		Use:   "slice [STL file]",
		Short: "Slice mesh by a plane to SVG",
		Long: `slice mesh by a specified plane and render to SVG graphics.
If no STL file is specified, it will read from stdin.`,
		Run: slice,
	}
	sliceCmd.Flags().StringVarP(&outPath, "output", "o", "", "Output SVG file. By default, it's stdout.")
	sliceCmd.Flags().BoolVarP(&verbose, "verbose", "v", false,
		"Verbosity. If verbose, skipped triangles will leave comments in the output SVG file.")
	sliceCmd.Flags().Float64VarP(&coordX, "x", "x", 0, "If specified, it will slice with YZ plane at specified x.")
	sliceCmd.Flags().Float64VarP(&coordY, "y", "y", 0, "If specified, it will slice with XZ plane at specified y.")
	sliceCmd.Flags().Float64VarP(&coordZ, "z", "z", 0, "If specified, it will slice with XY plane at specified z.")
	rootCmd.AddCommand(sliceCmd)

	rootCmd.Execute()
}
<|endoftext|>


<commit_before>// Copyright 2016 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 main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	configmaps   stringSlice
	namespaces   stringSlice
	noop         bool
	onetime      bool
	syncInterval time.Duration
)

func main() {
	flag.Var(&namespaces, "namespace", "the namespace to process.")
	flag.Var(&configmaps, "configmap", "the configmap to process.")
	flag.BoolVar(&noop, "noop", false, "print processed configmaps and secrets and do not submit them to the cluster.")
	flag.BoolVar(&onetime, "onetime", false, "run one time and exit.")
	flag.DurationVar(&syncInterval, "sync-interval", 60, "the number of seconds between template processing.")
	flag.Parse()

	if onetime {
		process(namespaces, configmaps, noop)
		os.Exit(0)
	}

	log.Println("Starting konfd...")
	var wg sync.WaitGroup
	done := make(chan struct{})

	go func() {
		wg.Add(1)
		for {
			process(namespaces, configmaps, noop)
			log.Printf("Syncing templates complete. Next sync in %d seconds.", syncInterval)
			select {
			case <-time.After(syncInterval * time.Second):
			case <-done:
				wg.Done()
				return
			}
		}
	}()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	<-signalChan
	log.Printf("Shutdown signal received, exiting...")
	close(done)
	wg.Wait()
	os.Exit(0)
}
<commit_msg>set default sync interval to 1 minute<commit_after>// Copyright 2016 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 main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

var (
	configmaps   stringSlice
	namespaces   stringSlice
	noop         bool
	onetime      bool
	syncInterval time.Duration
)

func main() {
	flag.Var(&namespaces, "namespace", "the namespace to process.")
	flag.Var(&configmaps, "configmap", "the configmap to process.")
	flag.BoolVar(&noop, "noop", false, "print processed configmaps and secrets and do not submit them to the cluster.")
	flag.BoolVar(&onetime, "onetime", false, "run one time and exit.")
	flag.DurationVar(&syncInterval, "sync-interval", (60 * time.Second), "the number of seconds between template processing.")
	flag.Parse()

	if onetime {
		process(namespaces, configmaps, noop)
		os.Exit(0)
	}

	log.Println("Starting konfd...")
	var wg sync.WaitGroup
	done := make(chan struct{})

	go func() {
		wg.Add(1)
		for {
			process(namespaces, configmaps, noop)
			log.Printf("Syncing templates complete. Next sync in %d seconds.", syncInterval)
			select {
			case <-time.After(syncInterval):
			case <-done:
				wg.Done()
				return
			}
		}
	}()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	<-signalChan
	log.Printf("Shutdown signal received, exiting...")
	close(done)
	wg.Wait()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/bitrise-io/go-utils/command"
	"strings"
)

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func getOptionalEnv(env string) string {
	return os.Getenv(env)
}

func getRequiredEnv(env string) string {
	if len(env) == 0 {
		panic(env + " is not defined!")
	}

	return os.Getenv(env)
}

func isEmpty(str string) bool {
	return len(str) == 0
}

func checkFileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

func runCommand(cmd string) {
	cmdSlice :=  strings.Fields(cmd)

	cmdObj := command.NewWithStandardOuts(cmdSlice[0], cmdSlice[1:]...)
	checkError(cmdObj.Run())
}

func main() {
	// todo: refactor this into a method which populates a struct
	gcloud_user := ""    //getOptionalEnv("GCLOUD_USER")
	gcloud_project := "" //getOptionalEnv("GCLOUD_PROJECT")

	app_apk := getRequiredEnv("APP_APK")
	checkFileExists(app_apk)

	test_apk := getOptionalEnv("TEST_APK")
	if !isEmpty(test_apk) {
		checkFileExists(test_apk)
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(getRequiredEnv("GCLOUD_KEY"))
	checkError(err)

	empty_gcloud_user := isEmpty(gcloud_user)
	empty_gcloud_project := isEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if isEmpty(gcloud_user) {
				panic("Missing gcloud user")
			}
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if isEmpty(gcloud_project) {
				panic("Missing gcloud project")
			}
		}
	}

	fmt.Println("User: ", gcloud_user)
	fmt.Println("Project: ", gcloud_project)
	fmt.Println("App APK: ", app_apk)
	fmt.Println("Test APK: ", app_apk)

	home_dir := getRequiredEnv("HOME")
	key_file_path := path.Join(home_dir, "gcloudkey.json")
	checkError(ioutil.WriteFile(key_file_path, gcloud_key, 0644))

	runCommand("gcloud config set project " + gcloud_project)
	runCommand("gcloud auth activate-service-account --key-file " + key_file_path + " " + gcloud_user )

	// TODO: allow configuration options
	// TODO: how to grab stdout from executed command

	/*
	device_ids := "NexusLowRes"
	api_level := 25

	test_type := "robo"

	if !isEmpty(test_apk) {
		test_type = "instrumentation"
	}
	*/

	/*
    @device_ids = %w[NexusLowRes]
    @api_level  = 25

    if @robo
      type = '--type robo'
    else
      type         = '--type instrumentation'
      test_apk     = %Q(--test "#{ENV['TEST_APK']}")
      sd_card_path = '--directories-to-pull=/sdcard'
    end

    flags = [
        type,
        %Q(--app "#{ENV['APP_APK']}"),
        test_apk,
        "--results-bucket android-#{@app_name}",
        "--device-ids #{@device_ids.join(',')}",
        "--os-version-ids #{@api_level}",
        '--locales en',
        '--orientations portrait',
        '--timeout 25m',
        sd_card_path
    ].reject &:nil?

    flags << %Q(--test-targets "#{@test_targets}") unless @test_targets.empty?

    # must use custom env separator or gcloud CLI will get confused on comma separated annotations
    if @opts[:coverage] || @annotations
      env_vars      = []
      env_vars      += ['coverage=true', 'coverageFile=/sdcard/coverage.ec'] if @opts[:coverage]
      env_vars      += ["annotation=#{@annotations}"] if @annotations
      env_separator = ':'
      flags << "--environment-variables ^#{env_separator}^#{env_vars.join(env_separator)}"
    end

    gcloud firebase test android run FLAGS

    parse bucket link from gcloud CLI

    // download firebase.ec

    bucket = 'gs:/' + bucket_link.split('storage/browser').last
    bucket = "#{bucket}#{@device_ids.first}-#{@api_level}-en-portrait/artifacts/coverage.ec"
    _run_command "gsutil cp #{bucket} /bitrise/src/#{@app_name}/app/build/firebase.ec"
	*/

	// TODO: use exit code from gcloud CLI

	//
	// --- Exit codes:
	// The exit code of your Step is very important. If you return
	//  with a 0 exit code `bitrise` will register your Step as "successful".
	// Any non zero exit code will be registered as "failed" by `bitrise`.
	os.Exit(0)
}
<commit_msg>Progress on arg parsing<commit_after>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/bitrise-io/go-utils/command"
	"strings"
	"time"
	"math/rand"
	"github.com/kballard/go-shellquote"
	"os/exec"
	"github.com/bitrise-tools/gows/config"
)

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

// Matches api_lib/firebase/test/arg_validate.py _GenerateUniqueGcsObjectName from gcloud SDK
// Example output: 2017-07-12_11:36:12.467586_XVlB
func gcsObjectName() string {
	letterCount := 4
	bytes := make([]byte, letterCount)

	for i := 0; i < letterCount; i++ {
		bytes[i] = letters[rand.Intn(len(letters))]
	}

	return time.Now().Format("2006-01-02_3:04:05.999999") + "_" + string(bytes)
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func getOptionalEnv(env string) string {
	return os.Getenv(env)
}

func getRequiredEnv(env string) string {
	if len(env) == 0 {
		panic(env + " is not defined!")
	}

	return os.Getenv(env)
}

func isEmpty(str string) bool {
	return len(str) == 0
}

func checkFileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

func runCommand(cmd string) {
	cmdSlice := strings.Fields(cmd)

	cmdObj := command.NewWithStandardOuts(cmdSlice[0], cmdSlice[1:]...)
	checkError(cmdObj.Run())
}

// Env string names
const GCLOUD_USER = "GCLOUD_USER"
const GCLOUD_PROJECT = "GCLOUD_PROJECT"
const GCLOUD_BUCKET = "GCLOUD_BUCKET"
const GCLOUD_OPTIONS = "GCLOUD_OPTIONS"
const APP_APK = "APP_APK"
const TEST_APK = "TEST_APK"
const GCLOUD_KEY = "GCLOUD_KEY"
const GCS_RESULTS_DIR = "GCS_RESULTS_DIR"

type FirebaseConfig struct {
	ResultsBucket string
	Options       string
	User          string
	Project       string
	KeyPath       string
	AppApk        string
	TestApk       string
}

func populateConfig() FirebaseConfig {
	gcloud_user := getOptionalEnv(GCLOUD_USER)
	gcloud_project := getOptionalEnv(GCLOUD_PROJECT)

	app_apk := getRequiredEnv(APP_APK)
	checkFileExists(app_apk)

	test_apk := getOptionalEnv(TEST_APK)
	if !isEmpty(test_apk) {
		checkFileExists(test_apk)
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(getRequiredEnv(GCLOUD_KEY))
	checkError(err)

	empty_gcloud_user := isEmpty(gcloud_user)
	empty_gcloud_project := isEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if isEmpty(gcloud_user) {
				panic("Missing gcloud user")
			}
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if isEmpty(gcloud_project) {
				panic("Missing gcloud project")
			}
		}
	}

	home_dir := getRequiredEnv("HOME")
	key_file_path := path.Join(home_dir, "gcloudkey.json")
	checkError(ioutil.WriteFile(key_file_path, gcloud_key, 0644))

	return FirebaseConfig{
		ResultsBucket: getRequiredEnv(GCLOUD_BUCKET),
		User:          gcloud_user,
		Project:       gcloud_project,
		KeyPath:       key_file_path,
		AppApk:        app_apk,
		TestApk:       test_apk,
		Options:       getRequiredEnv(GCLOUD_OPTIONS),
	}
}

func exportGcsDir(bucket string, object string) {
	gcs_results_dir := "gs://" +bucket + "/" + object
	fmt.Println("Exporting ", GCS_RESULTS_DIR, " ", gcs_results_dir)
	cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", GCS_RESULTS_DIR, "--value", gcs_results_dir).CombinedOutput()
	if err != nil {
		fmt.Printf("Failed to export "+GCS_RESULTS_DIR+", error: %#v | output: %s", err, cmdLog)
		os.Exit(1)
	}
}

func main() {
	config := populateConfig()
	fmt.Println("Config: ", config)

	runCommand("gcloud config set project " + config.Project)
	runCommand("gcloud auth activate-service-account --key-file " + config.KeyPath + " " + config.User)

	// https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
	gcloudOptions, err := shellquote.Split(config.Options)
	checkError(err)
	fmt.Println("user options: ", gcloudOptions)

	// todo: may require all args to be supplied by user
	args := make([]string, 0)

	test_type := "robo"

	if !isEmpty(config.TestApk) {
		test_type = "instrumentation"
		args = append(args, "--test", config.TestApk)
		args = append(args, "--directories-to-pull=/sdcard")
	}

	args = append(args, test_type)
	args = append(args, "--app", config.AppApk)
	args = append(args, "--device-ids", "NexusLowRes")
	args = append(args, "--os-version-ids", "25")
	args = append(args, "--locales", "en")
	args = append(args, "--orientations", "portrait")
	args = append(args, "--timeout", "25m")
	args = append(args, "--results-bucket="+config.ResultsBucket)
	gcs_object := gcsObjectName()
	args = append(args, "--results-dir="+gcs_object)

	fmt.Println("args: ", args)

	exportGcsDir(config.ResultsBucket, gcs_object)

	os.Exit(0)
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	_ "expvar"
	_ "net/http/pprof"

	"github.com/foursquare/fsgo/adminz"
	"github.com/foursquare/fsgo/report"
	"github.com/foursquare/quiver/hfile"
)

type SettingDefs struct {
	port  int
	debug bool
	mlock bool

	configJsonUrl string

	cachePath string

	zk            string
	discoveryPath string
}

var Settings SettingDefs

func readSettings() []string {
	s := SettingDefs{}
	flag.IntVar(&s.port, "port", 9999, "listen port")

	flag.BoolVar(&s.debug, "debug", false, "print debug output")

	flag.BoolVar(&s.mlock, "mlock", false, "mlock mapped files in memory rather than copy to heap.")

	flag.StringVar(&s.configJsonUrl, "config-json", "", "URL of collection configuration json")

	flag.StringVar(&s.cachePath, "cache", os.TempDir(), "local path to write files fetched (*not* cleaned up automatically)")

	flag.StringVar(&s.zk, "zookeeper", "", "zookeeper")
	flag.StringVar(&s.discoveryPath, "discovery", "", "service discovery base path")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			`
Usage: %s [options] col1=path1 col2=path2 ...
`, os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	Settings = s

	if (len(flag.Args()) > 0) == (Settings.configJsonUrl != "") {
		log.Println("Collections must be specified OR URL to configuration json.")
		flag.Usage()
		os.Exit(-1)
	}

	return flag.Args()
}

func main() {
	graphite := report.Flag()
	args := readSettings()

	stats := report.NewRecorder().
		EnableGCInfoCollection().
		MaybeReportTo(graphite).
		SetAsDefault()

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}

	registrations := new(Registrations)
	if Settings.discoveryPath != "" {
		registrations.Connect()
		defer registrations.Close()
	}

	configs := getCollectionConfig(args)

	log.Printf("Loading collections (debug %v)...\n", Settings.debug)
	cs, err := hfile.LoadCollections(configs, Settings.cachePath)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Serving on http://%s:%d/ \n", hostname, Settings.port)

	if Settings.discoveryPath != "" {
		go registrations.Join(hostname, Settings.discoveryPath, configs, 5*time.Second)
	}

	http.Handle("/rpc/HFileService", NewHttpRpcHandler(cs, stats))
	http.Handle("/", &DebugHandler{cs})

	adminzPages := adminz.New()
	adminzPages.KillfilePaths(adminz.Killfiles(Settings.port))
	adminzPages.Pause(func() error {
		registrations.Leave()
		return nil
	})
	adminzPages.Resume(func() error {
		registrations.Join(hostname, Settings.discoveryPath, configs, 0)
		return nil
	})
	adminzPages.Build()

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", Settings.port), nil))
}
<commit_msg>print out gomaxprocs<commit_after>// Copyright (C) 2015 Foursquare Labs Inc.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"time"

	_ "expvar"
	_ "net/http/pprof"

	"github.com/foursquare/fsgo/adminz"
	"github.com/foursquare/fsgo/report"
	"github.com/foursquare/quiver/hfile"
)

type SettingDefs struct {
	port  int
	debug bool
	mlock bool

	configJsonUrl string

	cachePath string

	zk            string
	discoveryPath string
}

var Settings SettingDefs

func readSettings() []string {
	s := SettingDefs{}
	flag.IntVar(&s.port, "port", 9999, "listen port")

	flag.BoolVar(&s.debug, "debug", false, "print debug output")

	flag.BoolVar(&s.mlock, "mlock", false, "mlock mapped files in memory rather than copy to heap.")

	flag.StringVar(&s.configJsonUrl, "config-json", "", "URL of collection configuration json")

	flag.StringVar(&s.cachePath, "cache", os.TempDir(), "local path to write files fetched (*not* cleaned up automatically)")

	flag.StringVar(&s.zk, "zookeeper", "", "zookeeper")
	flag.StringVar(&s.discoveryPath, "discovery", "", "service discovery base path")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			`
Usage: %s [options] col1=path1 col2=path2 ...
`, os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()
	Settings = s

	if (len(flag.Args()) > 0) == (Settings.configJsonUrl != "") {
		log.Println("Collections must be specified OR URL to configuration json.")
		flag.Usage()
		os.Exit(-1)
	}

	return flag.Args()
}

func main() {
	fmt.Println("max procs:", runtime.GOMAXPROCS(-1))

	graphite := report.Flag()
	args := readSettings()

	stats := report.NewRecorder().
		EnableGCInfoCollection().
		MaybeReportTo(graphite).
		SetAsDefault()

	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}

	registrations := new(Registrations)
	if Settings.discoveryPath != "" {
		registrations.Connect()
		defer registrations.Close()
	}

	configs := getCollectionConfig(args)

	log.Printf("Loading collections (debug %v)...\n", Settings.debug)
	cs, err := hfile.LoadCollections(configs, Settings.cachePath)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Serving on http://%s:%d/ \n", hostname, Settings.port)

	if Settings.discoveryPath != "" {
		go registrations.Join(hostname, Settings.discoveryPath, configs, 5*time.Second)
	}

	http.Handle("/rpc/HFileService", NewHttpRpcHandler(cs, stats))
	http.Handle("/", &DebugHandler{cs})

	adminzPages := adminz.New()
	adminzPages.KillfilePaths(adminz.Killfiles(Settings.port))
	adminzPages.Pause(func() error {
		registrations.Leave()
		return nil
	})
	adminzPages.Resume(func() error {
		registrations.Join(hostname, Settings.discoveryPath, configs, 0)
		return nil
	})
	adminzPages.Build()

	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", Settings.port), nil))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"
	shellwords "github.com/mattn/go-shellwords"
	"gopkg.in/urfave/cli.v1"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
	colorGreen = string([]byte{27, 91, 57, 55, 59, 51, 50, 59, 49, 109})
	colorRed   = string([]byte{27, 91, 57, 55, 59, 51, 49, 59, 49, 109})
	colorReset = string([]byte{27, 91, 48, 109})
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "laddr,l",
			Value:  "",
			EnvVar: "GIN_LADDR",
			Usage:  "listening address for the proxy server",
		},
		cli.IntFlag{
			Name:   "port,p",
			Value:  3000,
			EnvVar: "GIN_PORT",
			Usage:  "port for the proxy server",
		},
		cli.IntFlag{
			Name:   "appPort,a",
			Value:  3001,
			EnvVar: "BIN_APP_PORT",
			Usage:  "port for the Go web server",
		},
		cli.StringFlag{
			Name:   "bin,b",
			Value:  "gin-bin",
			EnvVar: "GIN_BIN",
			Usage:  "name of generated binary file",
		},
		cli.StringFlag{
			Name:   "path,t",
			Value:  ".",
			EnvVar: "GIN_PATH",
			Usage:  "Path to watch files from",
		},
		cli.StringFlag{
			Name:   "build,d",
			Value:  "",
			EnvVar: "GIN_BUILD",
			Usage:  "Path to build files from (defaults to same value as --path)",
		},
		cli.StringSliceFlag{
			Name:   "excludeDir,x",
			Value:  &cli.StringSlice{},
			EnvVar: "GIN_EXCLUDE_DIR",
			Usage:  "Relative directories to exclude",
		},
		cli.BoolFlag{
			Name:   "immediate,i",
			EnvVar: "GIN_IMMEDIATE",
			Usage:  "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:   "all",
			EnvVar: "GIN_ALL",
			Usage:  "reloads whenever any file changes, as opposed to reloading only on .go file change",
		},
		cli.BoolFlag{
			Name:   "godep,g",
			EnvVar: "GIN_GODEP",
			Usage:  "use godep when building",
		},
		cli.StringFlag{
			Name:   "buildArgs",
			EnvVar: "GIN_BUILD_ARGS",
			Usage:  "Additional go build arguments",
		},
		cli.StringFlag{
			Name:   "certFile",
			EnvVar: "GIN_CERT_FILE",
			Usage:  "TLS Certificate",
		},
		cli.StringFlag{
			Name:   "keyFile",
			EnvVar: "GIN_KEY_FILE",
			Usage:  "TLS Certificate Key",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	laddr := c.GlobalString("laddr")
	port := c.GlobalInt("port")
	all := c.GlobalBool("all")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	keyFile := c.GlobalString("keyFile")
	certFile := c.GlobalString("certFile")

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	buildArgs, err := shellwords.Parse(c.GlobalString("buildArgs"))
	if err != nil {
		logger.Fatal(err)
	}

	buildPath := c.GlobalString("build")
	if buildPath == "" {
		buildPath = c.GlobalString("path")
	}
	builder := gin.NewBuilder(buildPath, c.GlobalString("bin"), c.GlobalBool("godep"), wd, buildArgs)
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Laddr:    laddr,
		Port:     port,
		ProxyTo:  "http://localhost:" + appPort,
		KeyFile:  keyFile,
		CertFile: certFile,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	if laddr != "" {
		logger.Printf("Listening at %s:%d\n", laddr, port)
	} else {
		logger.Printf("Listening on port %d\n", port)
	}

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("path"), c.GlobalStringSlice("excludeDir"), all, func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	logger.Println("Building...")

	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Printf("%sBuild failed%s\n", colorRed, colorReset)
		fmt.Println(builder.Errors())
	} else {
		buildError = nil
		logger.Printf("%sBuild finished%s\n", colorGreen, colorReset)
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludeDirs []string, allFiles bool, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" && info.IsDir() {
				return filepath.SkipDir
			}
			for _, x := range excludeDirs {
				if x == path {
					return filepath.SkipDir
				}
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if (allFiles || filepath.Ext(path) == ".go") && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<commit_msg>Add possibility to set up custom log prefix<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"
	shellwords "github.com/mattn/go-shellwords"
	"gopkg.in/urfave/cli.v1"

	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	buildError error
	colorGreen = string([]byte{27, 91, 57, 55, 59, 51, 50, 59, 49, 109})
	colorRed   = string([]byte{27, 91, 57, 55, 59, 51, 49, 59, 49, 109})
	colorReset = string([]byte{27, 91, 48, 109})
)

func main() {
	app := cli.NewApp()
	app.Name = "gin"
	app.Usage = "A live reload utility for Go web applications."
	app.Action = MainAction
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "laddr,l",
			Value:  "",
			EnvVar: "GIN_LADDR",
			Usage:  "listening address for the proxy server",
		},
		cli.IntFlag{
			Name:   "port,p",
			Value:  3000,
			EnvVar: "GIN_PORT",
			Usage:  "port for the proxy server",
		},
		cli.IntFlag{
			Name:   "appPort,a",
			Value:  3001,
			EnvVar: "BIN_APP_PORT",
			Usage:  "port for the Go web server",
		},
		cli.StringFlag{
			Name:   "bin,b",
			Value:  "gin-bin",
			EnvVar: "GIN_BIN",
			Usage:  "name of generated binary file",
		},
		cli.StringFlag{
			Name:   "path,t",
			Value:  ".",
			EnvVar: "GIN_PATH",
			Usage:  "Path to watch files from",
		},
		cli.StringFlag{
			Name:   "build,d",
			Value:  "",
			EnvVar: "GIN_BUILD",
			Usage:  "Path to build files from (defaults to same value as --path)",
		},
		cli.StringSliceFlag{
			Name:   "excludeDir,x",
			Value:  &cli.StringSlice{},
			EnvVar: "GIN_EXCLUDE_DIR",
			Usage:  "Relative directories to exclude",
		},
		cli.BoolFlag{
			Name:   "immediate,i",
			EnvVar: "GIN_IMMEDIATE",
			Usage:  "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:   "all",
			EnvVar: "GIN_ALL",
			Usage:  "reloads whenever any file changes, as opposed to reloading only on .go file change",
		},
		cli.BoolFlag{
			Name:   "godep,g",
			EnvVar: "GIN_GODEP",
			Usage:  "use godep when building",
		},
		cli.StringFlag{
			Name:   "buildArgs",
			EnvVar: "GIN_BUILD_ARGS",
			Usage:  "Additional go build arguments",
		},
		cli.StringFlag{
			Name:   "certFile",
			EnvVar: "GIN_CERT_FILE",
			Usage:  "TLS Certificate",
		},
		cli.StringFlag{
			Name:   "keyFile",
			EnvVar: "GIN_KEY_FILE",
			Usage:  "TLS Certificate Key",
		},
		cli.StringFlag{
			Name:   "logPrefix",
			EnvVar: "GIN_LOG_PREFIX",
			Usage:  "Log prefix",
			Value:  "gin",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "Run the gin proxy in the current working directory",
			Action:    MainAction,
		},
		{
			Name:      "env",
			ShortName: "e",
			Usage:     "Display environment variables set by the .env file",
			Action:    EnvAction,
		},
	}

	app.Run(os.Args)
}

func MainAction(c *cli.Context) {
	laddr := c.GlobalString("laddr")
	port := c.GlobalInt("port")
	all := c.GlobalBool("all")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	keyFile := c.GlobalString("keyFile")
	certFile := c.GlobalString("certFile")
	logPrefix := c.GlobalString("logPrefix")

	logger.SetPrefix(fmt.Sprintf("[%s] ", logPrefix))

	// Bootstrap the environment
	envy.Bootstrap()

	// Set the PORT env
	os.Setenv("PORT", appPort)

	wd, err := os.Getwd()
	if err != nil {
		logger.Fatal(err)
	}

	buildArgs, err := shellwords.Parse(c.GlobalString("buildArgs"))
	if err != nil {
		logger.Fatal(err)
	}

	buildPath := c.GlobalString("build")
	if buildPath == "" {
		buildPath = c.GlobalString("path")
	}
	builder := gin.NewBuilder(buildPath, c.GlobalString("bin"), c.GlobalBool("godep"), wd, buildArgs)
	runner := gin.NewRunner(filepath.Join(wd, builder.Binary()), c.Args()...)
	runner.SetWriter(os.Stdout)
	proxy := gin.NewProxy(builder, runner)

	config := &gin.Config{
		Laddr:    laddr,
		Port:     port,
		ProxyTo:  "http://localhost:" + appPort,
		KeyFile:  keyFile,
		CertFile: certFile,
	}

	err = proxy.Run(config)
	if err != nil {
		logger.Fatal(err)
	}

	if laddr != "" {
		logger.Printf("Listening at %s:%d\n", laddr, port)
	} else {
		logger.Printf("Listening on port %d\n", port)
	}

	shutdown(runner)

	// build right now
	build(builder, runner, logger)

	// scan for changes
	scanChanges(c.GlobalString("path"), c.GlobalStringSlice("excludeDir"), all, func(path string) {
		runner.Kill()
		build(builder, runner, logger)
	})
}

func EnvAction(c *cli.Context) {
	logPrefix := c.GlobalString("logPrefix")
	logger.SetPrefix(fmt.Sprintf("[%s] ", logPrefix))

	// Bootstrap the environment
	env, err := envy.Bootstrap()
	if err != nil {
		logger.Fatalln(err)
	}

	for k, v := range env {
		fmt.Printf("%s: %s\n", k, v)
	}

}

func build(builder gin.Builder, runner gin.Runner, logger *log.Logger) {
	logger.Println("Building...")

	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Printf("%sBuild failed%s\n", colorRed, colorReset)
		fmt.Println(builder.Errors())
	} else {
		buildError = nil
		logger.Printf("%sBuild finished%s\n", colorGreen, colorReset)
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludeDirs []string, allFiles bool, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" && info.IsDir() {
				return filepath.SkipDir
			}
			for _, x := range excludeDirs {
				if x == path {
					return filepath.SkipDir
				}
			}

			// ignore hidden files
			if filepath.Base(path)[0] == '.' {
				return nil
			}

			if (allFiles || filepath.Ext(path) == ".go") && info.ModTime().After(startTime) {
				cb(path)
				startTime = time.Now()
				return errors.New("done")
			}

			return nil
		})
		time.Sleep(500 * time.Millisecond)
	}
}

func shutdown(runner gin.Runner) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		s := <-c
		log.Println("Got signal: ", s)
		err := runner.Kill()
		if err != nil {
			log.Print("Error killing: ", err)
		}
		os.Exit(1)
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/NoahShen/gotunnelme/src/gotunnelme" // for tunneling
	"github.com/fatih/color"
	"github.com/gorilla/mux"
)

const (
	headerEvent     = "X-GitHub-Event"  // HTTP header where the webhook event is stored
	headerSignature = "X-Hub-Signature" // HTTP header where the sha1 signature of the payload is stored
)

var (
	config            = tomlConfig{} // the program config
	verbose           = false        // weither we should log the output of the command
	verboseTunnel     = false        // weither we should log the output of the tunneling
	configFile        = ""
	gitHubSecretToken = os.Getenv("GITHUB_HOOK_SECRET_TOKEN") // the webhook secret token, used to verify signature
)

// HookHandler receive hooks from GitHub.
func HookHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	// read the HTTP request body
	payload, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString("Error: "+err.Error()))
		BadRequestHandler(w, r)
		return
	}

	// validate signature
	if gitHubSecretToken != "" {
		sign := r.Header.Get(headerSignature)

		// to compute the HMAC in order to check for equality with what has been sent by GitHub
		mac := hmac.New(sha1.New, []byte(gitHubSecretToken))
		mac.Write(payload)
		expectedHash := hex.EncodeToString(mac.Sum(nil))
		receivedHash := sign[5:] // remove 'sha1='

		// signature mismatch, do not process
		if !hmac.Equal([]byte(receivedHash), []byte(expectedHash)) {
			color.Set(color.FgRed)
			fmt.Fprintf(os.Stderr, "Mismatch between expected (%s) and received (%s) hash.", expectedHash, receivedHash)
			color.Set(color.Faint)
			BadRequestHandler(w, r)
			return
		}
	}

	var eventPayload HookWithRepository
	json.Unmarshal(payload, &eventPayload)

	// verify that this is an event that we should process
	// event := r.Header.Get(headerEvent)
	event := eventPayload.EventName
	if event == "ping" {
		return // always respond 200 to pings
	}

	// check whether we're interested in that event
	if shouldHandleEvent(config.Events, event, eventPayload) {
		handleEvent(event, eventPayload, []byte(payload))
	} else {
		if verbose {
			color.Set(color.FgRed)
			fmt.Fprintf(os.Stderr, "Discarding %s on %s with ref %s.\n",
				color.CyanString(event), color.YellowString(eventPayload.Project.PathWithNamespace), color.YellowString(eventPayload.Ref))
			color.Set(color.Faint)
			BadRequestHandler(w, r)
			return // 400 Bad Request
		}
	}

}

func shouldHandleEvent(events map[string]event, event string, eventPayload HookWithRepository) bool {
	if _, ok := events[event+":"+eventPayload.Project.PathWithNamespace+":"+eventPayload.Ref]; ok {
		return true
	} else if _, ok := events[event+":"+eventPayload.Project.PathWithNamespace+":all"]; ok {
		return true
	} else if _, ok := events[event+":all:all"]; ok {
		return true
	}
	return false
}

// handleEvent handles any event.
func handleEvent(event string, hook HookWithRepository, payload []byte) {
	// show related commits if push event
	if event == "push" {
		var pushEvent HookPush
		json.Unmarshal(payload, &pushEvent)
		fmt.Println(event, "detected on", color.YellowString(hook.Project.PathWithNamespace),
			"with ref", color.YellowString(hook.Ref), "with the following commits:")
		for _, commit := range pushEvent.Commits {
			fmt.Printf("\t%s - %s by %s\n", commit.Timestamp, color.CyanString(commit.Message), color.BlueString(commit.Author.Name))
		}
	}

	// prepare the command
	eventKey := event + ":" + hook.Project.PathWithNamespace + ":" + hook.Ref
	if _, ok := config.Events[eventKey]; !ok {
		eventKey = event + ":" + hook.Project.PathWithNamespace + ":all"
	}
	cmd := exec.Command(config.Events[eventKey].cmd,
		strings.Split(config.Events[eventKey].args, " ")...)

	// in case of -verbose we log the output of the executed command
	if verbose {
		cmdReader, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
			return
		}
		scanner := bufio.NewScanner(cmdReader)
		go func() {
			for scanner.Scan() {
				color.White("> " + scanner.Text() + "\n")
			}
		}()
		cmdReader, err = cmd.StderrPipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error creating StderrPipe for Cmd", err)
			return
		}
		scanner = bufio.NewScanner(cmdReader)
		go func() {
			for scanner.Scan() {
				color.Yellow("> " + scanner.Text() + "\n")
			}
		}()
	}

	// launch it
	err := cmd.Start()
	if err != nil {
		color.Set(color.FgRed)
		fmt.Fprintln(os.Stderr, "Error starting Cmd ", cmd.Path, cmd.Args, err)
		color.Set(color.Faint)
		return
	}
}

// BadRequestHandler handles bad requests. Status 400 and JSON error message.
func BadRequestHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusBadRequest)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write([]byte(`{"message": "I don't know what you're talking about"}`))
}

// HeyHandler handles GET request on /.
func HeyHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.Write([]byte(`Hey, what's up?`))
}

func main() {
	flag.BoolVar(&verbose, "v", false, "Whether we output stuff.")
	flag.BoolVar(&verboseTunnel, "vt", false, "Whether we output stuff regarding tunneling.")
	flag.StringVar(&configFile, "c", "", "config file to load other than ./config.toml")
	flag.Parse()

	// load the config.toml
	config = loadConfig()
	addr := config.Addr + ":" + strconv.Itoa(config.Port)
	color.White(`    __
   / /_  ____ __________  ____  ____  ____
  / __ \/ __ ` + "`" + `/ ___/ __ \/ __ \/ __ \/ __ \
 / / / / /_/ / /  / /_/ / /_/ / /_/ / / / /
/_/ /_/\__,_/_/  / .___/\____/\____/_/ /_/
                /_/                        v2
`)
	color.White("\tListening on " + addr)
	readyToListen := false

	if config.Tunnel {
		if verboseTunnel {
			gotunnelme.Debug = true
		}
		tunnel := gotunnelme.NewTunnel()
		url, err := tunnel.GetUrl(config.TunnelName)
		if err != nil {
			panic("Could not get localtunnel.me URL. " + err.Error())
		}
		go func() {
			for !readyToListen {
				time.Sleep(1 * time.Second)
			}
			color.Cyan("\tTunnel URL: " + url)
			err := tunnel.CreateTunnel(config.Port)
			if err != nil {
				panic("Could not create tunnel. " + err.Error())
			}
		}()
	}

	// router & server
	r := mux.NewRouter()
	r.HandleFunc("/", HookHandler).Methods("POST")
	r.HandleFunc("/", HeyHandler).Methods("GET")
	readyToListen = true
	http.ListenAndServe(addr, r)
}
<commit_msg>allow all:all<commit_after>package main

import (
	"bufio"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/NoahShen/gotunnelme/src/gotunnelme" // for tunneling
	"github.com/fatih/color"
	"github.com/gorilla/mux"
)

const (
	headerEvent     = "X-GitHub-Event"  // HTTP header where the webhook event is stored
	headerSignature = "X-Hub-Signature" // HTTP header where the sha1 signature of the payload is stored
)

var (
	config            = tomlConfig{} // the program config
	verbose           = false        // weither we should log the output of the command
	verboseTunnel     = false        // weither we should log the output of the tunneling
	configFile        = ""
	gitHubSecretToken = os.Getenv("GITHUB_HOOK_SECRET_TOKEN") // the webhook secret token, used to verify signature
)

// HookHandler receive hooks from GitHub.
func HookHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	// read the HTTP request body
	payload, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString("Error: "+err.Error()))
		BadRequestHandler(w, r)
		return
	}

	// validate signature
	if gitHubSecretToken != "" {
		sign := r.Header.Get(headerSignature)

		// to compute the HMAC in order to check for equality with what has been sent by GitHub
		mac := hmac.New(sha1.New, []byte(gitHubSecretToken))
		mac.Write(payload)
		expectedHash := hex.EncodeToString(mac.Sum(nil))
		receivedHash := sign[5:] // remove 'sha1='

		// signature mismatch, do not process
		if !hmac.Equal([]byte(receivedHash), []byte(expectedHash)) {
			color.Set(color.FgRed)
			fmt.Fprintf(os.Stderr, "Mismatch between expected (%s) and received (%s) hash.", expectedHash, receivedHash)
			color.Set(color.Faint)
			BadRequestHandler(w, r)
			return
		}
	}

	var eventPayload HookWithRepository
	json.Unmarshal(payload, &eventPayload)

	// verify that this is an event that we should process
	// event := r.Header.Get(headerEvent)
	event := eventPayload.EventName
	if event == "ping" {
		return // always respond 200 to pings
	}

	// check whether we're interested in that event
	if shouldHandleEvent(config.Events, event, eventPayload) {
		handleEvent(event, eventPayload, []byte(payload))
	} else {
		if verbose {
			color.Set(color.FgRed)
			fmt.Fprintf(os.Stderr, "Discarding %s on %s with ref %s.\n",
				color.CyanString(event), color.YellowString(eventPayload.Project.PathWithNamespace), color.YellowString(eventPayload.Ref))
			color.Set(color.Faint)
			BadRequestHandler(w, r)
			return // 400 Bad Request
		}
	}

}

func shouldHandleEvent(events map[string]event, event string, eventPayload HookWithRepository) bool {
	if _, ok := events[event+":"+eventPayload.Project.PathWithNamespace+":"+eventPayload.Ref]; ok {
		return true
	} else if _, ok := events[event+":"+eventPayload.Project.PathWithNamespace+":all"]; ok {
		return true
	} else if _, ok := events[event+":all:all"]; ok {
		return true
	}
	return false
}

// handleEvent handles any event.
func handleEvent(event string, hook HookWithRepository, payload []byte) {
	// show related commits if push event
	if event == "push" {
		var pushEvent HookPush
		json.Unmarshal(payload, &pushEvent)
		fmt.Println(event, "detected on", color.YellowString(hook.Project.PathWithNamespace),
			"with ref", color.YellowString(hook.Ref), "with the following commits:")
		for _, commit := range pushEvent.Commits {
			fmt.Printf("\t%s - %s by %s\n", commit.Timestamp, color.CyanString(commit.Message), color.BlueString(commit.Author.Name))
		}
	}

	// prepare the command
	eventKey := event + ":" + hook.Project.PathWithNamespace + ":" + hook.Ref
	if _, ok := config.Events[eventKey]; !ok {
		eventKey = event + ":" + hook.Project.PathWithNamespace + ":all"
	}
	if _, ok := config.Events[eventKey]; !ok {
		eventKey = event + ":all:all"
	}

	cmd := exec.Command(config.Events[eventKey].Cmd,
		strings.Split(config.Events[eventKey].Args, " ")...)

	// in case of -verbose we log the output of the executed command
	if verbose {
		cmdReader, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
			return
		}
		scanner := bufio.NewScanner(cmdReader)
		go func() {
			for scanner.Scan() {
				color.White("> " + scanner.Text() + "\n")
			}
		}()
		cmdReader, err = cmd.StderrPipe()
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error creating StderrPipe for Cmd", err)
			return
		}
		scanner = bufio.NewScanner(cmdReader)
		go func() {
			for scanner.Scan() {
				color.Yellow("> " + scanner.Text() + "\n")
			}
		}()
	}

	// launch it
	err := cmd.Start()
	if err != nil {
		color.Set(color.FgRed)
		fmt.Fprintln(os.Stderr, "Error starting Cmd ", cmd.Path, cmd.Args, err)
		color.Set(color.Faint)
		return
	}
}

// BadRequestHandler handles bad requests. Status 400 and JSON error message.
func BadRequestHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusBadRequest)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write([]byte(`{"message": "I don't know what you're talking about"}`))
}

// HeyHandler handles GET request on /.
func HeyHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.Write([]byte(`Hey, what's up?`))
}

func main() {
	flag.BoolVar(&verbose, "v", false, "Whether we output stuff.")
	flag.BoolVar(&verboseTunnel, "vt", false, "Whether we output stuff regarding tunneling.")
	flag.StringVar(&configFile, "c", "", "config file to load other than ./config.toml")
	flag.Parse()

	// load the config.toml
	config = loadConfig()
	addr := config.Addr + ":" + strconv.Itoa(config.Port)
	color.White(`    __
   / /_  ____ __________  ____  ____  ____
  / __ \/ __ ` + "`" + `/ ___/ __ \/ __ \/ __ \/ __ \
 / / / / /_/ / /  / /_/ / /_/ / /_/ / / / /
/_/ /_/\__,_/_/  / .___/\____/\____/_/ /_/
                /_/                        v2
`)
	color.White("\tListening on " + addr)
	readyToListen := false

	if config.Tunnel {
		if verboseTunnel {
			gotunnelme.Debug = true
		}
		tunnel := gotunnelme.NewTunnel()
		url, err := tunnel.GetUrl(config.TunnelName)
		if err != nil {
			panic("Could not get localtunnel.me URL. " + err.Error())
		}
		go func() {
			for !readyToListen {
				time.Sleep(1 * time.Second)
			}
			color.Cyan("\tTunnel URL: " + url)
			err := tunnel.CreateTunnel(config.Port)
			if err != nil {
				panic("Could not create tunnel. " + err.Error())
			}
		}()
	}

	// router & server
	r := mux.NewRouter()
	r.HandleFunc("/", HookHandler).Methods("POST")
	r.HandleFunc("/", HeyHandler).Methods("GET")
	readyToListen = true
	http.ListenAndServe(addr, r)
}
<|endoftext|>