<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

const (
	cormFilename = "Cormfile"
	cormDir      = "_corm"
)

var (
	currentDir     string
	dirtyVendorDir string
	cormfile       string
)

var errCannotParseLine = errors.New("cannot parse line")

var vcsMetaDir = []string{".svn", ".git", ".hg"}

type repository struct {
	Path   string
	Commit string
}

func parse(s string) (*repository, error) {
	// TODO: Consider the format
	fields := strings.Fields(s)
	switch len(fields) {
	case 1:
		return &repository{Path: fields[0]}, nil
	case 2:
		return &repository{Path: fields[0], Commit: fields[1]}, nil
	default:
		return nil, errCannotParseLine
	}
}

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

func readCorm(filepath string) ([]*repository, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, fmt.Errorf("cannot open Cormfile: %s", err)
	}
	defer f.Close()

	repos := make([]*repository, 0, 30)

	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		if line == "" {
			continue
		}

		repo, err := parse(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "SKIPPED: %s: %s\n", err, line)
			continue
		}
		repos = append(repos, repo)
	}

	return repos, nil
}

func goGet(repo *repository) error {
	// TODO: can handle options for `go get`
	fmt.Printf("go get %s\n", repo.Path)
	cmd := exec.Command("go", "get", repo.Path)
	cmd.Stdout = os.Stdout
	err := cmd.Run()
	// Commit が指定されている場合の処理は後で実装する
	return err
}

func newCopyFileFun(srcBase string, destBase string) filepath.WalkFunc {

	return func(srcPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		relPath, err := filepath.Rel(srcBase, srcPath)
		if err != nil {
			return err
		}

		destPath := filepath.Join(destBase, relPath)

		if info.IsDir() {
			index := sort.SearchStrings(vcsMetaDir, info.Name())
			if index < len(vcsMetaDir) {
				return filepath.SkipDir
			}

			return os.MkdirAll(destPath, info.Mode())
		}

		if exists(destPath) {
			return nil
		}

		return os.Link(srcPath, destPath)
	}

}

func export(src string, dest string) error {
	copyFile := newCopyFileFun(src, dest)
	err := filepath.Walk(src, copyFile)
	return err
}

func init() {
	var err error
	currentDir, err = os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, "cannot get current directory")
		os.Exit(1)
	}

	dirtyVendorDir = filepath.Join(currentDir, cormDir)
}

func fakeGopath() {

	// if target package exists default GOPATH, then go get does nothing.
	// So, set GOPATH dirtyVendorDir only

	os.Setenv("GOPATH", dirtyVendorDir)

}

func mainCmd() int {
	repos, err := readCorm(cormfile)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	if len(repos) == 0 {
		fmt.Fprintln(os.Stderr, "no repositories in Cormfile")
		return 1
	}

	fakeGopath()
	for _, repo := range repos {
		err := goGet(repo)
		if err != nil {
			fmt.Fprintf(os.Stderr, "cannot exec go get: %s, %s\n", repo.Path, err)
		}
	}

	return 0
}

func exportCmd() int {
	dirtyVendorSrcDir := filepath.Join(dirtyVendorDir, "src")

	if !exists(dirtyVendorDir) {
		fmt.Fprintf(os.Stderr, "%s does not exists\n", dirtyVendorSrcDir)
		return 1
	}

	cleanVendorDir := filepath.Join(currentDir, "vendor")
	err := export(dirtyVendorSrcDir, cleanVendorDir)
	if err != nil {
		fmt.Fprintln(os.Stderr, "export error:", err)
		return 1
	}

	return 0
}

func execCmd(args []string) int {
	if len(args) == 0 {
		fmt.Fprintln(os.Stderr, "no exec target command")
		return 1
	}

	fakeGopath()
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// TODO: cant exec vim main.go
	err := cmd.Run()
	if err != nil {
		fmt.Fprintln(os.Stderr, "exec error:", err)
		return 1
	}
	return 0
}

func goSubCmd(sub string, args []string) error {
	fakeGopath()
	args = append([]string{sub}, args...)
	cmd := exec.Command("go", args...)
	err := cmd.Run()

	if err != nil {
		fmt.Fprintln(os.Stderr, sub, "error:", err)
		return err
	}

	return nil
}

func buildCmd(args []string) int {
	err := goSubCmd("build", args)
	if err != nil {
		return 1
	}

	return 0
}

func usage() int {
	fmt.Println(`Usage: corm command
	install	:	install packages from Cormfile.
	export	:	export packages to vendor directory.`)

	return 1
}

func main() {
	sort.Strings(vcsMetaDir)

	if len(os.Args) == 1 {
		os.Exit(usage())
	}

	cormfile = filepath.Join(currentDir, cormFilename)
	if !exists(cormfile) {
		fmt.Fprintf(os.Stderr, "%s does not exists\n", cormfile)
		os.Exit(1)
	}

	command := os.Args[1]

	switch command {
	case "install":
		os.Exit(mainCmd())
	case "export":
		os.Exit(exportCmd())
	case "exec":
		os.Exit(execCmd(os.Args[2:]))
	case "build":
		os.Exit(buildCmd(os.Args[2:]))
	default:
		os.Exit(usage())
	}
}
<commit_msg>testコマンドを追加<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

const (
	cormFilename = "Cormfile"
	cormDir      = "_corm"
)

var (
	currentDir     string
	dirtyVendorDir string
	cormfile       string
)

var errCannotParseLine = errors.New("cannot parse line")

var vcsMetaDir = []string{".svn", ".git", ".hg"}

type repository struct {
	Path   string
	Commit string
}

func parse(s string) (*repository, error) {
	// TODO: Consider the format
	fields := strings.Fields(s)
	switch len(fields) {
	case 1:
		return &repository{Path: fields[0]}, nil
	case 2:
		return &repository{Path: fields[0], Commit: fields[1]}, nil
	default:
		return nil, errCannotParseLine
	}
}

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

func readCorm(filepath string) ([]*repository, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, fmt.Errorf("cannot open Cormfile: %s", err)
	}
	defer f.Close()

	repos := make([]*repository, 0, 30)

	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		if line == "" {
			continue
		}

		repo, err := parse(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "SKIPPED: %s: %s\n", err, line)
			continue
		}
		repos = append(repos, repo)
	}

	return repos, nil
}

func goGet(repo *repository) error {
	// TODO: can handle options for `go get`
	fmt.Printf("go get %s\n", repo.Path)
	cmd := exec.Command("go", "get", repo.Path)
	cmd.Stdout = os.Stdout
	err := cmd.Run()
	// Commit が指定されている場合の処理は後で実装する
	return err
}

func newCopyFileFun(srcBase string, destBase string) filepath.WalkFunc {

	return func(srcPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		relPath, err := filepath.Rel(srcBase, srcPath)
		if err != nil {
			return err
		}

		destPath := filepath.Join(destBase, relPath)

		if info.IsDir() {
			index := sort.SearchStrings(vcsMetaDir, info.Name())
			if index < len(vcsMetaDir) {
				return filepath.SkipDir
			}

			return os.MkdirAll(destPath, info.Mode())
		}

		if exists(destPath) {
			return nil
		}

		return os.Link(srcPath, destPath)
	}

}

func export(src string, dest string) error {
	copyFile := newCopyFileFun(src, dest)
	err := filepath.Walk(src, copyFile)
	return err
}

func init() {
	var err error
	currentDir, err = os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, "cannot get current directory")
		os.Exit(1)
	}

	dirtyVendorDir = filepath.Join(currentDir, cormDir)
}

func fakeGopath() {

	// if target package exists default GOPATH, then go get does nothing.
	// So, set GOPATH dirtyVendorDir only

	os.Setenv("GOPATH", dirtyVendorDir)

}

func mainCmd() int {
	repos, err := readCorm(cormfile)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	if len(repos) == 0 {
		fmt.Fprintln(os.Stderr, "no repositories in Cormfile")
		return 1
	}

	fakeGopath()
	for _, repo := range repos {
		err := goGet(repo)
		if err != nil {
			fmt.Fprintf(os.Stderr, "cannot exec go get: %s, %s\n", repo.Path, err)
		}
	}

	return 0
}

func exportCmd() int {
	dirtyVendorSrcDir := filepath.Join(dirtyVendorDir, "src")

	if !exists(dirtyVendorDir) {
		fmt.Fprintf(os.Stderr, "%s does not exists\n", dirtyVendorSrcDir)
		return 1
	}

	cleanVendorDir := filepath.Join(currentDir, "vendor")
	err := export(dirtyVendorSrcDir, cleanVendorDir)
	if err != nil {
		fmt.Fprintln(os.Stderr, "export error:", err)
		return 1
	}

	return 0
}

func execCmd(args []string) int {
	if len(args) == 0 {
		fmt.Fprintln(os.Stderr, "no exec target command")
		return 1
	}

	fakeGopath()
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// TODO: cant exec vim main.go
	err := cmd.Run()
	if err != nil {
		fmt.Fprintln(os.Stderr, "exec error:", err)
		return 1
	}
	return 0
}

func goSubCmd(sub string, args []string) error {
	fakeGopath()
	args = append([]string{sub}, args...)
	cmd := exec.Command("go", args...)
	err := cmd.Run()

	if err != nil {
		fmt.Fprintln(os.Stderr, sub, "error:", err)
		return err
	}

	return nil
}

func buildCmd(args []string) int {
	err := goSubCmd("build", args)
	if err != nil {
		return 1
	}

	return 0
}

func testCmd(args []string) int {
	err := goSubCmd("test", args)
	if err != nil {
		return 1
	}

	return 0
}

func usage() int {
	fmt.Println(`Usage: corm command
	install	:	install packages from Cormfile.
	export	:	export packages to vendor directory.`)

	return 1
}

func main() {
	sort.Strings(vcsMetaDir)

	if len(os.Args) == 1 {
		os.Exit(usage())
	}

	cormfile = filepath.Join(currentDir, cormFilename)
	if !exists(cormfile) {
		fmt.Fprintf(os.Stderr, "%s does not exists\n", cormfile)
		os.Exit(1)
	}

	command := os.Args[1]

	switch command {
	case "install":
		os.Exit(mainCmd())
	case "export":
		os.Exit(exportCmd())
	case "exec":
		os.Exit(execCmd(os.Args[2:]))
	case "build":
		os.Exit(buildCmd(os.Args[2:]))
	case "test":
		os.Exit(testCmd(os.Args[2:]))
	default:
		os.Exit(usage())
	}
}
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"

	"github.com/nickvanw/ircx"
	"github.com/sorcix/irc"
	"gopkg.in/yaml.v2"
)

var (
	configfile = flag.String("config", "config.yaml", "Path to config file")
	config     Config
)

func init() {
	flag.Parse()
}

type Channel struct {
	Name string
	Pass string
}

type BotConfig struct {
	Name     string
	Server   string
	Channels []Channel
}

type Config struct {
	General  BotConfig
	Forecast struct{ Key string }
}

func RegisterHandlers(bot *ircx.Bot) {
	bot.AddCallback(irc.RPL_WELCOME, ircx.Callback{Handler: ircx.HandlerFunc(RegisterConnect)})
	bot.AddCallback(irc.PING, ircx.Callback{Handler: ircx.HandlerFunc(PingHandler)})
	bot.AddCallback(irc.PRIVMSG, ircx.Callback{Handler: ircx.HandlerFunc(PrivMsgHandler)})
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	b, err := ioutil.ReadFile(*configfile)

	if err != nil {
		fmt.Printf("Can not read file (%s)\n", *configfile)
		os.Exit(1)
	}

	err = yaml.Unmarshal(b, &config)

	if err != nil {
		fmt.Printf("Config (%s) is invalid\n", *configfile)
		os.Exit(1)
	}

	bot := ircx.Classic(config.General.Server, config.General.Name)
	if err := bot.Connect(); err != nil {
		log.Panicln("Unable to dial IRC Server ", err)
	}

	RegisterHandlers(bot)
	bot.CallbackLoop()
	log.Println("Exiting..")
}

func PingHandler(s ircx.Sender, m *irc.Message) {
	s.Send(&irc.Message{
		Command:  irc.PONG,
		Params:   m.Params,
		Trailing: m.Trailing,
	})
}

func RegisterConnect(s ircx.Sender, m *irc.Message) {
	for _, irc_chan := range config.General.Channels {
		s.Send(&irc.Message{
			Command: irc.JOIN,
			Params:  []string{irc_chan.Name, irc_chan.Pass},
		})
	}
}

func PrivMsgHandler(s ircx.Sender, message *irc.Message) {
	go GetWeather(s, message)
	go GetTitle(s, message)
}
<commit_msg>Remove runtime.GOMAXPROCS as it's no longer needed in 1.5. Also, update for ircx API changes.<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/nickvanw/ircx"
	"github.com/sorcix/irc"
	"gopkg.in/yaml.v2"
)

var (
	configfile = flag.String("config", "config.yaml", "Path to config file")
	config     Config
)

func init() {
	flag.Parse()
}

type Channel struct {
	Name string
	Pass string
}

type BotConfig struct {
	Name     string
	Server   string
	Channels []Channel
}

type Config struct {
	General  BotConfig
	Forecast struct{ Key string }
}

func RegisterHandlers(bot *ircx.Bot) {
	bot.HandleFunc(irc.RPL_WELCOME, RegisterConnect)
	bot.HandleFunc(irc.PING, PingHandler)
	bot.HandleFunc(irc.PRIVMSG, PrivMsgHandler)
}

func main() {
	b, err := ioutil.ReadFile(*configfile)

	if err != nil {
		fmt.Printf("Can not read file (%s)\n", *configfile)
		os.Exit(1)
	}

	err = yaml.Unmarshal(b, &config)

	if err != nil {
		fmt.Printf("Config (%s) is invalid\n", *configfile)
		os.Exit(1)
	}

	bot := ircx.Classic(config.General.Server, config.General.Name)
	if err := bot.Connect(); err != nil {
		log.Panicln("Unable to dial IRC Server ", err)
	}

	RegisterHandlers(bot)
	bot.HandleLoop()
	log.Println("Exiting..")
}

func PingHandler(s ircx.Sender, m *irc.Message) {
	s.Send(&irc.Message{
		Command:  irc.PONG,
		Params:   m.Params,
		Trailing: m.Trailing,
	})
}

func RegisterConnect(s ircx.Sender, m *irc.Message) {
	for _, irc_chan := range config.General.Channels {
		s.Send(&irc.Message{
			Command: irc.JOIN,
			Params:  []string{irc_chan.Name, irc_chan.Pass},
		})
	}
}

func PrivMsgHandler(s ircx.Sender, message *irc.Message) {
	go GetWeather(s, message)
	go GetTitle(s, message)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"text/template"

	"github.com/gogap/config"
	"github.com/gogap/go-wkhtmltox/wkhtmltox"
	"github.com/gorilla/mux"
	"github.com/phyber/negroni-gzip/gzip"
	"github.com/rs/cors"
	"github.com/urfave/cli"
	"github.com/urfave/negroni"
)

import (
	_ "github.com/gogap/go-wkhtmltox/wkhtmltox/fetcher/http"
)

const (
	defaultTemplateText = `{"code":{{.Code}},"message":"{{.Message}}"{{if .Result}},"result":{{.Result|Jsonify}}{{end}}}`
)

var (
	htmlToX *wkhtmltox.WKHtmlToX

	renderTmpls = make(map[string]*template.Template)

	defaultTmpl *template.Template
)

type ConvertData struct {
	Data []byte `json:"data"`
}

type WKHtmlToXOptions struct {
	To        string                   `json:"to"`
	Fetcher   wkhtmltox.FetcherOptions `json:"fetcher"`
	Converter json.RawMessage          `json:"converter"`
	Template  string                   `json:"template"`
}

type ConvertArgs struct {
	Options WKHtmlToXOptions `json:"options"`
}

type ConvertResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Result  interface{} `json:"result"`
}

func main() {

	var err error

	defer func() {
		if err != nil {
			fmt.Printf("[go-wkhtmltox]: %s\n", err.Error())
		}
	}()

	app := cli.NewApp()

	app.Commands = cli.Commands{
		cli.Command{
			Name:   "run",
			Usage:  "run go-wkhtmltox service",
			Action: run,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "config,c",
					Usage: "config filename",
					Value: "app.conf",
				},
				cli.StringFlag{
					Name:  "cwd",
					Usage: "change work dir",
				},
			},
		},
	}

	err = app.Run(os.Args)
}

func run(ctx *cli.Context) (err error) {

	cwd := ctx.String("cwd")
	if len(cwd) != 0 {
		err = os.Chdir(cwd)
	}

	if err != nil {
		return
	}

	configFile := ctx.String("config")

	conf := config.NewConfig(
		config.ConfigFile(configFile),
	)

	serviceConf := conf.GetConfig("service")

	wkHtmlToXConf := conf.GetConfig("wkhtmltox")

	htmlToX, err = wkhtmltox.New(wkHtmlToXConf)

	if err != nil {
		return
	}

	// init templates

	defaultTmpl, err = template.New("default").Funcs(funcMap).Parse(defaultTemplateText)

	if err != nil {
		return
	}

	err = loadTemplates(
		serviceConf.GetConfig("templates"),
	)

	if err != nil {
		return
	}

	// init http server
	c := cors.New(
		cors.Options{
			AllowedOrigins:     serviceConf.GetStringList("cors.allowed-origins"),
			AllowedMethods:     serviceConf.GetStringList("cors.allowed-methods"),
			AllowedHeaders:     serviceConf.GetStringList("cors.allowed-headers"),
			ExposedHeaders:     serviceConf.GetStringList("cors.exposed-headers"),
			AllowCredentials:   serviceConf.GetBoolean("cors.allow-credentials"),
			MaxAge:             int(serviceConf.GetInt64("cors.max-age")),
			OptionsPassthrough: serviceConf.GetBoolean("cors.options-passthrough"),
			Debug:              serviceConf.GetBoolean("cors.debug"),
		},
	)

	r := mux.NewRouter()

	pathPrefix := serviceConf.GetString("path", "/")

	r.PathPrefix(pathPrefix).Path("/convert").
		Methods("POST").
		HandlerFunc(handleHtmlToX)

	n := negroni.Classic()

	n.Use(c) // use cors

	if serviceConf.GetBoolean("gzip-enabled", true) {
		n.Use(gzip.Gzip(gzip.DefaultCompression))
	}

	n.UseHandler(r)

	enableHTTP := serviceConf.GetBoolean("http.enabled", true)
	enableHTTPS := serviceConf.GetBoolean("https.enabled", false)

	wg := sync.WaitGroup{}

	if enableHTTP {
		wg.Add(1)

		listenAddr := serviceConf.GetString("address", "127.0.0.1:8080")
		go func() {
			defer wg.Done()
			e := http.ListenAndServe(listenAddr, n)
			if e != nil {
				log.Printf("[go-wkhtmltox]: %s\n", e.Error())
			}
		}()
	}

	if enableHTTPS {
		wg.Add(1)

		listenAddr := serviceConf.GetString("address", "127.0.0.1:443")
		certFile := serviceConf.GetString("https.cert")
		keyFile := serviceConf.GetString("https.key")

		go func() {
			defer wg.Done()
			e := http.ListenAndServeTLS(listenAddr, certFile, keyFile, n)
			if e != nil {
				log.Printf("[go-wkhtmltox]: %s\n", e.Error())
			}
		}()
	}

	wg.Wait()

	return
}

func writeResp(rw http.ResponseWriter, converArgs ConvertArgs, resp ConvertResponse) {

	var tmpl *template.Template
	if len(converArgs.Options.Template) == 0 {
		tmpl = defaultTmpl
	} else {
		var exist bool

		tmpl, exist = renderTmpls[converArgs.Options.Template]
		if !exist {
			tmpl = defaultTmpl
		}
	}

	args := map[string]interface{}{
		"Code":    resp.Code,
		"Message": resp.Message,
		"Result":  resp.Result,
		"Header":  rw.Header(),
		"To":      converArgs.Options.To,
	}

	err := tmpl.Execute(rw, args)

	if err != nil {
		log.Println(err)
	}
}

func handleHtmlToX(rw http.ResponseWriter, req *http.Request) {

	decoder := json.NewDecoder(req.Body)

	decoder.UseNumber()

	args := ConvertArgs{}

	err := decoder.Decode(&args)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	if len(args.Options.Converter) == 0 {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, "options.converter is nil", nil})
		return
	}

	to := strings.ToUpper(args.Options.To)

	var opts wkhtmltox.ConvertOptions

	if to == "IMAGE" {
		opts = &wkhtmltox.ToImageOptions{}
	} else if to == "PDF" {
		opts = &wkhtmltox.ToPDFOptions{}
	} else {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, "argument of to is illegal (image|pdf)", nil})
		return
	}

	err = json.Unmarshal(args.Options.Converter, opts)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	var convData []byte

	convData, err = htmlToX.Convert(args.Options.Fetcher, opts)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	writeResp(rw, args, ConvertResponse{0, "", ConvertData{Data: convData}})

	return
}

func loadTemplates(tmplsConf config.Configuration) (err error) {
	if tmplsConf == nil {
		return
	}

	tmpls := tmplsConf.Keys()

	for _, name := range tmpls {

		file := tmplsConf.GetString(name + ".template")

		tmpl := template.New(name).Funcs(funcMap)

		var data []byte
		data, err = ioutil.ReadFile(file)

		if err != nil {
			return
		}

		tmpl, err = tmpl.Parse(string(data))

		if err != nil {
			return
		}

		renderTmpls[name] = tmpl
	}

	return
}
<commit_msg>improve app usage<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"text/template"

	"github.com/gogap/config"
	"github.com/gogap/go-wkhtmltox/wkhtmltox"
	"github.com/gorilla/mux"
	"github.com/phyber/negroni-gzip/gzip"
	"github.com/rs/cors"
	"github.com/urfave/cli"
	"github.com/urfave/negroni"
)

import (
	_ "github.com/gogap/go-wkhtmltox/wkhtmltox/fetcher/http"
)

const (
	defaultTemplateText = `{"code":{{.Code}},"message":"{{.Message}}"{{if .Result}},"result":{{.Result|Jsonify}}{{end}}}`
)

var (
	htmlToX *wkhtmltox.WKHtmlToX

	renderTmpls = make(map[string]*template.Template)

	defaultTmpl *template.Template
)

type ConvertData struct {
	Data []byte `json:"data"`
}

type WKHtmlToXOptions struct {
	To        string                   `json:"to"`
	Fetcher   wkhtmltox.FetcherOptions `json:"fetcher"`
	Converter json.RawMessage          `json:"converter"`
	Template  string                   `json:"template"`
}

type ConvertArgs struct {
	Options WKHtmlToXOptions `json:"options"`
}

type ConvertResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Result  interface{} `json:"result"`
}

func main() {

	var err error

	defer func() {
		if err != nil {
			fmt.Printf("[go-wkhtmltox]: %s\n", err.Error())
		}
	}()

	app := cli.NewApp()

	app.Usage = "A server for wkhtmltopdf and wkhtmltoimage"

	app.Commands = cli.Commands{
		cli.Command{
			Name:   "run",
			Usage:  "run go-wkhtmltox service",
			Action: run,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "config,c",
					Usage: "config filename",
					Value: "app.conf",
				},
				cli.StringFlag{
					Name:  "cwd",
					Usage: "change work dir",
				},
			},
		},
	}

	err = app.Run(os.Args)
}

func run(ctx *cli.Context) (err error) {

	cwd := ctx.String("cwd")
	if len(cwd) != 0 {
		err = os.Chdir(cwd)
	}

	if err != nil {
		return
	}

	configFile := ctx.String("config")

	conf := config.NewConfig(
		config.ConfigFile(configFile),
	)

	serviceConf := conf.GetConfig("service")

	wkHtmlToXConf := conf.GetConfig("wkhtmltox")

	htmlToX, err = wkhtmltox.New(wkHtmlToXConf)

	if err != nil {
		return
	}

	// init templates

	defaultTmpl, err = template.New("default").Funcs(funcMap).Parse(defaultTemplateText)

	if err != nil {
		return
	}

	err = loadTemplates(
		serviceConf.GetConfig("templates"),
	)

	if err != nil {
		return
	}

	// init http server
	c := cors.New(
		cors.Options{
			AllowedOrigins:     serviceConf.GetStringList("cors.allowed-origins"),
			AllowedMethods:     serviceConf.GetStringList("cors.allowed-methods"),
			AllowedHeaders:     serviceConf.GetStringList("cors.allowed-headers"),
			ExposedHeaders:     serviceConf.GetStringList("cors.exposed-headers"),
			AllowCredentials:   serviceConf.GetBoolean("cors.allow-credentials"),
			MaxAge:             int(serviceConf.GetInt64("cors.max-age")),
			OptionsPassthrough: serviceConf.GetBoolean("cors.options-passthrough"),
			Debug:              serviceConf.GetBoolean("cors.debug"),
		},
	)

	r := mux.NewRouter()

	pathPrefix := serviceConf.GetString("path", "/")

	r.PathPrefix(pathPrefix).Path("/convert").
		Methods("POST").
		HandlerFunc(handleHtmlToX)

	n := negroni.Classic()

	n.Use(c) // use cors

	if serviceConf.GetBoolean("gzip-enabled", true) {
		n.Use(gzip.Gzip(gzip.DefaultCompression))
	}

	n.UseHandler(r)

	enableHTTP := serviceConf.GetBoolean("http.enabled", true)
	enableHTTPS := serviceConf.GetBoolean("https.enabled", false)

	wg := sync.WaitGroup{}

	if enableHTTP {
		wg.Add(1)

		listenAddr := serviceConf.GetString("address", "127.0.0.1:8080")
		go func() {
			defer wg.Done()
			e := http.ListenAndServe(listenAddr, n)
			if e != nil {
				log.Printf("[go-wkhtmltox]: %s\n", e.Error())
			}
		}()
	}

	if enableHTTPS {
		wg.Add(1)

		listenAddr := serviceConf.GetString("address", "127.0.0.1:443")
		certFile := serviceConf.GetString("https.cert")
		keyFile := serviceConf.GetString("https.key")

		go func() {
			defer wg.Done()
			e := http.ListenAndServeTLS(listenAddr, certFile, keyFile, n)
			if e != nil {
				log.Printf("[go-wkhtmltox]: %s\n", e.Error())
			}
		}()
	}

	wg.Wait()

	return
}

func writeResp(rw http.ResponseWriter, converArgs ConvertArgs, resp ConvertResponse) {

	var tmpl *template.Template
	if len(converArgs.Options.Template) == 0 {
		tmpl = defaultTmpl
	} else {
		var exist bool

		tmpl, exist = renderTmpls[converArgs.Options.Template]
		if !exist {
			tmpl = defaultTmpl
		}
	}

	args := map[string]interface{}{
		"Code":    resp.Code,
		"Message": resp.Message,
		"Result":  resp.Result,
		"Header":  rw.Header(),
		"To":      converArgs.Options.To,
	}

	err := tmpl.Execute(rw, args)

	if err != nil {
		log.Println(err)
	}
}

func handleHtmlToX(rw http.ResponseWriter, req *http.Request) {

	decoder := json.NewDecoder(req.Body)

	decoder.UseNumber()

	args := ConvertArgs{}

	err := decoder.Decode(&args)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	if len(args.Options.Converter) == 0 {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, "options.converter is nil", nil})
		return
	}

	to := strings.ToUpper(args.Options.To)

	var opts wkhtmltox.ConvertOptions

	if to == "IMAGE" {
		opts = &wkhtmltox.ToImageOptions{}
	} else if to == "PDF" {
		opts = &wkhtmltox.ToPDFOptions{}
	} else {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, "argument of to is illegal (image|pdf)", nil})
		return
	}

	err = json.Unmarshal(args.Options.Converter, opts)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	var convData []byte

	convData, err = htmlToX.Convert(args.Options.Fetcher, opts)

	if err != nil {
		writeResp(rw, args, ConvertResponse{http.StatusBadRequest, err.Error(), nil})
		return
	}

	writeResp(rw, args, ConvertResponse{0, "", ConvertData{Data: convData}})

	return
}

func loadTemplates(tmplsConf config.Configuration) (err error) {
	if tmplsConf == nil {
		return
	}

	tmpls := tmplsConf.Keys()

	for _, name := range tmpls {

		file := tmplsConf.GetString(name + ".template")

		tmpl := template.New(name).Funcs(funcMap)

		var data []byte
		data, err = ioutil.ReadFile(file)

		if err != nil {
			return
		}

		tmpl, err = tmpl.Parse(string(data))

		if err != nil {
			return
		}

		renderTmpls[name] = tmpl
	}

	return
}
<|endoftext|>


<commit_before>// SVFS implements a virtual file system for Openstack Swift.
package main

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

	"github.com/ncw/swift"
	"github.com/xlucas/svfs/svfs"

	fuse "bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

func main() {
	var (
		debug bool
		fs    *svfs.SVFS
		sc    = swift.Connection{}
		srv   *fusefs.Server
		conf  = svfs.Config{}
		cconf = svfs.CacheConfig{}
	)

	// Logger
	log.SetOutput(os.Stdout)
	flag.BoolVar(&debug, "debug", false, "Enable fuse debug log")

	// Swift options
	flag.StringVar(&sc.AuthUrl, "os-auth-url", "https://auth.cloud.ovh.net/v2.0", "Authentication URL")
	flag.StringVar(&conf.Container, "os-container-name", "", "Container name")
	flag.StringVar(&sc.AuthToken, "os-auth-token", "", "Authentication token")
	flag.StringVar(&sc.ApiKey, "os-password", "", "User password")
	flag.StringVar(&sc.UserName, "os-username", "", "User name")
	flag.StringVar(&sc.Region, "os-region-name", "", "Region")
	flag.StringVar(&sc.StorageUrl, "os-storage-url", "", "Storage URL")
	flag.StringVar(&sc.Tenant, "os-tenant-name", "", "Tenant name")
	flag.IntVar(&sc.AuthVersion, "os-auth-version", 0, "Authentication version, 0 = auto")
	flag.DurationVar(&conf.ConnectTimeout, "os-connect-timeout", 5*time.Minute, "Swift connection timeout")

	// Concurrency
	flag.Uint64Var(&conf.MaxReaddirConcurrency, "max-readdir-concurrency", 20, "Overall concurrency factor when listing directories")
	flag.UintVar(&conf.ReadAheadSize, "readahead-size", 131072, "Per file readahead size in bytes")

	// Cache Options
	flag.DurationVar(&cconf.Timeout, "cache-ttl", 1*time.Minute, "Cache timeout")
	flag.Int64Var(&cconf.MaxEntries, "cache-max-entries", -1, "Maximum overall entries allowed in cache")
	flag.Int64Var(&cconf.MaxAccess, "cache-max-access", -1, "Maximum access count to cached entries")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] MOUNTPOINT\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Available options :\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	// Debug
	if debug {
		fuse.Debug = func(msg interface{}) {
			log.Printf("FUSE: %s\n", msg)
		}
	}

	// Mountpoint is mandatory
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(2)
	}
	mountpoint := os.Args[len(os.Args)-1]

	// Mount SVFS
	c, err := fuse.Mount(
		mountpoint,
		fuse.FSName("svfs"),
		fuse.Subtype("svfs"),
		fuse.MaxReadahead(uint32(conf.ReadAheadSize)),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	// Pre-Serve: authenticate to identity endpoint
	// if no token is specified
	if !sc.Authenticated() {
		if err = sc.Authenticate(); err != nil {
			goto Err
		}
	}

	// Init SVFS
	fs = &svfs.SVFS{}
	if err = fs.Init(&sc, &conf, &cconf); err != nil {
		goto Err
	}

	// Serve SVFS
	srv = fusefs.New(c, nil)
	if err = srv.Serve(fs); err != nil {
		goto Err
	}

	// Check for mount errors
	<-c.Ready
	if err = c.MountError; err != nil {
		goto Err
	}

	return

Err:
	fuse.Unmount(mountpoint)
	log.Fatal(err)
}
<commit_msg>CPU and Memory profiling fixes #22<commit_after>// SVFS implements a virtual file system for Openstack Swift.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"

	"github.com/ncw/swift"
	"github.com/xlucas/svfs/svfs"

	fuse "bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

func main() {
	var (
		debug   bool
		fs      *svfs.SVFS
		sc      = swift.Connection{}
		srv     *fusefs.Server
		conf    = svfs.Config{}
		cconf   = svfs.CacheConfig{}
		cpuProf string
		memProf string
	)

	// Swift options
	flag.StringVar(&sc.AuthUrl, "os-auth-url", "https://auth.cloud.ovh.net/v2.0", "Authentication URL")
	flag.StringVar(&conf.Container, "os-container-name", "", "Container name")
	flag.StringVar(&sc.AuthToken, "os-auth-token", "", "Authentication token")
	flag.StringVar(&sc.ApiKey, "os-password", "", "User password")
	flag.StringVar(&sc.UserName, "os-username", "", "User name")
	flag.StringVar(&sc.Region, "os-region-name", "", "Region")
	flag.StringVar(&sc.StorageUrl, "os-storage-url", "", "Storage URL")
	flag.StringVar(&sc.Tenant, "os-tenant-name", "", "Tenant name")
	flag.IntVar(&sc.AuthVersion, "os-auth-version", 0, "Authentication version, 0 = auto")
	flag.DurationVar(&conf.ConnectTimeout, "os-connect-timeout", 5*time.Minute, "Swift connection timeout")

	// Concurrency
	flag.Uint64Var(&conf.MaxReaddirConcurrency, "max-readdir-concurrency", 20, "Overall concurrency factor when listing directories")
	flag.UintVar(&conf.ReadAheadSize, "readahead-size", 131072, "Per file readahead size in bytes")

	// Cache Options
	flag.DurationVar(&cconf.Timeout, "cache-ttl", 1*time.Minute, "Cache timeout")
	flag.Int64Var(&cconf.MaxEntries, "cache-max-entries", -1, "Maximum overall entries allowed in cache")
	flag.Int64Var(&cconf.MaxAccess, "cache-max-access", -1, "Maximum access count to cached entries")

	// Debug and profiling
	log.SetOutput(os.Stdout)
	flag.BoolVar(&debug, "debug", false, "Enable fuse debug log")
	flag.StringVar(&cpuProf, "profile-cpu", "", "Write cpu profile to this file")
	flag.StringVar(&memProf, "profile-ram", "", "Write memory profile to this file")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] MOUNTPOINT\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Available options :\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	// Debug
	if debug {
		fuse.Debug = func(msg interface{}) {
			log.Printf("FUSE: %s\n", msg)
		}
	}

	// CPU profiling
	if cpuProf != "" {
		f, err := os.Create(cpuProf)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// Mountpoint is mandatory
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(2)
	}
	mountpoint := os.Args[len(os.Args)-1]

	// Mount SVFS
	c, err := fuse.Mount(
		mountpoint,
		fuse.FSName("svfs"),
		fuse.Subtype("svfs"),
		fuse.MaxReadahead(uint32(conf.ReadAheadSize)),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	// Pre-Serve: authenticate to identity endpoint
	// if no token is specified
	if !sc.Authenticated() {
		if err = sc.Authenticate(); err != nil {
			goto Err
		}
	}

	// Init SVFS
	fs = &svfs.SVFS{}
	if err = fs.Init(&sc, &conf, &cconf); err != nil {
		goto Err
	}

	// Serve SVFS
	srv = fusefs.New(c, nil)
	if err = srv.Serve(fs); err != nil {
		goto Err
	}

	// Check for mount errors
	<-c.Ready

	// Memory profiling
	if memProf != "" {
		f, err := os.Create(memProf)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
		return
	}

	if err = c.MountError; err != nil {
		goto Err
	}

	return

Err:
	fuse.Unmount(mountpoint)
	log.Fatal(err)
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "time"
    "sync"
    "regexp"
    "net/http"
    "io/ioutil"
)

func main() {

    //holds all the pages for BFS
    queue := make(chan []string, 100000000)

    //regex to extract exclusively wikipedia links from fetched pages
    reFindLink, err := regexp.Compile("href=\"(/wiki/[^\"/ :#]*)\"")

    if err != nil {
        fmt.Println(err)
        return
    }

    //To prevent undefined behavior of concurrently used maps
    mutex := new(sync.Mutex)

    //list of pages the program has already visited, stored as wikipedia.org suffixes
    visited := make(map[string]bool)

    //link to add suffixes onto
    base_link := "https://en.wikipedia.org"

    //page to start at
    http_start := []string{"/wiki/Raspberry_Pi"}

    //page to find
    link_goal := "/wiki/Egypt"

    queue <- http_start

    done := false

    //Value to keep track of distance from original page
    level := 1

    thread_count := 512

    running_threads := 0;

    var solution []string

    for i := 0; i < thread_count; i++ {

    	//Start thread
    	running_threads++
        fmt.Printf("[%2d] Launching...\n", i)
        go func (id int) {
            defer func() {running_threads--}()
            fmt.Printf("[%2d] Starting...\n", id)

            for L := range queue {
                if len(L) > level {
                    level++
                    fmt.Println("Level:", level, "Len:", len(queue))
                }
                //Fetch Wikipedia page
                req, err := http.Get(base_link + L[len(L)-1])
                if err == nil {
                	//Extract Links
                    b, _ := ioutil.ReadAll(req.Body)
                    links := reFindLink.FindAllStringSubmatch(string(b), -1)

                    //Add links to queue in batches
                    //mutex lock to prevent concurrent map usage
                    mutex.Lock()
                    for t := range links {
                        _, v := visited[links[t][1]]
                        if links[t][1] == link_goal {
                            solution = append(L, links[t][1])
                            done = true
                            return
                        }
                        if !v {
                            visited[links[t][1]] = true
                            queue <- append(L, links[t][1])
                        }
                    }
                    mutex.Unlock()
                } else {
                    fmt.Println(err)
                }
                if done {
                    return
                }
                time.Sleep(10);
            }

            thread_count--
            return;
        }(i)
    }

    //wait for threads to find the solution
    ///TODO: should probably find a better way to do this (maybe mutex?)
    for !done {
        time.Sleep(1)
    }

    fmt.Println(solution)

    fmt.Println("Done!")
}
<commit_msg>Cleaned up exit code and made sure all the threads are stopped when we exit<commit_after>package main

import (
    "fmt"
    "time"
    "sync"
    "regexp"
    "net/http"
    "io/ioutil"
)

func main() {

    //holds all the pages for BFS
    queue := make(chan []string, 100000000)

    //regex to extract exclusively wikipedia links from fetched pages
    reFindLink, err := regexp.Compile("href=\"(/wiki/[^\"/ :#]*)\"")

    if err != nil {
        fmt.Println(err)
        return
    }

    //To prevent undefined behavior of concurrently used maps
    mutex := new(sync.Mutex)

    //list of pages the program has already visited, stored as wikipedia.org suffixes
    visited := make(map[string]bool)

    //link to add suffixes onto
    base_link := "https://en.wikipedia.org"

    //page to start at
    http_start := []string{"/wiki/Raspberry_Pi"}

    //page to find
    link_goal := "/wiki/Egypt"

    queue <- http_start

    done := false

    //Value to keep track of distance from original page
    level := 1

    thread_count := 512

    var solution []string

    for i := 0; i < thread_count; i++ {

    	//Start thread
        fmt.Printf("[%2d] Launching...\n", i)
        go func (id int) {
        	defer func() {
            	fmt.Printf("[%2d] Stopping...\n", id)
        		thread_count--
        	} ()
            fmt.Printf("[%2d] Starting...\n", id)

            for L := range queue {
                if len(L) > level {
                    level++
                    fmt.Println("Level:", level, "Len:", len(queue))
                }
                //Fetch Wikipedia page
                req, err := http.Get(base_link + L[len(L)-1])
                if err == nil {
                	//Extract Links
                    b, _ := ioutil.ReadAll(req.Body)
                    links := reFindLink.FindAllStringSubmatch(string(b), -1)

                    //Add links to queue in batches
                    //mutex lock to prevent concurrent map usage
                    mutex.Lock()
                    if !done {
	                    for t := range links {
	                        _, v := visited[links[t][1]]
	                        if links[t][1] == link_goal {
	                            solution = append(L, links[t][1])
	                            done = true
	                            mutex.Unlock()
	                            return
	                        }
	                        if !v {
	                            visited[links[t][1]] = true
	                            queue <- append(L, links[t][1])
	                        }
	                    }
	                }
                    mutex.Unlock()
                } else {
                    fmt.Println(err)
                }
                if done {
                    return
                }
                time.Sleep(10);
            }

            return;
        }(i)
    }

    //wait for threads to find the solution
    ///TODO: should probably find a better way to do this (maybe a channel?)
    for !done || thread_count > 0 {
        time.Sleep(1)
    }

    fmt.Println(solution)

    fmt.Println("Done!")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
)

type Redirect struct {
	URI          string `json:"uri"`
	Android      string `json:"android"`
	IPhone       string `json:"iphone"`
	IPod         string `json:"ipod"`
	IPad         string `json:"ipad"`
	BlackBerry   string `json:"bb"`
	WindowsPhone string `json:"wp"`
	WinXin       string `json:"wx"`
	Unknown      string `json:"unknown"`
}

type Config struct {
	Address   string      `json:"address"`
	Port      uint16      `json:"port"`
	Redirects []*Redirect `json:"redirect"`
}

var (
	config *Config = new(Config)
)

func handler(w http.ResponseWriter, r *http.Request) {
	var redirect *Redirect = nil
	for _, v := range config.Redirects {
		if v.URI == r.RequestURI {
			redirect = v
			break
		}
	}
	if redirect != nil {
		userAgent := r.UserAgent()
		matched := regexp.MustCompile("MQQBrowser").MatchString(userAgent)
		if matched {
			log.Println("wx", redirect.WinXin)
			http.Redirect(w, r, redirect.WinXin, 302)
			return
		}
		matched = regexp.MustCompile("TBS").MatchString(userAgent)
		if matched {
			log.Println("wx", redirect.WinXin)
			http.Redirect(w, r, redirect.WinXin, 302)
			return
		}
		matched = regexp.MustCompile("Android").MatchString(userAgent)
		if matched {
			log.Println("android", redirect.Android)
			http.Redirect(w, r, redirect.Android, 302)
			return
		}
		matched = regexp.MustCompile("iPod").MatchString(userAgent)
		if matched {
			log.Println("ipod", redirect.IPod)
			http.Redirect(w, r, redirect.IPod, 302)
			return
		}
		matched = regexp.MustCompile("iPhone").MatchString(userAgent)
		if matched {
			log.Println("iphone", redirect.IPhone)
			http.Redirect(w, r, redirect.IPhone, 302)
			return
		}
		matched = regexp.MustCompile("iPad").MatchString(userAgent)
		if matched {
			log.Println("ipad", redirect.IPad)
			http.Redirect(w, r, redirect.IPad, 302)
			return
		}
		matched = regexp.MustCompile("BlackBerry").MatchString(userAgent)
		if matched {
			log.Println("bb", redirect.BlackBerry)
			http.Redirect(w, r, redirect.BlackBerry, 302)
			return
		}
		matched = regexp.MustCompile("IEMobile").MatchString(userAgent)
		if matched {
			log.Println("wp", redirect.WindowsPhone)
			http.Redirect(w, r, redirect.WindowsPhone, 302)
			return
		}

		log.Println("unknown", redirect.Unknown)
		http.Redirect(w, r, redirect.Unknown, 302)
		return
	}
	log.Println("unmatched", config.Redirects[0].Unknown)
	http.Redirect(w, r, config.Redirects[0].Unknown, 302)
}

func main() {
	fh, err := os.Open("config.json")
	if err != nil {
		log.Fatal("opening config.json failed")
		return
	}
	defer fh.Close()
	configcontent, err := ioutil.ReadAll(fh)
	if err != nil {
		log.Fatal("reading config.json failed")
		return
	}

	if err = json.Unmarshal(configcontent, config); err != nil {
		log.Fatal("parsing config.json failed")
		return
	}

	http.HandleFunc("/", handler)
	log.Fatal(
		http.ListenAndServe(
			fmt.Sprintf("%s:%d", config.Address, config.Port),
			nil))
}
<commit_msg>(*)code refactoring: table driven<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
)

type Redirect struct {
	URI          string `json:"uri"`
	Android      string `json:"android"`
	IPhone       string `json:"iphone"`
	IPod         string `json:"ipod"`
	IPad         string `json:"ipad"`
	BlackBerry   string `json:"bb"`
	WindowsPhone string `json:"wp"`
	WeiXin       string `json:"wx"`
	Unknown      string `json:"unknown"`
}

type Config struct {
	Address   string      `json:"address"`
	Port      uint16      `json:"port"`
	Redirects []*Redirect `json:"redirect"`
}

var (
	config *Config = new(Config)
)

func handler(w http.ResponseWriter, r *http.Request) {
	var redirect *Redirect = nil
	for _, v := range config.Redirects {
		if v.URI == r.RequestURI {
			redirect = v
			break
		}
	}

	pairs := []struct {
		Regexp string
		To     string
		Desc   string
	}{
		{Regexp: "MQQBrowser", To: redirect.WeiXin, Desc: "wx"},
		{Regexp: "TBS", To: redirect.WeiXin, Desc: "wx"},
		{Regexp: "Android", To: redirect.Android, Desc: "android"},
		{Regexp: "iPod", To: redirect.IPod, Desc: "ipod"},
		{Regexp: "iPhone", To: redirect.IPhone, Desc: "iphone"},
		{Regexp: "iPad", To: redirect.IPad, Desc: "ipad"},
		{Regexp: "BlackBerry", To: redirect.BlackBerry, Desc: "bb"},
		{Regexp: "IEMobile", To: redirect.WindowsPhone, Desc: "wp"},
	}

	if redirect != nil {
		userAgent := r.UserAgent()
		for _, p := range pairs {
			matched := regexp.MustCompile(p.Regexp).MatchString(userAgent)
			if matched {
				log.Println(p.Desc, p.To)
				http.Redirect(w, r, p.To, 302)
				return
			}
		}

		log.Println("unknown", redirect.Unknown)
		http.Redirect(w, r, redirect.Unknown, 302)
		return
	}
	log.Println("unmatched", config.Redirects[0].Unknown)
	http.Redirect(w, r, config.Redirects[0].Unknown, 302)
}

func main() {
	fh, err := os.Open("config.json")
	if err != nil {
		log.Fatal("opening config.json failed")
		return
	}
	defer fh.Close()
	configcontent, err := ioutil.ReadAll(fh)
	if err != nil {
		log.Fatal("reading config.json failed")
		return
	}

	if err = json.Unmarshal(configcontent, config); err != nil {
		log.Fatal("parsing config.json failed")
		return
	}

	http.HandleFunc("/", handler)
	log.Fatal(
		http.ListenAndServe(
			fmt.Sprintf("%s:%d", config.Address, config.Port),
			nil))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	s "strings"
	"time"
)

var data = make(map[string]interface{})

const filename = "db.txt"

type Proj struct {
	Title       string
	Description string
}

func ScrapProj() {
	fmt.Println("scraping ..")
	doc, err := goquery.NewDocument("https://github.com/karan/Projects/blob/master/README.md")
	if err != nil {
		log.Fatal(err)
	}

	if !isFileExist(filename) {
		// if the file doesn't exist, create a new one
		w, err := os.Create(filename)
		if err != nil {
			panic(err)
		}
		defer w.Close()

		// write empty one first
		err = ioutil.WriteFile(filename, []byte(""), 0644)

		doc.Find(".markdown-body p").Slice(7, 95).Each(func(i int, s *goquery.Selection) {
			txt := s.Text() + "\n"
			f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
			if err != nil {
				panic(err)
			}

			defer f.Close()

			if _, err = f.WriteString(txt); err != nil {
				panic(err)
			}
		})
	}
}

func ReadProjFromFile() []string {
	fmt.Println("reading from file ..")
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	projs := string(b)
	return s.Split(projs, "\n")
}

func random(min, max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max-min) + min
}

func main() {

	m := martini.Classic()
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
	}))

	m.Get("/suggest", func(ren render.Render) {
		// i := random(7, 95)
		// ren.HTML(200, "index", ScrapProj(i))
	})

	m.Get("/", func(ren render.Render) {
		ren.HTML(200, "index", nil)
	})

	// ScrapProj()
	fmt.Println("total : ", len(ReadProjFromFile()))
}

// Check if file already exists or not
func isFileExist(filename string) bool {
	if _, err := os.Stat(filename); err == nil {
		return true
	}
	return false
}
<commit_msg>almost done!<commit_after>package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	s "strings"
	"time"
)

var data = make(map[string]interface{})

const filename = "db.txt"

type Proj struct {
	Title       string
	Description string
}

func ScrapProj() {
	fmt.Println("scraping ..")
	doc, err := goquery.NewDocument("https://github.com/karan/Projects/blob/master/README.md")
	if err != nil {
		log.Fatal(err)
	}

	if !isFileExist(filename) {
		// if the file doesn't exist, create a new one
		w, err := os.Create(filename)
		if err != nil {
			panic(err)
		}
		defer w.Close()

		// write empty one first
		err = ioutil.WriteFile(filename, []byte(""), 0644)

		doc.Find(".markdown-body p").Slice(7, 95).Each(func(i int, s *goquery.Selection) {
			txt := s.Text() + "\n"
			f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
			if err != nil {
				panic(err)
			}

			defer f.Close()

			if _, err = f.WriteString(txt); err != nil {
				panic(err)
			}
		})
	}
}

func ReadProjFromFile() []string {
	fmt.Println("reading from file ..")
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return s.Split(string(b), "\n")
}

func GetRandomProj(projs []string) map[string]interface{} {
	i := random(0, len(projs))
	project := projs[i]

	data["title"] = s.Split(project, " - ")[0]
	data["desc"] = s.Split(project, " - ")[1]
	return data
}

func random(min, max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max-min) + min
}

func main() {

	m := martini.Classic()
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
	}))

	m.Get("/suggest", func(ren render.Render) {
		ren.HTML(200, "index", GetRandomProj(ReadProjFromFile()))
	})

	m.Get("/", func(ren render.Render) {
		ren.HTML(200, "index", nil)
	})

	m.Run()
}

// Check if file already exists or not
func isFileExist(filename string) bool {
	if _, err := os.Stat(filename); err == nil {
		return true
	}
	return false
}
<|endoftext|>


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

// vendor tool to copy external source code to the local repository.
/*
govendor: copy go packages locally and optionally re-write imports.
govendor init
govendor list [-v] [+status] [import-path-filter]
govendor {add, update, remove} [-n] [+status] [import-path-filter]
govendor migrate [auto, godep, internal]

	init
		create a vendor file if it does not exist.

	add
		copy one or more packages into the internal folder and re-write paths.

	update
		update one or more packages from GOPATH into the internal folder.

	remove
		remove one or more packages from the internal folder and re-write packages to vendor paths.

	migrate
		change from a one schema to use the vendor folder.

Expanding "..."
	A package import path may be expanded to other paths that
	show up in "govendor list" be ending the "import-path" with "...".
	NOTE: this uses the import tree from "vendor list" and NOT the file system.

Flags
	-n		print actions but do not run them

Status list:
	external - package does not share root path
	vendor - vendor folder; copied locally
	unused - the package has been copied locally, but isn't used
	local - shares the root path and is not a vendor package
	missing - referenced but not found in GOROOT or GOPATH
	std - standard library package
	program - package is a main package
	---
	all - all of the above status

Status can be referenced by their initial letters.
	"st" == "std"
	"e" == "external"

Ignoring files with build tags:
	The "vendor.json" file contains a string field named "ignore".
	It may contain a space separated list of build tags to ignore when
	listing and copying files. By default the init command adds the
	the "test" tag to the ignore list.

Example:
	govendor add github.com/kardianos/osext
	govendor update github.com/kardianos/...
	govendor add +external
	govendor update +ven github.com/company/project/... bitbucket.org/user/pkg
	govendor remove +vendor
	govendor list +ext +std

To opt use the standard vendor directory:
set GO15VENDOREXPERIMENT=1

When GO15VENDOREXPERIMENT=1 imports are copied to the vendor directory without
rewriting their import paths.
*/
package main

import (
	"fmt"
	"os"
)

func main() {
	printHelp, err := run(os.Stdout, os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	}
	if printHelp {
		fmt.Fprint(os.Stderr, help)
	}
	if printHelp || err != nil {
		os.Exit(1)
	}
}
<commit_msg>govendor: update pkg doc<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// vendor tool to copy external source code to the local vendor folder.
/*
govendor: copy go packages locally. Uses vendor folder.
govendor init
govendor list [-v] [-no-status] [+<status>] [import-path-filter]
govendor {add, update, remove} [-n] [-short | -long] [+status] [import-path-filter]
govendor migrate [auto, godep, internal]

	init
		create a vendor file if it does not exist.

	add
		copy one or more packages into the vendor folder.

	update
		update one or more packages from GOPATH into the vendor folder.

	remove
		remove one or more packages from the vendor folder.

	migrate
		change from a one schema to use the vendor folder.

Expanding "..."
	A package import path may be expanded to other paths that
	show up in "govendor list" be ending the "import-path" with "...".
	NOTE: this uses the import tree from "vendor list" and NOT the file system.

Flags
	-n		print actions but do not run them
	-short	chooses the shorter path in case of conflict
	-long	chooses the longer path in case of conflict

Status list:
	external - package does not share root path
	vendor - vendor folder; copied locally
	unused - the package has been copied locally, but isn't used
	local - shares the root path and is not a vendor package
	missing - referenced but not found in GOROOT or GOPATH
	std - standard library package
	program - package is a main package
	---
	all - all of the above status

Status can be referenced by their initial letters.
	"st" == "std"
	"e" == "external"

Ignoring files with build tags:
	The "vendor.json" file contains a string field named "ignore".
	It may contain a space separated list of build tags to ignore when
	listing and copying files. By default the init command adds the
	the "test" tag to the ignore list.

Example:
	govendor add github.com/kardianos/osext
	govendor update github.com/kardianos/...
	govendor add +external
	govendor update +ven github.com/company/project/... bitbucket.org/user/pkg
	govendor remove +vendor
	govendor list +ext +std

If using go1.5, ensure you set GO15VENDOREXPERIMENT=1
*/
package main

import (
	"fmt"
	"os"
)

func main() {
	printHelp, err := run(os.Stdout, os.Args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
	}
	if printHelp {
		fmt.Fprint(os.Stderr, help)
	}
	if printHelp || err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

var winTitle string = "No Good"
var winWidth, winHeight int = 800, 600

func main() {
	var window *sdl.Window
	var renderer *sdl.Renderer
	var event sdl.Event
	var running bool

	sdl.Init(sdl.INIT_VIDEO)

	window = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, winWidth, winHeight, sdl.WINDOW_SHOWN)
	if window == nil {
		fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", sdl.GetError())
		os.Exit(1)
	}

	renderer = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
	if renderer == nil {
		fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", sdl.GetError())
		os.Exit(2)
	}

	ttf.Init()
	f, err := ttf.OpenFont("assets/roboto/Roboto-Regular.ttf", 12)
	if err != nil {
		fmt.Println("font error:", err)
	}
	textsurf := f.RenderText_Solid("No Good", sdl.Color{200,200,200,128})
	fmt.Println(textsurf)

	texttex := renderer.CreateTextureFromSurface(textsurf)
	//textrect := sdl.Rect{0, 0, 128, 32}

	running = true
	for running {
		renderer.Clear()
		renderer.Copy(texttex, nil, nil)
		renderer.Present()
		for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			case *sdl.QuitEvent:
				running = false
//			case *sdl.MouseMotionEvent:
//				fmt.Printf("[%d ms] MouseMotion\ttype:%d\tid:%d\tx:%d\ty:%d\txrel:%d\tyrel:%d\n",
//					t.Timestamp, t.Type, t.Which, t.X, t.Y, t.XRel, t.YRel)
			case *sdl.MouseButtonEvent:
				fmt.Printf("[%d ms] MouseButton\ttype:%d\tid:%d\tx:%d\ty:%d\tbutton:%d\tstate:%d\n",
					t.Timestamp, t.Type, t.Which, t.X, t.Y, t.Button, t.State)
			case *sdl.MouseWheelEvent:
				fmt.Printf("[%d ms] MouseWheel\ttype:%d\tid:%d\tx:%d\ty:%d\n",
					t.Timestamp, t.Type, t.Which, t.X, t.Y)
			case *sdl.KeyUpEvent:
				fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n",
					t.Timestamp, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat)
			}
		}
	}

	renderer.Destroy()
	window.Destroy()
}
<commit_msg>Try get some readable text on screen<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

var winTitle string = "No Good"
var winWidth, winHeight int = 800, 600

func main() {
	var window *sdl.Window
	var renderer *sdl.Renderer
	var event sdl.Event
	var running bool

	sdl.Init(sdl.INIT_VIDEO)

	window = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, winWidth, winHeight, sdl.WINDOW_SHOWN)
	if window == nil {
		fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", sdl.GetError())
		os.Exit(1)
	}

	renderer = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
	if renderer == nil {
		fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", sdl.GetError())
		os.Exit(2)
	}

	ttf.Init()
	f, err := ttf.OpenFont("assets/roboto/Roboto-Regular.ttf", 14)
	if err != nil {
		fmt.Println("font error:", err)
	}
	textsurf := f.RenderText_Blended("No Good", sdl.Color{230,230,230,255})
	fmt.Println(textsurf)

	texttex := renderer.CreateTextureFromSurface(textsurf)
	fmt.Println(f.Height())
	textrect := sdl.Rect{100, 100, 56, 20}

	running = true
	for running {
		renderer.Clear()
		renderer.Copy(texttex, nil, &textrect)
		renderer.Present()
		for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			case *sdl.QuitEvent:
				running = false
//			case *sdl.MouseMotionEvent:
//				fmt.Printf("[%d ms] MouseMotion\ttype:%d\tid:%d\tx:%d\ty:%d\txrel:%d\tyrel:%d\n",
//					t.Timestamp, t.Type, t.Which, t.X, t.Y, t.XRel, t.YRel)
			case *sdl.MouseButtonEvent:
				fmt.Printf("[%d ms] MouseButton\ttype:%d\tid:%d\tx:%d\ty:%d\tbutton:%d\tstate:%d\n",
					t.Timestamp, t.Type, t.Which, t.X, t.Y, t.Button, t.State)
			case *sdl.MouseWheelEvent:
				fmt.Printf("[%d ms] MouseWheel\ttype:%d\tid:%d\tx:%d\ty:%d\n",
					t.Timestamp, t.Type, t.Which, t.X, t.Y)
			case *sdl.KeyUpEvent:
				fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n",
					t.Timestamp, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat)
			}
		}
	}

	renderer.Destroy()
	window.Destroy()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"time"

	"gopkg.in/tylerb/graceful.v1"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const (
	githubStatusSquashContext     = "review/squash"
	githubStatusPeerReviewContext = "review/peer"
)

func main() {
	conf := NewConfig()
	githubClient := initGithubClient(conf.AccessToken)
	reposDir, err := ioutil.TempDir("", "github-review-helper")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(reposDir)

	git := NewGit(reposDir)

	mux := http.NewServeMux()
	mux.Handle("/", CreateHandler(conf, git, githubClient.PullRequests, githubClient.Repositories))

	graceful.Run(fmt.Sprintf(":%d", conf.Port), 10*time.Second, mux)
}

func CreateHandler(conf Config, git Git, pullRequests PullRequests, repositories Repositories) Handler {
	return func(w http.ResponseWriter, r *http.Request) Response {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return ErrorResponse{err, http.StatusInternalServerError, "Failed to read the request's body"}
		}
		if errResp := checkAuthentication(body, r, conf.Secret); errResp != nil {
			return errResp
		}
		eventType := r.Header.Get("X-Github-Event")
		switch eventType {
		case "issue_comment":
			return handleIssueComment(body, git, pullRequests, repositories)
		case "pull_request":
			return handlePullRequest(body, pullRequests, repositories)
		}
		return SuccessResponse{"Not an event I understand. Ignoring."}
	}
}

// startsWithPlusOne matches strings that start with either a +1 (not followed by other digits) or a :+1: emoji
var startsWithPlusOne = regexp.MustCompile(`^(:\+1:|\+1($|\D))`)

func handleIssueComment(body []byte, git Git, pullRequests PullRequests, repositories Repositories) Response {
	issueComment, err := parseIssueComment(body)
	if err != nil {
		return ErrorResponse{err, http.StatusInternalServerError, "Failed to parse the request's body"}
	}
	if !issueComment.IsPullRequest {
		return SuccessResponse{"Not a PR. Ignoring."}
	}
	switch {
	case issueComment.Comment == "!squash":
		return handleSquash(issueComment, git, pullRequests, repositories)
	case startsWithPlusOne.MatchString(issueComment.Comment):
		return handlePlusOne(issueComment, pullRequests, repositories)
	}
	return SuccessResponse{"Not a command I understand. Ignoring."}
}

func handlePullRequest(body []byte, pullRequests PullRequests, repositories Repositories) Response {
	pullRequestEvent, err := parsePullRequestEvent(body)
	if err != nil {
		return ErrorResponse{err, http.StatusInternalServerError, "Failed to parse the request's body"}
	}
	return checkForFixupCommits(pullRequestEvent, pullRequests, repositories)
}

func initGithubClient(accessToken string) *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: accessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	return github.NewClient(tc)
}
<commit_msg>Add more descriptive name for PR event handler<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"time"

	"gopkg.in/tylerb/graceful.v1"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const (
	githubStatusSquashContext     = "review/squash"
	githubStatusPeerReviewContext = "review/peer"
)

func main() {
	conf := NewConfig()
	githubClient := initGithubClient(conf.AccessToken)
	reposDir, err := ioutil.TempDir("", "github-review-helper")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(reposDir)

	git := NewGit(reposDir)

	mux := http.NewServeMux()
	mux.Handle("/", CreateHandler(conf, git, githubClient.PullRequests, githubClient.Repositories))

	graceful.Run(fmt.Sprintf(":%d", conf.Port), 10*time.Second, mux)
}

func CreateHandler(conf Config, git Git, pullRequests PullRequests, repositories Repositories) Handler {
	return func(w http.ResponseWriter, r *http.Request) Response {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return ErrorResponse{err, http.StatusInternalServerError, "Failed to read the request's body"}
		}
		if errResp := checkAuthentication(body, r, conf.Secret); errResp != nil {
			return errResp
		}
		eventType := r.Header.Get("X-Github-Event")
		switch eventType {
		case "issue_comment":
			return handleIssueComment(body, git, pullRequests, repositories)
		case "pull_request":
			return handlePullRequestEvent(body, pullRequests, repositories)
		}
		return SuccessResponse{"Not an event I understand. Ignoring."}
	}
}

// startsWithPlusOne matches strings that start with either a +1 (not followed by other digits) or a :+1: emoji
var startsWithPlusOne = regexp.MustCompile(`^(:\+1:|\+1($|\D))`)

func handleIssueComment(body []byte, git Git, pullRequests PullRequests, repositories Repositories) Response {
	issueComment, err := parseIssueComment(body)
	if err != nil {
		return ErrorResponse{err, http.StatusInternalServerError, "Failed to parse the request's body"}
	}
	if !issueComment.IsPullRequest {
		return SuccessResponse{"Not a PR. Ignoring."}
	}
	switch {
	case issueComment.Comment == "!squash":
		return handleSquash(issueComment, git, pullRequests, repositories)
	case startsWithPlusOne.MatchString(issueComment.Comment):
		return handlePlusOne(issueComment, pullRequests, repositories)
	}
	return SuccessResponse{"Not a command I understand. Ignoring."}
}

func handlePullRequestEvent(body []byte, pullRequests PullRequests, repositories Repositories) Response {
	pullRequestEvent, err := parsePullRequestEvent(body)
	if err != nil {
		return ErrorResponse{err, http.StatusInternalServerError, "Failed to parse the request's body"}
	}
	return checkForFixupCommits(pullRequestEvent, pullRequests, repositories)
}

func initGithubClient(accessToken string) *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: accessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	return github.NewClient(tc)
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v1"
	"os"
	"time"
)

func main() {
	// This won't be needed in cli v2
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	cli.HelpFlag.Hidden = true

	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "a next generation load generator"
	app.Version = "0.2.0"
	app.Commands = []cli.Command{
		cli.Command{
			Name:      "run",
			Aliases:   []string{"r"},
			Usage:     "Starts running a load test",
			ArgsUsage: "url|filename",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "vus, u",
					Usage: "virtual users to simulate",
					Value: 10,
				},
				cli.DurationFlag{
					Name:  "duration, d",
					Usage: "test duration, 0 to run until cancelled",
					Value: 10 * time.Second,
				},
				cli.StringFlag{
					Name:  "type, t",
					Usage: "input type, one of: auto, url, js",
					Value: "auto",
				},
			},
			Action: actionRun,
		},
		cli.Command{
			Name:   "status",
			Usage:  "Looks up the status of a running test",
			Action: actionStatus,
		},
		cli.Command{
			Name:      "scale",
			Usage:     "Scales a running test",
			ArgsUsage: "vus",
			Action:    actionScale,
		},
		cli.Command{
			Name:      "abort",
			Usage:     "Aborts a running test",
			ArgsUsage: "",
			Action:    actionAbort,
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "show debug messages",
		},
		cli.StringFlag{
			Name:  "address, a",
			Usage: "address for the HTTP API",
			Value: "127.0.0.1:6565",
		},
	}
	app.Before = func(cc *cli.Context) error {
		if cc.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		return nil
	}
	if err := app.Run(os.Args); err != nil {
		os.Exit(1)
	}
}
<commit_msg>[docs] Better help texts<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v1"
	"os"
	"time"
)

func main() {
	// This won't be needed in cli v2
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	cli.HelpFlag.Hidden = true

	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "a next generation load generator"
	app.Version = "0.2.0"
	app.Commands = []cli.Command{
		cli.Command{
			Name:      "run",
			Usage:     "Starts running a load test",
			ArgsUsage: "url|filename",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "vus, u",
					Usage: "virtual users to simulate",
					Value: 10,
				},
				cli.DurationFlag{
					Name:  "duration, d",
					Usage: "test duration, 0 to run until cancelled",
					Value: 10 * time.Second,
				},
				cli.StringFlag{
					Name:  "type, t",
					Usage: "input type, one of: auto, url, js",
					Value: "auto",
				},
			},
			Action: actionRun,
		},
		cli.Command{
			Name:      "status",
			Usage:     "Looks up the status of a running test",
			ArgsUsage: " ",
			Action:    actionStatus,
		},
		cli.Command{
			Name:      "scale",
			Usage:     "Scales a running test",
			ArgsUsage: "vus",
			Action:    actionScale,
		},
		cli.Command{
			Name:      "abort",
			Usage:     "Aborts a running test",
			ArgsUsage: " ",
			Action:    actionAbort,
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "show debug messages",
		},
		cli.StringFlag{
			Name:  "address, a",
			Usage: "address for the API",
			Value: "127.0.0.1:6565",
		},
	}
	app.Before = func(cc *cli.Context) error {
		if cc.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		return nil
	}
	if err := app.Run(os.Args); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("firefox_ip")
	viper.BindEnv("firefox_port")
	viper.BindEnv("gotator_port")

	if !viper.IsSet("firefox_ip") || !viper.IsSet("firefox_port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both FIREFOX_IP and FIREFOX_PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		fmt.Printf("Content will change immediately.\n\n")
	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	log.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			log.Println("Finished loading programs.")
			break
		}
		if err != nil {
			log.Printf("Error reading line from program file: %s.  Abandoning attempt to read programs.\n", filename)
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			log.Println("Program rejected.  Invalid duration.")
		}

		log.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program) {

	ip := viper.Get("FIREFOX_IP")
	port := viper.GetInt("FIREFOX_PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("It is possible Firefox needs to be started or restarted.")
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	fmt.Printf("Running program for %s:  %s -> RESULT: ", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		fmt.Println("ERROR - URL didn't load as desired.")
	}

	var statusParsed interface{}
	err = json.Unmarshal([]byte(status), &statusParsed)

	m := statusParsed.(map[string]interface{})

	if m["result"] == program.URL {
		fmt.Println("OK")
	} else {
		fmt.Println("ERROR - URL didn't load as desired.")
	}

	select {
	case <-time.After(program.Duration):
		// Do nothing.
		Unpause()
	case <-skip:
		log.Println("Current program skipped")

		return
	}
}

func Pause() {
	mu.Lock()
	pause = true
	mu.Unlock()
}

func Unpause() {
	mu.Lock()
	pause = false
	mu.Unlock()
}

func IsPaused() bool {
	mu.Lock()
	defer mu.Unlock()

	return pause == true
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		for IsPaused() {
			fmt.Println("Paused.")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for IsPaused() {
				// log.Println("Program list is paused.")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		log.Println("Looping back to play program list from beginning")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	log.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	log.Printf("Duration: %s\n", d)

	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Needs validation...

	// Now do something with the program.. play it?

	// Stop normal rotation
	Pause()
	skip <- struct{}{}

	go runProgram(p)
	w.Write([]byte("Program accepted\n"))

}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	Pause()
	log.Println("Pausing from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	Unpause()
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skippingfrom web request")
	Unpause()
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		log.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		Unpause()
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)

var skip = make(chan struct{})
var exitprogram = make(chan struct{})
var pause bool
var mu = &sync.Mutex{}
var version = "0.0.5"

func main() {

	if len(os.Args) > 1 {
		if os.Args[1] == "version" {
			fmt.Println("Gotator version:", version)
			os.Exit(0)
		}
	}
	fmt.Println("Starting gotator: version", version)
	Unpause()

	InitializeConfig()

	go LoadAndRunLoop()
	go readKeyboardLoop()

	if viper.IsSet("apienabled") && viper.Get("apienabled") == true {
		listen_port := ":8080"
		if viper.IsSet("gotator_port") {
			listen_port = ":" + viper.GetString("gotator_port")
		}

		log.Printf("Starting API server on port %s.  Notice:  This allows UNAUTHENTICATED remote control of Firefox. set 'apienabled: false' in config.yaml to disable.\n",
			listen_port)

		r := mux.NewRouter()
		r.HandleFunc("/play", PlayHandler)
		r.HandleFunc("/pause", PauseHandler)
		r.HandleFunc("/resume", ResumeHandler)
		r.HandleFunc("/skip", SkipHandler)

		go log.Fatal(http.ListenAndServe(listen_port, r))
	} else {
		log.Println("notice: rest API not enabled in configuration and will be unavailable.  set 'apienabled: true' in config.yaml if you want to use it.\n")
		// If we aren't doing http.ListenAndServe() we need to block here or else gotator would exit immediately
		<-exitprogram
	}

}
<commit_msg>Added config to enable/disable console input.<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/mux"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig() {

	viper.SetConfigType("yaml")
	viper.SetConfigName("config")

	viper.AddConfigPath("$HOME/.gotator")
	viper.AddConfigPath(".")

	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("firefox_ip")
	viper.BindEnv("firefox_port")
	viper.BindEnv("gotator_port")

	if !viper.IsSet("firefox_ip") || !viper.IsSet("firefox_port") {
		fmt.Fprintln(os.Stderr, "Configuration error.  Both FIREFOX_IP and FIREFOX_PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		skip <- struct{}{}
		fmt.Printf("Content will change immediately.\n\n")
	})

}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	log.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))
	r.LazyQuotes = true

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			log.Println("Finished loading programs.")
			break
		}
		if err != nil {
			log.Printf("Error reading line from program file: %s.  Abandoning attempt to read programs.\n", filename)
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		if err != nil {
			log.Println("Program rejected.  Invalid duration.")
		}

		log.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program) {

	ip := viper.Get("FIREFOX_IP")
	port := viper.GetInt("FIREFOX_PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("It is possible Firefox needs to be started or restarted.")
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	fmt.Printf("Running program for %s:  %s -> RESULT: ", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		fmt.Println("ERROR - URL didn't load as desired.")
	}

	var statusParsed interface{}
	err = json.Unmarshal([]byte(status), &statusParsed)

	m := statusParsed.(map[string]interface{})

	if m["result"] == program.URL {
		fmt.Println("OK")
	} else {
		fmt.Println("ERROR - URL didn't load as desired.")
	}

	select {
	case <-time.After(program.Duration):
		// Do nothing.
		Unpause()
	case <-skip:
		log.Println("Current program skipped")

		return
	}
}

func Pause() {
	mu.Lock()
	pause = true
	mu.Unlock()
}

func Unpause() {
	mu.Lock()
	pause = false
	mu.Unlock()
}

func IsPaused() bool {
	mu.Lock()
	defer mu.Unlock()

	return pause == true
}

func LoadAndRunLoop() {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		for IsPaused() {
			fmt.Println("Paused.")
			time.Sleep(1 * time.Second)
		}

		pl := loadProgramList(filename)

		for _, p := range pl {
			for IsPaused() {
				// log.Println("Program list is paused.")
				time.Sleep(1 * time.Second)
			}
			runProgram(p)
		}

		log.Println("Looping back to play program list from beginning")
	}

}

func PlayHandler(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	var p Program
	p.URL = r.Form.Get("url")
	log.Printf("URL: %s\n", p.URL)

	d := r.Form.Get("duration")
	log.Printf("Duration: %s\n", d)

	var err error
	p.Duration, err = time.ParseDuration(r.Form.Get("duration"))
	if err != nil {
		w.Write([]byte("Program rejected.  Invalid duration.\n"))
		return
	}

	// Needs validation...

	// Now do something with the program.. play it?

	// Stop normal rotation
	Pause()
	skip <- struct{}{}

	go runProgram(p)
	w.Write([]byte("Program accepted\n"))

}

func PauseHandler(w http.ResponseWriter, r *http.Request) {
	Pause()
	log.Println("Pausing from web request")
	w.Write([]byte("Ok, paused.\n"))
}

func ResumeHandler(w http.ResponseWriter, r *http.Request) {
	Unpause()
	log.Println("Unpausing from web request")
	w.Write([]byte("Ok, unpaused.\n"))
}
func SkipHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Skippingfrom web request")
	Unpause()
	skip <- struct{}{}

	w.Write([]byte("Skipping current programming and resume program list runner from web request.\n"))
}

func readKeyboardLoop() {
	for {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		log.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		Unpause()
		skip <- struct{}{}
	}
}

// Control channel to stop running programs immediately (yes, global)

var skip = make(chan struct{})
var exitprogram = make(chan struct{})
var pause bool
var mu = &sync.Mutex{}
var version = "0.0.5"

func main() {

	if len(os.Args) > 1 {
		if os.Args[1] == "version" {
			fmt.Println("Gotator version:", version)
			os.Exit(0)
		}
	}
	fmt.Println("Starting gotator: version", version)
	Unpause()

	InitializeConfig()

	go LoadAndRunLoop()

	if viper.IsSet("interactive") && viper.Get("interactive") == true {
		go readKeyboardLoop()
	}

	if viper.IsSet("apienabled") && viper.Get("apienabled") == true {
		listen_port := ":8080"
		if viper.IsSet("gotator_port") {
			listen_port = ":" + viper.GetString("gotator_port")
		}

		log.Printf("Starting API server on port %s.  Notice:  This allows UNAUTHENTICATED remote control of Firefox. set 'apienabled: false' in config.yaml to disable.\n",
			listen_port)

		r := mux.NewRouter()
		r.HandleFunc("/play", PlayHandler)
		r.HandleFunc("/pause", PauseHandler)
		r.HandleFunc("/resume", ResumeHandler)
		r.HandleFunc("/skip", SkipHandler)

		go log.Fatal(http.ListenAndServe(listen_port, r))
	} else {
		log.Println("notice: rest API not enabled in configuration and will be unavailable.  set 'apienabled: true' in config.yaml if you want to use it.\n")
		// If we aren't doing http.ListenAndServe() we need to block here or else gotator would exit immediately
		<-exitprogram
	}

}
<|endoftext|>