<commit_before>package chalmers_chop

import (
	"errors"
	"github.com/mmcdole/gofeed"
	"strconv"
	"strings"
	"time"
)

// Defines the mapping between  sources and allergens
var allergenImages map[string]Allergen = map[string]Allergen{
	"gluten-white.png": Gluten,
	"gluten-black.png": Gluten,
	"egg-white.png":    Egg,
	"egg-black.png":    Egg,
	"lactose-white":    Lactose,
	"lactose-black":    Lactose,
	"FISH_black.png":   Fish,
	"FISH_white.png":   Fish,
}

func ParseDailyMenu(feed *gofeed.Feed) *Menu {
	var menu Menu

	menu.Title = feed.Title
	menu.Date = getMenuDate(feed)

	for _, item := range feed.Items {
		menu.AddDish(parseDish(item))
	}

	return &menu
}

func getMenuDate(feed *gofeed.Feed) string {
	d, err := parseMenuDate(feed)

	if err != nil {
		// Default to today's date
		return time.Now().Format("2006-01-02")
	}

	return d
}

// parseMenuDate parses the date of a menu.
//
// The menu date is retrieved by parsing the contents of the <guid> tag and fetching
// the last 10 runes, which will always be a date on the format YYYY-mm-dd
func parseMenuDate(feed *gofeed.Feed) (string, error) {
	if len(feed.Items) == 0 {
		return "", errors.New("Could not find menu date")
	}

	t := feed.Items[0].GUID

	return string(t[len(t)-10:]), nil
}

// parseDish parses a dish from a feed item.
//
// The dish contents, price, and allergy information is contained in a CDATA tag inside the <description> tag. For example:
//
// <description>
// <![CDATA[Beef, wheat bread, french fries@80
   >

]]>
//          ^                               ^                                                                                ^
//          contents                        price                                                                             allergen (Gluten)
//</description>
func parseDish(item *gofeed.Item) Dish {
	var dish Dish

	dish.Name = item.Title

	desc := trimCDATA(item.Description)

	dish.Contents = parseContents(desc)
	dish.Price = parsePrice(desc)
	dish.Allergens = parseAllergens(desc)

	return dish
}

// parseContents parses a dish's contents from its description text.
//
// The dish contents is all text content leading up to the @ sign.
func parseContents(desc string) string {
	return strings.Split(desc, "@")[0]
}

// parsePrice parses the price of a dish from its description text.
//
// The dish price is all text content following the first @ sign leading up to
// the first space sign.
func parsePrice(desc string) int {
	afterAtSign := strings.Split(desc, "@")[1]
	priceText := beforeSpace(afterAtSign)

	p, err := strconv.Atoi(priceText)

	if err != nil {
		return 0
	}

	return p
}

// parseAllergens parses the allergens of a dish from its description text.
//
// The dish allergens can be determined by searching the description text for
// occurrences of image sources defined in allergenImages.
//
// For example, the following dish description indicates that the dish contains the Gluten allergen ("gluten-white.png"):
//
// <![CDATA[Hamburger of the Day@80
   >

]]>
func parseAllergens(desc string) []Allergen {
	var al []Allergen

	for k, a := range allergenImages {
		if strings.Contains(desc, k) {
			al = append(al, a)
		}
	}

	return al
}

func trimCDATA(text string) string {
	if !strings.HasPrefix(text, "<![CDATA") || !strings.HasSuffix(text, "]]>") {
		return text
	}

	return strings.TrimSuffix(strings.TrimPrefix(text, "<![CDATA"), "]]>")
}

func beforeSpace(str string) string {
	indexFunc := func(r rune) bool {
		return r == ' '
	}

	value := strings.IndexFunc(str, indexFunc)

	if value >= 0 && value <= len(str) {
		return str[:value]
	}

	return str
}
<commit_msg>Improve beforeSpace documentation<commit_after>package chalmers_chop

import (
	"errors"
	"github.com/mmcdole/gofeed"
	"strconv"
	"strings"
	"time"
)

// Defines the mapping between  sources and allergens
var allergenImages map[string]Allergen = map[string]Allergen{
	"gluten-white.png": Gluten,
	"gluten-black.png": Gluten,
	"egg-white.png":    Egg,
	"egg-black.png":    Egg,
	"lactose-white":    Lactose,
	"lactose-black":    Lactose,
	"FISH_black.png":   Fish,
	"FISH_white.png":   Fish,
}

func ParseDailyMenu(feed *gofeed.Feed) *Menu {
	var menu Menu

	menu.Title = feed.Title
	menu.Date = getMenuDate(feed)

	for _, item := range feed.Items {
		menu.AddDish(parseDish(item))
	}

	return &menu
}

func getMenuDate(feed *gofeed.Feed) string {
	d, err := parseMenuDate(feed)

	if err != nil {
		// Default to today's date
		return time.Now().Format("2006-01-02")
	}

	return d
}

// parseMenuDate parses the date of a menu.
//
// The menu date is retrieved by parsing the contents of the <guid> tag and fetching
// the last 10 runes, which will always be a date on the format YYYY-mm-dd
func parseMenuDate(feed *gofeed.Feed) (string, error) {
	if len(feed.Items) == 0 {
		return "", errors.New("Could not find menu date")
	}

	t := feed.Items[0].GUID

	return string(t[len(t)-10:]), nil
}

// parseDish parses a dish from a feed item.
//
// The dish contents, price, and allergy information is contained in a CDATA tag inside the <description> tag. For example:
//
// <description>
// <![CDATA[Beef, wheat bread, french fries@80
   >

]]>
//          ^                               ^                                                                                ^
//          contents                        price                                                                             allergen (Gluten)
//</description>
func parseDish(item *gofeed.Item) Dish {
	var dish Dish

	dish.Name = item.Title

	desc := trimCDATA(item.Description)

	dish.Contents = parseContents(desc)
	dish.Price = parsePrice(desc)
	dish.Allergens = parseAllergens(desc)

	return dish
}

// parseContents parses a dish's contents from its description text.
//
// The dish contents is all text content leading up to the @ sign.
func parseContents(desc string) string {
	return strings.Split(desc, "@")[0]
}

// parsePrice parses the price of a dish from its description text.
//
// The dish price is all text content following the first @ sign leading up to
// the first space sign.
func parsePrice(desc string) int {
	afterAtSign := strings.Split(desc, "@")[1]
	priceText := beforeSpace(afterAtSign)

	p, err := strconv.Atoi(priceText)

	if err != nil {
		return 0
	}

	return p
}

// parseAllergens parses the allergens of a dish from its description text.
//
// The dish allergens can be determined by searching the description text for
// occurrences of image sources defined in allergenImages.
//
// For example, the following dish description indicates that the dish contains the Gluten allergen ("gluten-white.png"):
//
// <![CDATA[Hamburger of the Day@80
   >

]]>
func parseAllergens(desc string) []Allergen {
	var al []Allergen

	for k, a := range allergenImages {
		if strings.Contains(desc, k) {
			al = append(al, a)
		}
	}

	return al
}

func trimCDATA(text string) string {
	if !strings.HasPrefix(text, "<![CDATA") || !strings.HasSuffix(text, "]]>") {
		return text
	}

	return strings.TrimSuffix(strings.TrimPrefix(text, "<![CDATA"), "]]>")
}

// beforeSpace returns a substring of str, starting at the beginning of str
// and ending right before the first occurrence of a space character (0x20).
//
// Returns str unmodified if str does not contain any spaces.
func beforeSpace(str string) string {
	indexFunc := func(r rune) bool {
		return r == ' '
	}

	value := strings.IndexFunc(str, indexFunc)

	if value >= 0 && value <= len(str) {
		return str[:value]
	}

	return str
}
<|endoftext|>


<commit_before>package main

import "flag"

// configuration ...
type configuration struct {
	host      string
	port      int
	namespace string
	subsystem string
	logger    struct {
		adapter string
		format  string
		level   int
	}
	monitoring struct {
		engine string
	}
	postgres struct {
		connectionString string
		retry            int
	}
}

// Init ...
func (c *configuration) Init() {
	if c == nil {
		*c = configuration{}
	}

	flag.StringVar(&c.host, "h", "127.0.0.1", "host")
	flag.IntVar(&c.port, "p", 8080, "port")
	flag.StringVar(&c.namespace, "n", "", "namespace")
	flag.StringVar(&c.subsystem, "s", "mnemosyne", "subsystem")
	flag.StringVar(&c.logger.adapter, "la", loggerAdapterStdOut, "logger adapter")
	flag.StringVar(&c.logger.format, "lf", loggerFormatJSON, "logger format")
	flag.IntVar(&c.logger.level, "ll", 6, "logger level")
	flag.StringVar(&c.monitoring.engine, "me", monitoringEnginePrometheus, "monitoring engine")
	flag.StringVar(&c.postgres.connectionString, "spcs", "postgres://localhost:5432?sslmode=disable", "storage postgres connection string")
	flag.IntVar(&c.postgres.retry, "spr", 10, "storage postgres possible attempts")
}

// Parse ...
func (c *configuration) Parse() {
	if !flag.Parsed() {
		flag.Parse()
	}
}
<commit_msg>postgres flag rename<commit_after>package main

import "flag"

// configuration ...
type configuration struct {
	host      string
	port      int
	namespace string
	subsystem string
	logger    struct {
		adapter string
		format  string
		level   int
	}
	monitoring struct {
		engine string
	}
	postgres struct {
		connectionString string
		retry            int
	}
}

// Init ...
func (c *configuration) Init() {
	if c == nil {
		*c = configuration{}
	}

	flag.StringVar(&c.host, "h", "127.0.0.1", "host")
	flag.IntVar(&c.port, "p", 8080, "port")
	flag.StringVar(&c.namespace, "n", "", "namespace")
	flag.StringVar(&c.subsystem, "s", "mnemosyne", "subsystem")
	flag.StringVar(&c.logger.adapter, "la", loggerAdapterStdOut, "logger adapter")
	flag.StringVar(&c.logger.format, "lf", loggerFormatJSON, "logger format")
	flag.IntVar(&c.logger.level, "ll", 6, "logger level")
	flag.StringVar(&c.monitoring.engine, "me", monitoringEnginePrometheus, "monitoring engine")
	flag.StringVar(&c.postgres.connectionString, "pcs", "postgres://localhost:5432?sslmode=disable", "storage postgres connection string")
	flag.IntVar(&c.postgres.retry, "pr", 10, "storage postgres possible attempts")
}

// Parse ...
func (c *configuration) Parse() {
	if !flag.Parsed() {
		flag.Parse()
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"syscall"
	"time"

	"github.com/docopt/docopt-go"
	"github.com/oragono/oragono/irc"
	"github.com/oragono/oragono/irc/logger"
	"github.com/oragono/oragono/irc/mkcerts"
	"github.com/oragono/oragono/irc/passwd"
	stackimpact "github.com/stackimpact/stackimpact-go"
	"golang.org/x/crypto/ssh/terminal"
)

var commit = ""

func main() {
	version := irc.SemVer
	usage := `oragono.
Usage:
	oragono initdb [--conf <filename>] [--quiet]
	oragono upgradedb [--conf <filename>] [--quiet]
	oragono genpasswd [--conf <filename>] [--quiet]
	oragono mkcerts [--conf <filename>] [--quiet]
	oragono run [--conf <filename>] [--quiet]
	oragono -h | --help
	oragono --version
Options:
	--conf <filename>  Configuration file to use [default: ircd.yaml].
	--quiet            Don't show startup/shutdown lines.
	-h --help          Show this screen.
	--version          Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, version, false)

	configfile := arguments["--conf"].(string)
	config, err := irc.LoadConfig(configfile)
	if err != nil {
		log.Fatal("Config file did not load successfully: ", err.Error())
	}

	logman, err := logger.NewManager(config.Logging)
	if err != nil {
		log.Fatal("Logger did not load successfully:", err.Error())
	}

	if arguments["genpasswd"].(bool) {
		fmt.Print("Enter Password: ")
		bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
		if err != nil {
			log.Fatal("Error reading password:", err.Error())
		}
		password := string(bytePassword)
		encoded, err := passwd.GenerateEncodedPassword(password)
		if err != nil {
			log.Fatal("encoding error:", err.Error())
		}
		fmt.Print("\n")
		fmt.Println(encoded)
	} else if arguments["initdb"].(bool) {
		irc.InitDB(config.Datastore.Path)
		if !arguments["--quiet"].(bool) {
			log.Println("database initialized: ", config.Datastore.Path)
		}
	} else if arguments["upgradedb"].(bool) {
		irc.UpgradeDB(config.Datastore.Path)
		if !arguments["--quiet"].(bool) {
			log.Println("database upgraded: ", config.Datastore.Path)
		}
	} else if arguments["mkcerts"].(bool) {
		if !arguments["--quiet"].(bool) {
			log.Println("making self-signed certificates")
		}

		for name, conf := range config.Server.TLSListeners {
			if !arguments["--quiet"].(bool) {
				log.Printf(" making cert for %s listener\n", name)
			}
			host := config.Server.Name
			err := mkcerts.CreateCert("Oragono", host, conf.Cert, conf.Key)
			if err == nil {
				if !arguments["--quiet"].(bool) {
					log.Printf("  Certificate created at %s : %s\n", conf.Cert, conf.Key)
				}
			} else {
				log.Fatal("  Could not create certificate:", err.Error())
			}
		}
	} else if arguments["run"].(bool) {
		rand.Seed(time.Now().UTC().UnixNano())
		if !arguments["--quiet"].(bool) {
			logman.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer))
			if commit == "" {
				logman.Debug("startup", fmt.Sprintf("Could not get current commit"))
			} else {
				logman.Info("startup", fmt.Sprintf("Running commit %s", commit))
			}
		}

		// set current git commit
		irc.Commit = commit
		if commit != "" {
			irc.Ver = fmt.Sprintf("%s-%s", irc.Ver, commit)
		}

		// profiling
		if config.Debug.StackImpact.Enabled {
			if config.Debug.StackImpact.AgentKey == "" || config.Debug.StackImpact.AppName == "" {
				logman.Error("startup", "Could not start StackImpact - agent-key or app-name are undefined")
				return
			}

			agent := stackimpact.NewAgent()
			agent.Start(stackimpact.Options{AgentKey: config.Debug.StackImpact.AgentKey, AppName: config.Debug.StackImpact.AppName})
			defer agent.RecordPanic()

			logman.Info("startup", fmt.Sprintf("StackImpact profiling started as %s", config.Debug.StackImpact.AppName))
		}

		// warning if running a non-final version
		if strings.Contains(irc.SemVer, "unreleased") {
			logman.Warning("startup", "You are currently running an unreleased beta version of Oragono that may be unstable and could corrupt your database.\nIf you are running a production network, please download the latest build from https://oragono.io/downloads.html and run that instead.")
		}

		server, err := irc.NewServer(config, logman)
		if err != nil {
			logman.Error("startup", fmt.Sprintf("Could not load server: %s", err.Error()))
			return
		}
		if !arguments["--quiet"].(bool) {
			logman.Info("startup", "Server running")
			defer logman.Info("shutdown", fmt.Sprintf("Oragono v%s exiting", irc.SemVer))
		}
		server.Run()
	}
}
<commit_msg>when entering/generating the password it's helpful to at least be asked to confirm the input so if you get something like an extraneous character, on confirmation, you could catch that<commit_after>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"syscall"
	"time"

	"github.com/docopt/docopt-go"
	"github.com/oragono/oragono/irc"
	"github.com/oragono/oragono/irc/logger"
	"github.com/oragono/oragono/irc/mkcerts"
	"github.com/oragono/oragono/irc/passwd"
	stackimpact "github.com/stackimpact/stackimpact-go"
	"golang.org/x/crypto/ssh/terminal"
)

var commit = ""

// get a password from stdin from the user
func getPassword() string {
	bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
	if err != nil {
		log.Fatal("Error reading password:", err.Error())
	}
	return string(bytePassword)
}

func main() {
	version := irc.SemVer
	usage := `oragono.
Usage:
	oragono initdb [--conf <filename>] [--quiet]
	oragono upgradedb [--conf <filename>] [--quiet]
	oragono genpasswd [--conf <filename>] [--quiet]
	oragono mkcerts [--conf <filename>] [--quiet]
	oragono run [--conf <filename>] [--quiet]
	oragono -h | --help
	oragono --version
Options:
	--conf <filename>  Configuration file to use [default: ircd.yaml].
	--quiet            Don't show startup/shutdown lines.
	-h --help          Show this screen.
	--version          Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, version, false)

	configfile := arguments["--conf"].(string)
	config, err := irc.LoadConfig(configfile)
	if err != nil {
		log.Fatal("Config file did not load successfully: ", err.Error())
	}

	logman, err := logger.NewManager(config.Logging)
	if err != nil {
		log.Fatal("Logger did not load successfully:", err.Error())
	}

	if arguments["genpasswd"].(bool) {
		fmt.Print("Enter Password: ")
		password := getPassword()
		fmt.Print("\n")
		fmt.Print("Reenter Password: ")
		confirm := getPassword()
		fmt.Print("\n")
		if confirm != password {
			log.Fatal("passwords do not match")
		}
		encoded, err := passwd.GenerateEncodedPassword(password)
		if err != nil {
			log.Fatal("encoding error:", err.Error())
		}
		fmt.Println(encoded)
	} else if arguments["initdb"].(bool) {
		irc.InitDB(config.Datastore.Path)
		if !arguments["--quiet"].(bool) {
			log.Println("database initialized: ", config.Datastore.Path)
		}
	} else if arguments["upgradedb"].(bool) {
		irc.UpgradeDB(config.Datastore.Path)
		if !arguments["--quiet"].(bool) {
			log.Println("database upgraded: ", config.Datastore.Path)
		}
	} else if arguments["mkcerts"].(bool) {
		if !arguments["--quiet"].(bool) {
			log.Println("making self-signed certificates")
		}

		for name, conf := range config.Server.TLSListeners {
			if !arguments["--quiet"].(bool) {
				log.Printf(" making cert for %s listener\n", name)
			}
			host := config.Server.Name
			err := mkcerts.CreateCert("Oragono", host, conf.Cert, conf.Key)
			if err == nil {
				if !arguments["--quiet"].(bool) {
					log.Printf("  Certificate created at %s : %s\n", conf.Cert, conf.Key)
				}
			} else {
				log.Fatal("  Could not create certificate:", err.Error())
			}
		}
	} else if arguments["run"].(bool) {
		rand.Seed(time.Now().UTC().UnixNano())
		if !arguments["--quiet"].(bool) {
			logman.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer))
			if commit == "" {
				logman.Debug("startup", fmt.Sprintf("Could not get current commit"))
			} else {
				logman.Info("startup", fmt.Sprintf("Running commit %s", commit))
			}
		}

		// set current git commit
		irc.Commit = commit
		if commit != "" {
			irc.Ver = fmt.Sprintf("%s-%s", irc.Ver, commit)
		}

		// profiling
		if config.Debug.StackImpact.Enabled {
			if config.Debug.StackImpact.AgentKey == "" || config.Debug.StackImpact.AppName == "" {
				logman.Error("startup", "Could not start StackImpact - agent-key or app-name are undefined")
				return
			}

			agent := stackimpact.NewAgent()
			agent.Start(stackimpact.Options{AgentKey: config.Debug.StackImpact.AgentKey, AppName: config.Debug.StackImpact.AppName})
			defer agent.RecordPanic()

			logman.Info("startup", fmt.Sprintf("StackImpact profiling started as %s", config.Debug.StackImpact.AppName))
		}

		// warning if running a non-final version
		if strings.Contains(irc.SemVer, "unreleased") {
			logman.Warning("startup", "You are currently running an unreleased beta version of Oragono that may be unstable and could corrupt your database.\nIf you are running a production network, please download the latest build from https://oragono.io/downloads.html and run that instead.")
		}

		server, err := irc.NewServer(config, logman)
		if err != nil {
			logman.Error("startup", fmt.Sprintf("Could not load server: %s", err.Error()))
			return
		}
		if !arguments["--quiet"].(bool) {
			logman.Info("startup", "Server running")
			defer logman.Info("shutdown", fmt.Sprintf("Oragono v%s exiting", irc.SemVer))
		}
		server.Run()
	}
}
<|endoftext|>


<commit_before>package backoff

import (
    "errors"
    "net/http"
    "os"
    "os/signal"
    "time"
)

type HttpRoundTripper struct {
    Transport http.RoundTripper

    b   Backoffer
}

func NewHttpRoundTripper(b Backoffer) *HttpRoundTripper {
    return &HttpRoundTripper{
        Transport: http.DefaultTransport,
        b:         b,
    }
}

type rtres struct {
    res *http.Response
    err error
}

func (t *HttpRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) {
    n := uint(0)

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, os.Kill)
    defer signal.Stop(c)

    rtrc := make(chan *rtres)

    for delay, next := t.b.Next(n); next; delay, next = t.b.Next(n) {
        go func() {
            rtr := new(rtres)
            rtr.res, rtr.err = t.Transport.RoundTrip(req)
            rtrc <- rtr
        }()

        select {
        case rtr := <-rtrc:
            res = rtr.res
            err = rtr.err
        case <-c:
            err = errors.New("Exited before response could be obtained")
            return
        }

        if err == nil {
            return
        }

        select {
        case <-time.After(delay):
            n++
        case <-c:
            return
        }
    }

    return
}
<commit_msg>Backoff transport shouldn't know about signals<commit_after>package backoff

import (
    "net/http"
    "time"
)

type HttpTransport struct {
    Transport http.RoundTripper

    b   Backoffer
}

func NewHttpTransport(b Backoffer) *HttpTransport {
    return &HttpTransport{
        Transport: http.DefaultTransport,
        b:         b,
    }
}

func (t *HttpTransport) RoundTrip(req *http.Request) (res *http.Response, err error) {
    n := uint(0)

    for delay, next := t.b.Next(n); next; delay, next = t.b.Next(n) {
        res, err = t.Transport.RoundTrip(req)

        if err == nil {
            return
        }

        <-time.After(delay)
        n++
    }

    return
}
<|endoftext|>


<commit_before>package checks

import (
	"fmt"
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/util"
)

type Status string

const (
	StatusOK       Status = "OK"
	StatusWarning         = "WARNING"
	StatusCritical        = "CRITICAL"
	StatusUnknown         = "UNKNOWN"
)

var exitCodeToStatus = map[int]Status{
	0: StatusOK,
	1: StatusWarning,
	2: StatusCritical,
	3: StatusUnknown,
}

type Checker struct {
	Name string
	// NOTE(motemen): We make use of config.PluginConfig as it happens
	// to have Command field which was used by metrics.pluginGenerator.
	// If the configuration of checks.Checker and metrics.pluginGenerator goes different ones,
	// we should reconcider using config.PluginConfig.
	Config config.PluginConfig
}

func (c Checker) String() string {
	return fmt.Sprintf("checker %q command=[%s]", c.Name, c.Config.Command)
}

func (c Checker) Check() (status Status, message string, err error) {
	command := c.Config.Command
	stdout, stderr, exitCode, err := util.RunCommand(command)
	if err != nil {
		return StatusUnknown, "", err
	}

	fmt.Printf("%q %q", stdout, stderr)

	if s, ok := exitCodeToStatus[exitCode]; ok {
		status = s
	} else {
		status = StatusUnknown
	}

	return status, stdout, nil
}
<commit_msg>checks<commit_after>package checks

import (
	"fmt"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/util"
)

var logger = logging.GetLogger("checks")

// Status is a status that is produced by periodical checking.
// It is currently compatible with Nagios.
type Status string

// Current possible statuses, which is taken from command's exit code.
// the mapping is given as exitCodeToStatus.
const (
	StatusOK       Status = "OK"
	StatusWarning  Status = "WARNING"
	StatusCritical Status = "CRITICAL"
	StatusUnknown  Status = "UNKNOWN"
)

var exitCodeToStatus = map[int]Status{
	0: StatusOK,
	1: StatusWarning,
	2: StatusCritical,
	3: StatusUnknown,
}

// Checker is the main interface of check monitoring.
// It invokes its given command and transforms the result to a Report
// to be sent to Mackerel periodically.
type Checker struct {
	Name string
	// NOTE(motemen): We make use of config.PluginConfig as it happens
	// to have Command field which was used by metrics.pluginGenerator.
	// If the configuration of checks.Checker and metrics.pluginGenerator goes different ones,
	// we should reconcider using config.PluginConfig.
	Config config.PluginConfig
}

// Report is what Checker produces by invoking its command.
type Report struct {
	Name       string
	Status     Status
	Message    string
	OccurredAt time.Time
}

func (c Checker) String() string {
	return fmt.Sprintf("checker %q command=[%s]", c.Name, c.Config.Command)
}

// Check invokes the command and transforms its result to a Report.
func (c Checker) Check() (*Report, error) {
	now := time.Now()

	command := c.Config.Command
	logger.Debugf("Checker %q executing command %q", c.Name, command)
	stdout, _, exitCode, err := util.RunCommand(command)
	if err != nil {
		return nil, err
	}

	status := StatusUnknown
	if s, ok := exitCodeToStatus[exitCode]; ok {
		status = s
	}

	logger.Debugf("Checker %q status=%s", c.Name, status)

	return &Report{
		Name:       c.Name,
		Status:     status,
		Message:    stdout,
		OccurredAt: now,
	}, nil
}

// Interval is the interval where the command is invoked.
// (Will be configurable in the future)
func (c Checker) Interval() time.Duration {
	return 1 * time.Minute
}
<|endoftext|>


<commit_before>package models

import (
	json "encoding/json"
	"regexp"
	"github.com/Sirupsen/logrus"
)

type ResponseDelay struct {
	HostPattern string
	Delay int
	DelayStdDev int
}

type ResponseDelayJson struct {
	Data []ResponseDelay
}

func ParseResponseDelayJson(j []byte) []ResponseDelay {
	var responseDelayJson ResponseDelayJson
	result := make([]ResponseDelay,0)
	json.Unmarshal(j, &responseDelayJson)

	// filter any entries that don't meet the invariants
	for _, delay := range responseDelayJson.Data {
		if delay.HostPattern != "" && delay.Delay != 0 {
			if _, err := regexp.Compile(delay.HostPattern); err == nil {
				result = append(result, delay)
			} else {
				logrus.Warn("Response delay entry skipped due to invalid pattern : %s", delay.HostPattern)
			}
		} else {
			logrus.Warn("Response delay entry skipped due to missing values: %v", delay)
		}
	}
	return result
}

func (this *ResponseDelay) Execute() {
	// apply the delay
	panic("execute delay not implemented")
}<commit_msg>warn instead of panic for unimplemented method<commit_after>package models

import (
	json "encoding/json"
	"regexp"
	log "github.com/Sirupsen/logrus"
)

type ResponseDelay struct {
	HostPattern string
	Delay int
	DelayStdDev int
}

type ResponseDelayJson struct {
	Data []ResponseDelay
}

func ParseResponseDelayJson(j []byte) []ResponseDelay {
	var responseDelayJson ResponseDelayJson
	result := make([]ResponseDelay,0)
	json.Unmarshal(j, &responseDelayJson)

	// filter any entries that don't meet the invariants
	for _, delay := range responseDelayJson.Data {
		if delay.HostPattern != "" && delay.Delay != 0 {
			if _, err := regexp.Compile(delay.HostPattern); err == nil {
				result = append(result, delay)
			} else {
				log.Warn("Response delay entry skipped due to invalid pattern : %s", delay.HostPattern)
			}
		} else {
			log.Warn("Response delay entry skipped due to missing values: %v", delay)
		}
	}
	return result
}

func (this *ResponseDelay) Execute() {
	// apply the delay
	log.Warn("execute delay not implemented")
}<|endoftext|>


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

// This bot collects current interest rate for CDI, from Cetip's home page, and
// provides an HTTP server for serving the current annual and daily rate.
package main

import (
	"code.google.com/p/cascadia"
	"code.google.com/p/go.net/html"
	"encoding/json"
	"flag"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type AnnualInterest struct {
	Year float64
	Day  float64
}

func (i *AnnualInterest) CalculateDay() {
	i.Day = (math.Pow(1+i.Year/100, 1.0/252.0) - 1) * 100
}

func (i *AnnualInterest) CalculateYear() {
	i.Year = (math.Pow(1+i.Day/100, 252.0) - 1) * 100
}

func (i *AnnualInterest) Equiv(value float64) AnnualInterest {
	var equiv AnnualInterest
	equiv.Day = value * i.Day
	equiv.CalculateYear()
	return equiv
}

var cdi AnnualInterest

var (
	interval time.Duration
	bind     string
)

func init() {
	flag.DurationVar(&interval, "interval", time.Hour, "Interval between updates in the value")
	flag.StringVar(&bind, "bind", "0.0.0.0:5555", "Address to bind")
}

func collectCDI() {
	resp, err := http.Get("http://www.cetip.com.br/Home")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	selector := cascadia.MustCompile("#ctl00_Banner_lblTaxDI")
	root, err := html.Parse(resp.Body)
	if err != nil {
		panic(err)
	}
	node := selector.MatchFirst(root)
	interest := strings.TrimRight(node.FirstChild.Data, "%")
	interest = strings.Replace(interest, ",", ".", 1)
	if value, err := strconv.ParseFloat(interest, 64); err == nil {
		cdi.Year = value
		cdi.CalculateDay()
	}
}

func main() {
	flag.Parse()
	collectCDI()
	go func() {
		for _ = range time.Tick(interval) {
			collectCDI()
		}
	}()
	err := http.ListenAndServe(bind, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(cdi)
	}))
	if err != nil {
		panic(err)
	}
}
<commit_msg>cdi: add handle to calculate and display cdi equivalence<commit_after>// Copyright 2014 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This bot collects current interest rate for CDI, from Cetip's home page, and
// provides an HTTP server for serving the current annual and daily rate.
package main

import (
	"code.google.com/p/cascadia"
	"code.google.com/p/go.net/html"
	"encoding/json"
	"flag"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type AnnualInterest struct {
	Year float64
	Day  float64
}

func (i *AnnualInterest) CalculateDay() {
	i.Day = (math.Pow(1+i.Year/100, 1.0/252.0) - 1) * 100
}

func (i *AnnualInterest) CalculateYear() {
	i.Year = (math.Pow(1+i.Day/100, 252.0) - 1) * 100
}

func (i *AnnualInterest) Equiv(value float64) AnnualInterest {
	var equiv AnnualInterest
	equiv.Day = value * i.Day
	equiv.CalculateYear()
	return equiv
}

var cdi AnnualInterest

var (
	interval time.Duration
	bind     string
)

func init() {
	flag.DurationVar(&interval, "interval", time.Hour, "Interval between updates in the value")
	flag.StringVar(&bind, "bind", "0.0.0.0:5555", "Address to bind")
}

func collectCDI() {
	resp, err := http.Get("http://www.cetip.com.br/Home")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	selector := cascadia.MustCompile("#ctl00_Banner_lblTaxDI")
	root, err := html.Parse(resp.Body)
	if err != nil {
		panic(err)
	}
	node := selector.MatchFirst(root)
	interest := strings.TrimRight(node.FirstChild.Data, "%")
	interest = strings.Replace(interest, ",", ".", 1)
	if value, err := strconv.ParseFloat(interest, 64); err == nil {
		cdi.Year = value
		cdi.CalculateDay()
	}
}

func cdiHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(cdi)
}

func equivHandler(w http.ResponseWriter, r *http.Request) {
	queryValue := r.URL.Query().Get("value")
	value, err := strconv.ParseFloat(queryValue, 64)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if value > 100 {
		value /= 100
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(cdi.Equiv(value))
}

func main() {
	flag.Parse()
	collectCDI()
	go func() {
		for _ = range time.Tick(interval) {
			collectCDI()
		}
	}()
	http.HandleFunc("/equiv", equivHandler)
	http.HandleFunc("/", cdiHandler)
	err := http.ListenAndServe(bind, nil)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package fire

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"github.com/256dpi/jsonapi/v2"
	"go.mongodb.org/mongo-driver/bson"

	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/stick"
)

// ErrAccessDenied can be returned by any callback to deny access.
var ErrAccessDenied = jsonapi.ErrorFromStatus(http.StatusUnauthorized, "access denied")

// BasicAuthorizer authorizes requests based on a simple credentials list.
func BasicAuthorizer(credentials map[string]string) *Callback {
	return C("fire/BasicAuthorizer", All(), func(ctx *Context) error {
		// check for credentials
		user, password, ok := ctx.HTTPRequest.BasicAuth()
		if !ok {
			return ErrAccessDenied
		}

		// check if credentials match
		if val, ok := credentials[user]; !ok || val != password {
			return ErrAccessDenied
		}

		return nil
	})
}

// ModelValidator performs a validation of the model using the Validate method.
func ModelValidator() *Callback {
	return C("fire/ModelValidator", Only(Create, Update), func(ctx *Context) error {
		// check model
		m, ok := ctx.Model.(coal.ValidatableModel)
		if !ok {
			return fmt.Errorf("model is not validatable")
		}

		// validate model
		err := m.Validate()
		if err != nil {
			return err
		}

		return nil
	})
}

// TimestampValidator will set timestamp fields on create and update operations.
// The fields are inferred from the model using the "fire-created-timestamp" and
// "fire-updated-timestamp" flags. Missing created timestamps are retroactively
// set using the timestamp encoded in the model id.
func TimestampValidator() *Callback {
	return C("fire/TimestampValidator", Only(Create, Update), func(ctx *Context) error {
		// get time
		now := time.Now()

		// get timestamp fields
		ctf := coal.L(ctx.Model, "fire-created-timestamp", false)
		utf := coal.L(ctx.Model, "fire-updated-timestamp", false)

		// set created timestamp on creation and set missing create timestamps
		// to the timestamp inferred from the model id
		if ctf != "" {
			if ctx.Operation == Create {
				stick.MustSet(ctx.Model, ctf, now)
			} else if t := stick.MustGet(ctx.Model, ctf).(time.Time); t.IsZero() {
				stick.MustSet(ctx.Model, ctf, ctx.Model.ID().Timestamp())
			}
		}

		// always set updated timestamp
		if utf != "" {
			stick.MustSet(ctx.Model, utf, now)
		}

		return nil
	})
}

// NoDefault marks the specified field to have no default that needs to be
// enforced while executing the ProtectedFieldsValidator.
const NoDefault noDefault = iota

type noDefault int

// ProtectedFieldsValidator compares protected fields against their default
// during Create (if provided) or stored value during Update and returns an error
// if they have been changed.
//
// Protected fields are defined by passing pairs of fields and default values:
//
//	fire.ProtectedFieldsValidator(map[string]interface{}{
//		"Title": NoDefault, // can only be set during Create
//		"Link":  "",        // default is fixed and cannot be changed
//	})
//
// The special NoDefault value can be provided to skip the default enforcement
// on Create.
func ProtectedFieldsValidator(pairs map[string]interface{}) *Callback {
	return C("fire/ProtectedFieldsValidator", Only(Create, Update), func(ctx *Context) error {
		// handle resource creation
		if ctx.Operation == Create {
			// check all fields
			for field, def := range pairs {
				// skip fields that have no default
				if def == NoDefault {
					continue
				}

				// check equality
				if !reflect.DeepEqual(stick.MustGet(ctx.Model, field), def) {
					return E("field " + field + " is protected")
				}
			}
		}

		// handle resource updates
		if ctx.Operation == Update {
			// check all fields
			for field := range pairs {
				// check equality
				if !reflect.DeepEqual(stick.MustGet(ctx.Model, field), stick.MustGet(ctx.Original, field)) {
					return E("field " + field + " is protected")
				}
			}
		}

		return nil
	})
}

// DependentResourcesValidator counts related documents and returns an error if
// some are found. This callback is meant to protect resources from breaking
// relations when requested to be deleted.
//
// Dependent resources are defined by passing pairs of models and fields that
// reference the current model.
//
//	fire.DependentResourcesValidator(map[coal.Model]string{
//		&Post{}:    "Author",
//		&Comment{}: "Author",
//	})
//
// The callback supports models that use the soft delete mechanism.
func DependentResourcesValidator(pairs map[coal.Model]string) *Callback {
	return C("fire/DependentResourcesValidator", Only(Delete), func(ctx *Context) error {
		// check all relations
		for model, field := range pairs {
			// prepare query
			query := bson.M{
				field: ctx.Model.ID(),
			}

			// exclude soft deleted documents if supported
			if sdf := coal.L(model, "fire-soft-delete", false); sdf != "" {
				query[sdf] = nil
			}

			// count referencing documents
			count, err := ctx.M(model).Count(ctx, query, 0, 1, false)
			if err != nil {
				return err
			}

			// return err of documents are found
			if count != 0 {
				return E("resource has dependent resources")
			}
		}

		// pass validation
		return nil
	})
}

// ReferencedResourcesValidator makes sure all references in the document are
// existing by counting the referenced documents.
//
// References are defined by passing pairs of fields and models which are
// referenced by the current model:
//
//	fire.ReferencedResourcesValidator(map[string]coal.Model{
//		"Post":   &Post{},
//		"Author": &User{},
//	})
//
// The callbacks supports to-one, optional to-one and to-many relationships.
func ReferencedResourcesValidator(pairs map[string]coal.Model) *Callback {
	return C("fire/ReferencedResourcesValidator", Only(Create, Update), func(ctx *Context) error {
		// check all references
		for field, collection := range pairs {
			// read referenced id
			ref := stick.MustGet(ctx.Model, field)

			// continue if reference is not set
			if oid, ok := ref.(*coal.ID); ok && oid == nil {
				continue
			}

			// continue if slice is empty
			if ids, ok := ref.([]coal.ID); ok && ids == nil {
				continue
			}

			// handle to-many relationships
			if ids, ok := ref.([]coal.ID); ok {
				// prepare query
				query := bson.M{
					"_id": bson.M{
						"$in": ids,
					},
				}

				// count entities in database
				count, err := ctx.M(collection).Count(ctx, query, 0, 0, false)
				if err != nil {
					return err
				}

				// check for existence
				if int(count) != len(ids) {
					return E("missing references for field " + field)
				}

				continue
			}

			// handle to-one relationships

			// count entities in database
			count, err := ctx.M(collection).Count(ctx, bson.M{
				"_id": ref,
			}, 0, 1, false)
			if err != nil {
				return err
			}

			// check for existence
			if count != 1 {
				return E("missing reference for field " + field)
			}
		}

		// pass validation
		return nil
	})
}

// RelationshipValidator makes sure all relationships of a model are correct and
// in place. It does so by combining a DependentResourcesValidator and a
// ReferencedResourcesValidator based on the specified model and catalog.
func RelationshipValidator(model coal.Model, catalog *coal.Catalog, excludedFields ...string) *Callback {
	// prepare lists
	dependentResources := make(map[coal.Model]string)
	references := make(map[string]coal.Model)

	// iterate through all fields
	for _, field := range coal.GetMeta(model).Relationships {
		// exclude field if requested
		if stick.Contains(excludedFields, field.Name) {
			continue
		}

		// handle has-one and has-many relationships
		if field.HasOne || field.HasMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic(fmt.Sprintf(`fire: missing model in catalog: "%s"`, field.RelType))
			}

			// get related bson field
			bsonField := ""
			for _, relatedField := range coal.GetMeta(relatedModel).Relationships {
				if relatedField.RelName == field.RelInverse {
					bsonField = relatedField.Name
				}
			}
			if bsonField == "" {
				panic(fmt.Sprintf(`fire: missing field for inverse relationship: "%s"`, field.RelInverse))
			}

			// add relationship
			dependentResources[relatedModel] = bsonField
		}

		// handle to-one and to-many relationships
		if field.ToOne || field.ToMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic(fmt.Sprintf(`fire: missing model in catalog: "%s"`, field.RelType))
			}

			// add relationship
			references[field.Name] = relatedModel
		}
	}

	// create callbacks
	cb1 := DependentResourcesValidator(dependentResources)
	cb2 := ReferencedResourcesValidator(references)

	return C("fire/RelationshipValidator", func(ctx *Context) bool {
		return cb1.Matcher(ctx) || cb2.Matcher(ctx)
	}, func(ctx *Context) error {
		// run dependent resources validator
		if cb1.Matcher(ctx) {
			err := cb1.Handler(ctx)
			if err != nil {
				return err
			}
		}

		// run dependent resources validator
		if cb2.Matcher(ctx) {
			err := cb2.Handler(ctx)
			if err != nil {
				return err
			}
		}

		return nil
	})
}

// MatchingReferencesValidator compares the model with one related model or all
// related models and checks if the specified references are shared exactly.
//
// The target is defined by passing the reference on the current model and the
// target model. The matcher is defined by passing pairs of fields on the current
// and target model:
//
//	fire.MatchingReferencesValidator("Blog", &Blog{}, map[string]string{
//		"Owner": "Owner",
//	})
//
// To-many, optional to-many and has-many relationships are supported both for
// the initial reference and in the matchers.
func MatchingReferencesValidator(reference string, target coal.Model, matcher map[string]string) *Callback {
	return C("fire/MatchingReferencesValidator", Only(Create, Update), func(ctx *Context) error {
		// prepare ids
		var ids []coal.ID

		// get reference
		ref := stick.MustGet(ctx.Model, reference)

		// handle to-one reference
		if id, ok := ref.(coal.ID); ok {
			ids = []coal.ID{id}
		}

		// handle optional to-one reference
		if oid, ok := ref.(*coal.ID); ok {
			// return immediately if not set
			if oid == nil {
				return nil
			}

			// set id
			ids = []coal.ID{*oid}
		}

		// handle to-many reference
		if list, ok := ref.([]coal.ID); ok {
			// return immediately if empty
			if len(list) == 0 {
				return nil
			}

			// set list
			ids = list
		}

		// ensure list is unique
		ids = coal.Unique(ids)

		// prepare query
		query := bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}

		// add matchers
		for sourceField, targetField := range matcher {
			query[targetField] = stick.MustGet(ctx.Model, sourceField)
		}

		// find matching documents
		count, err := ctx.M(target).Count(ctx, query, 0, 0, false)
		if err != nil {
			return err
		}

		// return error if a document is missing (does not match)
		if int(count) != len(ids) {
			return E("references do not match")
		}

		return nil
	})
}
<commit_msg>little cleanup<commit_after>package fire

import (
	"fmt"
	"net/http"
	"reflect"
	"time"

	"github.com/256dpi/jsonapi/v2"
	"go.mongodb.org/mongo-driver/bson"

	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/stick"
)

// ErrAccessDenied can be returned by any callback to deny access.
var ErrAccessDenied = jsonapi.ErrorFromStatus(http.StatusUnauthorized, "access denied")

// BasicAuthorizer authorizes requests based on a simple credentials list.
func BasicAuthorizer(credentials map[string]string) *Callback {
	return C("fire/BasicAuthorizer", All(), func(ctx *Context) error {
		// check for credentials
		user, password, ok := ctx.HTTPRequest.BasicAuth()
		if !ok {
			return ErrAccessDenied
		}

		// check if credentials match
		if val, ok := credentials[user]; !ok || val != password {
			return ErrAccessDenied
		}

		return nil
	})
}

// ModelValidator performs a validation of the model using the Validate method.
func ModelValidator() *Callback {
	return C("fire/ModelValidator", Only(Create, Update), func(ctx *Context) error {
		// check model
		m, ok := ctx.Model.(coal.ValidatableModel)
		if !ok {
			return fmt.Errorf("model is not validatable")
		}

		// validate model
		err := m.Validate()
		if err != nil {
			return err
		}

		return nil
	})
}

// TimestampValidator will set timestamp fields on create and update operations.
// The fields are inferred from the model using the "fire-created-timestamp" and
// "fire-updated-timestamp" flags. Missing created timestamps are retroactively
// set using the timestamp encoded in the model id.
func TimestampValidator() *Callback {
	return C("fire/TimestampValidator", Only(Create, Update), func(ctx *Context) error {
		// get time
		now := time.Now()

		// get timestamp fields
		ctf := coal.L(ctx.Model, "fire-created-timestamp", false)
		utf := coal.L(ctx.Model, "fire-updated-timestamp", false)

		// set created timestamp on creation and set missing create timestamps
		// to the timestamp inferred from the model id
		if ctf != "" {
			if ctx.Operation == Create {
				stick.MustSet(ctx.Model, ctf, now)
			} else if t := stick.MustGet(ctx.Model, ctf).(time.Time); t.IsZero() {
				stick.MustSet(ctx.Model, ctf, ctx.Model.ID().Timestamp())
			}
		}

		// always set updated timestamp
		if utf != "" {
			stick.MustSet(ctx.Model, utf, now)
		}

		return nil
	})
}

// NoDefault marks the specified field to have no default that needs to be
// enforced while executing the ProtectedFieldsValidator.
const NoDefault noDefault = iota

type noDefault int

// ProtectedFieldsValidator compares protected fields against their default
// during Create (if provided) or stored value during Update and returns an error
// if they have been changed.
//
// Protected fields are defined by passing pairs of fields and default values:
//
//	fire.ProtectedFieldsValidator(map[string]interface{}{
//		"Title": NoDefault, // can only be set during Create
//		"Link":  "",        // default is fixed and cannot be changed
//	})
//
// The special NoDefault value can be provided to skip the default enforcement
// on Create.
func ProtectedFieldsValidator(pairs map[string]interface{}) *Callback {
	return C("fire/ProtectedFieldsValidator", Only(Create, Update), func(ctx *Context) error {
		// handle resource creation
		if ctx.Operation == Create {
			// check all fields
			for field, def := range pairs {
				// skip fields that have no default
				if def == NoDefault {
					continue
				}

				// check equality
				if !reflect.DeepEqual(stick.MustGet(ctx.Model, field), def) {
					return E("field " + field + " is protected")
				}
			}
		}

		// handle resource updates
		if ctx.Operation == Update {
			// check all fields
			for field := range pairs {
				// check equality
				if !reflect.DeepEqual(stick.MustGet(ctx.Model, field), stick.MustGet(ctx.Original, field)) {
					return E("field " + field + " is protected")
				}
			}
		}

		return nil
	})
}

// DependentResourcesValidator counts related documents and returns an error if
// some are found. This callback is meant to protect resources from breaking
// relations when requested to be deleted.
//
// Dependent resources are defined by passing pairs of models and fields that
// reference the current model.
//
//	fire.DependentResourcesValidator(map[coal.Model]string{
//		&Post{}:    "Author",
//		&Comment{}: "Author",
//	})
//
// The callback supports models that use the soft delete mechanism.
func DependentResourcesValidator(pairs map[coal.Model]string) *Callback {
	return C("fire/DependentResourcesValidator", Only(Delete), func(ctx *Context) error {
		// check all relations
		for model, field := range pairs {
			// prepare query
			query := bson.M{
				field: ctx.Model.ID(),
			}

			// exclude soft deleted documents if supported
			if sdf := coal.L(model, "fire-soft-delete", false); sdf != "" {
				query[sdf] = nil
			}

			// count referencing documents
			count, err := ctx.M(model).Count(ctx, query, 0, 1, false)
			if err != nil {
				return err
			}

			// return error if documents are found
			if count != 0 {
				return E("resource has dependent resources")
			}
		}

		// pass validation
		return nil
	})
}

// ReferencedResourcesValidator makes sure all references in the document are
// existing by counting the referenced documents.
//
// References are defined by passing pairs of fields and models which are
// referenced by the current model:
//
//	fire.ReferencedResourcesValidator(map[string]coal.Model{
//		"Post":   &Post{},
//		"Author": &User{},
//	})
//
// The callbacks supports to-one, optional to-one and to-many relationships.
func ReferencedResourcesValidator(pairs map[string]coal.Model) *Callback {
	return C("fire/ReferencedResourcesValidator", Only(Create, Update), func(ctx *Context) error {
		// check all references
		for field, collection := range pairs {
			// read referenced id
			ref := stick.MustGet(ctx.Model, field)

			// continue if reference is not set
			if oid, ok := ref.(*coal.ID); ok && oid == nil {
				continue
			}

			// continue if slice is empty
			if ids, ok := ref.([]coal.ID); ok && ids == nil {
				continue
			}

			// handle to-many relationships
			if ids, ok := ref.([]coal.ID); ok {
				// prepare query
				query := bson.M{
					"_id": bson.M{
						"$in": ids,
					},
				}

				// count entities in database
				count, err := ctx.M(collection).Count(ctx, query, 0, 0, false)
				if err != nil {
					return err
				}

				// check for existence
				if int(count) != len(ids) {
					return E("missing references for field " + field)
				}

				continue
			}

			// handle to-one relationships

			// count entities in database
			count, err := ctx.M(collection).Count(ctx, bson.M{
				"_id": ref,
			}, 0, 1, false)
			if err != nil {
				return err
			}

			// check for existence
			if count != 1 {
				return E("missing reference for field " + field)
			}
		}

		// pass validation
		return nil
	})
}

// RelationshipValidator makes sure all relationships of a model are correct and
// in place. It does so by combining a DependentResourcesValidator and a
// ReferencedResourcesValidator based on the specified model and catalog.
func RelationshipValidator(model coal.Model, catalog *coal.Catalog, exclude ...string) *Callback {
	// prepare lists
	resources := make(map[coal.Model]string)
	references := make(map[string]coal.Model)

	// iterate through all fields
	for _, field := range coal.GetMeta(model).Relationships {
		// continue if relationship is excluded
		if stick.Contains(exclude, field.Name) {
			continue
		}

		// handle has-one and has-many relationships
		if field.HasOne || field.HasMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic(fmt.Sprintf(`fire: missing model in catalog: "%s"`, field.RelType))
			}

			// get related field
			relatedField := ""
			for _, relationship := range coal.GetMeta(relatedModel).Relationships {
				if relationship.RelName == field.RelInverse {
					relatedField = relationship.Name
				}
			}
			if relatedField == "" {
				panic(fmt.Sprintf(`fire: missing field for inverse relationship: "%s"`, field.RelInverse))
			}

			// add resource
			resources[relatedModel] = relatedField
		}

		// handle to-one and to-many relationships
		if field.ToOne || field.ToMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic(fmt.Sprintf(`fire: missing model in catalog: "%s"`, field.RelType))
			}

			// add reference
			references[field.Name] = relatedModel
		}
	}

	// create callbacks
	drv := DependentResourcesValidator(resources)
	rrv := ReferencedResourcesValidator(references)

	return C("fire/RelationshipValidator", func(ctx *Context) bool {
		return drv.Matcher(ctx) || rrv.Matcher(ctx)
	}, func(ctx *Context) error {
		// run dependent resources validator
		if drv.Matcher(ctx) {
			err := drv.Handler(ctx)
			if err != nil {
				return err
			}
		}

		// run referenced resources validator
		if rrv.Matcher(ctx) {
			err := rrv.Handler(ctx)
			if err != nil {
				return err
			}
		}

		return nil
	})
}

// MatchingReferencesValidator compares the model with one related model or all
// related models and checks if the specified references are shared exactly.
//
// The target is defined by passing the reference on the current model and the
// target model. The matcher is defined by passing pairs of fields on the current
// and target model:
//
//	fire.MatchingReferencesValidator("Blog", &Blog{}, map[string]string{
//		"Owner": "Owner",
//	})
//
// To-many, optional to-many and has-many relationships are supported both for
// the initial reference and in the matchers.
func MatchingReferencesValidator(reference string, target coal.Model, matcher map[string]string) *Callback {
	return C("fire/MatchingReferencesValidator", Only(Create, Update), func(ctx *Context) error {
		// prepare ids
		var ids []coal.ID

		// get reference
		ref := stick.MustGet(ctx.Model, reference)

		// handle to-one reference
		if id, ok := ref.(coal.ID); ok {
			ids = []coal.ID{id}
		}

		// handle optional to-one reference
		if oid, ok := ref.(*coal.ID); ok {
			// return immediately if not set
			if oid == nil {
				return nil
			}

			// set id
			ids = []coal.ID{*oid}
		}

		// handle to-many reference
		if list, ok := ref.([]coal.ID); ok {
			// return immediately if empty
			if len(list) == 0 {
				return nil
			}

			// set list
			ids = list
		}

		// ensure list is unique
		ids = coal.Unique(ids)

		// prepare query
		query := bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}

		// add matchers
		for sourceField, targetField := range matcher {
			query[targetField] = stick.MustGet(ctx.Model, sourceField)
		}

		// find matching documents
		count, err := ctx.M(target).Count(ctx, query, 0, 0, false)
		if err != nil {
			return err
		}

		// return error if a document is missing (does not match)
		if int(count) != len(ids) {
			return E("references do not match")
		}

		return nil
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
)

func gatherResources(s *state) map[string][]string {
	groups := make(map[string][]string, 0)
	for _, res := range s.resources() {
		for _, grp := range res.Groups() {
			tmpGroup := []string{}

			_, ok := groups[grp]
			if ok {
				tmpGroup = groups[grp].([]string)
			}

			tmpGroup = append(tmpGroup, res.Address())
			groups[grp] = tmpGroup
		}
	}

	if(len(s.outputs()) > 0) {
		groups["all"] = make(map[string]string, 0)
		for _, out := range s.outputs() {
			groups["all"].(map[string]string)[out.keyName] = out.value
		}
	}
    return groups
}

func cmdList(stdout io.Writer, stderr io.Writer, s *state) int {
	return output(stdout, stderr, gatherResources(s))
}

func cmdInventory(stdout io.Writer, stderr io.Writer, s *state) int {
    groups := gatherResources(s)
    for group, res := range groups {

        _, err := io.WriteString(stdout, "["+group+"]\n")
        if err != nil {
		    fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
            return 1;
        }

        for _, ress := range res {

            _, err := io.WriteString(stdout, ress + "\n")
            if err != nil {
		        fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
                return 1;
            }
        }

        _, err = io.WriteString(stdout, "\n")
        if err != nil {
		    fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
            return 1;
        }
    }

    return 0;
}

func cmdHost(stdout io.Writer, stderr io.Writer, s *state, hostname string) int {
	for _, res := range s.resources() {
		if hostname == res.Address() {
			return output(stdout, stderr, res.Attributes())
		}
	}

	fmt.Fprintf(stderr, "No such host: %s\n", hostname)
	return 1
}

// output marshals an arbitrary JSON object and writes it to stdout, or writes
// an error to stderr, then returns the appropriate exit code.
func output(stdout io.Writer, stderr io.Writer, whatever interface{}) int {
	b, err := json.Marshal(whatever)
	if err != nil {
		fmt.Fprintf(stderr, "Error encoding JSON: %s\n", err)
		return 1
	}

	_, err = stdout.Write(b)
	if err != nil {
		fmt.Fprintf(stderr, "Error writing JSON: %s\n", err)
		return 1
	}

	return 0
}
<commit_msg>Made compatible with pull request #31<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
)

func gatherResources(s *state) map[string]interface{} {
	groups := make(map[string]interface{}, 0)
for _, res := range s.resources() {
	for _, grp := range res.Groups() {

		_, ok := groups[grp]
		if !ok {
			groups[grp] = []string{}
		}

		groups[grp] = append(groups[grp].([]string), res.Address())
	}
}

	if(len(s.outputs()) > 0) {
		groups["all"] = make(map[string]string, 0)
		for _, out := range s.outputs() {
			groups["all"].(map[string]string)[out.keyName] = out.value
		}
	}
    return groups
}

func cmdList(stdout io.Writer, stderr io.Writer, s *state) int {
	return output(stdout, stderr, gatherResources(s))
}

func cmdInventory(stdout io.Writer, stderr io.Writer, s *state) int {
    groups := gatherResources(s)
    for group, res := range groups {

        _, err := io.WriteString(stdout, "["+group+"]\n")
        if err != nil {
		    fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
            return 1;
        }

        for _, ress := range res.([]string) {

            _, err := io.WriteString(stdout, ress + "\n")
            if err != nil {
		        fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
                return 1;
            }
        }

        _, err = io.WriteString(stdout, "\n")
        if err != nil {
		    fmt.Fprintf(stderr, "Error writing Invetory: %s\n", err)
            return 1;
        }
    }

    return 0;
}

func cmdHost(stdout io.Writer, stderr io.Writer, s *state, hostname string) int {
	for _, res := range s.resources() {
		if hostname == res.Address() {
			return output(stdout, stderr, res.Attributes())
		}
	}

	fmt.Fprintf(stderr, "No such host: %s\n", hostname)
	return 1
}

// output marshals an arbitrary JSON object and writes it to stdout, or writes
// an error to stderr, then returns the appropriate exit code.
func output(stdout io.Writer, stderr io.Writer, whatever interface{}) int {
	b, err := json.Marshal(whatever)
	if err != nil {
		fmt.Fprintf(stderr, "Error encoding JSON: %s\n", err)
		return 1
	}

	_, err = stdout.Write(b)
	if err != nil {
		fmt.Fprintf(stderr, "Error writing JSON: %s\n", err)
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is environmental var to set GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is environmental var to set GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise user.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is environmental var to handle debug mode
	EnvDebug      = "GHR_DEBUG"
	EnvStackTrace = "GHR_TRACE"
)

// Exit codes are in value that represnet an exit code for a paticular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeRleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	DefaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintErrorf prints red error message on console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf("[red]%s[reset]"+format, args...)))
}

// CLI is the command line object
type CLI struct {
	// outStream and errStream are the stdout and stderr
	// to write message from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", DefaultParallel, "")
	flags.IntVar(&parallel, "p", DefaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flag
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner name.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
		}

		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository owner name not found\n")
			fmt.Fprintf(cli.errStream,
				"Please set it via `-u` option.\n\n"+
					"You can set default owner name in `github.username` or `user.name`\n"+
					"in `~/.gitconfig` file")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub api token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API. Base URL can be provided via env var. This is for GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	// Prepare create release request
	req := &github.RepositoryRelease{
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
	}

	ctx := context.TODO()
	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s", err)
		return ExitCodeError
	}

	return ExitCodeOK
}

// maskString is used to mask string which should not be displayed
// directly like auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository onwer name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

`
<commit_msg>Fix format<commit_after>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is environmental var to set GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is environmental var to set GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise user.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is environmental var to handle debug mode
	EnvDebug      = "GHR_DEBUG"
	EnvStackTrace = "GHR_TRACE"
)

// Exit codes are in value that represnet an exit code for a paticular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeRleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	DefaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintErrorf prints red error message on console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the command line object
type CLI struct {
	// outStream and errStream are the stdout and stderr
	// to write message from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", DefaultParallel, "")
	flags.IntVar(&parallel, "p", DefaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flag
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner name.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
		}

		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository owner name not found\n")
			fmt.Fprintf(cli.errStream,
				"Please set it via `-u` option.\n\n"+
					"You can set default owner name in `github.username` or `user.name`\n"+
					"in `~/.gitconfig` file")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub api token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API. Base URL can be provided via env var. This is for GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	// Prepare create release request
	req := &github.RepositoryRelease{
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
	}

	ctx := context.TODO()
	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s", err)
		return ExitCodeError
	}

	return ExitCodeOK
}

// maskString is used to mask string which should not be displayed
// directly like auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository onwer name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

`
<|endoftext|>


<commit_before>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is environmental var to set GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is environmental var to set GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise user.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is environmental var to handle debug mode
	EnvDebug      = "GHR_DEBUG"
	EnvStackTrace = "GHR_TRACE"
)

// Exit codes are in value that represnet an exit code for a paticular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeRleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	DefaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintErrorf prints red error message on console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the command line object
type CLI struct {
	// outStream and errStream are the stdout and stderr
	// to write message from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", DefaultParallel, "")
	flags.IntVar(&parallel, "p", DefaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flag
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner name.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
		}

		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository owner name not found\n")
			fmt.Fprintf(cli.errStream,
				"Please set it via `-u` option.\n\n"+
					"You can set default owner name in `github.username` or `user.name`\n"+
					"in `~/.gitconfig` file")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub api token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API. Base URL can be provided via env var. This is for GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(tag),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()
	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s", err)
		return ExitCodeError
	}

	return ExitCodeOK
}

// maskString is used to mask string which should not be displayed
// directly like auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository onwer name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

`
<commit_msg>Add a terminal line-break that is missing in some CLI error messages<commit_after>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is environmental var to set GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is environmental var to set GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise user.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is environmental var to handle debug mode
	EnvDebug      = "GHR_DEBUG"
	EnvStackTrace = "GHR_TRACE"
)

// Exit codes are in value that represnet an exit code for a paticular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeRleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	DefaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintErrorf prints red error message on console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the command line object
type CLI struct {
	// outStream and errStream are the stdout and stderr
	// to write message from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", DefaultParallel, "")
	flags.IntVar(&parallel, "p", DefaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flag
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner name.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		var err error
		owner, err = gitconfig.GithubUser()
		if err != nil {
			owner, err = gitconfig.Username()
		}

		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository owner name not found\n")
			fmt.Fprintf(cli.errStream,
				"Please set it via `-u` option.\n\n"+
					"You can set default owner name in `github.username` or `user.name`\n"+
					"in `~/.gitconfig` file\n")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeOwnerNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub api token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API. Base URL can be provided via env var. This is for GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(tag),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()
	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	return ExitCodeOK
}

// maskString is used to mask string which should not be displayed
// directly like auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository onwer name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

`
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/mitchellh/cli"
	"github.com/tcnksm/gcli/command"
)

// Run execute RunCustom() with color and output to Stdout/Stderr.
// It returns exit code.
func Run(args []string) int {

	// Meta-option for executables.
	// It defines output color and its stdout/stderr stream.
	meta := &command.Meta{
		Ui: &cli.ColoredUi{
			InfoColor:  cli.UiColorBlue,
			ErrorColor: cli.UiColorRed,
			Ui: &cli.BasicUi{
				Writer:      os.Stdout,
				ErrorWriter: os.Stderr,
				Reader:      os.Stdin,
			},
		}}

	return RunCustom(args, Commands(meta))
}

// RunCustom execute mitchellh/cli and return its exit code.
func RunCustom(args []string, commands map[string]cli.CommandFactory) int {

	cli := &cli.CLI{
		Args:       args[1:],
		Commands:   commands,
		Version:    Version,
		HelpFunc:   cli.BasicHelpFunc(Name),
		HelpWriter: os.Stdout,
	}

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to execute: %s\n", err.Error())
	}

	return exitCode
}
<commit_msg>Golint<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/mitchellh/cli"
	"github.com/tcnksm/gcli/command"
)

// Run execute RunCustom() with color and output to Stdout/Stderr.
// It returns exit code.
func Run(args []string) int {

	// Meta-option for executables.
	// It defines output color and its stdout/stderr stream.
	meta := &command.Meta{
		UI: &cli.ColoredUi{
			InfoColor:  cli.UiColorBlue,
			ErrorColor: cli.UiColorRed,
			Ui: &cli.BasicUi{
				Writer:      os.Stdout,
				ErrorWriter: os.Stderr,
				Reader:      os.Stdin,
			},
		}}

	return RunCustom(args, Commands(meta))
}

// RunCustom execute mitchellh/cli and return its exit code.
func RunCustom(args []string, commands map[string]cli.CommandFactory) int {

	cli := &cli.CLI{
		Args:       args[1:],
		Commands:   commands,
		Version:    Version,
		HelpFunc:   cli.BasicHelpFunc(Name),
		HelpWriter: os.Stdout,
	}

	exitCode, err := cli.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to execute: %s\n", err.Error())
	}

	return exitCode
}
<|endoftext|>