Example #1
0
func logsCmdRun(c *cobra.Command, args []string) {
	lv := &logsCmdLocalValues

	l, err := pv.project.Logs(pv.session, lv.name, lv.Filter)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	if lv.Follow {
		for event := range l.Tail() {
			fmt.Printf("%s", *event.Message)
		}

		if err := l.Err(); err != nil {
			log.Fatalf("error: %s", err)
		}
	}

	events, err := l.Fetch()
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	for _, event := range events {
		fmt.Printf("%s", *event.Message)
	}

}
Example #2
0
func (pv *PersistentValues) preRun(c *cobra.Command, args []string) {
	if l, err := log.ParseLevel(pv.LogLevel); err == nil {
		log.SetLevel(l)
	}

	pv.session = session.New(aws.NewConfig())

	pv.project = &project.Project{
		Log:  log.Log,
		Path: ".",
	}

	if pv.DryRun {
		log.SetLevel(log.WarnLevel)
		pv.project.Service = dryrun.New(pv.session)
		pv.project.Concurrency = 1
	} else {
		pv.project.Service = lambda.New(pv.session)
	}

	if pv.Chdir != "" {
		if err := os.Chdir(pv.Chdir); err != nil {
			log.Fatalf("error: %s", err)
		}
	}

	if err := pv.project.Open(); err != nil {
		log.Fatalf("error opening project: %s", err)
	}
}
Example #3
0
// Action is the default cli action to execute
func Action(c *cli.Context) {
	// Initialize the SSH server
	server, err := ssh2docker.NewServer()
	if err != nil {
		log.Fatalf("Cannot create server: %v", err)
	}

	// Restrict list of allowed images
	if c.String("allowed-images") != "" {
		server.AllowedImages = strings.Split(c.String("allowed-images"), ",")
	}

	// Configure server
	server.DefaultShell = c.String("shell")
	server.DockerRunArgsInline = c.String("docker-run-args")
	server.DockerExecArgsInline = c.String("docker-exec-args")
	server.NoJoin = c.Bool("no-join")
	server.CleanOnStartup = c.Bool("clean-on-startup")
	server.PasswordAuthScript = c.String("password-auth-script")
	server.PublicKeyAuthScript = c.String("publickey-auth-script")
	server.LocalUser = c.String("local-user")
	server.Banner = c.String("banner")

	// Register the SSH host key
	hostKey := c.String("host-key")
	switch hostKey {
	case "built-in":
		hostKey = DefaultHostKey
	case "system":
		hostKey = "/etc/ssh/ssh_host_rsa_key"
	}
	err = server.AddHostKey(hostKey)
	if err != nil {
		log.Fatalf("Cannot add host key: %v", err)
	}

	// Bind TCP socket
	bindAddress := c.String("bind")
	listener, err := net.Listen("tcp", bindAddress)
	if err != nil {
		log.Fatalf("Failed to start listener on %q: %v", bindAddress, err)
	}
	log.Infof("Listening on %q", bindAddress)

	// Initialize server
	if err = server.Init(); err != nil {
		log.Fatalf("Failed to initialize the server: %v", err)
	}

	// Accept new clients
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Errorf("Accept failed: %v", err)
			continue
		}
		go server.Handle(conn)
	}
}
Example #4
0
File: apex.go Project: yuishug/apex
func main() {
	args, err := docopt.Parse(usage, nil, true, version, false)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	log.SetHandler(cli.Default)

	if l, err := log.ParseLevel(args["--log-level"].(string)); err == nil {
		log.SetLevel(l)
	}

	if args["help"].(bool) {
		showHelp(args["<topic>"])
		return
	}

	session := session.New(aws.NewConfig())

	project := &project.Project{
		Log:  log.Log,
		Path: ".",
	}

	if args["--dry-run"].(bool) {
		log.SetLevel(log.WarnLevel)
		project.Service = dryrun.New(session)
		project.Concurrency = 1
	} else {
		project.Service = lambda.New(session)
	}

	if dir, ok := args["--chdir"].(string); ok {
		if err := os.Chdir(dir); err != nil {
			log.Fatalf("error: %s", err)
		}
	}

	if err := project.Open(); err != nil {
		log.Fatalf("error opening project: %s", err)
	}

	switch {
	case args["list"].(bool):
		list(project)
	case args["deploy"].(bool):
		deploy(project, args["<name>"].([]string), args["--env"].([]string))
	case args["delete"].(bool):
		delete(project, args["<name>"].([]string), args["--yes"].(bool))
	case args["invoke"].(bool):
		invoke(project, args["<name>"].([]string), args["--verbose"].(bool), args["--async"].(bool))
	case args["rollback"].(bool):
		rollback(project, args["<name>"].([]string), args["<version>"])
	case args["build"].(bool):
		build(project, args["<name>"].([]string))
	case args["logs"].(bool):
		tail(project, args["<name>"].([]string), args["--filter"].(string))
	}
}
Example #5
0
File: main.go Project: tobyjoe/grab
func downloadReleaseAsset(r *github.ReleaseAsset, newName *string, newPath *string) {
	var outDir string
	if newPath == nil {
		outDir = binDir()
	} else {
		outDir = *newPath
	}

	downloadUrlString := *r.BrowserDownloadURL

	log.Infof("Downloading release asset: %s", downloadUrlString)
	log.Infof("Copying to: %s", outDir)

	downloadUrl, err := url.Parse(downloadUrlString)
	if err != nil {
		log.Fatalf("Error: Could not parse URL: %v", err)
	}

	var fileName string
	if newName == nil {
		_, fileName = path.Split(downloadUrl.Path)
	} else {
		fileName = *newName
	}

	outName := outDir + "/" + fileName
	out, err := os.Create(outName)
	if err != nil {
		log.Fatalf("Error: Could not create local file: %v", err)
	}

	defer out.Close()
	resp, err := http.Get(downloadUrlString)
	if err != nil {
		log.Fatalf("Error: Could not get remote file: %v", err)
	}
	defer resp.Body.Close()
	bar := ioprogress.DrawTextFormatBar(20)
	progressFunc := ioprogress.DrawTerminalf(os.Stdout, func(progress, total int64) string {
		return fmt.Sprintf("%s %s %20s", fileName, bar(progress, total), ioprogress.DrawTextFormatBytes(progress, total))
	})

	progress := &ioprogress.Reader{
		Reader:       resp.Body,
		Size:         resp.ContentLength,
		DrawInterval: time.Millisecond,
		DrawFunc:     progressFunc,
	}

	_, err = io.Copy(out, progress)
	if err != nil {
		log.Fatalf("Error: Could not copy local file: %v", err)
	}

	err = os.Chmod(outName, 0755)
	if err != nil {
		log.Fatalf("Error: Could not make %s executable. Try with (sudo)?", outName)
	}
}
Example #6
0
// reads request json from stdin and outputs the responses
func invokeCmdRun(c *cobra.Command, args []string) {
	lv := &invokeCmdLocalValues
	dec := json.NewDecoder(os.Stdin)
	kind := function.RequestResponse

	if lv.Async {
		kind = function.Event
	}

	err := pv.project.LoadFunctions(lv.name)
	if err != nil {
		log.Fatalf("error: %s", err)
		return
	}

	fn := pv.project.Functions[0]

	for {
		var v map[string]interface{}
		err := dec.Decode(&v)

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("error parsing response: %s", err)
		}

		var reply, logs io.Reader

		if e, ok := v["event"].(map[string]interface{}); ok {
			reply, logs, err = fn.Invoke(e, v["context"], kind)
		} else {
			reply, logs, err = fn.Invoke(v, nil, kind)
		}

		if err != nil {
			log.Fatalf("error response: %s", err)
		}

		if lv.Logs {
			io.Copy(os.Stderr, logs)
		}

		io.Copy(os.Stdout, reply)
		fmt.Fprintf(os.Stdout, "\n")
	}
}
Example #7
0
// build outputs the generated archive to stdout.
func build(project *project.Project, name []string) {
	fn, err := project.FunctionByName(name[0])
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	zip, err := fn.Zip()
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	_, err = io.Copy(os.Stdout, zip)
	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #8
0
File: main.go Project: elizar/apex
func main() {
	log.SetHandler(cli.Default)

	if err := root.Command.Execute(); err != nil {
		log.Fatalf("Error: %s", err)
	}
}
Example #9
0
func main() {
	log.SetHandler(cli.Default)

	args := os.Args[1:]

	// Cobra does not (currently) allow us to pass flags for a sub-command
	// as if they were arguments, so we inject -- here after the first TF command.
	// TODO(tj): replace with a real solution and send PR to Cobra #251
	if len(os.Args) > 1 && os.Args[1] == "infra" {
		off := 1

	out:
		for i, a := range args {
			for _, cmd := range tf {
				if a == cmd {
					off = i
					break out
				}
			}
		}

		args = append(args[0:off], append([]string{"--"}, args[off:]...)...)
	}

	root.Command.SetArgs(args)

	if err := root.Command.Execute(); err != nil {
		log.Fatalf("Error: %s", err)
	}

	stats.Client.ConditionalFlush(500, 24*time.Hour)
}
Example #10
0
File: main.go Project: tobyjoe/grab
func binDir() string {
	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error: No working directory")
	}
	log.Infof("GOOS: %s", runtime.GOOS)

	switch runtime.GOOS {
	case "darwin":
		fallthrough
	case "dragonfly":
		fallthrough
	case "linux":
		fallthrough
	case "freebsd":
		fallthrough
	case "netbsd":
		fallthrough
	case "openbsd":
		dir = "/usr/local/bin"
		break
	default:
		break
	}
	return dir
}
Example #11
0
// rollback the function with optional version.
func rollback(project *project.Project, name []string, version interface{}) {
	fn, err := project.FunctionByName(name[0])
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	if version == nil {
		err = fn.Rollback()
	} else {
		err = fn.RollbackVersion(version.(string))
	}

	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #12
0
func metricsCmdRun(c *cobra.Command, args []string) {
	lv := &metricsCmdLocalValues

	err := pv.project.LoadFunctions(lv.name)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	fn := pv.project.Functions[0]

	d, err := time.ParseDuration(lv.duration)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	start := time.Now().UTC().Add(-d)
	end := time.Now().UTC()

	mc := &metrics.MetricCollector{
		Metrics:      []string{"Invocations", "Errors", "Duration", "Throttles"},
		Collected:    0,
		FunctionName: fn.FunctionName,
		Service:      cloudwatch.New(pv.session),
		StartDate:    start,
		EndDate:      end,
	}

	aggregated := make(map[string]aggregatedMetric)

	for n := range mc.Collect() {
		aggregated[n.Name] = aggregatedMetric{n.Name, aggregate(n.Value)}
	}

	println()
	defer println()

	for _, m := range aggregated {
		switch m.Name {
		case "Duration":
			fmt.Printf("  \033[34m%11s:\033[0m %vms\n", m.Name, m.Count)
		default:
			fmt.Printf("  \033[34m%11s:\033[0m %v\n", m.Name, m.Count)
		}
	}
}
Example #13
0
// outputs the generated archive to stdout
func buildCmdRun(c *cobra.Command, args []string) {
	lv := &buildCmdLocalValues

	fn, err := pv.project.FunctionByName(lv.name)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	zip, err := fn.Build()
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	_, err = io.Copy(os.Stdout, zip)
	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #14
0
func New(network, raddr string, priority syslog.Priority, tag string) *Handler {
	w, err := syslog.Dial(network, raddr, priority, tag)
	if err != nil {
		log.Fatalf("%v", err)
	}
	return &Handler{
		w: w,
	}
}
Example #15
0
func rollbackCmdRun(c *cobra.Command, args []string) {
	lv := &rollbackCmdLocalValues

	fn, err := pv.project.FunctionByName(lv.name)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	if lv.version == "" {
		err = fn.Rollback()
	} else {
		err = fn.RollbackVersion(lv.version)
	}

	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #16
0
// reads request json from stdin and outputs the responses
func invokeCmdRun(c *cobra.Command, args []string) {
	lv := &invokeCmdLocalValues
	dec := json.NewDecoder(os.Stdin)
	kind := function.RequestResponse

	if lv.Async {
		kind = function.Event
	}

	fn, err := pv.project.FunctionByName(lv.name)
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	for {
		var v struct {
			Event   interface{}
			Context interface{}
		}

		err := dec.Decode(&v)

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("error parsing response: %s", err)
		}

		reply, logs, err := fn.Invoke(v.Event, v.Context, kind)
		if err != nil {
			log.Fatalf("error response: %s", err)
		}

		if lv.Logs {
			io.Copy(os.Stderr, logs)
		}

		io.Copy(os.Stdout, reply)
		fmt.Fprintf(os.Stdout, "\n")
	}
}
Example #17
0
func deployCmdRun(c *cobra.Command, args []string) {
	lv := &deployCmdLocalValues

	pv.project.Concurrency = lv.concurrency

	if err := pv.project.LoadFunctions(args...); err != nil {
		log.Fatalf("error: %s", err)
		return
	}

	for _, s := range pv.Env {
		parts := strings.Split(s, "=")
		pv.project.Setenv(parts[0], parts[1])
	}

	if err := pv.project.DeployAndClean(); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #18
0
// invoke reads request json from stdin and outputs the responses.
func invoke(project *project.Project, name []string, verbose, async bool) {
	dec := json.NewDecoder(os.Stdin)
	kind := function.RequestResponse

	if async {
		kind = function.Event
	}

	fn, err := project.FunctionByName(name[0])
	if err != nil {
		log.Fatalf("error: %s", err)
	}

	for {
		var v struct {
			Event   interface{}
			Context interface{}
		}

		err := dec.Decode(&v)

		if err == io.EOF {
			break
		}

		if err != nil {
			log.Fatalf("error: %s", err)
		}

		reply, logs, err := fn.Invoke(v.Event, v.Context, kind)
		if err != nil {
			log.Fatalf("error: %s", err)
		}

		// TODO(tj) rename flag to --with-logs or --logs
		if verbose {
			io.Copy(os.Stderr, logs)
		}

		io.Copy(os.Stdout, reply)
		fmt.Fprintf(os.Stdout, "\n")
	}
}
Example #19
0
func deployCmdRun(c *cobra.Command, args []string) {
	lv := &deployCmdLocalValues

	for _, s := range pv.Env {
		parts := strings.Split(s, "=")
		pv.project.SetEnv(parts[0], parts[1])
	}

	if err := pv.project.DeployAndClean(lv.names); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #20
0
File: apex.go Project: yuishug/apex
// showHelp outputs help pulled from the GitHub wiki.
func showHelp(topic interface{}) {
	var err error

	if s, ok := topic.(string); ok {
		err = help.HelpTopic(s, os.Stdout)
	} else {
		err = help.HelpTopics(os.Stdout)
	}

	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #21
0
// deploy code and config changes.
func deploy(project *project.Project, names []string, env []string) {
	for _, s := range env {
		parts := strings.Split(s, "=")
		project.SetEnv(parts[0], parts[1])
	}

	if len(names) == 0 {
		names = project.FunctionNames()
	}

	if err := project.DeployAndClean(names); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #22
0
func main() {
	log.SetHandler(cli.New(os.Stdout))
	log.SetLevel(log.DebugLevel)

	s, err := config.LoadConfig(ConfigurationFileName)
	if err != nil {
		log.Warnf("Unable to read configuration file: %s", err.Error())
	}

	d, err := store.Init(s.Location.Database, 2500)
	if err != nil {
		log.Fatalf("Unable to connect to data store at %s: %s", s.Location.Database, err.Error())
	}

	r := gin.New()

	// If redirects to the secure are enabled, attach the secure middleware helper
	if s.Bind.Redirect {
		log.Debug("Secure redirects enabled")
		r.Use(secure.RedirectToSecureByProxy(s.Domain, s.Bind.Ports.Secure, s.Bind.Proxy.Secure))
	}

	r.Use(gin.Logger())
	r.Use(gin.Recovery())
	// Add out own Middleware
	r.Use(store.Connect(d))

	// Connect the relevant modules to the router
	alive.Init(r)

	log.Debug("Starting Run()")
	err = r.Run(s.Bind.Address + ":" + strconv.FormatInt(int64(s.Bind.Ports.Standard), 10))
	if err != nil {
		log.Fatalf("Fatal error during Run: %s", err.Error())
	}

}
Example #23
0
func wikiCmdRun(c *cobra.Command, args []string) {
	lv := &wikiCmdLocalValues

	var err error

	if lv.topic != "" {
		err = wiki.Topic(lv.topic, os.Stdout)
	} else {
		err = wiki.Topics(os.Stdout)
	}

	if err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #24
0
func deleteCmdRun(c *cobra.Command, args []string) {
	lv := &deleteCmdLocalValues

	if err := pv.project.LoadFunctions(args...); err != nil {
		log.Fatalf("error: %s", err)
		return
	}

	if !lv.Force && len(pv.project.Functions) > 1 {
		fmt.Printf("The following will be deleted:\n\n")
		for _, fn := range pv.project.Functions {
			fmt.Printf("  - %s\n", fn.Name)
		}
		fmt.Printf("\n")
	}

	if !lv.Force && !prompt.Confirm("Are you sure? (yes/no)") {
		return
	}

	if err := pv.project.Delete(); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #25
0
// delete the functions.
func delete(project *project.Project, names []string, force bool) {
	if len(names) == 0 {
		names = project.FunctionNames()
	}

	if !force && len(names) > 1 {
		fmt.Printf("The following will be deleted:\n\n")
		for _, name := range names {
			fmt.Printf("  - %s\n", name)
		}
		fmt.Printf("\n")
	}

	if !force && !prompt.Confirm("Are you sure? (yes/no)") {
		return
	}

	if err := project.Delete(names); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #26
0
// tail outputs logs with optional filter pattern.
func tail(project *project.Project, name []string, filter string) {
	service := cloudwatchlogs.New(session.New(aws.NewConfig()))

	// TODO(tj): refactor logs.Logs to take Project so this hack
	// can be removed, it'll also make multi-function tailing easier
	group := fmt.Sprintf("/aws/lambda/%s_%s", project.Name, name[0])

	l := logs.Logs{
		LogGroupName:  group,
		FilterPattern: filter,
		Service:       service,
		Log:           log.Log,
	}

	for event := range l.Tail() {
		fmt.Printf("%s", *event.Message)
	}

	if err := l.Err(); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #27
0
func logsCmdRun(c *cobra.Command, args []string) {
	lv := &logsCmdLocalValues
	service := cloudwatchlogs.New(pv.session)

	// TODO(tj): refactor logs.Logs to take Project so this hack
	// can be removed, it'll also make multi-function tailing easier
	group := fmt.Sprintf("/aws/lambda/%s_%s", pv.project.Name, lv.name)

	l := logs.Logs{
		LogGroupName:  group,
		FilterPattern: lv.Filter,
		Service:       service,
		Log:           log.Log,
	}

	for event := range l.Tail() {
		fmt.Printf("%s", *event.Message)
	}

	if err := l.Err(); err != nil {
		log.Fatalf("error: %s", err)
	}
}
Example #28
0
File: list.go Project: kujohn/apex
// outputList format.
func outputList() {
	fmt.Println()
	for _, fn := range root.Project.Functions {
		fmt.Printf("  \033[%dm%s\033[0m\n", colors.Blue, fn.Name)
		if fn.Description != "" {
			fmt.Printf("    description: %v\n", fn.Description)
		}
		fmt.Printf("    runtime: %v\n", fn.Runtime)
		fmt.Printf("    memory: %vmd\n", fn.Memory)
		fmt.Printf("    timeout: %vs\n", fn.Timeout)
		fmt.Printf("    role: %v\n", fn.Role)
		fmt.Printf("    handler: %v\n", fn.Handler)

		config, err := fn.GetConfigCurrent()
		if err != nil {
			log.Fatalf("error: %s", err)
		}

		fmt.Printf("    current version: %s\n", *config.Configuration.Version)

		fmt.Println()
	}
}
Example #29
0
func (c *Consumer) handlerLoop(shardID string, handler Handler) {
	buf := &Buffer{
		MaxRecordCount: c.BufferSize,
		shardID:        shardID,
	}

	params := &kinesis.GetShardIteratorInput{
		ShardId:    aws.String(shardID),
		StreamName: aws.String(c.StreamName),
	}

	if c.Checkpoint.CheckpointExists(shardID) {
		params.ShardIteratorType = aws.String("AFTER_SEQUENCE_NUMBER")
		params.StartingSequenceNumber = aws.String(c.Checkpoint.SequenceNumber())
	} else {
		params.ShardIteratorType = aws.String("TRIM_HORIZON")
	}

	resp, err := c.svc.GetShardIterator(params)
	if err != nil {
		c.Logger.WithError(err).Error("GetShardIterator")
		os.Exit(1)
	}

	shardIterator := resp.ShardIterator

	ctx := c.Logger.WithFields(log.Fields{
		"shard": shardID,
	})

	ctx.Info("processing")

	for {
		resp, err := c.svc.GetRecords(
			&kinesis.GetRecordsInput{
				ShardIterator: shardIterator,
			},
		)

		if err != nil {
			log.Fatalf("Error GetRecords %v", err)
		}

		if len(resp.Records) > 0 {
			for _, r := range resp.Records {
				buf.AddRecord(r)

				if buf.ShouldFlush() {
					handler.HandleRecords(*buf)
					ctx.WithField("count", buf.RecordCount()).Info("flushed")
					c.Checkpoint.SetCheckpoint(shardID, buf.LastSeq())
					buf.Flush()
				}
			}
		} else if resp.NextShardIterator == aws.String("") || shardIterator == resp.NextShardIterator {
			c.Logger.Error("NextShardIterator")
			os.Exit(1)
		}

		shardIterator = resp.NextShardIterator
	}
}
Example #30
0
File: main.go Project: tobyjoe/grab
func main() {
	renameFlag := flag.String("r", "", "-r [newname]")
	pathFlag := flag.String("p", "", "-p [/some/path/]")
	dryRunFlag := flag.Bool("d", false, "-d")
	flag.Usage = func() {
		println(USAGE)
	}

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

	log.Infof("Flags: rename(%s), path(%s), dry(%t)", *renameFlag, *pathFlag, *dryRunFlag)
	log.Infof("Args : %s", strings.Join(args, ", "))

	if len(args) < 1 {
		log.Fatal(USAGE)
	}

	project := strings.TrimSpace(args[0])
	if project == "" {
		log.Fatal(USAGE)
	}

	parts := strings.Split(project, "/")
	if len(parts) != 2 {
		log.Fatal(USAGE)
	}

	owner := strings.TrimSpace(parts[0])
	repo := strings.TrimSpace(parts[1])
	if owner == "" || repo == "" {
		log.Fatal(USAGE)
	}

	projectUrl := fmt.Sprintf("github.com/%s/%s", owner, repo)

	client := github.NewClient(nil)

	log.Infof("Grabbing from %s...", project)

	_, resp, err := client.Repositories.Get(owner, repo)
	if err != nil {
		if resp == nil {
			log.Fatalf("Error: Cannot reach %s", projectUrl)
		}

		if resp.StatusCode == 404 {
			log.Fatalf("Error: Project does not exist: %s", projectUrl)
		} else {
			log.Fatalf("Error: Unknown error (%d) reaching: %s", resp.StatusCode, projectUrl)
		}
	}

	release, resp, err := client.Repositories.GetLatestRelease(owner, repo)
	if err != nil {
		if resp.StatusCode == 404 {
			log.Infof("No official releases yet. Checking pre-release tags...")
			tags, _, err := client.Repositories.ListTags(owner, repo, nil)
			if err != nil || len(tags) < 1 {
				log.Fatalf(ERR_NO_RELEASES, err)
			}

			tag := tags[0]
			log.Infof("Latest tag: %s (%s)", *tag.Name, *tag.Commit.SHA)

			release, resp, err = client.Repositories.GetReleaseByTag(owner, repo, *tag.Name)
			if err != nil || len(tags) < 1 {
				log.Fatalf(ERR_NO_RELEASES, err)
			}
		}
	}

	assets := release.Assets
	var asset github.ReleaseAsset

	switch len(assets) {
	case 0:
		log.Fatal("Error: No assets to download")
		break
	case 1:
		asset = assets[0]
		break
	default:
		log.Info("Release Assets:")
		for i, a := range assets {
			log.Infof(" (%d) %s (%s)", i+1, *a.Name, *a.BrowserDownloadURL)
		}

		reader := bufio.NewReader(os.Stdin)
		fmt.Print("Which asset would you like to download? ")
		choice, _ := reader.ReadString('\n')
		choice = strings.TrimSpace(choice)
		choiceInt, err := strconv.Atoi(choice)
		if err != nil {
			log.Fatalf("Error: You must select an asset to download")
		}
		println("")
		asset = assets[choiceInt-1]

		break
	}

	if *dryRunFlag {
		log.Info("Dry-run completed")
	} else {
		downloadReleaseAsset(&asset, renameFlag, pathFlag)
	}
}