<commit_before>/*
 * Minimalist Object Storage, (C) 2015 Minio, Inc.
 *
 * 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 (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/minio-io/cli"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/server/httpserver"
	"github.com/minio-io/minio/pkg/utils/log"
)

var globalDebugFlag = false

var commands = []cli.Command{
	modeCmd,
}

var modeCommands = []cli.Command{
	memoryCmd,
	donutCmd,
}

var modeCmd = cli.Command{
	Name:        "mode",
	Subcommands: modeCommands,
	Description: "Mode of execution",
}

var memoryCmd = cli.Command{
	Name:        "memory",
	Description: "Limit maximum memory usage to SIZE in [B, KB, MB, GB]",
	Action:      runMemory,
	CustomHelpTemplate: `NAME:
  minio mode {{.Name}} - {{.Description}}

USAGE:
  minio mode {{.Name}} SIZE

EXAMPLES:
  1. Limit maximum memory usage to 64MB
      $ minio mode {{.Name}} 64MB

  2. Limit maximum memory usage to 4GB
      $ minio mode {{.Name}} 4GB
`,
}

var donutCmd = cli.Command{
	Name:        "donut",
	Description: "Specify a path to instantiate donut",
	Action:      runDonut,
	CustomHelpTemplate: `NAME:
  minio mode {{.Name}} - {{.Description}}

USAGE:
  minio mode {{.Name}} PATH

EXAMPLES:
  1. Create a donut volume under "/mnt/backup"
      $ minio mode {{.Name}} /mnt/backup

  2. Create a temporary donut volume under "/tmp"
      $ minio mode {{.Name}} /tmp

  3. Create a donut volume under collection of paths
      $ minio mode {{.Name}} /mnt/backup2014feb /mnt/backup2014feb

`,
}

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain,d",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address,a",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address,w",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert,c",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key,k",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.BoolFlag{
		Name:  "debug",
		Usage: "print debug information",
	},
}

func init() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		log.Fatalf("minio: Unable to obtain user's home directory. \nError: %s\n", err)
	}
}

func runMemory(c *cli.Context) {
	if len(c.Args()) < 1 {
		cli.ShowCommandHelpAndExit(c, "memory", 1) // last argument is exit code
	}
	apiServerConfig := getAPIServerConfig(c)
	maxMemory, err := humanize.ParseBytes(c.Args().First())
	if err != nil {
		log.Fatalf("MaxMemory not a numeric value with reason: %s", err)
	}
	memoryDriver := server.MemoryFactory{
		Config:    apiServerConfig,
		MaxMemory: maxMemory,
	}
	apiServer := memoryDriver.GetStartServerFunc()
	webServer := getWebServerConfigFunc(c)
	servers := []server.StartServerFunc{apiServer, webServer}
	server.StartMinio(servers)
}

func runDonut(c *cli.Context) {
	u, err := user.Current()
	if err != nil {
		log.Fatalln(err)
	}
	if len(c.Args()) < 1 {
		cli.ShowCommandHelpAndExit(c, "donut", 1) // last argument is exit code
	}

	// supporting multiple paths
	var paths []string
	if strings.TrimSpace(c.Args().First()) == "" {
		p := path.Join(u.HomeDir, "minio-storage", "donut")
		paths = append(paths, p)
	} else {
		for _, arg := range c.Args() {
			paths = append(paths, strings.TrimSpace(arg))
		}
	}
	apiServerConfig := getAPIServerConfig(c)
	donutDriver := server.DonutFactory{
		Config: apiServerConfig,
		Paths:  paths,
	}
	apiServer := donutDriver.GetStartServerFunc()
	webServer := getWebServerConfigFunc(c)
	servers := []server.StartServerFunc{apiServer, webServer}
	server.StartMinio(servers)
}

func getAPIServerConfig(c *cli.Context) httpserver.Config {
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatalln("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	return httpserver.Config{
		Domain:   c.GlobalString("domain"),
		Address:  c.GlobalString("api-address"),
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
	}
}

func getWebServerConfigFunc(c *cli.Context) server.StartServerFunc {
	config := httpserver.Config{
		Domain:   c.GlobalString("domain"),
		Address:  c.GlobalString("web-address"),
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
	}
	webDrivers := server.WebFactory{
		Config: config,
	}
	return webDrivers.GetStartServerFunc()
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		humanize.Bytes(memstats.Alloc),
		humanize.Bytes(memstats.TotalAlloc),
		humanize.Bytes(memstats.HeapAlloc),
		humanize.Bytes(memstats.HeapSys))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

// Version is based on MD5SUM of its binary
var Version = mustHashBinarySelf()

// BuildDate - build time
var BuildDate string

func main() {
	// set up iodine
	iodine.SetGlobalState("minio.version", Version)
	iodine.SetGlobalState("minio.starttime", time.Now().Format(time.RFC3339))

	// set up app
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = Version
	app.Compiled, _ = time.Parse(time.RFC3339Nano, BuildDate)
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.Flags = flags
	app.Commands = commands
	app.Before = func(c *cli.Context) error {
		globalDebugFlag = c.GlobalBool("debug")
		if globalDebugFlag {
			app.ExtraInfo = getSystemData()
		} else {
			log.Debug = log.New(ioutil.Discard, "", 0)
		}
		return nil
	}
	app.RunAndExitOnError()
}
<commit_msg>Removing short flags<commit_after>/*
 * Minimalist Object Storage, (C) 2015 Minio, Inc.
 *
 * 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 (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/minio-io/cli"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/server/httpserver"
	"github.com/minio-io/minio/pkg/utils/log"
)

var globalDebugFlag = false

var commands = []cli.Command{
	modeCmd,
}

var modeCommands = []cli.Command{
	memoryCmd,
	donutCmd,
}

var modeCmd = cli.Command{
	Name:        "mode",
	Subcommands: modeCommands,
	Description: "Mode of execution",
}

var memoryCmd = cli.Command{
	Name:        "memory",
	Description: "Limit maximum memory usage to SIZE in [B, KB, MB, GB]",
	Action:      runMemory,
	CustomHelpTemplate: `NAME:
  minio mode {{.Name}} - {{.Description}}

USAGE:
  minio mode {{.Name}} SIZE

EXAMPLES:
  1. Limit maximum memory usage to 64MB
      $ minio mode {{.Name}} 64MB

  2. Limit maximum memory usage to 4GB
      $ minio mode {{.Name}} 4GB
`,
}

var donutCmd = cli.Command{
	Name:        "donut",
	Description: "Specify a path to instantiate donut",
	Action:      runDonut,
	CustomHelpTemplate: `NAME:
  minio mode {{.Name}} - {{.Description}}

USAGE:
  minio mode {{.Name}} PATH

EXAMPLES:
  1. Create a donut volume under "/mnt/backup"
      $ minio mode {{.Name}} /mnt/backup

  2. Create a temporary donut volume under "/tmp"
      $ minio mode {{.Name}} /tmp

  3. Create a donut volume under collection of paths
      $ minio mode {{.Name}} /mnt/backup2014feb /mnt/backup2014feb

`,
}

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.BoolFlag{
		Name:  "debug",
		Usage: "print debug information",
	},
}

func init() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		log.Fatalf("minio: Unable to obtain user's home directory. \nError: %s\n", err)
	}
}

func runMemory(c *cli.Context) {
	if len(c.Args()) < 1 {
		cli.ShowCommandHelpAndExit(c, "memory", 1) // last argument is exit code
	}
	apiServerConfig := getAPIServerConfig(c)
	maxMemory, err := humanize.ParseBytes(c.Args().First())
	if err != nil {
		log.Fatalf("MaxMemory not a numeric value with reason: %s", err)
	}
	memoryDriver := server.MemoryFactory{
		Config:    apiServerConfig,
		MaxMemory: maxMemory,
	}
	apiServer := memoryDriver.GetStartServerFunc()
	webServer := getWebServerConfigFunc(c)
	servers := []server.StartServerFunc{apiServer, webServer}
	server.StartMinio(servers)
}

func runDonut(c *cli.Context) {
	u, err := user.Current()
	if err != nil {
		log.Fatalln(err)
	}
	if len(c.Args()) < 1 {
		cli.ShowCommandHelpAndExit(c, "donut", 1) // last argument is exit code
	}

	// supporting multiple paths
	var paths []string
	if strings.TrimSpace(c.Args().First()) == "" {
		p := path.Join(u.HomeDir, "minio-storage", "donut")
		paths = append(paths, p)
	} else {
		for _, arg := range c.Args() {
			paths = append(paths, strings.TrimSpace(arg))
		}
	}
	apiServerConfig := getAPIServerConfig(c)
	donutDriver := server.DonutFactory{
		Config: apiServerConfig,
		Paths:  paths,
	}
	apiServer := donutDriver.GetStartServerFunc()
	webServer := getWebServerConfigFunc(c)
	servers := []server.StartServerFunc{apiServer, webServer}
	server.StartMinio(servers)
}

func getAPIServerConfig(c *cli.Context) httpserver.Config {
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatalln("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	return httpserver.Config{
		Domain:   c.GlobalString("domain"),
		Address:  c.GlobalString("api-address"),
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
	}
}

func getWebServerConfigFunc(c *cli.Context) server.StartServerFunc {
	config := httpserver.Config{
		Domain:   c.GlobalString("domain"),
		Address:  c.GlobalString("web-address"),
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
	}
	webDrivers := server.WebFactory{
		Config: config,
	}
	return webDrivers.GetStartServerFunc()
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		humanize.Bytes(memstats.Alloc),
		humanize.Bytes(memstats.TotalAlloc),
		humanize.Bytes(memstats.HeapAlloc),
		humanize.Bytes(memstats.HeapSys))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

// Version is based on MD5SUM of its binary
var Version = mustHashBinarySelf()

// BuildDate - build time
var BuildDate string

func main() {
	// set up iodine
	iodine.SetGlobalState("minio.version", Version)
	iodine.SetGlobalState("minio.starttime", time.Now().Format(time.RFC3339))

	// set up app
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = Version
	app.Compiled, _ = time.Parse(time.RFC3339Nano, BuildDate)
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.Flags = flags
	app.Commands = commands
	app.Before = func(c *cli.Context) error {
		globalDebugFlag = c.GlobalBool("debug")
		if globalDebugFlag {
			app.ExtraInfo = getSystemData()
		} else {
			log.Debug = log.New(ioutil.Discard, "", 0)
		}
		return nil
	}
	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>package main

import (
	ps "github.com/mitchellh/go-ps"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"github.com/andlabs/ui"
	"time"
	"net/http"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"syscall"
	"strings"
)

// Telegram and program settings (config.yml)
// Guide: http://sweetohm.net/article/go-yaml-parsers.en.html
type Config struct {
	Token string
	Botid string
	Chatid string
	Message string
	StayAlive bool
	Process string
	TimeBetweenChecksInS int
	KillOnDC bool
	ShutdownOnDC bool
	KillCoherentUI bool
}

// Variables
var STATUS bool = false
var CONNECTION bool = false
var PID int

func main() {

	////  SETTINGS
	//--------------------------------------------------------------------------------------------------------------
	// YAML PARSING
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}
	newex := strings.Replace(ex, "BDO-Watchdog.exe", "config.yml", -1)
	// This is necessary for dynamic builds in Jetbrains Gogland IDE
	//newex = strings.Replace(ex, "Application.exe", "config.yml", -1)

	var config Config
	source, err := ioutil.ReadFile(newex)

	if err != nil {
		// in theory, using yml.Marshal() would be more elegant, but we want to preserve the yaml comments
		// as well as set some default values/hints
		defconf :=
				"## Telegram Bot Settings\r\n" +
				"token: \r\n" +
				"botid: \r\n" +
				"chatid: \r\n" +
				"message: BDO disconnected \r\n" +
				"\r\n" +
				"## Program Settings\r\n" +
				"stayalive: false\r\n" +
				"process: BlackDesert64.exe\r\n" +
				"timebetweenchecksins: 60\r\n" +
				"\r\n" +
				"# These settings require the .exe to be run with admin rights! \r\n" +
				"killondc: true\r\n" +
				"shutdownondc: false\r\n" +
				"killcoherentui: false"
		ioutil.WriteFile("config.yml", []byte(defconf), os.FileMode(int(0777)))
		panic(err)
	}
	err = yaml.Unmarshal(source, &config)
	if err != nil {
		panic(err)
	}

	//// GUI
	//--------------------------------------------------------------------------------------------------------------
	ui := ui.Main(func() {
		window := ui.NewWindow("BDO Watchdog", 300, 80, false)

		label_Process := ui.NewLabel("  Process: " + config.Process)
		label_Status := ui.NewLabel("  Initializing...")
		label_PID := ui.NewLabel("-")
		label_Connection := ui.NewLabel("-")
		label_Update := ui.NewLabel("")

		box := ui.NewVerticalBox()

		tbtn := ui.NewButton("Send Telegram test message")
		tbtn.OnClicked(func(*ui.Button) {
			send_TelegramMessage(config)
		})

		sep := ui.NewHorizontalSeparator()
		pb := ui.NewProgressBar()

		// Append all UI elements to the box container
		box.Append(label_Process, false)
		box.Append(label_Status, false)
		box.Append(label_PID, false)
		box.Append(label_Connection, false)
		box.Append(label_Update, false)
		box.Append(tbtn, false)
		box.Append(sep, false)
		box.Append(pb, true)

		// If this tool requires admin rights add a text label to inform the user
		if config.KillOnDC || config.KillCoherentUI {
			box.Append(ui.NewLabel("Make sure to have started this program with admin rights!"), true)
		}

		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			ui.Quit()
			return true
		})
		window.Show()
		go observer(config, label_Status, label_PID, label_Connection, label_Update, pb)
	})
	if ui != nil {
		panic(ui)
	}
}

//--------------------------------------------------------------------------------------------------------------
// PROCESS
//--------------------------------------------------------------------------------------------------------------
func observer(
	config Config,
	label_Status *ui.Label,
	label_PID *ui.Label,
	label_Connection *ui.Label,
	label_Update *ui.Label,
	pb *ui.ProgressBar) {

	// KILL CoherentUI_Host.exe
	if config.KillCoherentUI {

		// Find process(es)
		chp, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		// Find PID and kill
		for _, v := range chp {
			if v.Executable() == "CoherentUI_Host.exe" {
				proc, err := os.FindProcess(v.Pid())

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				defer proc.Kill()
			}
		}
	}

	// INFINITE MAIN LOOP
	for {
		label_Update.SetText("")

		//// EXIT CONDITION
		//-----------------
		// If the process is running, but no longer connected we trigger the following actions
		if STATUS && !CONNECTION {

			// Use the Telegram API to send a message
			send_TelegramMessage(config)

			// Optional: shutdown the computer if the monitored process is disconnected
			if config.ShutdownOnDC {
				exec.Command("cmd", "/C", "shutdown", "/s").Run()
			}

			// Optional: kill the monitored process if it is disconnected
			// requires elevated rights --> start .exe as administrator
			if config.KillOnDC {

				proc, err := os.FindProcess(PID)

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				defer proc.Kill()

				time.Sleep(5 * time.Second)
			}

			// Optional (YAML file, default: false): keep this program open even if
			// the process is disconnected
			if !config.StayAlive {
				os.Exit(1)
			}
		}

		//// PROCESS
		//----------
		p, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		//// PID
		//------
		for _, v := range p {
			if v.Executable() == config.Process {
				PID = v.Pid()
			}
		}
		if (PID == 0) {
			ui.QueueMain(func () {
				STATUS = false
				label_Status.SetText("  Status: not running")
				label_PID.SetText("  PID: -")
				label_Connection.SetText("  Connection: -" )
			})

			wait(config, label_Update, pb)
			continue
		} else {

			ui.QueueMain(func () {
				STATUS = true
				label_Status.SetText("  Status: running")
				label_PID.SetText("  PID: " + strconv.Itoa(PID))
			})
		}

		//// CONNECTION STATUS
		//--------------------
		// NETSTAT
		// the syscall.SysProcAttr trick found here:
		// https://www.reddit.com/r/golang/comments/2c1g3x/build_golang_app_reverse_shell_to_run_in_windows/
		cmd := exec.Command("cmd.exe", "/C netstat -aon")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		out, err := cmd.Output()
		if err != nil {
			log.Fatal(err)
		}

		// RegEx matching; try to find the PID in the netstat output
		re := regexp.MustCompile(strconv.Itoa(PID))
		byteIndex := re.FindIndex([]byte(out))

		if (len(byteIndex) == 0) {
			ui.QueueMain(func () {
				CONNECTION = false
				label_Connection.SetText("  Connection: Offline" )
			})
		} else {
			// Update labels
			ui.QueueMain(func () {
				CONNECTION = true
				label_Connection.SetText("  Connection: online")
			})
		}

		// Wait x seconds before next iteration
		wait(config, label_Update, pb)
	}
}

// ---------------------------------------------------------------------------------------------------------------------
// A wrapper for time.Sleep() that also updates the UI label and progressbar
func wait(config Config, label_Update *ui.Label, pb *ui.ProgressBar) {
	tstep := config.TimeBetweenChecksInS
	var pbVal int

	if tstep <= 0 {
		tstep = 1
	} // otherwise division by 0
	for i := 0; i <= tstep; i++ {
		pbVal = int(100/float32(tstep) * float32(i))
		if pbVal > 100 {
			pbVal = 100
		}
		pb.SetValue(pbVal)
		label_Update.SetText("  Next update in... " + strconv.Itoa(tstep - i) + " s")
		time.Sleep(1 * time.Second)
	}
	pb.SetValue(0)
}

// ---------------------------------------------------------------------------------------------------------------------
// Send a telegram message using a query URL
func send_TelegramMessage(config Config) {
	// Learn how to setup a telegram bot: https://core.telegram.org/bots
	resp, _ := http.Get("https://api.telegram.org/bot" + config.Botid +
		":" + config.Token +
		"/sendMessage?chat_id=" + config.Chatid +
		"&text=" + config.Message)
	defer resp.Body.Close()
}
<commit_msg>Improve error handling (#4)<commit_after>package main

import (
	ps "github.com/mitchellh/go-ps"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"github.com/andlabs/ui"
	"time"
	"net/http"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"syscall"
	"strings"
)

// Telegram and program settings (config.yml)
// Guide: http://sweetohm.net/article/go-yaml-parsers.en.html
type Config struct {
	Token string
	Botid string
	Chatid string
	Message string
	StayAlive bool
	Process string
	TimeBetweenChecksInS int
	KillOnDC bool
	ShutdownOnDC bool
	KillCoherentUI bool
}

// Variables
var STATUS bool = false
var CONNECTION bool = false
var PID int

func main() {

	////  SETTINGS
	//--------------------------------------------------------------------------------------------------------------
	// YAML PARSING
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}
	newex := strings.Replace(ex, "BDO-Watchdog.exe", "config.yml", -1)
	// This is necessary for dynamic builds in Jetbrains Gogland IDE
	//newex = strings.Replace(ex, "Application.exe", "config.yml", -1)

	var config Config
	source, err := ioutil.ReadFile(newex)

	if err != nil {
		// in theory, using yml.Marshal() would be more elegant, but we want to preserve the yaml comments
		// as well as set some default values/hints
		defconf :=
				"## Telegram Bot Settings\r\n" +
				"token: \r\n" +
				"botid: \r\n" +
				"chatid: \r\n" +
				"message: BDO disconnected \r\n" +
				"\r\n" +
				"## Program Settings\r\n" +
				"stayalive: false\r\n" +
				"process: BlackDesert64.exe\r\n" +
				"timebetweenchecksins: 60\r\n" +
				"\r\n" +
				"# These settings require the .exe to be run with admin rights! \r\n" +
				"killondc: true\r\n" +
				"shutdownondc: false\r\n" +
				"killcoherentui: false"
		ioutil.WriteFile("config.yml", []byte(defconf), os.FileMode(int(0777)))
		panic(err)
	}
	err = yaml.Unmarshal(source, &config)
	if err != nil {
		panic(err)
	}

	//// GUI
	//--------------------------------------------------------------------------------------------------------------
	ui := ui.Main(func() {
		window := ui.NewWindow("BDO Watchdog", 300, 80, false)

		label_Process := ui.NewLabel("  Process: " + config.Process)
		label_Status := ui.NewLabel("  Initializing...")
		label_PID := ui.NewLabel("-")
		label_Connection := ui.NewLabel("-")
		label_Update := ui.NewLabel("")

		box := ui.NewVerticalBox()

		tbtn := ui.NewButton("Send Telegram test message")
		tbtn.OnClicked(func(*ui.Button) {
			send_TelegramMessage(config)
		})

		sep := ui.NewHorizontalSeparator()
		pb := ui.NewProgressBar()

		// Append all UI elements to the box container
		box.Append(label_Process, false)
		box.Append(label_Status, false)
		box.Append(label_PID, false)
		box.Append(label_Connection, false)
		box.Append(label_Update, false)
		box.Append(tbtn, false)
		box.Append(sep, false)
		box.Append(pb, true)

		// If this tool requires admin rights add a text label to inform the user
		if config.KillOnDC || config.KillCoherentUI {
			box.Append(ui.NewLabel("Make sure to have started this program with admin rights!"), true)
		}

		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			ui.Quit()
			return true
		})
		window.Show()
		go observer(config, label_Status, label_PID, label_Connection, label_Update, pb)
	})
	if ui != nil {
		panic(ui)
	}
}

//--------------------------------------------------------------------------------------------------------------
// PROCESS
//--------------------------------------------------------------------------------------------------------------
func observer(
	config Config,
	label_Status *ui.Label,
	label_PID *ui.Label,
	label_Connection *ui.Label,
	label_Update *ui.Label,
	pb *ui.ProgressBar) {

	// KILL CoherentUI_Host.exe
	if config.KillCoherentUI {

		// Find process(es)
		chp, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		// Find PID and kill
		for _, v := range chp {
			if v.Executable() == "CoherentUI_Host.exe" {
				proc, err := os.FindProcess(v.Pid())

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

				defer func() {
					recover() // 1
					return
				}()
				// Kill the process
				proc.Kill()
			}
		}
	}

	// INFINITE MAIN LOOP
	for {
		label_Update.SetText("")

		//// EXIT CONDITION
		//-----------------
		// If the process is running, but no longer connected we trigger the following actions
		if STATUS && !CONNECTION {

			// Use the Telegram API to send a message
			send_TelegramMessage(config)

			// Optional: shutdown the computer if the monitored process is disconnected
			if config.ShutdownOnDC {
				exec.Command("cmd", "/C", "shutdown", "/s").Run()
			}

			// Optional: kill the monitored process if it is disconnected
			// requires elevated rights --> start .exe as administrator
			if config.KillOnDC {

				proc, err := os.FindProcess(PID)

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

				defer func() {
					recover() // 1
					return
				}()
				// Kill the process
				proc.Kill()

				time.Sleep(5 * time.Second)
			}

			// Optional (YAML file, default: false): keep ts program open even if
			// the process is disconnected
			if !config.StayAlive {
				os.Exit(1)
			}
		}

		//// PROCESS
		//----------
		p, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		//// PID
		//------
		for _, v := range p {
			if v.Executable() == config.Process {
				PID = v.Pid()
			}
		}
		if (PID == 0) {
			ui.QueueMain(func () {
				STATUS = false
				label_Status.SetText("  Status: not running")
				label_PID.SetText("  PID: -")
				label_Connection.SetText("  Connection: -" )
			})

			wait(config, label_Update, pb)
			continue
		} else {

			ui.QueueMain(func () {
				STATUS = true
				label_Status.SetText("  Status: running")
				label_PID.SetText("  PID: " + strconv.Itoa(PID))
			})
		}

		//// CONNECTION STATUS
		//--------------------
		// NETSTAT
		// the syscall.SysProcAttr trick found here:
		// https://www.reddit.com/r/golang/comments/2c1g3x/build_golang_app_reverse_shell_to_run_in_windows/
		cmd := exec.Command("cmd.exe", "/C netstat -aon")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		out, err := cmd.Output()
		if err != nil {
			log.Fatal(err)
		}

		// RegEx matching; try to find the PID in the netstat output
		re := regexp.MustCompile(strconv.Itoa(PID))
		byteIndex := re.FindIndex([]byte(out))

		if (len(byteIndex) == 0) {
			ui.QueueMain(func () {
				CONNECTION = false
				label_Connection.SetText("  Connection: Offline" )
			})
		} else {
			// Update labels
			ui.QueueMain(func () {
				CONNECTION = true
				label_Connection.SetText("  Connection: online")
			})
		}

		// Wait x seconds before next iteration
		wait(config, label_Update, pb)
	}
}

// ---------------------------------------------------------------------------------------------------------------------
// A wrapper for time.Sleep() that also updates the UI label and progressbar
func wait(config Config, label_Update *ui.Label, pb *ui.ProgressBar) {
	tstep := config.TimeBetweenChecksInS
	var pbVal int

	if tstep <= 0 {
		tstep = 1
	} // otherwise division by 0
	for i := 0; i <= tstep; i++ {
		pbVal = int(100/float32(tstep) * float32(i))
		if pbVal > 100 {
			pbVal = 100
		}
		pb.SetValue(pbVal)
		label_Update.SetText("  Next update in... " + strconv.Itoa(tstep - i) + " s")
		time.Sleep(1 * time.Second)
	}
	pb.SetValue(0)
}

// ---------------------------------------------------------------------------------------------------------------------
// Send a telegram message using a query URL
func send_TelegramMessage(config Config) {
	// Learn how to setup a telegram bot: https://core.telegram.org/bots
	resp, _ := http.Get("https://api.telegram.org/bot" + config.Botid +
		":" + config.Token +
		"/sendMessage?chat_id=" + config.Chatid +
		"&text=" + config.Message)
	defer resp.Body.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	qp "github.com/Ariemeth/quantum-pulse"
)

func main() {

	engine := new(qp.Engine)

	engine.Init()

	sceneId, err := engine.LoadSceneFile("scene1.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	engine.LoadScene(sceneId)

	engine.Run()
}
<commit_msg>Updated variable name based on go lint<commit_after>package main

import (
	"fmt"
	qp "github.com/Ariemeth/quantum-pulse"
)

func main() {

	engine := new(qp.Engine)

	engine.Init()

	sceneID, err := engine.LoadSceneFile("scene1.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	engine.LoadScene(sceneID)

	engine.Run()
}
<|endoftext|>


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

/*
Gomir mirrors git repositories

Usage:

	gomir add <fetchURL> <pushURL> [<localDest>]    # Add a repo to mirror
	gomir fetch                                     # Fetch latest updates from source repo
	gomir push                                      # Push updates to mirror destination

TODO:
	- Add support for concurrency during fetch/push
	- Add support for controlling concurrency during fetch/push
	- Add documentation describing where repos are stored
	- Add documentation describing what to do if something goes wrong during a fetch/push
	- Add documentation for a normal synchronization workflow
	- Add usage (gomir -h)
*/
package main

import (
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/pkg/errors"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Not enough arguments")
		os.Exit(1)
	}

	action := strings.ToLower(os.Args[1])
	switch action {
	case "add":
		var fetchURL string
		var pushURL string
		var localDest string
		switch len(os.Args) {
		case 4:
			fetchURL = os.Args[2]
			pushURL = os.Args[3]
		case 5:
			fetchURL = os.Args[2]
			pushURL = os.Args[3]
			localDest = os.Args[4]
		default:
			fmt.Println("Wrong number of arguments")
			os.Exit(1)
		}

		add(fetchURL, pushURL, localDest)
	case "fetch":
		fetch()
	case "push":
		push()
	default:
		fmt.Printf("Unrecognized action '%v'\n", action)
		os.Exit(1)
	}
}

func add(fetchURL, pushURL, localDest string) {
	// Try to generate a localDest
	if localDest == "" {
		u, err := url.Parse(fetchURL)
		if err != nil {
			fmt.Println("Could not generate a localDest")
			os.Exit(1)
		}
		localDest = fmt.Sprintf("%v%v", u.Host, u.Path)
	}

	localDest = ensureGitExt(localDest)

	// Clone
	if err := gitCloneMirror(fetchURL, localDest); err != nil {
		fmt.Println("Error cloning repository")
		os.Exit(1)
	}

	// Set Push URL
	if err := gitSetOriginPushURL(localDest, pushURL); err != nil {
		fmt.Println("Error setting push URL")
		os.Exit(1)
	}
}

func fetch() {
	for _, gitDir := range findGitDirs() {
		if fetchSingle(gitDir) {
			color.Green("[✔] %v", gitDir)
		} else {
			color.Red("[X] %v", gitDir)
		}
	}
}

func fetchSingle(gitDir string) bool {
	logFile, logger, err := getLog(gitDir, "FETCH: ")
	if err != nil {
		color.Red("Error opening log file for %v", gitDir)
		return false
	}
	defer logFile.Close()

	logger.Println("Start")
	success := gitFetchPrune(gitDir, logFile) == nil
	logger.Printf("Done, success:%v", success)
	return success
}

func push() {
	for _, gitDir := range findGitDirs() {
		if pushSingle(gitDir) {
			color.Green("[✔] %v", gitDir)
		} else {
			color.Red("[X] %v", gitDir)
		}
	}
}

func pushSingle(gitDir string) bool {
	logFile, logger, err := getLog(gitDir, "PUSH: ")
	if err != nil {
		color.Red("Error opening log file for %v", gitDir)
		return false
	}
	defer logFile.Close()

	logger.Println("Start")

	// Where are we pushing to?
	pushURL, err := gitGetOriginPushURL(gitDir)
	if err != nil {
		logger.Printf("Error retrieving push URL: %+v", err)
		return false
	}

	// If pushing using file protocol and destination repository does
	// not alread exist, initialize it
	isFileProtocol := pushURL.Scheme == "" || strings.ToLower(pushURL.Scheme) == "file"
	if isFileProtocol {
		_, err := os.Stat(pushURL.Path)
		if err != nil && os.IsNotExist(err) {
			if err := gitInitBareRepo(pushURL.Path, logFile); err != nil {
				logger.Printf("Error initializing bare git repository: %+v", err)
				return false
			}
		}
	}

	// Push
	if err := gitPushMirror(gitDir, logFile); err != nil {
		logger.Printf("Error pushing: %+v", err)
		return false
	}

	// Update server info
	if isFileProtocol {
		if err := gitUpdateServerInfo(pushURL.Path, logFile); err != nil {
			logger.Printf("Error updating server info: %+v", err)
			return false
		}
	}

	logger.Println("Done")
	return true
}

func findGitDirs() []string {
	gitDirs := []string{}
	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
		if err == nil &&
			info.IsDir() &&
			strings.ToLower(filepath.Base(path)) != ".git" &&
			strings.ToLower(filepath.Ext(path)) == ".git" {
			gitDirs = append(gitDirs, path)
			return filepath.SkipDir
		}
		return nil
	})
	return gitDirs
}

func ensureGitExt(str string) string {
	if strings.ToLower(filepath.Ext(str)) != ".git" {
		return fmt.Sprintf("%v.git", str)
	}
	return str
}

func getLog(gitDir, prefix string) (io.WriteCloser, *log.Logger, error) {
	logPath := fmt.Sprintf("%v.log", gitDir)
	logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Error opening log file")
	}

	// TODO: What flags should I set for the logger?
	logger := log.New(logFile, prefix, log.Lshortfile)
	return logFile, logger, nil
}
<commit_msg>Add date and time to logs<commit_after>// Copyright 2017 Brian Lachniet. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

/*
Gomir mirrors git repositories

Usage:

	gomir add <fetchURL> <pushURL> [<localDest>]    # Add a repo to mirror
	gomir fetch                                     # Fetch latest updates from source repo
	gomir push                                      # Push updates to mirror destination

TODO:
	- Add support for concurrency during fetch/push
	- Add support for controlling concurrency during fetch/push
	- Add documentation describing where repos are stored
	- Add documentation describing what to do if something goes wrong during a fetch/push
	- Add documentation for a normal synchronization workflow
	- Add usage (gomir -h)
*/
package main

import (
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/pkg/errors"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Not enough arguments")
		os.Exit(1)
	}

	action := strings.ToLower(os.Args[1])
	switch action {
	case "add":
		var fetchURL string
		var pushURL string
		var localDest string
		switch len(os.Args) {
		case 4:
			fetchURL = os.Args[2]
			pushURL = os.Args[3]
		case 5:
			fetchURL = os.Args[2]
			pushURL = os.Args[3]
			localDest = os.Args[4]
		default:
			fmt.Println("Wrong number of arguments")
			os.Exit(1)
		}

		add(fetchURL, pushURL, localDest)
	case "fetch":
		fetch()
	case "push":
		push()
	default:
		fmt.Printf("Unrecognized action '%v'\n", action)
		os.Exit(1)
	}
}

func add(fetchURL, pushURL, localDest string) {
	// Try to generate a localDest
	if localDest == "" {
		u, err := url.Parse(fetchURL)
		if err != nil {
			fmt.Println("Could not generate a localDest")
			os.Exit(1)
		}
		localDest = fmt.Sprintf("%v%v", u.Host, u.Path)
	}

	localDest = ensureGitExt(localDest)

	// Clone
	if err := gitCloneMirror(fetchURL, localDest); err != nil {
		fmt.Println("Error cloning repository")
		os.Exit(1)
	}

	// Set Push URL
	if err := gitSetOriginPushURL(localDest, pushURL); err != nil {
		fmt.Println("Error setting push URL")
		os.Exit(1)
	}
}

func fetch() {
	for _, gitDir := range findGitDirs() {
		if fetchSingle(gitDir) {
			color.Green("[✔] %v", gitDir)
		} else {
			color.Red("[X] %v", gitDir)
		}
	}
}

func fetchSingle(gitDir string) bool {
	logFile, logger, err := getLog(gitDir, "FETCH: ")
	if err != nil {
		color.Red("Error opening log file for %v", gitDir)
		return false
	}
	defer logFile.Close()

	logger.Println("Start")
	success := gitFetchPrune(gitDir, logFile) == nil
	logger.Printf("Done, success:%v", success)
	return success
}

func push() {
	for _, gitDir := range findGitDirs() {
		if pushSingle(gitDir) {
			color.Green("[✔] %v", gitDir)
		} else {
			color.Red("[X] %v", gitDir)
		}
	}
}

func pushSingle(gitDir string) bool {
	logFile, logger, err := getLog(gitDir, "PUSH: ")
	if err != nil {
		color.Red("Error opening log file for %v", gitDir)
		return false
	}
	defer logFile.Close()

	logger.Println("Start")

	// Where are we pushing to?
	pushURL, err := gitGetOriginPushURL(gitDir)
	if err != nil {
		logger.Printf("Error retrieving push URL: %+v", err)
		return false
	}

	// If pushing using file protocol and destination repository does
	// not alread exist, initialize it
	isFileProtocol := pushURL.Scheme == "" || strings.ToLower(pushURL.Scheme) == "file"
	if isFileProtocol {
		_, err := os.Stat(pushURL.Path)
		if err != nil && os.IsNotExist(err) {
			if err := gitInitBareRepo(pushURL.Path, logFile); err != nil {
				logger.Printf("Error initializing bare git repository: %+v", err)
				return false
			}
		}
	}

	// Push
	if err := gitPushMirror(gitDir, logFile); err != nil {
		logger.Printf("Error pushing: %+v", err)
		return false
	}

	// Update server info
	if isFileProtocol {
		if err := gitUpdateServerInfo(pushURL.Path, logFile); err != nil {
			logger.Printf("Error updating server info: %+v", err)
			return false
		}
	}

	logger.Println("Done")
	return true
}

func findGitDirs() []string {
	gitDirs := []string{}
	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
		if err == nil &&
			info.IsDir() &&
			strings.ToLower(filepath.Base(path)) != ".git" &&
			strings.ToLower(filepath.Ext(path)) == ".git" {
			gitDirs = append(gitDirs, path)
			return filepath.SkipDir
		}
		return nil
	})
	return gitDirs
}

func ensureGitExt(str string) string {
	if strings.ToLower(filepath.Ext(str)) != ".git" {
		return fmt.Sprintf("%v.git", str)
	}
	return str
}

func getLog(gitDir, prefix string) (io.WriteCloser, *log.Logger, error) {
	logPath := fmt.Sprintf("%v.log", gitDir)
	logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Error opening log file")
	}

	// TODO: What flags should I set for the logger?
	logger := log.New(logFile, prefix, log.Ldate|log.Ltime|log.Lshortfile|log.LUTC)
	return logFile, logger, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"

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

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	verbose    = false
	buildError error
)

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.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.StringFlag{
			Name:  "exclude,e",
			Value: "",
			Usage: "Path to exclude files from",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
		cli.BoolFlag{
			Name:  "verbose,v",
			Usage: "Verbose logging",
		},
	}
	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) {
	port := c.GlobalInt("port")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	verbose = c.GlobalBool("verbose")

	// Bootstrap the environment
	envy.Bootstrap()

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

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

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

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

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

	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.GlobalString("exclude"), 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) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Println("ERROR! Build failed.")
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Println("Build Successful")
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludePath string, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" {
				return filepath.SkipDir
			}

			// ignore excluded paths
			if filepath.Base(path) == excludePath {
				if verbose {
					logger.Println("Excluded ", path)
				}
				return filepath.SkipDir
			}

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

			if 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>Take out short flag for verbose<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/envy/lib"
	"github.com/codegangsta/gin/lib"

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

var (
	startTime  = time.Now()
	logger     = log.New(os.Stdout, "[gin] ", 0)
	immediate  = false
	verbose    = false
	buildError error
)

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.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "port for the proxy server",
		},
		cli.IntFlag{
			Name:  "appPort,a",
			Value: 3001,
			Usage: "port for the Go web server",
		},
		cli.StringFlag{
			Name:  "bin,b",
			Value: "gin-bin",
			Usage: "name of generated binary file",
		},
		cli.StringFlag{
			Name:  "path,t",
			Value: ".",
			Usage: "Path to watch files from",
		},
		cli.StringFlag{
			Name:  "exclude,e",
			Value: "",
			Usage: "Path to exclude files from",
		},
		cli.BoolFlag{
			Name:  "immediate,i",
			Usage: "run the server immediately after it's built",
		},
		cli.BoolFlag{
			Name:  "godep,g",
			Usage: "use godep when building",
		},
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Verbose logging",
		},
	}
	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) {
	port := c.GlobalInt("port")
	appPort := strconv.Itoa(c.GlobalInt("appPort"))
	immediate = c.GlobalBool("immediate")
	verbose = c.GlobalBool("verbose")

	// Bootstrap the environment
	envy.Bootstrap()

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

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

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

	config := &gin.Config{
		Port:    port,
		ProxyTo: "http://localhost:" + appPort,
	}

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

	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.GlobalString("exclude"), 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) {
	err := builder.Build()
	if err != nil {
		buildError = err
		logger.Println("ERROR! Build failed.")
		fmt.Println(builder.Errors())
	} else {
		// print success only if there were errors before
		if buildError != nil {
			logger.Println("Build Successful")
		}
		buildError = nil
		if immediate {
			runner.Run()
		}
	}

	time.Sleep(100 * time.Millisecond)
}

type scanCallback func(path string)

func scanChanges(watchPath string, excludePath string, cb scanCallback) {
	for {
		filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
			if path == ".git" {
				return filepath.SkipDir
			}

			// ignore excluded paths
			if filepath.Base(path) == excludePath {
				if verbose {
					logger.Println("Excluded ", path)
				}
				return filepath.SkipDir
			}

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

			if 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 "github.com/howeyc/fsnotify"
import "log"
import "flag"
import "fmt"
import "path/filepath"
import "os"
import "os/exec"
import "regexp"

func Subfolders(path string) (paths []string) {
	filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			name := info.Name()
			// skip folders that begin with a dot
			hidden := filepath.HasPrefix(name, ".") && name != "." && name != ".."
			if hidden {
				return filepath.SkipDir
			} else {
				paths = append(paths, newPath)
			}
		}
		return nil
	})
	return paths
}

func main() {
	var helpFlag = flag.Bool("h", false, "help")
	var buildFlag = flag.Bool("b", true, "build, default behavior")
	var testFlag = flag.Bool("t", false, "test")
	var installFlag = flag.Bool("i", false, "install")
	var xFlag = flag.Bool("x", false, "show verbose command")

	flag.Parse()
	args := flag.Args()

	if *helpFlag {
		fmt.Println("Usage: gomon [options] [dir] [-- command]")
		fmt.Println("   -b build")
		fmt.Println("   -t test")
		fmt.Println("   -i install")
		fmt.Println("   -x show verbose command")
		fmt.Println("   -h help")
		os.Exit(0)
	}

	var dirs = []string{}
	var cmds = []string{}

	var takeDir = true
	for _, a := range args {
		if a == "--" {
			takeDir = false
			continue
		}
		if takeDir {
			dirs = append(dirs, a)
		} else {
			cmds = append(cmds, a)
		}
	}

	if len(cmds) == 0 {
		if *testFlag {
			cmds = []string{"go", "test"}
		} else if *buildFlag {
			cmds = []string{"go", "build"}
		} else if *installFlag {
			cmds = []string{"go", "install"}
		}
		if *xFlag && len(cmds) > 0 {
			cmds = append(cmds, "-x")
		}
	}

	if len(cmds) == 0 {
		fmt.Println("No command specified")
		os.Exit(2)
	}

	if len(dirs) == 0 {
		var cwd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		dirs = []string{cwd}
	}

	fmt.Println("Watching", dirs, "for", cmds)

	watcher, err := fsnotify.NewWatcher()

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

	for _, dir := range dirs {
		subfolders := Subfolders(dir)
		for _, f := range subfolders {
			err = watcher.WatchFlags(f, fsnotify.FSN_CREATE|fsnotify.FSN_MODIFY)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	var cmd *exec.Cmd

	for {
		select {
		case e := <-watcher.Event:
			matched, err := regexp.MatchString("\\.go$", e.Name)
			if err != nil {
				log.Println(err)
			}

			if !matched {
				continue
			}

			log.Println("Event:", e)

			if cmd != nil {
				err := cmd.Process.Kill()
				if err != nil {
					log.Println(err)
				}
			}
			cmd = exec.Command(cmds[0], cmds[1:]...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Start()
		case err := <-watcher.Error:
			log.Println("Error:", err)
		}
	}

	watcher.Close()
}
<commit_msg>Add FileExists method to check argument<commit_after>package main

import "github.com/howeyc/fsnotify"
import "log"
import "flag"
import "fmt"
import "path/filepath"
import "os"
import "os/exec"
import "regexp"

func FileExists(path string) (bool, error) {
	file, err := os.Open(path) // For read access.
	if err != nil {
		return false, err
	}
	_, err = file.Stat()
	if err != nil {
		return false, err
	}
	return true, nil
}

func Subfolders(path string) (paths []string) {
	filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			name := info.Name()
			// skip folders that begin with a dot
			hidden := filepath.HasPrefix(name, ".") && name != "." && name != ".."
			if hidden {
				return filepath.SkipDir
			} else {
				paths = append(paths, newPath)
			}
		}
		return nil
	})
	return paths
}

func main() {
	var helpFlag = flag.Bool("h", false, "help")
	var buildFlag = flag.Bool("b", true, "build, default behavior")
	var testFlag = flag.Bool("t", false, "test")
	var installFlag = flag.Bool("i", false, "install")
	var xFlag = flag.Bool("x", false, "show verbose command")

	flag.Parse()
	args := flag.Args()

	if *helpFlag {
		fmt.Println("Usage: gomon [options] [dir] [-- command]")
		fmt.Println("   -b build")
		fmt.Println("   -t test")
		fmt.Println("   -i install")
		fmt.Println("   -x show verbose command")
		fmt.Println("   -h help")
		os.Exit(0)
	}

	var dirs = []string{}
	var cmds = []string{}

	var takeDir = true
	for _, a := range args {
		var exists, _ = FileExists(a)
		if a == "--" || !exists {
			takeDir = false
			continue
		}
		if takeDir {
			dirs = append(dirs, a)
		} else {
			cmds = append(cmds, a)
		}
	}

	if len(cmds) == 0 {
		if *testFlag {
			cmds = []string{"go", "test"}
		} else if *buildFlag {
			cmds = []string{"go", "build"}
		} else if *installFlag {
			cmds = []string{"go", "install"}
		}
		if *xFlag && len(cmds) > 0 {
			cmds = append(cmds, "-x")
		}
	}

	if len(cmds) == 0 {
		fmt.Println("No command specified")
		os.Exit(2)
	}

	if len(dirs) == 0 {
		var cwd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		dirs = []string{cwd}
	}

	fmt.Println("Watching", dirs, "for", cmds)

	watcher, err := fsnotify.NewWatcher()

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

	for _, dir := range dirs {
		subfolders := Subfolders(dir)
		for _, f := range subfolders {
			err = watcher.WatchFlags(f, fsnotify.FSN_CREATE|fsnotify.FSN_MODIFY)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	var cmd *exec.Cmd

	for {
		select {
		case e := <-watcher.Event:
			matched, err := regexp.MatchString("\\.go$", e.Name)
			if err != nil {
				log.Println(err)
			}

			if !matched {
				continue
			}

			log.Println("Event:", e)

			if cmd != nil {
				err := cmd.Process.Kill()
				if err != nil {
					log.Println(err)
				}
			}
			cmd = exec.Command(cmds[0], cmds[1:]...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Start()
		case err := <-watcher.Error:
			log.Println("Error:", err)
		}
	}

	watcher.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"github.com/codegangsta/cli"
	"fmt"
	"errors"
)

func main() {
	app := cli.NewApp()
	app.Name = "fetch"
	app.Usage = "download a file or folder from a specific release of a public or private GitHub repo subject to the Semantic Versioning constraints you impose"
	app.UsageText = "fetch [global options] [<repo-download-filter>] <local-download-path>\n   (See https://github.com/gruntwork-io/fetch for additional docs.)"
	app.Version = getVersion(Version, VersionPrerelease)

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "repo",
			Usage: "Required. Fully qualified URL of the GitHub repo.",
		},
		cli.StringFlag{
			Name: "tag",
			Usage: "The specific git tag to download, expressed with Version Constraint Operators.\n\tIf left blank, fetch will download the latest git tag.\n\tSee https://github.com/gruntwork-io/fetch#version-constraint-operators for examples.",
		},
		cli.StringFlag{
			Name: "github-oauth-token",
			Usage: "Required for private repos. A GitHub Personal Access Token (https://help.github.com/articles/creating-an-access-token-for-command-line-use/).",
		},
	}

	app.Action = runFetchWrapper

	// Run the definition of App.Action
	app.Run(os.Args)
}

// We just want to call runFetch(), but app.Action won't permit us to return an error, so call a wrapper function instead.
func runFetchWrapper (c *cli.Context) {
	err := runFetch(c)
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %s", err)
		os.Exit(1)
	}
}

// Run the fetch program
func runFetch (c *cli.Context) error {

	// Validate required flags
	if c.String("repo") == "" {
		return fmt.Errorf("The --repo flag is required. Run \"fetch --help\" for full usage info.")
	}

	repoUrl := c.String("repo")
	tagConstraint := c.String("tag")
	githubToken := c.String("github-oauth-token")

	// Validate args
	if len(c.Args()) == 0 || len(c.Args()) > 2 {
		return fmt.Errorf("Missing required arguments. Run \"fetch --help\" for full usage info.")
	}

	var repoDownloadFilter string
	var localFileDst string

	// Assume the <repo-download-filter> arg is missing, so set a default
	if len(c.Args()) == 1 {
		repoDownloadFilter = "/"
		localFileDst = c.Args()[0]
	}

	// We have two args so load both
	if len(c.Args()) == 2 {
		repoDownloadFilter = c.Args()[0]
		localFileDst = c.Args()[1]
	}

	// Get the tags for the given repo
	tags, err := FetchTags(repoUrl, githubToken)
	if err != nil {
		if err.errorCode == INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED {
			return errors.New(getErrorMessage(INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED, err.details))
		} else if err.errorCode == REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED {
			return errors.New(getErrorMessage(REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED, err.details))
		} else {
			return fmt.Errorf("Error occurred while getting tags from GitHub repo: %s", err)
		}
	}

	// Find the specific release that matches the latest version constraint
	latestTag, err := getLatestAcceptableTag(tagConstraint, tags)
	if err != nil {
		if err.errorCode == INVALID_TAG_CONSTRAINT_EXPRESSION {
			return errors.New(getErrorMessage(INVALID_TAG_CONSTRAINT_EXPRESSION, err.details))
		} else {
			return fmt.Errorf("Error occurred while computing latest tag that satisfies version contraint expression: %s", err)
		}
	}

	// Download that release as a .zip file
	fmt.Printf("Downloading tag \"%s\" of GitHub repo %s\n", latestTag, repoUrl)

	repo, goErr := ParseUrlIntoGitHubRepo(repoUrl)
	if goErr != nil {
		return fmt.Errorf("Error occurred while parsing GitHub URL: %s", err)
	}

	gitHubCommit := GitHubCommit{
		repo: repo,
		gitTag: latestTag,
	}

	localZipFilePath, err := downloadGithubZipFile(gitHubCommit, githubToken)
	if err != nil {
		return fmt.Errorf("Error occurred while downloading zip file from GitHub repo: %s", err)
	}
	defer cleanupZipFile(localZipFilePath)

	// Unzip and move the files we need to our destination
	fmt.Printf("Unzipping...\n")
	if goErr = extractFiles(localZipFilePath, repoDownloadFilter, localFileDst); goErr != nil {
		return fmt.Errorf("Error occurred while extracting files from GitHub zip file: %s", goErr)
	}

	fmt.Println("Download and file extraction complete.")
	return nil
}

// Delete the given zip file.
func cleanupZipFile(localZipFilePath string) error {
	err := os.Remove(localZipFilePath)
	if err != nil {
		return fmt.Errorf("Failed to delete local zip file at %s", localZipFilePath)
	}
	fmt.Println("Deleted zip file.")

	return nil
}

// getVersion returns a properly formatted version string
func getVersion(version string, versionPreRelease string) string {
	if versionPreRelease != "" {
		return version
	} else {
		return fmt.Sprintf("%s-%s", version, versionPreRelease)
	}
}

func getErrorMessage(errorCode int, errorDetails string) string {
	switch errorCode {
	case INVALID_TAG_CONSTRAINT_EXPRESSION:
		return fmt.Sprintf(`
The --tag value you entered is not a valid constraint expression.
See https://github.com/gruntwork-io/fetch#version-constraint-operators for examples.

Underlying error message:
%s
`, errorDetails)
	case INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED:
		return fmt.Sprintf(`
Received an HTTP 401 Response when attempting to query the repo for its tags.

This means that either your GitHub oAuth Token is invalid, or that the token is valid but is being used to request access
to either a public repo or a private repo to which you don't have access.

Underlying error message:
%s
`, errorDetails)
	case REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED:
		return fmt.Sprintf(`
Received an HTTP 404 Response when attempting to query the repo for its tags.

This means that either no GitHub repo exists at the URL provided, or that you don't have permission to access it.
If the URL is correct, you may need to pass in a --github-oauth-token.

Underlying error message:
%s
`, errorDetails)
	}

	return ""
}<commit_msg>Errors should return with a newline at the end.<commit_after>package main

import (
	"os"
	"github.com/codegangsta/cli"
	"fmt"
	"errors"
)

func main() {
	app := cli.NewApp()
	app.Name = "fetch"
	app.Usage = "download a file or folder from a specific release of a public or private GitHub repo subject to the Semantic Versioning constraints you impose"
	app.UsageText = "fetch [global options] [<repo-download-filter>] <local-download-path>\n   (See https://github.com/gruntwork-io/fetch for additional docs.)"
	app.Version = getVersion(Version, VersionPrerelease)

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "repo",
			Usage: "Required. Fully qualified URL of the GitHub repo.",
		},
		cli.StringFlag{
			Name: "tag",
			Usage: "The specific git tag to download, expressed with Version Constraint Operators.\n\tIf left blank, fetch will download the latest git tag.\n\tSee https://github.com/gruntwork-io/fetch#version-constraint-operators for examples.",
		},
		cli.StringFlag{
			Name: "github-oauth-token",
			Usage: "Required for private repos. A GitHub Personal Access Token (https://help.github.com/articles/creating-an-access-token-for-command-line-use/).",
		},
	}

	app.Action = runFetchWrapper

	// Run the definition of App.Action
	app.Run(os.Args)
}

// We just want to call runFetch(), but app.Action won't permit us to return an error, so call a wrapper function instead.
func runFetchWrapper (c *cli.Context) {
	err := runFetch(c)
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
		os.Exit(1)
	}
}

// Run the fetch program
func runFetch (c *cli.Context) error {

	// Validate required flags
	if c.String("repo") == "" {
		return fmt.Errorf("The --repo flag is required. Run \"fetch --help\" for full usage info.")
	}

	repoUrl := c.String("repo")
	tagConstraint := c.String("tag")
	githubToken := c.String("github-oauth-token")

	// Validate args
	if len(c.Args()) == 0 || len(c.Args()) > 2 {
		return fmt.Errorf("Missing required arguments. Run \"fetch --help\" for full usage info.")
	}

	var repoDownloadFilter string
	var localFileDst string

	// Assume the <repo-download-filter> arg is missing, so set a default
	if len(c.Args()) == 1 {
		repoDownloadFilter = "/"
		localFileDst = c.Args()[0]
	}

	// We have two args so load both
	if len(c.Args()) == 2 {
		repoDownloadFilter = c.Args()[0]
		localFileDst = c.Args()[1]
	}

	// Get the tags for the given repo
	tags, err := FetchTags(repoUrl, githubToken)
	if err != nil {
		if err.errorCode == INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED {
			return errors.New(getErrorMessage(INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED, err.details))
		} else if err.errorCode == REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED {
			return errors.New(getErrorMessage(REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED, err.details))
		} else {
			return fmt.Errorf("Error occurred while getting tags from GitHub repo: %s", err)
		}
	}

	// Find the specific release that matches the latest version constraint
	latestTag, err := getLatestAcceptableTag(tagConstraint, tags)
	if err != nil {
		if err.errorCode == INVALID_TAG_CONSTRAINT_EXPRESSION {
			return errors.New(getErrorMessage(INVALID_TAG_CONSTRAINT_EXPRESSION, err.details))
		} else {
			return fmt.Errorf("Error occurred while computing latest tag that satisfies version contraint expression: %s", err)
		}
	}

	// Download that release as a .zip file
	fmt.Printf("Downloading tag \"%s\" of GitHub repo %s\n", latestTag, repoUrl)

	repo, goErr := ParseUrlIntoGitHubRepo(repoUrl)
	if goErr != nil {
		return fmt.Errorf("Error occurred while parsing GitHub URL: %s", err)
	}

	gitHubCommit := GitHubCommit{
		repo: repo,
		gitTag: latestTag,
	}

	localZipFilePath, err := downloadGithubZipFile(gitHubCommit, githubToken)
	if err != nil {
		return fmt.Errorf("Error occurred while downloading zip file from GitHub repo: %s", err)
	}
	defer cleanupZipFile(localZipFilePath)

	// Unzip and move the files we need to our destination
	fmt.Printf("Unzipping...\n")
	if goErr = extractFiles(localZipFilePath, repoDownloadFilter, localFileDst); goErr != nil {
		return fmt.Errorf("Error occurred while extracting files from GitHub zip file: %s", goErr)
	}

	fmt.Println("Download and file extraction complete.")
	return nil
}

// Delete the given zip file.
func cleanupZipFile(localZipFilePath string) error {
	err := os.Remove(localZipFilePath)
	if err != nil {
		return fmt.Errorf("Failed to delete local zip file at %s", localZipFilePath)
	}
	fmt.Println("Deleted zip file.")

	return nil
}

// getVersion returns a properly formatted version string
func getVersion(version string, versionPreRelease string) string {
	if versionPreRelease != "" {
		return version
	} else {
		return fmt.Sprintf("%s-%s", version, versionPreRelease)
	}
}

func getErrorMessage(errorCode int, errorDetails string) string {
	switch errorCode {
	case INVALID_TAG_CONSTRAINT_EXPRESSION:
		return fmt.Sprintf(`
The --tag value you entered is not a valid constraint expression.
See https://github.com/gruntwork-io/fetch#version-constraint-operators for examples.

Underlying error message:
%s
`, errorDetails)
	case INVALID_GITHUB_TOKEN_OR_ACCESS_DENIED:
		return fmt.Sprintf(`
Received an HTTP 401 Response when attempting to query the repo for its tags.

This means that either your GitHub oAuth Token is invalid, or that the token is valid but is being used to request access
to either a public repo or a private repo to which you don't have access.

Underlying error message:
%s
`, errorDetails)
	case REPO_DOES_NOT_EXIST_OR_ACCESS_DENIED:
		return fmt.Sprintf(`
Received an HTTP 404 Response when attempting to query the repo for its tags.

This means that either no GitHub repo exists at the URL provided, or that you don't have permission to access it.
If the URL is correct, you may need to pass in a --github-oauth-token.

Underlying error message:
%s
`, errorDetails)
	}

	return ""
}<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/garyburd/redigo/redis"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/google/go-github/github"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/labstack/echo"
	mw "github.com/orktes/captainhub/Godeps/_workspace/src/github.com/labstack/echo/middleware"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/robertkrimen/otto"
	"github.com/orktes/captainhub/Godeps/_workspace/src/golang.org/x/oauth2"

	_ "github.com/orktes/captainhub/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore"
)

//go:generate go-bindata -prefix=plugins/ -pkg=main plugins/...

var redisClient redis.Conn

func matchFilePath(call otto.FunctionCall) otto.Value {
	pattern := call.Argument(0).String()
	name := call.Argument(1).String()

	match, _ := filepath.Match(pattern, name)
	val, _ := otto.ToValue(match)
	return val
}

func getGithubClient() *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: os.Getenv("GITHUB_ACCESS_TOKEN")},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)

	return client
}

// Handler
func payload(c *echo.Context) error {
	r := c.Request()
	eventType := r.Header.Get("X-Github-Event")

	owner := c.Param("owner")
	repo := c.Param("repo")

	data := map[string]interface{}{}
	if err := c.Bind(&data); err != nil {
		fmt.Printf("%s", err.Error())
		return err
	}

	cfg, err := getCaptainConfig(owner, repo)

	if err != nil {
		return err
	}

	for _, plugin := range cfg.Plugins {
		pluginData, err := getCaptainPlugin(owner, repo, plugin.Name)

		if err != nil {
			// TODO set info about this
			fmt.Printf("%s", err.Error())
			continue
		}

		vm := otto.New()
		vm.Set("eventType", eventType)
		vm.Set("eventData", data)
		vm.Set("config", plugin.Config)
		vm.Set("matchFilePath", matchFilePath)

		vm.Set("getPullRequestDetails", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			pr, err := getPullRequestDetails(owner, repo, int(prNumber))

			data, err := json.Marshal(pr)
			if err != nil {
				panic(err)
			}

			val, err := vm.Run(fmt.Sprintf("(%s)", string(data)))
			if err != nil {
				panic(err)
			}

			return val
		})

		vm.Set("getPullRequestFiles", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			files, err := listPullRequestFiles(owner, repo, int(prNumber))
			if err != nil {
				panic(err)
			}

			data, err := json.Marshal(files)
			if err != nil {
				panic(err)
			}

			val, err := vm.Run(string(data))
			if err != nil {
				panic(err)
			}

			return val
		})

		vm.Set("createPullRequestComment", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			body := call.Argument(1).String()
			if err != nil {
				panic(err)
			}

			err = createPullRequestComment(owner, repo, int(prNumber), body)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("createIssueComment", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			body := call.Argument(1).String()
			if err != nil {
				panic(err)
			}

			err = createIssueComment(owner, repo, int(prNumber), body)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("createStatus", func(call otto.FunctionCall) otto.Value {
			sha, err := call.Argument(0).ToString()
			if err != nil {
				panic(err)
			}

			state, err := call.Argument(1).ToString()
			if err != nil {
				panic(err)
			}

			targetURL, err := call.Argument(2).ToString()
			if err != nil {
				panic(err)
			}

			description, err := call.Argument(3).ToString()
			if err != nil {
				panic(err)
			}

			context, err := call.Argument(4).ToString()
			if err != nil {
				panic(err)
			}

			err = createStatus(owner, repo, sha, state, targetURL, description, context)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("saveData", func(call otto.FunctionCall) otto.Value {
			key := call.Argument(0).String()
			data := call.Argument(1).String()

			storeKey := fmt.Sprintf("%s/%s/%s", owner, repo, key)

			_, err := redisClient.Do("SET", storeKey, data)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("loadData", func(call otto.FunctionCall) otto.Value {
			key := call.Argument(0).String()
			storeKey := fmt.Sprintf("%s/%s/%s", owner, repo, key)

			s, err := redis.String(redisClient.Do("GET", storeKey))
			if err != nil {
				panic(err)
			}

			ottoValue, err := otto.ToValue(s)
			if err != nil {
				panic(err)
			}

			return ottoValue
		})

		_, err = vm.Run(string(pluginData))
		if err != nil {
			fmt.Printf("%s\n", err.Error())
			return err
		}
	}

	return nil
}

func main() {
	// Init redis client
	var err error
	redisClient, err = redis.DialURL(os.Getenv("REDISCLOUD_URL"))
	defer redisClient.Close()

	if err != nil {
		panic(err)
	}

	// Echo instance
	e := echo.New()

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())

	// Routes
	e.Post("/:owner/:repo", payload)

	// Start server
	e.Run(":" + os.Getenv("PORT"))
}
<commit_msg>Print error if there is one<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/garyburd/redigo/redis"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/google/go-github/github"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/labstack/echo"
	mw "github.com/orktes/captainhub/Godeps/_workspace/src/github.com/labstack/echo/middleware"
	"github.com/orktes/captainhub/Godeps/_workspace/src/github.com/robertkrimen/otto"
	"github.com/orktes/captainhub/Godeps/_workspace/src/golang.org/x/oauth2"

	_ "github.com/orktes/captainhub/Godeps/_workspace/src/github.com/robertkrimen/otto/underscore"
)

//go:generate go-bindata -prefix=plugins/ -pkg=main plugins/...

var redisClient redis.Conn

func matchFilePath(call otto.FunctionCall) otto.Value {
	pattern := call.Argument(0).String()
	name := call.Argument(1).String()

	match, _ := filepath.Match(pattern, name)
	val, _ := otto.ToValue(match)
	return val
}

func getGithubClient() *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: os.Getenv("GITHUB_ACCESS_TOKEN")},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)

	return client
}

// Handler
func payload(c *echo.Context) error {
	r := c.Request()
	eventType := r.Header.Get("X-Github-Event")

	owner := c.Param("owner")
	repo := c.Param("repo")

	data := map[string]interface{}{}
	if err := c.Bind(&data); err != nil {
		fmt.Printf("%s", err.Error())
		return err
	}

	cfg, err := getCaptainConfig(owner, repo)

	if err != nil {
		return err
	}

	for _, plugin := range cfg.Plugins {
		pluginData, err := getCaptainPlugin(owner, repo, plugin.Name)

		if err != nil {
			// TODO set info about this
			fmt.Printf("%s", err.Error())
			continue
		}

		vm := otto.New()
		vm.Set("eventType", eventType)
		vm.Set("eventData", data)
		vm.Set("config", plugin.Config)
		vm.Set("matchFilePath", matchFilePath)

		vm.Set("getPullRequestDetails", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			pr, err := getPullRequestDetails(owner, repo, int(prNumber))

			data, err := json.Marshal(pr)
			if err != nil {
				panic(err)
			}

			val, err := vm.Run(fmt.Sprintf("(%s)", string(data)))
			if err != nil {
				panic(err)
			}

			return val
		})

		vm.Set("getPullRequestFiles", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			files, err := listPullRequestFiles(owner, repo, int(prNumber))
			if err != nil {
				panic(err)
			}

			data, err := json.Marshal(files)
			if err != nil {
				panic(err)
			}

			val, err := vm.Run(string(data))
			if err != nil {
				panic(err)
			}

			return val
		})

		vm.Set("createPullRequestComment", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			body := call.Argument(1).String()
			if err != nil {
				panic(err)
			}

			err = createPullRequestComment(owner, repo, int(prNumber), body)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("createIssueComment", func(call otto.FunctionCall) otto.Value {
			prNumber, err := call.Argument(0).ToInteger()
			if err != nil {
				panic(err)
			}

			body := call.Argument(1).String()
			if err != nil {
				panic(err)
			}

			err = createIssueComment(owner, repo, int(prNumber), body)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("createStatus", func(call otto.FunctionCall) otto.Value {
			sha, err := call.Argument(0).ToString()
			if err != nil {
				panic(err)
			}

			state, err := call.Argument(1).ToString()
			if err != nil {
				panic(err)
			}

			targetURL, err := call.Argument(2).ToString()
			if err != nil {
				panic(err)
			}

			description, err := call.Argument(3).ToString()
			if err != nil {
				panic(err)
			}

			context, err := call.Argument(4).ToString()
			if err != nil {
				panic(err)
			}

			err = createStatus(owner, repo, sha, state, targetURL, description, context)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("saveData", func(call otto.FunctionCall) otto.Value {
			key := call.Argument(0).String()
			data := call.Argument(1).String()

			storeKey := fmt.Sprintf("%s/%s/%s", owner, repo, key)

			_, err := redisClient.Do("SET", storeKey, data)
			if err != nil {
				panic(err)
			}

			return otto.Value{}
		})

		vm.Set("loadData", func(call otto.FunctionCall) otto.Value {
			key := call.Argument(0).String()
			storeKey := fmt.Sprintf("%s/%s/%s", owner, repo, key)

			s, err := redis.String(redisClient.Do("GET", storeKey))
			if err != nil {
				panic(err)
			}

			ottoValue, err := otto.ToValue(s)
			if err != nil {
				panic(err)
			}

			return ottoValue
		})

		_, err = vm.Run(string(pluginData))
		if err != nil {
			fmt.Printf("%s\n", err.Error())
			return err
		}
	}

	return nil
}

func main() {
	// Init redis client
	var err error
	redisClient, err = redis.DialURL(os.Getenv("REDISCLOUD_URL"))
	if err != nil {
		panic(err)
	}
	defer redisClient.Close()

	// Echo instance
	e := echo.New()

	// Middleware
	e.Use(mw.Logger())
	e.Use(mw.Recover())

	// Routes
	e.Post("/:owner/:repo", payload)

	// Start server
	e.Run(":" + os.Getenv("PORT"))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS   = "http://localhost:9900"
	LENGTH    = 6
	PASTEARGS = ADDRESS + "/p/{PASTE}/(python|language)\n"
	URLARGS   = ADDRESS + "/save/XML|JSON\n"
	SOURCE    = "Source: https://github.com/ewhal/Pastebin\n"
	TEXT      = "$ <command> | curl -F 'p=<-' " + ADDRESS + "\n" + PASTEARGS + URLARGS + SOURCE
	PORT      = ":9900"
	USERNAME  = ""
	PASS      = ""
	NAME      = ""
	DATABASE  = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste []byte) string {
	hasher := sha1.New()
	hasher.Write(paste)
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw []byte) []string {
	p := raw[86 : len(raw)-46]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(p)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	url := ADDRESS + "/p/" + id
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(string(p))

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=? id=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(paste))
	check(err)

	affect, err := res.RowsAffected()
	check(err)

	io.WriteString(w, string(affect))

	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		buf, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
		}

		values := save(buf)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		default:
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, TEXT)
}
func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}
func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", rootHandler)
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Add deletion key reference<commit_after>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS   = "http://localhost:9900"
	LENGTH    = 6
	DELETE    = ADDRESS + "/{PASTE}/{DELKEY}"
	PASTEARGS = ADDRESS + "/p/{PASTE}/(python|language)\n"
	URLARGS   = ADDRESS + "/save/XML|JSON\n"
	SOURCE    = "Source: https://github.com/ewhal/Pastebin\n"
	TEXT      = "$ <command> | curl -F 'p=<-' " + ADDRESS + "\n" + PASTEARGS + URLARGS + DELETE + SOURCE
	PORT      = ":9900"
	USERNAME  = ""
	PASS      = ""
	NAME      = ""
	DATABASE  = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste []byte) string {
	hasher := sha1.New()
	hasher.Write(paste)
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw []byte) []string {
	p := raw[86 : len(raw)-46]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(p)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	url := ADDRESS + "/p/" + id
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(string(p))

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=? id=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(paste))
	check(err)

	affect, err := res.RowsAffected()
	check(err)

	io.WriteString(w, string(affect))

	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		buf, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
		}

		values := save(buf)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		default:
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, TEXT)
}
func langHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}
func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", rootHandler)
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>// Goed is a terminal based editor.
// https://github.com/tcolar/goed
package main

import (
	"fmt"
	"log"
	"os/exec"
	"runtime/debug"
	"time"

	"github.com/davecheney/profile"
	"github.com/tcolar/goed/actions"
	"github.com/tcolar/goed/api"
	"github.com/tcolar/goed/core"
	"github.com/tcolar/goed/ui"
	"gopkg.in/alecthomas/kingpin.v1"
)

var (
	app     = kingpin.New("goed", "A code editor")
	test    = kingpin.Flag("testterm", "Prints colors to the terminal to check them.").Bool()
	colors  = kingpin.Flag("c", "Number of colors(0,2,16,256). 0 means Detect.").Default("0").Int()
	config  = kingpin.Flag("config", "Config file.").Default("config.toml").String()
	cpuprof = kingpin.Flag("cpuprof", "Cpu profile").Default("false").Bool()
	memprof = kingpin.Flag("memprof", "Mem profile").Default("false").Bool()

	locs = kingpin.Arg("location", "location to open").Strings()
)

func main() {

	kingpin.Version(core.Version)

	kingpin.Parse()
	if *test {
		core.TestTerm()
		return
	}
	if *colors == 0 {
		*colors = core.DetectColors()
	}
	if *colors != 256 && *colors != 16 {
		*colors = 2
	}
	if *cpuprof == true || *memprof == true {
		cfg := profile.Config{
			CPUProfile: *cpuprof,
			MemProfile: *memprof,
		}
		defer profile.Start(&cfg).Stop()
	}

	id := time.Now().UnixNano()

	core.Colors = *colors
	core.Bus = actions.NewActionBus()
	core.InitHome(id)
	core.ConfFile = *config
	core.Ed = ui.NewEditor()
	apiServer := api.Api{}

	defer func() {
		if fail := recover(); fail != nil {
			// writing panic to file because shell will be garbled
			fmt.Printf("Panicked with %v\n", fail)
			fmt.Printf("Writing panic to log %s \n", core.LogFile.Name())
			data := debug.Stack()
			log.Fatal(string(data))
		}
		core.Cleanup()

		core.Bus.Shutdown()

		// attempts to reset the terminal in case we left it in a bad state
		exec.Command("reset").Run()
	}()

	apiServer.Start()

	go core.Bus.Start()

	core.Ed.Start(*locs)
}
<commit_msg>Profiling tweaks<commit_after>// Goed is a terminal based editor.
// https://github.com/tcolar/goed
package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime/debug"
	"runtime/pprof"
	"time"

	"github.com/tcolar/goed/actions"
	"github.com/tcolar/goed/api"
	"github.com/tcolar/goed/core"
	"github.com/tcolar/goed/ui"
	"gopkg.in/alecthomas/kingpin.v1"
)

var (
	app     = kingpin.New("goed", "A code editor")
	test    = kingpin.Flag("testterm", "Prints colors to the terminal to check them.").Bool()
	colors  = kingpin.Flag("c", "Number of colors(0,2,16,256). 0 means Detect.").Default("0").Int()
	config  = kingpin.Flag("config", "Config file.").Default("config.toml").String()
	cpuprof = kingpin.Flag("cpuprof", "Cpu profile").Default("false").Bool()
	memprof = kingpin.Flag("memprof", "Mem profile").Default("false").Bool()

	locs = kingpin.Arg("location", "location to open").Strings()
)

func main() {

	kingpin.Version(core.Version)

	kingpin.Parse()
	if *test {
		core.TestTerm()
		return
	}
	if *colors == 0 {
		*colors = core.DetectColors()
	}
	if *colors != 256 && *colors != 16 {
		*colors = 2
	}
	if *cpuprof == true {
		f, err := os.Create("prof.cprof")
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if *memprof == true {
		f, err := os.Create("prof.mprof")
		if err != nil {
			log.Fatal(err)
		}
		go func() {
			time.Sleep(1 * time.Minute)
			pprof.WriteHeapProfile(f)
			f.Close()
			os.Exit(0)
		}()
	}

	id := time.Now().UnixNano()

	core.Colors = *colors
	core.Bus = actions.NewActionBus()
	core.InitHome(id)
	core.ConfFile = *config
	core.Ed = ui.NewEditor()
	apiServer := api.Api{}

	defer func() {
		if fail := recover(); fail != nil {
			// writing panic to file because shell will be garbled
			fmt.Printf("Panicked with %v\n", fail)
			fmt.Printf("Writing panic to log %s \n", core.LogFile.Name())
			data := debug.Stack()
			log.Fatal(string(data))
		}
		core.Cleanup()

		core.Bus.Shutdown()

		// attempts to reset the terminal in case we left it in a bad state
		exec.Command("reset").Run()
	}()

	apiServer.Start()

	go core.Bus.Start()

	core.Ed.Start(*locs)
}
<|endoftext|>