<commit_before>//
// command/run.go
//
// Copyright (c) 2016-2017 Junpei Kawamoto
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

package command

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"

	colorable "github.com/mattn/go-colorable"
	gitconfig "github.com/tcnksm/go-gitconfig"
	"github.com/ttacon/chalk"
	"github.com/urfave/cli"
)

// SourceArchive defines a name of source archive file.
const SourceArchive = "source.tar.gz"

// RunOpt defines a option parameter for run function.
type RunOpt struct {
	// Same options as DockerfileOpt.
	*DockerfileOpt
	// Travis configuration file.
	Filename string
	// Container name.
	Name string
	// Runtime version to which only versions matching will be run.
	Version string
	// Image tag.
	Tag string
	// Max processors to be used.
	Processors int
	// If true, logging information to be stored to files.
	OutputLog bool
	// If true, not using cache during buidling a docker image.
	NoCache bool
	// If true, omit printing color codes.
	NoColor bool
	// Printed on the header.
	Title string
}

// Run implements the action of this command.
func Run(c *cli.Context) error {

	opt := RunOpt{
		DockerfileOpt: &DockerfileOpt{
			BaseImage:  c.String("base"),
			AptProxy:   c.String("apt-proxy"),
			PypiProxy:  c.String("pypi-proxy"),
			HTTPProxy:  c.String("http-proxy"),
			HTTPSProxy: c.String("https-proxy"),
			NoProxy:    c.String("no-proxy"),
		},
		Filename:   c.Args().First(),
		Name:       c.String("name"),
		Version:    c.String("select"),
		Tag:        c.String("tag"),
		Processors: c.Int("max-processors"),
		OutputLog:  c.Bool("log"),
		NoCache:    c.Bool("no-cache"),
		NoColor:    c.Bool("no-color"),
		Title:      fmt.Sprintf("%v %v", c.App.Name, c.App.Version),
	}
	if err := run(&opt); err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	return nil
}

func run(opt *RunOpt) (err error) {

	// Prepare to be canceled.
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, os.Kill, syscall.SIGQUIT)
	go func() {
		<-sig
		cancel()
	}()

	// Prepare interface.
	display, ctx, err := NewDisplay(ctx, opt.Title, opt.Processors)
	if err != nil {
		return
	}
	defer display.Close()
	logger := display.Header.Logger

	var stdout io.Writer
	if opt.NoColor {
		stdout = colorable.NewNonColorable(os.Stdout)
		logger = colorable.NewNonColorable(logger)
		cli.ErrWriter = colorable.NewNonColorable(cli.ErrWriter)
	} else {
		stdout = colorable.NewColorableStdout()
	}

	// Load a Travis's script file.
	if opt.Filename == "" {
		opt.Filename = ".travis.yml"
	}
	fmt.Fprintln(logger, chalk.Cyan.Color("Loading .travis.yml"))

	travis, err := NewTravisFromFile(opt.Filename)
	if err != nil {
		return
	}

	// Get repository information.
	fmt.Fprintln(logger, chalk.Cyan.Color("Checking repository information"))
	origin, err := gitconfig.OriginURL()
	if err != nil {
		return
	}
	opt.Repository = getRepository(origin)

	// Set up the tag name of the container image.
	if opt.Tag == "" {
		opt.Tag = fmt.Sprintf("loci/%s", strings.ToLower(path.Base(opt.Repository)))
	}

	// Prepare docker images.
	fmt.Fprintln(logger, chalk.Cyan.Color("Preparing docker images for sandbox containers"))
	err = PrepareBaseImage(ctx, opt.BaseImage, logger)
	if err != nil {
		return
	}

	// Archive source files.
	fmt.Fprintln(logger, chalk.Cyan.Color("Archiving source code"))
	tempDir := filepath.Join(os.TempDir(), opt.Tag)
	if err = os.MkdirAll(tempDir, 0777); err != nil {
		return
	}
	defer os.RemoveAll(tempDir)
	pwd, err := os.Getwd()
	if err != nil {
		return
	}
	if err = Archive(ctx, pwd, filepath.Join(tempDir, SourceArchive)); err != nil {
		return
	}

	// Create Dockerfile.
	fmt.Fprintln(logger, chalk.Cyan.Color("Creating Dockerfile"))
	docker, err := Dockerfile(travis, opt.DockerfileOpt, SourceArchive)
	if err != nil {
		return
	}
	if err = ioutil.WriteFile(filepath.Join(tempDir, "Dockerfile"), docker, 0644); err != nil {
		return
	}

	// Create entrypoint.sh.
	fmt.Fprintln(logger, chalk.Cyan.Color("Creating entrypoint.sh"))
	entry, err := Entrypoint(travis)
	if err != nil {
		return
	}
	if err = ioutil.WriteFile(filepath.Join(tempDir, "entrypoint.sh"), entry, 0644); err != nil {
		return
	}

	argset, err := travis.ArgumentSet(logger)
	if err != nil {
		return
	}

	// Start testing with goroutines.
	fmt.Fprintln(logger, chalk.Cyan.Color("Building sandbox images and running tests"))
	var i int
	var wg sync.WaitGroup
	semaphore := make(chan struct{}, opt.Processors)
	errs := NewErrorSet()
	for version, set := range argset {

		if opt.Version != "" && version != opt.Version {
			continue
		}

		wg.Add(1)
		go func(version string, set [][]string) (err error) {
			semaphore <- struct{}{}
			defer func() {
				<-semaphore
				wg.Done()
			}()

			// Build a container image.
			sec := display.AddSection(fmt.Sprintf("Building a docker image for %v", version))
			defer display.DeleteSection(sec)

			var output io.Writer
			writer := sec.Writer()
			defer writer.Close()
			output = writer
			if opt.NoColor {
				output = colorable.NewNonColorable(output)
			}

			if opt.OutputLog {
				var fp *os.File
				fp, err = os.OpenFile(fmt.Sprintf("loci-build-%v.log", version), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
				if err != nil {
					errs.Add(version, err)
					return
				}
				defer fp.Close()
				output = io.MultiWriter(output, colorable.NewColorable(fp))
			}

			tag := fmt.Sprintf("%v/%v", opt.Tag, version)
			err = Build(ctx, tempDir, tag, version, opt.NoCache, output)
			if err == context.Canceled {
				errs.Add("", err)
				return
			} else if err != nil {
				msg := fmt.Sprintf(chalk.Red.Color("Faild to build a docker image for %v"), version)
				errs.Add(
					version,
					fmt.Errorf("%v\n%v\n%v\n", msg, err.Error(), sec.String()))
				fmt.Fprintln(logger, msg)
				return
			}
			fmt.Fprintln(logger, chalk.Green.Color(fmt.Sprintf("Built a docker image for %v", version)))

			for _, envs := range set {

				wg.Add(1)
				go func(envs []string) {
					semaphore <- struct{}{}
					defer func() {
						<-semaphore
						wg.Done()
					}()

					// Run tests in a sandbox.
					sec := display.AddSection(fmt.Sprintf("Running tests (%v: %v)", version, envs))
					defer display.DeleteSection(sec)

					var output io.Writer
					writer := sec.Writer()
					defer writer.Close()
					output = writer
					if opt.NoColor {
						output = colorable.NewNonColorable(output)
					}

					if opt.OutputLog {
						var fp *os.File
						fp, err = os.OpenFile(
							fmt.Sprintf("loci-%v.log", strings.Join(append([]string{version}, envs...), "-")),
							os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
						if err != nil {
							errs.Add(fmt.Sprintf("%v:%v", version, envs), err)
							return
						}
						defer fp.Close()
						output = io.MultiWriter(output, colorable.NewColorable(fp))
					}

					name := opt.Name
					if name != "" {
						i++
						name = fmt.Sprintf("%s-%d", name, i)
					}

					err = Start(ctx, tag, name, envs, output)
					if err == context.Canceled {
						errs.Add("", err)
					} else if err != nil {
						errs.Add(fmt.Sprintf("%v:%v", version, envs), fmt.Errorf("%s\n%s", chalk.Red.Color(err.Error()), sec.String()))
						fmt.Fprintln(logger, chalk.Red.Color(fmt.Sprintf("Failed tests (%v: %v) ", version, envs)))
					} else {
						fmt.Fprintln(logger, chalk.Green.Color(fmt.Sprintf("Passed tests (%v: %v) ", version, envs)))
					}
					return

				}(envs)

			}

			return

		}(version, set)

	}

	wg.Wait()
	err = display.Close()
	if err != nil {
		return
	}

	if errs.Size() == 0 {
		fmt.Fprintln(stdout, chalk.Green.Color("All tests have been passed."))
	} else {
		errList := errs.GetList()
		if errList[0] == context.Canceled {
			err = cli.NewExitError("canceled", 1)
		} else {
			err = cli.NewMultiError(errList...)
		}
	}
	return

}

// getRepository returns the repository path from a given remote URL of
// origin repository. The repository path consists of a URL without
// sheme, user name, password, and .git suffix.
func getRepository(origin string) (res string) {

	switch {
	case strings.Contains(origin, "@"):
		res = strings.Replace(strings.Split(origin, "@")[1], ":", "/", 1)
	case strings.HasPrefix(origin, "http://"):
		res = origin[len("http://"):]
	case strings.HasPrefix(origin, "https://"):
		res = origin[len("https://"):]
	default:
		res = strings.Replace(origin, ":", "/", 1)
	}
	if strings.HasSuffix(res, ".git") {
		res = res[:len(res)-len(".git")]
	}

	return

}
<commit_msg>Output log messages even if tests are canceled but some of them are failed.<commit_after>//
// command/run.go
//
// Copyright (c) 2016-2017 Junpei Kawamoto
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

package command

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"

	colorable "github.com/mattn/go-colorable"
	gitconfig "github.com/tcnksm/go-gitconfig"
	"github.com/ttacon/chalk"
	"github.com/urfave/cli"
)

// SourceArchive defines a name of source archive file.
const SourceArchive = "source.tar.gz"

// RunOpt defines a option parameter for run function.
type RunOpt struct {
	// Same options as DockerfileOpt.
	*DockerfileOpt
	// Travis configuration file.
	Filename string
	// Container name.
	Name string
	// Runtime version to which only versions matching will be run.
	Version string
	// Image tag.
	Tag string
	// Max processors to be used.
	Processors int
	// If true, logging information to be stored to files.
	OutputLog bool
	// If true, not using cache during buidling a docker image.
	NoCache bool
	// If true, omit printing color codes.
	NoColor bool
	// Printed on the header.
	Title string
}

// Run implements the action of this command.
func Run(c *cli.Context) error {

	opt := RunOpt{
		DockerfileOpt: &DockerfileOpt{
			BaseImage:  c.String("base"),
			AptProxy:   c.String("apt-proxy"),
			PypiProxy:  c.String("pypi-proxy"),
			HTTPProxy:  c.String("http-proxy"),
			HTTPSProxy: c.String("https-proxy"),
			NoProxy:    c.String("no-proxy"),
		},
		Filename:   c.Args().First(),
		Name:       c.String("name"),
		Version:    c.String("select"),
		Tag:        c.String("tag"),
		Processors: c.Int("max-processors"),
		OutputLog:  c.Bool("log"),
		NoCache:    c.Bool("no-cache"),
		NoColor:    c.Bool("no-color"),
		Title:      fmt.Sprintf("%v %v", c.App.Name, c.App.Version),
	}
	if err := run(&opt); err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	return nil
}

func run(opt *RunOpt) (err error) {

	// Prepare to be canceled.
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, os.Kill, syscall.SIGQUIT)
	go func() {
		<-sig
		cancel()
	}()

	// Prepare interface.
	display, ctx, err := NewDisplay(ctx, opt.Title, opt.Processors)
	if err != nil {
		return
	}
	defer display.Close()
	logger := display.Header.Logger

	var stdout io.Writer
	if opt.NoColor {
		stdout = colorable.NewNonColorable(os.Stdout)
		logger = colorable.NewNonColorable(logger)
		cli.ErrWriter = colorable.NewNonColorable(cli.ErrWriter)
	} else {
		stdout = colorable.NewColorableStdout()
	}

	// Load a Travis's script file.
	if opt.Filename == "" {
		opt.Filename = ".travis.yml"
	}
	fmt.Fprintln(logger, chalk.Cyan.Color("Loading .travis.yml"))

	travis, err := NewTravisFromFile(opt.Filename)
	if err != nil {
		return
	}

	// Get repository information.
	fmt.Fprintln(logger, chalk.Cyan.Color("Checking repository information"))
	origin, err := gitconfig.OriginURL()
	if err != nil {
		return
	}
	opt.Repository = getRepository(origin)

	// Set up the tag name of the container image.
	if opt.Tag == "" {
		opt.Tag = fmt.Sprintf("loci/%s", strings.ToLower(path.Base(opt.Repository)))
	}

	// Prepare docker images.
	fmt.Fprintln(logger, chalk.Cyan.Color("Preparing docker images for sandbox containers"))
	err = PrepareBaseImage(ctx, opt.BaseImage, logger)
	if err != nil {
		return
	}

	// Archive source files.
	fmt.Fprintln(logger, chalk.Cyan.Color("Archiving source code"))
	tempDir := filepath.Join(os.TempDir(), opt.Tag)
	if err = os.MkdirAll(tempDir, 0777); err != nil {
		return
	}
	defer os.RemoveAll(tempDir)
	pwd, err := os.Getwd()
	if err != nil {
		return
	}
	if err = Archive(ctx, pwd, filepath.Join(tempDir, SourceArchive)); err != nil {
		return
	}

	// Create Dockerfile.
	fmt.Fprintln(logger, chalk.Cyan.Color("Creating Dockerfile"))
	docker, err := Dockerfile(travis, opt.DockerfileOpt, SourceArchive)
	if err != nil {
		return
	}
	if err = ioutil.WriteFile(filepath.Join(tempDir, "Dockerfile"), docker, 0644); err != nil {
		return
	}

	// Create entrypoint.sh.
	fmt.Fprintln(logger, chalk.Cyan.Color("Creating entrypoint.sh"))
	entry, err := Entrypoint(travis)
	if err != nil {
		return
	}
	if err = ioutil.WriteFile(filepath.Join(tempDir, "entrypoint.sh"), entry, 0644); err != nil {
		return
	}

	argset, err := travis.ArgumentSet(logger)
	if err != nil {
		return
	}

	// Start testing with goroutines.
	fmt.Fprintln(logger, chalk.Cyan.Color("Building sandbox images and running tests"))
	var i int
	var wg sync.WaitGroup
	semaphore := make(chan struct{}, opt.Processors)
	errs := NewErrorSet()
	for version, set := range argset {

		if opt.Version != "" && version != opt.Version {
			continue
		}

		wg.Add(1)
		go func(version string, set [][]string) (err error) {
			semaphore <- struct{}{}
			defer func() {
				<-semaphore
				wg.Done()
			}()

			// Build a container image.
			sec := display.AddSection(fmt.Sprintf("Building a docker image for %v", version))
			defer display.DeleteSection(sec)

			var output io.Writer
			writer := sec.Writer()
			defer writer.Close()
			output = writer
			if opt.NoColor {
				output = colorable.NewNonColorable(output)
			}

			if opt.OutputLog {
				var fp *os.File
				fp, err = os.OpenFile(fmt.Sprintf("loci-build-%v.log", version), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
				if err != nil {
					errs.Add(version, err)
					return
				}
				defer fp.Close()
				output = io.MultiWriter(output, colorable.NewColorable(fp))
			}

			tag := fmt.Sprintf("%v/%v", opt.Tag, version)
			err = Build(ctx, tempDir, tag, version, opt.NoCache, output)
			if err == context.Canceled {
				errs.Add("", err)
				return
			} else if err != nil {
				msg := fmt.Sprintf(chalk.Red.Color("Faild to build a docker image for %v"), version)
				errs.Add(
					version,
					fmt.Errorf("%v\n%v\n%v\n", msg, err.Error(), sec.String()))
				fmt.Fprintln(logger, msg)
				return
			}
			fmt.Fprintln(logger, chalk.Green.Color(fmt.Sprintf("Built a docker image for %v", version)))

			for _, envs := range set {

				wg.Add(1)
				go func(envs []string) {
					semaphore <- struct{}{}
					defer func() {
						<-semaphore
						wg.Done()
					}()

					// Run tests in a sandbox.
					sec := display.AddSection(fmt.Sprintf("Running tests (%v: %v)", version, envs))
					defer display.DeleteSection(sec)

					var output io.Writer
					writer := sec.Writer()
					defer writer.Close()
					output = writer
					if opt.NoColor {
						output = colorable.NewNonColorable(output)
					}

					if opt.OutputLog {
						var fp *os.File
						fp, err = os.OpenFile(
							fmt.Sprintf("loci-%v.log", strings.Join(append([]string{version}, envs...), "-")),
							os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
						if err != nil {
							errs.Add(fmt.Sprintf("%v:%v", version, envs), err)
							return
						}
						defer fp.Close()
						output = io.MultiWriter(output, colorable.NewColorable(fp))
					}

					name := opt.Name
					if name != "" {
						i++
						name = fmt.Sprintf("%s-%d", name, i)
					}

					err = Start(ctx, tag, name, envs, output)
					if err == context.Canceled {
						errs.Add("", err)
					} else if err != nil {
						errs.Add(fmt.Sprintf("%v:%v", version, envs), fmt.Errorf("%s\n%s", chalk.Red.Color(err.Error()), sec.String()))
						fmt.Fprintln(logger, chalk.Red.Color(fmt.Sprintf("Failed tests (%v: %v) ", version, envs)))
					} else {
						fmt.Fprintln(logger, chalk.Green.Color(fmt.Sprintf("Passed tests (%v: %v) ", version, envs)))
					}
					return

				}(envs)

			}

			return

		}(version, set)

	}

	wg.Wait()
	err = display.Close()
	if err != nil {
		errs.Add("", err)
	}

	if errs.Size() == 0 {
		fmt.Fprintln(stdout, chalk.Green.Color("All tests have been passed."))
	} else {
		errList := errs.GetList()
		err = cli.NewMultiError(errList...)
	}
	return

}

// getRepository returns the repository path from a given remote URL of
// origin repository. The repository path consists of a URL without
// sheme, user name, password, and .git suffix.
func getRepository(origin string) (res string) {

	switch {
	case strings.Contains(origin, "@"):
		res = strings.Replace(strings.Split(origin, "@")[1], ":", "/", 1)
	case strings.HasPrefix(origin, "http://"):
		res = origin[len("http://"):]
	case strings.HasPrefix(origin, "https://"):
		res = origin[len("https://"):]
	default:
		res = strings.Replace(origin, ":", "/", 1)
	}
	if strings.HasSuffix(res, ".git") {
		res = res[:len(res)-len(".git")]
	}

	return

}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
)

// BuildCommand is a Command implementation that generates Packer templates
// from named Builds.
type RunCommand struct {
	Ui cli.Ui
}

func (c *RunCommand) Help() string {
	helpText := `
    Usage: rancher Run [options]

        Generates Packer templates. At minimum, this command needs to be run
        with at least one RunList name.

            rancher run example1

        The above command generates Packer templates from all of the Rancher
	Builds that have been specified within the RunList 'example1'.

    Options:

        -log-level=info         Log level for Rancher.
`
	return strings.TrimSpace(helpText)
}

func (c *RunCommand) Run(args []string) int {
	var logLevel string

	cmdFlags := flag.NewFlagSet("run", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&logLevel, "log-level", "INFO", "log level")

	fmt.Printf("%+v\n", args)

	return 0

}

func (c *RunCommand) Synopsis() string {
	return "Create Packer templates from the Rancher Build templates specified in the passed RunList."
}
<commit_msg>updated run command info<commit_after>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
)

// BuildCommand is a Command implementation that generates Packer templates
// from named Builds.
type RunCommand struct {
	Ui cli.Ui
}

func (c *RunCommand) Help() string {
	helpText := `
    Usage: rancher Run <BuildList names...>

        Generates Packer templates. At minimum, this command needs to be run
        with at least one BuildList name. Multiple BuildList names can be
	specified by using a space separated list.

            rancher run example1

        The above command generates Packer templates from all of the Rancher
	Builds that have been specified within the RunList 'example1'.
`
	return strings.TrimSpace(helpText)
}

func (c *RunCommand) Run(args []string) int {
	var logLevel string

	cmdFlags := flag.NewFlagSet("run", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.StringVar(&logLevel, "log-level", "INFO", "log level")

	fmt.Printf("%+v\n", args)

	return 0

}

func (c *RunCommand) Synopsis() string {
	return "Create Packer templates from the Rancher Build templates specified in the passed BuildList(s)."
}
<|endoftext|>


<commit_before>package commands

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

	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
	docker "github.com/yungsang/dockerclient"
)

func CommandPs(ctx *cli.Context) {
	client, err := docker.NewDockerClient(ctx.GlobalString("host"), nil)
	if err != nil {
		log.Fatal(err)
	}

	var filters = ""
	if ctx.Bool("latest") {
		filters += "&limit=1"
	}
	if ctx.Bool("size") {
		filters += "&size=1"
	}

	containers, err := client.ListContainers(ctx.Bool("all"), ctx.Bool("size"), filters)
	if err != nil {
		log.Fatal(err)
	}

	if ctx.Bool("quiet") {
		for _, container := range containers {
			fmt.Println(Truncate(container.Id, 12))
		}
		return
	}

	trimNamePrefix := func(ss []string) []string {
		for i, s := range ss {
			ss[i] = strings.TrimPrefix(s, "/")
		}
		return ss
	}

	formatPorts := func(ports []docker.Port) string {
		result := []string{}
		for _, p := range ports {
			result = append(result, fmt.Sprintf("%s:%d->%d/%s",
				p.IP, p.PublicPort, p.PrivatePort, p.Type))
		}
		return strings.Join(result, ", ")
	}

	var items [][]string
	for _, container := range containers {
		out := []string{
			Truncate(container.Id, 12),
			strings.Join(trimNamePrefix(container.Names), ", "),
			container.Image,
			Truncate(container.Command, 20),
			FormatDateTime(time.Unix(container.Created, 0)),
			container.Status,
			formatPorts(container.Ports),
		}
		if ctx.Bool("size") {
			out = append(out, fmt.Sprintf("%.4g MB", float64(container.SizeRw)/1000000.0))
		}
		items = append(items, out)
	}

	var header = []string{
		"ID",
		"Names",
		"Image",
		"Command",
		"Created",
		"Status",
		"Ports",
	}
	if ctx.Bool("size") {
		header = append(header, "Size")
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.SetBorder(false)
	table.AppendBulk(items)
	table.Render()
}
<commit_msg>Revise a header<commit_after>package commands

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

	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
	docker "github.com/yungsang/dockerclient"
)

func CommandPs(ctx *cli.Context) {
	client, err := docker.NewDockerClient(ctx.GlobalString("host"), nil)
	if err != nil {
		log.Fatal(err)
	}

	var filters = ""
	if ctx.Bool("latest") {
		filters += "&limit=1"
	}
	if ctx.Bool("size") {
		filters += "&size=1"
	}

	containers, err := client.ListContainers(ctx.Bool("all"), ctx.Bool("size"), filters)
	if err != nil {
		log.Fatal(err)
	}

	if ctx.Bool("quiet") {
		for _, container := range containers {
			fmt.Println(Truncate(container.Id, 12))
		}
		return
	}

	trimNamePrefix := func(ss []string) []string {
		for i, s := range ss {
			ss[i] = strings.TrimPrefix(s, "/")
		}
		return ss
	}

	formatPorts := func(ports []docker.Port) string {
		result := []string{}
		for _, p := range ports {
			result = append(result, fmt.Sprintf("%s:%d->%d/%s",
				p.IP, p.PublicPort, p.PrivatePort, p.Type))
		}
		return strings.Join(result, ", ")
	}

	var items [][]string
	for _, container := range containers {
		out := []string{
			Truncate(container.Id, 12),
			strings.Join(trimNamePrefix(container.Names), ", "),
			container.Image,
			Truncate(container.Command, 20),
			FormatDateTime(time.Unix(container.Created, 0)),
			container.Status,
			formatPorts(container.Ports),
		}
		if ctx.Bool("size") {
			out = append(out, fmt.Sprintf("%.4g MB", float64(container.SizeRw)/1000000.0))
		}
		items = append(items, out)
	}

	var header = []string{
		"ID",
		"Names",
		"Image",
		"Command",
		"Created at",
		"Status",
		"Ports",
	}
	if ctx.Bool("size") {
		header = append(header, "Size")
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.SetBorder(false)
	table.AppendBulk(items)
	table.Render()
}
<|endoftext|>


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

package orchestrator

import (
	"allmark.io/modules/model"
	"allmark.io/modules/web/view/viewmodel"
	"fmt"
)

type FeedOrchestrator struct {
	*Orchestrator
}

func (orchestrator *FeedOrchestrator) GetRootEntry(baseUrl string) viewmodel.FeedEntry {

	rootItem := orchestrator.rootItem()
	if rootItem == nil {
		orchestrator.logger.Fatal("No root item found.")
	}

	return orchestrator.createFeedEntryModel(baseUrl, rootItem)
}

func (orchestrator *FeedOrchestrator) GetEntries(baseUrl string, itemsPerPage, page int) (entries []viewmodel.FeedEntry, found bool) {

	// validate page number
	if page < 1 {
		orchestrator.logger.Fatal("Invalid page number (%v).", page)
	}

	rootItem := orchestrator.rootItem()
	if rootItem == nil {
		orchestrator.logger.Fatal("No root item found")
	}

	feedEntries := make([]viewmodel.FeedEntry, 0)

	latestItems, found := pagedItems(orchestrator.getLatestItems(rootItem.Route()), itemsPerPage, page)
	if !found {
		return feedEntries, false
	}

	for _, item := range latestItems {
		feedEntries = append(feedEntries, orchestrator.createFeedEntryModel(baseUrl, item))
	}

	return feedEntries, true
}

func (orchestrator *FeedOrchestrator) createFeedEntryModel(baseUrl string, item *model.Item) viewmodel.FeedEntry {

	addressPrefix := fmt.Sprintf("%s/", baseUrl)
	pathProvider := orchestrator.absolutePather(addressPrefix)

	// item location
	location := pathProvider.Path(item.Route().Value())

	// content
	content, err := orchestrator.converter.Convert(orchestrator.getItemByAlias, pathProvider, item)
	if err != nil {
		content = err.Error()
	}

	// creation date
	creationDate := item.MetaData.CreationDate.Format("2006-01-02")

	return viewmodel.FeedEntry{
		Title:       item.Title,
		Description: content,
		Link:        location,
		PubDate:     creationDate,
	}
}
<commit_msg>Bug Fix "RSS Feed Entry Urls are Malformed": Use the correct path provider for rss feed item content.<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package orchestrator

import (
	"allmark.io/modules/model"
	"allmark.io/modules/web/view/viewmodel"
	"fmt"
)

type FeedOrchestrator struct {
	*Orchestrator
}

func (orchestrator *FeedOrchestrator) GetRootEntry(baseUrl string) viewmodel.FeedEntry {

	rootItem := orchestrator.rootItem()
	if rootItem == nil {
		orchestrator.logger.Fatal("No root item found.")
	}

	return orchestrator.createFeedEntryModel(baseUrl, rootItem)
}

func (orchestrator *FeedOrchestrator) GetEntries(baseUrl string, itemsPerPage, page int) (entries []viewmodel.FeedEntry, found bool) {

	// validate page number
	if page < 1 {
		orchestrator.logger.Fatal("Invalid page number (%v).", page)
	}

	rootItem := orchestrator.rootItem()
	if rootItem == nil {
		orchestrator.logger.Fatal("No root item found")
	}

	feedEntries := make([]viewmodel.FeedEntry, 0)

	latestItems, found := pagedItems(orchestrator.getLatestItems(rootItem.Route()), itemsPerPage, page)
	if !found {
		return feedEntries, false
	}

	for _, item := range latestItems {
		feedEntries = append(feedEntries, orchestrator.createFeedEntryModel(baseUrl, item))
	}

	return feedEntries, true
}

func (orchestrator *FeedOrchestrator) createFeedEntryModel(baseUrl string, item *model.Item) viewmodel.FeedEntry {

	itemPathProvider := orchestrator.absolutePather(fmt.Sprintf("%s/", baseUrl))
	itemContentPathProvider := orchestrator.absolutePather(fmt.Sprintf("%s/%s/", baseUrl, item.Route().Value()))

	// item location
	location := itemPathProvider.Path(item.Route().Value())

	// content
	content, err := orchestrator.converter.Convert(orchestrator.getItemByAlias, itemContentPathProvider, item)
	if err != nil {
		content = err.Error()
	}

	// creation date
	creationDate := item.MetaData.CreationDate.Format("2006-01-02")

	return viewmodel.FeedEntry{
		Title:       item.Title,
		Description: content,
		Link:        location,
		PubDate:     creationDate,
	}
}
<|endoftext|>


<commit_before>package dns

import (
	// "fmt"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/processors/server"
	"github.com/nanobox-io/nanobox/util"
	"github.com/nanobox-io/nanobox/util/display"
	"github.com/nanobox-io/nanobox/util/dns"
)

var AppSetup func(envModel *models.Env, appModel *models.App, name string) error

// Add adds a dns entry to the local hosts file
func Add(envModel *models.Env, appModel *models.App, name string) error {

	if err := AppSetup(envModel, appModel, appModel.Name); err != nil {
		return util.ErrorAppend(err, "failed to setup app")
	}

	// fetch the IP
	// env in dev is used in the dev container
	// env in sim is used for portal
	envIP := appModel.LocalIPs["env"]

	// generate the dns entry
	entry := dns.Entry(envIP, name, appModel.ID)

	// short-circuit if this entry already exists
	if dns.Exists(entry) {
		return nil
	}

	// make sure the server is running since it will do the dns addition
	if err := server.Setup(); err != nil {
		return util.ErrorAppend(err, "failed to setup server")
	}

	// add the entry
	if err := dns.Add(entry); err != nil {
		lumber.Error("dns:Add:dns.Add(%s): %s", entry, err.Error())
		return util.ErrorAppend(err, "unable to add dns entry")
	}

	display.Info("\n%s %s added\n", display.TaskComplete, name)

	return nil
}
<commit_msg>Issue a warning about .dev TLDs<commit_after>package dns

import (
	// "fmt"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/processors/server"
	"github.com/nanobox-io/nanobox/util"
	"github.com/nanobox-io/nanobox/util/display"
	"github.com/nanobox-io/nanobox/util/dns"
)

var AppSetup func(envModel *models.Env, appModel *models.App, name string) error

// Add adds a dns entry to the local hosts file
func Add(envModel *models.Env, appModel *models.App, name string) error {

	if err := AppSetup(envModel, appModel, appModel.Name); err != nil {
		return util.ErrorAppend(err, "failed to setup app")
	}

	// fetch the IP
	// env in dev is used in the dev container
	// env in sim is used for portal
	envIP := appModel.LocalIPs["env"]

	// generate the dns entry
	entry := dns.Entry(envIP, name, appModel.ID)

	// short-circuit if this entry already exists
	if dns.Exists(entry) {
		return nil
	}

	// make sure the server is running since it will do the dns addition
	if err := server.Setup(); err != nil {
		return util.ErrorAppend(err, "failed to setup server")
	}

	// issue a warning about `.dev` being unusable with Chrome
	if name[len(name)-4:] == ".dev" {
		tld := appModel.DisplayName()
		if tld == "dry-run" {
			tld = "test"
		}

		display.Warn("\nGoogle has been locking down use of the .dev TLD, and your app may not be accessible with this domain.\nTry using %s.%s instead\n", name[0:len(name)-4], tld)
	}

	// add the entry
	if err := dns.Add(entry); err != nil {
		lumber.Error("dns:Add:dns.Add(%s): %s", entry, err.Error())
		return util.ErrorAppend(err, "unable to add dns entry")
	}

	display.Info("\n%s %s added\n", display.TaskComplete, name)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const ptMethodName = "meek"

var ptInfo pt.ClientInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func copyLoop(a, b net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(b, a)
		wg.Done()
	}()
	go func() {
		io.Copy(a, b)
		wg.Done()
	}()

	wg.Wait()
}

func handler(conn *pt.SocksConn) error {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	defer conn.Close()
	remote, err := net.Dial("tcp", conn.Req.Target)
	if err != nil {
		conn.Reject()
		return err
	}
	defer remote.Close()
	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
	if err != nil {
		return err
	}

	copyLoop(conn, remote)

	return nil
}

func acceptLoop(ln *pt.SocksListener) error {
	defer ln.Close()
	for {
		conn, err := ln.AcceptSocks()
		if err != nil {
			if e, ok := err.(net.Error); ok && !e.Temporary() {
				return err
			}
			continue
		}
		go handler(conn)
	}
}

func main() {
	var err error

	ptInfo, err = pt.ClientSetup([]string{ptMethodName})
	if err != nil {
		os.Exit(1)
	}

	listeners := make([]net.Listener, 0)
	for _, methodName := range ptInfo.MethodNames {
		switch methodName {
		case ptMethodName:
			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
			if err != nil {
				pt.CmethodError(methodName, err.Error())
				break
			}
			go acceptLoop(ln)
			pt.Cmethod(methodName, ln.Version(), ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.CmethodError(methodName, "no such method")
		}
	}
	pt.CmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
}
<commit_msg>Add --log option.<commit_after>package main

import (
	"flag"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const ptMethodName = "meek"

var ptInfo pt.ClientInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func copyLoop(a, b net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(b, a)
		wg.Done()
	}()
	go func() {
		io.Copy(a, b)
		wg.Done()
	}()

	wg.Wait()
}

func handler(conn *pt.SocksConn) error {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	defer conn.Close()
	remote, err := net.Dial("tcp", conn.Req.Target)
	if err != nil {
		conn.Reject()
		return err
	}
	defer remote.Close()
	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
	if err != nil {
		return err
	}

	copyLoop(conn, remote)

	return nil
}

func acceptLoop(ln *pt.SocksListener) error {
	defer ln.Close()
	for {
		conn, err := ln.AcceptSocks()
		if err != nil {
			if e, ok := err.(net.Error); ok && !e.Temporary() {
				return err
			}
			continue
		}
		go handler(conn)
	}
}

func main() {
	var logFilename string

	flag.StringVar(&logFilename, "log", "", "name of log file")
	flag.Parse()

	if logFilename != "" {
		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			log.Fatalf("error opening log file: %s", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	var err error
	ptInfo, err = pt.ClientSetup([]string{ptMethodName})
	if err != nil {
		os.Exit(1)
	}

	listeners := make([]net.Listener, 0)
	for _, methodName := range ptInfo.MethodNames {
		switch methodName {
		case ptMethodName:
			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
			if err != nil {
				pt.CmethodError(methodName, err.Error())
				break
			}
			go acceptLoop(ln)
			pt.Cmethod(methodName, ln.Version(), ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.CmethodError(methodName, "no such method")
		}
	}
	pt.CmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
}
<|endoftext|>


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

package filesystem

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/go-fswatch"
)

type updateHubCallbacks map[string]func() fswatch.Watcher
type updateHubWatchers map[string]fswatch.Watcher

type watcherRegistry map[string]updateHubWatchers
type callbackRegistry map[string]updateHubCallbacks

func newUpdateHub(logger logger.Logger) *UpdateHub {
	return &UpdateHub{
		logger: logger,

		callbacks: make(callbackRegistry),
		watchers:  make(watcherRegistry),
	}
}

type UpdateHub struct {
	logger logger.Logger

	callbacks callbackRegistry
	watchers  watcherRegistry
}

func (hub *UpdateHub) StartWatching(route route.Route) {

	hub.logger.Debug("# folder watchers: %v", fswatch.NumberOfFolderWatchers())

	hub.logger.Debug(fmt.Sprintf("Starting callbacks for route %q", route.String()))

	for callbackType, callback := range hub.callbacks[routeToKey(route)] {
		hub.logger.Debug(fmt.Sprintf("Starting callback %q for route %q", callbackType, route.String()))

		// execute the callback
		watcher := callback()

		if watchers, exists := hub.watchers[routeToKey(route)]; !exists {
			watchers := make(updateHubWatchers)
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		} else {
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		}
	}
}

func (hub *UpdateHub) StopWatching(route route.Route) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	for callbackType, watcher := range watchers {
		hub.logger.Debug("Stopping watcher %q for route %q", callbackType, route.String())
		if watcher != nil {
			watcher.Stop()
		}
	}

}

func (hub *UpdateHub) Detach(route route.Route) {
	hub.logger.Debug("Detaching callbacks %q for route %q", route.String())
	hub.StopWatching(route)
	delete(hub.watchers, route.Value())
}

func (hub *UpdateHub) Attach(route route.Route, callbackType string, callback func() fswatch.Watcher) {
	hub.logger.Debug("Attaching callback %q for route %q", callbackType, route.String())

	if callbacks, exists := hub.callbacks[routeToKey(route)]; !exists {

		// create a new callback map
		callbacks := make(updateHubCallbacks)

		// attach the callback
		callbacks[callbackType] = callback

		hub.callbacks[routeToKey(route)] = callbacks
	} else {

		// attach the callback
		callbacks[callbackType] = callback

	}
}

func routeToKey(route route.Route) string {
	return route.Value()
}
<commit_msg>Still not working<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package filesystem

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/go-fswatch"
)

type updateHubCallbacks map[string]func() fswatch.Watcher
type updateHubWatchers map[string]fswatch.Watcher

type watcherRegistry map[string]updateHubWatchers
type callbackRegistry map[string]updateHubCallbacks

func newUpdateHub(logger logger.Logger) *UpdateHub {
	return &UpdateHub{
		logger: logger,

		callbacks: make(callbackRegistry),
		watchers:  make(watcherRegistry),
	}
}

type UpdateHub struct {
	logger logger.Logger

	callbacks callbackRegistry
	watchers  watcherRegistry
}

func (hub *UpdateHub) StartWatching(route route.Route) {

	hub.logger.Debug("# folder watchers: %v", fswatch.NumberOfFolderWatchers())

	hub.logger.Debug(fmt.Sprintf("Starting callbacks for route %q", route.String()))

	for callbackType, callback := range hub.callbacks[routeToKey(route)] {

		if hub.watcherExists(route, callbackType) {
			hub.logger.Debug(fmt.Sprintf("Callback %q for route %q is already running", callbackType, route.String()))
			continue
		}

		hub.logger.Debug(fmt.Sprintf("Starting callback %q for route %q", callbackType, route.String()))

		// execute the callback
		watcher := callback()

		if watchers, exists := hub.watchers[routeToKey(route)]; !exists {
			watchers := make(updateHubWatchers)
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		} else {
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		}
	}
}

func (hub *UpdateHub) StopWatching(route route.Route) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	for callbackType, _ := range watchers {
		hub.stopWatcher(route, callbackType)
	}

}

func (hub *UpdateHub) Detach(route route.Route) {
	hub.logger.Debug("Detaching callbacks %q for route %q", route.String())
	hub.StopWatching(route)
	delete(hub.watchers, route.Value())
}

func (hub *UpdateHub) Attach(route route.Route, callbackType string, callback func() fswatch.Watcher) {
	hub.logger.Debug("Attaching callback %q for route %q", callbackType, route.String())

	if callbacks, exists := hub.callbacks[routeToKey(route)]; !exists {

		// create a new callback map
		callbacks := make(updateHubCallbacks)

		// attach the callback
		callbacks[callbackType] = callback

		hub.callbacks[routeToKey(route)] = callbacks
	} else {

		// stop any existing callbacks
		hub.stopWatcher(route, callbackType)

		// attach the callback
		callbacks[callbackType] = callback

	}
}

func (hub *UpdateHub) watcherExists(route route.Route, callbackType string) bool {
	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		return false
	}

	_, watcherExists := watchers[callbackType]
	return watcherExists
}

func (hub *UpdateHub) stopWatcher(route route.Route, callbackType string) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	if watcher, exists := watchers[callbackType]; exists {
		hub.logger.Debug("Stopping watcher %q for route %q", callbackType, route.String())
		watcher.Stop()
	}

}

func routeToKey(route route.Route) string {
	return route.Value()
}
<|endoftext|>


<commit_before>package tuikit

import (
	"fmt"
	"sync"

	"time"

	termbox "github.com/nsf/termbox-go"
	"github.com/nsf/tulib"
	log "github.com/sgeb/go-sglog"
)

type Painter interface {
	PaintTo(buffer *tulib.Buffer, rect tulib.Rect) error
	SetPaintSubscriber(cb func())
}

type Responder interface {
	// HandleEvent should set Event.Handled to true if it was
	// handled so that the main loop knows to ignores it
	HandleEvent(*Event)
	SetCursorPainter(cb func(Point))
}

type Event struct {
	*termbox.Event
	Handled bool
}

var (
	rootPainter    Painter
	rootBuffer     tulib.Buffer
	firstResponder Responder

	// Event polling channel
	Events chan Event = make(chan Event, 20)

	// Controls event polling
	internalEvents chan termbox.Event = make(chan termbox.Event, 20)
	stopPolling    chan struct{}      = make(chan struct{}, 1)

	// Lock on screen drawing
	mutex sync.Mutex

	fpsCounter    *FpsCounter
	lastPaintTime time.Time
	framesSkipped uint64
)

func Init() error {
	err := termbox.Init()
	if err != nil {
		return fmt.Errorf("Could not init terminal: %v", err)
	}

	err = clearWithDefaultColors()
	if err != nil {
		return fmt.Errorf("Could not clear terminal: %v", err)
	}
	termbox.SetInputMode(termbox.InputAlt)
	hideCursor()

	internalEventProxying()
	StartEventPolling()

	fpsCounter = NewFpsCounter(time.Second)
	go func() {
		for fps := range fpsCounter.Fps {
			log.Debug.Printf("FPS: %v (%v frames skipped)", fps, framesSkipped)
			framesSkipped = 0
		}
	}()

	return nil
}

func internalEventProxying() {
	go func() {
		for {
			tbEvent := <-internalEvents
			ev := Event{Event: &tbEvent, Handled: false}

			switch {
			case ev.Type == termbox.EventResize:
				log.Debug.Println("Received Resize event, clearing screen " +
					"and setting new size")
				clearWithDefaultColors()
				paintForced()
				ev.Handled = true
			case ev.Type == termbox.EventKey && firstResponder != nil:
				firstResponder.HandleEvent(&ev)
			}

			Events <- ev
		}
	}()
}

func StartEventPolling() {
	go func() {
		for {
			select {
			case <-stopPolling:
				return
			case internalEvents <- termbox.PollEvent():
			}
		}
	}()
}

func StopEventPolling() {
	stopPolling <- struct{}{}
}

func SetPainter(p Painter) {
	rootPainter = p
}

func SetFirstResponder(eh Responder) {
	if firstResponder != nil {
		firstResponder.SetCursorPainter(nil)
	}
	if eh != nil {
		eh.SetCursorPainter(func(pos Point) { setCursor(pos) })
	}
	firstResponder = eh
}

func Paint() error {
	if time.Now().Sub(lastPaintTime) < time.Second/10 {
		framesSkipped++
		return fmt.Errorf("Above 10 FPS, skipping frame")
	}

	return paintForced()
}

func paintForced() error {
	err := rootPainter.PaintTo(&rootBuffer, rootBuffer.Rect)
	if err != nil {
		return err
	}

	if firstResponder == nil {
		hideCursor()
	}

	err = flush()
	if err != nil {
		return err
	}

	fpsCounter.Ticks <- struct{}{}
	lastPaintTime = time.Now()

	return nil
}

func Sync() error {
	mutex.Lock()
	err := termbox.Sync()
	mutex.Unlock()
	return err
}

func clearRect(buffer *tulib.Buffer, rect tulib.Rect) {
	buffer.Fill(rect, termbox.Cell{Ch: ' '})
}

func clearWithDefaultColors() error {
	return clear(termbox.ColorDefault, termbox.ColorDefault)
}

func clear(fg, bg termbox.Attribute) error {
	mutex.Lock()
	err := termbox.Clear(fg, bg)
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func updateRootBuffer() {
	mutex.Lock()
	rootBuffer = tulib.TermboxBuffer()
	mutex.Unlock()
}

func hideCursor() {
	// Probably a bug: Cursor must be set to valid position before hiding
	setCursor(PointZero)
	termbox.HideCursor()
}

func setCursor(p Point) {
	termbox.SetCursor(p.X, p.Y)
}

func flush() error {
	mutex.Lock()
	err := termbox.Flush()
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func Close() {
	mutex.Lock()
	termbox.Close()
	mutex.Unlock()
}
<commit_msg>Optimizations based on profiling<commit_after>package tuikit

import (
	"fmt"
	"sync"

	"time"

	termbox "github.com/nsf/termbox-go"
	"github.com/nsf/tulib"
	log "github.com/sgeb/go-sglog"
)

type Painter interface {
	PaintTo(buffer *tulib.Buffer, rect tulib.Rect) error
	SetPaintSubscriber(cb func())
}

type Responder interface {
	// HandleEvent should set Event.Handled to true if it was
	// handled so that the main loop knows to ignores it
	HandleEvent(*Event)
	SetCursorPainter(cb func(Point))
}

type Event struct {
	*termbox.Event
	Handled bool
}

const (
	MaxFps = 10
)

var (
	rootPainter    Painter
	rootBuffer     tulib.Buffer
	firstResponder Responder

	// Event polling channel
	Events chan Event = make(chan Event, 20)

	// Controls event polling
	internalEvents chan termbox.Event = make(chan termbox.Event, 20)
	stopPolling    chan struct{}      = make(chan struct{}, 1)

	// Lock on screen drawing
	mutex sync.Mutex

	fpsCounter    *FpsCounter
	paintTimeT0   time.Time
	paintTimeT1   time.Time
	framesSkipped uint64
	errFrameSkip  = fmt.Errorf("Above %v FPS, skipping frame", MaxFps)
)

func Init() error {
	err := termbox.Init()
	if err != nil {
		return fmt.Errorf("Could not init terminal: %v", err)
	}

	err = clearWithDefaultColors()
	if err != nil {
		return fmt.Errorf("Could not clear terminal: %v", err)
	}
	termbox.SetInputMode(termbox.InputAlt)
	hideCursor()

	internalEventProxying()
	StartEventPolling()

	fpsCounter = NewFpsCounter(time.Second)
	go func() {
		for fps := range fpsCounter.Fps {
			log.Debug.Printf("FPS: %v (%v frames skipped)", fps, framesSkipped)
			framesSkipped = 0
		}
	}()

	return nil
}

func internalEventProxying() {
	go func() {
		for {
			tbEvent := <-internalEvents
			ev := Event{Event: &tbEvent, Handled: false}

			switch {
			case ev.Type == termbox.EventResize:
				log.Debug.Println("Received Resize event, clearing screen " +
					"and setting new size")
				clearWithDefaultColors()
				paintForced()
				ev.Handled = true
			case ev.Type == termbox.EventKey && firstResponder != nil:
				firstResponder.HandleEvent(&ev)
			}

			Events <- ev
		}
	}()
}

func StartEventPolling() {
	go func() {
		for {
			select {
			case <-stopPolling:
				return
			case internalEvents <- termbox.PollEvent():
			}
		}
	}()
}

func StopEventPolling() {
	stopPolling <- struct{}{}
}

func SetPainter(p Painter) {
	rootPainter = p
}

func SetFirstResponder(eh Responder) {
	if firstResponder != nil {
		firstResponder.SetCursorPainter(nil)
	}
	if eh != nil {
		eh.SetCursorPainter(func(pos Point) { setCursor(pos) })
	}
	firstResponder = eh
}

func Paint() error {
	paintTimeT1 = time.Now()
	if paintTimeT1.Sub(paintTimeT0) < time.Second/MaxFps {
		framesSkipped++
		return errFrameSkip
	}

	return paintForced()
}

func paintForced() error {
	err := rootPainter.PaintTo(&rootBuffer, rootBuffer.Rect)
	if err != nil {
		return err
	}

	if firstResponder == nil {
		hideCursor()
	}

	err = flush()
	if err != nil {
		return err
	}

	fpsCounter.Ticks <- struct{}{}
	paintTimeT0 = paintTimeT1

	return nil
}

func Sync() error {
	mutex.Lock()
	err := termbox.Sync()
	mutex.Unlock()
	return err
}

func clearRect(buffer *tulib.Buffer, rect tulib.Rect) {
	buffer.Fill(rect, termbox.Cell{Ch: ' '})
}

func clearWithDefaultColors() error {
	return clear(termbox.ColorDefault, termbox.ColorDefault)
}

func clear(fg, bg termbox.Attribute) error {
	mutex.Lock()
	err := termbox.Clear(fg, bg)
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func updateRootBuffer() {
	mutex.Lock()
	rootBuffer = tulib.TermboxBuffer()
	mutex.Unlock()
}

func hideCursor() {
	// Probably a bug: Cursor must be set to valid position before hiding
	setCursor(PointZero)
	termbox.HideCursor()
}

func setCursor(p Point) {
	termbox.SetCursor(p.X, p.Y)
}

func flush() error {
	mutex.Lock()
	err := termbox.Flush()
	mutex.Unlock()
	updateRootBuffer()
	return err
}

func Close() {
	mutex.Lock()
	termbox.Close()
	mutex.Unlock()
}
<|endoftext|>


<commit_before>package parse

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"path"
	"reflect"
)

type updateTypeT int

const (
	opSet updateTypeT = iota
	opIncr
	opDelete
	opAdd
	opAddUnique
	opRemove
	opAddRelation
	opRemoveRelation
)

func (u updateTypeT) String() string {
	switch u {
	case opSet:
		return "Set"
	case opIncr:
		return "Increment"
	case opDelete:
		return "Delete"
	case opAdd:
		return "Add"
	case opAddUnique:
		return "AddUnique"
	case opRemove:
		return "Remove"
	case opAddRelation:
		return "AddRelation"
	case opRemoveRelation:
		return "RemoveRelation"
	}

	return "Unknown"
}

func (u updateTypeT) argKey() string {
	switch u {
	case opIncr:
		return "amount"
	case opAdd, opAddUnique, opRemove, opAddRelation, opRemoveRelation:
		return "objects"
	}

	return "unknown"
}

type updateOpT struct {
	UpdateType updateTypeT
	Value      interface{}
}

func (u updateOpT) MarshalJSON() ([]byte, error) {
	switch u.UpdateType {
	case opSet:
		return json.Marshal(u.Value)
	case opDelete:
		return json.Marshal(map[string]interface{}{
			"__op": u.UpdateType.String(),
		})
	default:
		return json.Marshal(map[string]interface{}{
			"__op":                u.UpdateType.String(),
			u.UpdateType.argKey(): u.Value,
		})
	}
}

type Update interface {

	//Set the field specified by f to the value of v
	Set(f string, v interface{}) Update

	// Increment the field specified by f by the amount specified by v.
	// v should be a numeric type
	Increment(f string, v interface{}) Update

	// Delete the field specified by f from the instance being updated
	Delete(f string) Update

	// Append the values provided to the Array field specified by f. This operation
	// is atomic
	Add(f string, vs ...interface{}) Update

	// Add any values provided that were not alread present to the Array field
	// specified by f. This operation is atomic
	AddUnique(f string, vs ...interface{}) Update

	// Remove the provided values from the array field specified by f
	Remove(f string, vs ...interface{}) Update

	// Update the ACL on the given object
	SetACL(a ACL) Update

	// Use the Master Key for this update request
	UseMasterKey() Update

	// Execute this update. This method also updates the proper fields
	// on the provided value with their repective new values
	Execute() error

	requestT
}

type updateT struct {
	inst               interface{}
	values             map[string]updateOpT
	shouldUseMasterKey bool
	currentSession     *sessionT
}

// Create a new update request for the Parse object represented by v.
//
// Note: v should be a pointer to a struct whose name represents a Parse class,
// or that implements the ClassName method
func NewUpdate(v interface{}) (Update, error) {
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return nil, errors.New("v must be a non-nil pointer")
	}

	return &updateT{
		inst:   v,
		values: map[string]updateOpT{},
	}, nil
}

func (u *updateT) Set(f string, v interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opSet, Value: encodeForRequest(v)}
	return u
}

func (u *updateT) Increment(f string, v interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opIncr, Value: v}
	return u
}

func (u *updateT) Delete(f string) Update {
	u.values[f] = updateOpT{UpdateType: opDelete}
	return u
}

func (u *updateT) Add(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opAdd, Value: vs}
	return u
}

func (u *updateT) AddUnique(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opAddUnique, Value: vs}
	return u
}

func (u *updateT) Remove(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opRemove, Value: vs}
	return u
}

func (u *updateT) SetACL(a ACL) Update {
	u.values["ACL"] = updateOpT{UpdateType: opSet, Value: a}
	return u
}

func (u *updateT) Execute() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()

	rv := reflect.ValueOf(u.inst)
	rvi := reflect.Indirect(rv)
	fieldMap := getFieldNameMap(rv)

	for k, v := range u.values {
		var fname string
		if fn, ok := fieldMap[k]; ok {
			fname = fn
		} else {
			fname = k
		}

		fname = firstToUpper(fname)

		dv := reflect.ValueOf(v.Value)
		dvi := reflect.Indirect(dv)

		if fv := rvi.FieldByName(fname); fv.IsValid() {
			fvi := reflect.Indirect(fv)

			switch v.UpdateType {
			case opSet:
				var tmp reflect.Value
				if fv.Kind() == reflect.Ptr {
					tmp = fv
				} else {
					tmp = fv.Addr()
				}
				if err := populateValue(tmp.Interface(), v.Value); err != nil {
					return err
				}
			case opIncr:
				switch fvi.Kind() {
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Int()
						amount := dvi.Convert(fvi.Type()).Int()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Uint()
						amount := dvi.Convert(fvi.Type()).Uint()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				case reflect.Float32, reflect.Float64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Float()
						amount := dvi.Convert(fvi.Type()).Float()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				}
			case opDelete:
				fv.Set(reflect.Zero(fv.Type()))
			}
		}
	}
	if b, err := defaultClient.doRequest(u); err != nil {
		return err
	} else {
		return handleResponse(b, u.inst)
	}
}

func (u *updateT) UseMasterKey() Update {
	u.shouldUseMasterKey = true
	return u
}

func (u *updateT) method() string {
	return "PUT"
}

func (u *updateT) endpoint() (string, error) {
	_url := url.URL{}
	p := getEndpointBase(u.inst)

	rv := reflect.ValueOf(u.inst)
	rvi := reflect.Indirect(rv)
	if f := rvi.FieldByName("Id"); f.IsValid() {
		if s, ok := f.Interface().(string); ok {
			p = path.Join(p, s)
		} else {
			return "", fmt.Errorf("Id field should be a string, received type %s", f.Type())
		}
	} else {
		return "", fmt.Errorf("can not update value - type has no Id field")
	}

	_url.Scheme = "https"
	_url.Host = parseHost
	_url.Path = p

	return _url.String(), nil
}

func (u *updateT) body() (string, error) {
	b, err := json.Marshal(u.values)
	if err != nil {
		return "", err
	}

	return string(b), nil
}

func (u *updateT) useMasterKey() bool {
	return u.shouldUseMasterKey
}

func (u *updateT) session() *sessionT {
	return u.currentSession
}

func (u *updateT) contentType() string {
	return "application/json"
}
<commit_msg>support liking facebook account with existing account<commit_after>package parse

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"path"
	"reflect"
)

type updateTypeT int

const (
	opSet updateTypeT = iota
	opIncr
	opDelete
	opAdd
	opAddUnique
	opRemove
	opAddRelation
	opRemoveRelation
)

func (u updateTypeT) String() string {
	switch u {
	case opSet:
		return "Set"
	case opIncr:
		return "Increment"
	case opDelete:
		return "Delete"
	case opAdd:
		return "Add"
	case opAddUnique:
		return "AddUnique"
	case opRemove:
		return "Remove"
	case opAddRelation:
		return "AddRelation"
	case opRemoveRelation:
		return "RemoveRelation"
	}

	return "Unknown"
}

func (u updateTypeT) argKey() string {
	switch u {
	case opIncr:
		return "amount"
	case opAdd, opAddUnique, opRemove, opAddRelation, opRemoveRelation:
		return "objects"
	}

	return "unknown"
}

type updateOpT struct {
	UpdateType updateTypeT
	Value      interface{}
}

func (u updateOpT) MarshalJSON() ([]byte, error) {
	switch u.UpdateType {
	case opSet:
		return json.Marshal(u.Value)
	case opDelete:
		return json.Marshal(map[string]interface{}{
			"__op": u.UpdateType.String(),
		})
	default:
		return json.Marshal(map[string]interface{}{
			"__op":                u.UpdateType.String(),
			u.UpdateType.argKey(): u.Value,
		})
	}
}

type Update interface {

	//Set the field specified by f to the value of v
	Set(f string, v interface{}) Update

	// Increment the field specified by f by the amount specified by v.
	// v should be a numeric type
	Increment(f string, v interface{}) Update

	// Delete the field specified by f from the instance being updated
	Delete(f string) Update

	// Append the values provided to the Array field specified by f. This operation
	// is atomic
	Add(f string, vs ...interface{}) Update

	// Add any values provided that were not alread present to the Array field
	// specified by f. This operation is atomic
	AddUnique(f string, vs ...interface{}) Update

	// Remove the provided values from the array field specified by f
	Remove(f string, vs ...interface{}) Update

	// Update the ACL on the given object
	SetACL(a ACL) Update

	// Use the Master Key for this update request
	UseMasterKey() Update

	// Execute this update. This method also updates the proper fields
	// on the provided value with their repective new values
	Execute() error

	requestT
}

type updateT struct {
	inst               interface{}
	values             map[string]updateOpT
	shouldUseMasterKey bool
	currentSession     *sessionT
}

// Create a new update request for the Parse object represented by v.
//
// Note: v should be a pointer to a struct whose name represents a Parse class,
// or that implements the ClassName method
func NewUpdate(v interface{}) (Update, error) {
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return nil, errors.New("v must be a non-nil pointer")
	}

	return &updateT{
		inst:   v,
		values: map[string]updateOpT{},
	}, nil
}

func (u *updateT) Set(f string, v interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opSet, Value: encodeForRequest(v)}
	return u
}

func (u *updateT) Increment(f string, v interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opIncr, Value: v}
	return u
}

func (u *updateT) Delete(f string) Update {
	u.values[f] = updateOpT{UpdateType: opDelete}
	return u
}

func (u *updateT) Add(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opAdd, Value: vs}
	return u
}

func (u *updateT) AddUnique(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opAddUnique, Value: vs}
	return u
}

func (u *updateT) Remove(f string, vs ...interface{}) Update {
	u.values[f] = updateOpT{UpdateType: opRemove, Value: vs}
	return u
}

func (u *updateT) SetACL(a ACL) Update {
	u.values["ACL"] = updateOpT{UpdateType: opSet, Value: a}
	return u
}

func (u *updateT) Execute() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()

	rv := reflect.ValueOf(u.inst)
	rvi := reflect.Indirect(rv)
	fieldMap := getFieldNameMap(rv)

	for k, v := range u.values {
		var fname string
		if fn, ok := fieldMap[k]; ok {
			fname = fn
		} else {
			fname = k
		}

		fname = firstToUpper(fname)

		dv := reflect.ValueOf(v.Value)
		dvi := reflect.Indirect(dv)

		if fv := rvi.FieldByName(fname); fv.IsValid() {
			fvi := reflect.Indirect(fv)

			switch v.UpdateType {
			case opSet:
				var tmp reflect.Value
				if fv.Kind() == reflect.Ptr {
					tmp = fv
				} else {
					tmp = fv.Addr()
				}
				if err := populateValue(tmp.Interface(), v.Value); err != nil {
					return err
				}
			case opIncr:
				switch fvi.Kind() {
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Int()
						amount := dvi.Convert(fvi.Type()).Int()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Uint()
						amount := dvi.Convert(fvi.Type()).Uint()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				case reflect.Float32, reflect.Float64:
					if dvi.Type().ConvertibleTo(fvi.Type()) {
						current := fvi.Float()
						amount := dvi.Convert(fvi.Type()).Float()
						current += amount
						fvi.Set(reflect.ValueOf(current).Convert(fvi.Type()))
					}
				}
			case opDelete:
				fv.Set(reflect.Zero(fv.Type()))
			}
		}
	}
	if b, err := defaultClient.doRequest(u); err != nil {
		return err
	} else {
		return handleResponse(b, u.inst)
	}
}

func (u *updateT) UseMasterKey() Update {
	u.shouldUseMasterKey = true
	return u
}

func (u *updateT) method() string {
	return "PUT"
}

func (u *updateT) endpoint() (string, error) {
	_url := url.URL{}
	p := getEndpointBase(u.inst)

	rv := reflect.ValueOf(u.inst)
	rvi := reflect.Indirect(rv)
	if f := rvi.FieldByName("Id"); f.IsValid() {
		if s, ok := f.Interface().(string); ok {
			p = path.Join(p, s)
		} else {
			return "", fmt.Errorf("Id field should be a string, received type %s", f.Type())
		}
	} else {
		return "", fmt.Errorf("can not update value - type has no Id field")
	}

	_url.Scheme = "https"
	_url.Host = parseHost
	_url.Path = p

	return _url.String(), nil
}

func (u *updateT) body() (string, error) {
	b, err := json.Marshal(u.values)
	if err != nil {
		return "", err
	}

	return string(b), nil
}

func (u *updateT) useMasterKey() bool {
	return u.shouldUseMasterKey
}

func (u *updateT) session() *sessionT {
	return u.currentSession
}

func (u *updateT) contentType() string {
	return "application/json"
}

func LinkFacebookAccount(u *User, a *FacebookAuthData) error {
	if u.Id == "" {
		return errors.New("user Id field must not be empty")
	}

	up, _ := NewUpdate(u)
	up.Set("authData", AuthData{Facebook: a})
	up.UseMasterKey()
	return up.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/dickeyxxx/golock"
	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/franela/goreq"
	"github.com/heroku/heroku-cli/gode"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Hidden:      true,
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Flags:       []Flag{{Name: "background", Hidden: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		t := "foreground"
		if ctx.Flags["background"] == true {
			t = "background"
		}
		Update(channel, t)
	},
}

var binPath string
var updateLockPath = filepath.Join(AppDir(), "updating.lock")
var autoupdateFile = filepath.Join(AppDir(), "autoupdate")

func init() {
	binPath = os.Args[0]
}

// Update updates the CLI and plugins
func Update(channel string, t string) {
	Debugln("running " + version() + " from " + binPath)
	if !IsUpdateNeeded(t) {
		return
	}
	done := make(chan bool)
	go func() {
		touchAutoupdateFile()
		updateCLI(channel)
		updateNode()
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 300):
		Errln("Timed out while updating")
	case <-done:
	}
}

func updatePlugins() {
	plugins := PluginNamesNotSymlinked()
	if len(plugins) == 0 {
		return
	}
	Err("Updating plugins... ")
	packages, err := gode.OutdatedPackages(plugins...)
	PrintError(err, true)
	if len(packages) > 0 {
		for name, version := range packages {
			lockPlugin(name)
			PrintError(gode.InstallPackages(name+"@"+version), true)
			plugin, err := ParsePlugin(name)
			PrintError(err, true)
			AddPluginsToCache(plugin)
			unlockPlugin(name)
		}
		Errf("done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
	} else {
		Errln("no plugins to update.")
	}
}

func updateCLI(channel string) {
	if channel == "?" {
		// do not update dev version
		return
	}
	manifest, err := getUpdateManifest(channel)
	if err != nil {
		Warn("Error updating CLI")
		PrintError(err, false)
		return
	}
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	LogIfError(golock.Lock(updateLockPath))
	defer golock.Unlock(updateLockPath)
	Errf("Updating Heroku v4 CLI to %s (%s)... ", manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", build.URL); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != build.Sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	os.Rename(binPath, binPath+".old")
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
	os.Remove(binPath + ".old")
	Errln("done")
	clearAutoupdateFile() // force full update
	reexec()              // reexec to finish updating with new code
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "background" {
		return time.Since(f.ModTime()) > 4*time.Hour
	} else if t == "block" {
		return time.Since(f.ModTime()) > 2160*time.Hour // 90 days
	}
	return true
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

// forces a full update on the next run
func clearAutoupdateFile() {
	PrintError(os.Remove(autoupdateFile), true)
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) (*manifest, error) {
	res, err := goreq.Request{
		Uri:       "https://cli-assets.heroku.com/" + channel + "/manifest.json",
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return nil, err
	}
	var m manifest
	res.Body.FromJsonTo(&m)
	return &m, nil
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	res, err := goreq.Request{
		Uri:       url + ".gz",
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return err
	}
	if res.StatusCode != 200 {
		b, _ := res.Body.ToString()
		return errors.New(b)
	}
	defer res.Body.Close()
	_, err = io.Copy(out, res.Body)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if IsUpdateNeeded("background") {
		exec.Command(binPath, "update", "--background").Start()
	}
}

// restarts the CLI with the same arguments
func reexec() {
	Debugln("reexecing new CLI...")
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	os.Exit(getExitCode(cmd.Run()))
}
<commit_msg>ensure lockfile is unlocked after updating<commit_after>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/dickeyxxx/golock"
	"github.com/heroku/heroku-cli/Godeps/_workspace/src/github.com/franela/goreq"
	"github.com/heroku/heroku-cli/gode"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Hidden:      true,
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Flags:       []Flag{{Name: "background", Hidden: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		t := "foreground"
		if ctx.Flags["background"] == true {
			t = "background"
		}
		Update(channel, t)
	},
}

var binPath string
var updateLockPath = filepath.Join(AppDir(), "updating.lock")
var autoupdateFile = filepath.Join(AppDir(), "autoupdate")

func init() {
	binPath = os.Args[0]
}

// Update updates the CLI and plugins
func Update(channel string, t string) {
	Debugln("running " + version() + " from " + binPath)
	if !IsUpdateNeeded(t) {
		return
	}
	done := make(chan bool)
	go func() {
		touchAutoupdateFile()
		updateCLI(channel)
		updateNode()
		updatePlugins()
		done <- true
	}()
	select {
	case <-time.After(time.Second * 300):
		Errln("Timed out while updating")
	case <-done:
	}
}

func updatePlugins() {
	plugins := PluginNamesNotSymlinked()
	if len(plugins) == 0 {
		return
	}
	Err("Updating plugins... ")
	packages, err := gode.OutdatedPackages(plugins...)
	PrintError(err, true)
	if len(packages) > 0 {
		for name, version := range packages {
			lockPlugin(name)
			PrintError(gode.InstallPackages(name+"@"+version), true)
			plugin, err := ParsePlugin(name)
			PrintError(err, true)
			AddPluginsToCache(plugin)
			unlockPlugin(name)
		}
		Errf("done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
	} else {
		Errln("no plugins to update.")
	}
}

func updateCLI(channel string) {
	if channel == "?" {
		// do not update dev version
		return
	}
	manifest, err := getUpdateManifest(channel)
	if err != nil {
		Warn("Error updating CLI")
		PrintError(err, false)
		return
	}
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	LogIfError(golock.Lock(updateLockPath))
	unlock := func() {
		golock.Unlock(updateLockPath)
	}
	defer unlock()
	Errf("Updating Heroku v4 CLI to %s (%s)... ", manifest.Version, manifest.Channel)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", build.URL); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != build.Sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	os.Rename(binPath, binPath+".old")
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
	os.Remove(binPath + ".old")
	Errln("done")
	unlock()
	clearAutoupdateFile() // force full update
	reexec()              // reexec to finish updating with new code
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "background" {
		return time.Since(f.ModTime()) > 4*time.Hour
	} else if t == "block" {
		return time.Since(f.ModTime()) > 2160*time.Hour // 90 days
	}
	return true
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

// forces a full update on the next run
func clearAutoupdateFile() {
	PrintError(os.Remove(autoupdateFile), true)
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) (*manifest, error) {
	res, err := goreq.Request{
		Uri:       "https://cli-assets.heroku.com/" + channel + "/manifest.json",
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return nil, err
	}
	var m manifest
	res.Body.FromJsonTo(&m)
	return &m, nil
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	res, err := goreq.Request{
		Uri:       url + ".gz",
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return err
	}
	if res.StatusCode != 200 {
		b, _ := res.Body.ToString()
		return errors.New(b)
	}
	defer res.Body.Close()
	_, err = io.Copy(out, res.Body)
	if err != nil {
		return err
	}
	return out.Close()
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	if IsUpdateNeeded("background") {
		exec.Command(binPath, "update", "--background").Start()
	}
}

// restarts the CLI with the same arguments
func reexec() {
	Debugln("reexecing new CLI...")
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	os.Exit(getExitCode(cmd.Run()))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/kr/binarydist"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"
)

const (
	upcktimePath = "cktime"
	plat         = runtime.GOOS + "-" + runtime.GOARCH
)

const devValidTime = 7 * 24 * time.Hour

// Update protocol.
//
//   GET hk.heroku.com/hk-current-linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET hkpatch.s3.amazonaws.com/hk-1-linux-amd64-to-2
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET hkdist.s3.amazonaws.com/hk-2-linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
type Updater struct {
	hkURL   string
	binURL  string
	diffURL string
	dir     string
	info    struct {
		Version   string
		Sha256 []byte
	}
}

func (u *Updater) run() {
	os.MkdirAll(u.dir, 0777)
	if u.wantUpdate() {
		l := exec.Command("logger", "-thk")
		c := exec.Command("hk", "update")
		if w, err := l.StdinPipe(); err == nil && l.Start() == nil {
			c.Stdout = w
			c.Stderr = w
		}
		c.Start()
	}
}

func (u *Updater) wantUpdate() bool {
	path := u.dir + upcktimePath
	if Version == "dev" || readTime(path).After(time.Now()) {
		return false
	}
	wait := 24*time.Hour + randDuration(24*time.Hour)
	return writeTime(path, time.Now().Add(wait))
}

func (u *Updater) update() error {
	filename := "hk"
	if runtime.GOOS == "windows" {
		filename += ".exe"
	}
	path, err := exec.LookPath(filename)
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	err = u.fetchInfo()
	if err != nil {
		return err
	}
	if u.info.Version == Version {
		return nil
	}
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		bin, err = u.fetchBin()
		if err != nil {
			return err
		}
	}
	h := sha256.New()
	h.Write(bin)
	if !bytes.Equal(h.Sum(nil), u.info.Sha256) {
		return errors.New("new file hash mismatch after patch")
	}
	return install(old.Name(), bin)
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.hkURL + "hk-current-" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.info)
	if err != nil {
		return err
	}
	if len(u.info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.diffURL + slug(Version) + "-to-" + u.info.Version)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.binURL + slug(u.info.Version) + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func install(name string, p []byte) error {
	execDir := filepath.Dir(name)
	part := filepath.Join(execDir, "hk.part")
	err := ioutil.WriteFile(part, p, 0755)
	if err != nil {
		return err
	}
	defer os.Remove(part)

	// move the existing executable to a new file in the same directory
	oldExecPath := fmt.Sprintf("%s.old", name)
	err = os.Rename(name, oldExecPath)
	if err != nil {
		return err
	}

	// move the new executable in to become the new program
	err = os.Rename(part, name)

	if err != nil {
		// copy unsuccessful
		errRecover := os.Rename(oldExecPath, name)
		if errRecover != nil {
			return errRecover
		} else {
			return err
		}
	} else {
		// copy successful, remove the old binary
		_ = os.Remove(oldExecPath)
	}

	return nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

func slug(ver string) string {
	return "hk-" + ver + "-" + plat
}

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
	return resp.Body, nil
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<commit_msg>always return original error after install failure<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/kr/binarydist"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"
)

const (
	upcktimePath = "cktime"
	plat         = runtime.GOOS + "-" + runtime.GOARCH
)

const devValidTime = 7 * 24 * time.Hour

// Update protocol.
//
//   GET hk.heroku.com/hk-current-linux-amd64.json
//
//   200 ok
//   {
//       "Version": "2",
//       "Sha256": "..." // base64
//   }
//
// then
//
//   GET hkpatch.s3.amazonaws.com/hk-1-linux-amd64-to-2
//
//   200 ok
//   [bsdiff data]
//
// or
//
//   GET hkdist.s3.amazonaws.com/hk-2-linux-amd64.gz
//
//   200 ok
//   [gzipped executable data]
type Updater struct {
	hkURL   string
	binURL  string
	diffURL string
	dir     string
	info    struct {
		Version   string
		Sha256 []byte
	}
}

func (u *Updater) run() {
	os.MkdirAll(u.dir, 0777)
	if u.wantUpdate() {
		l := exec.Command("logger", "-thk")
		c := exec.Command("hk", "update")
		if w, err := l.StdinPipe(); err == nil && l.Start() == nil {
			c.Stdout = w
			c.Stderr = w
		}
		c.Start()
	}
}

func (u *Updater) wantUpdate() bool {
	path := u.dir + upcktimePath
	if Version == "dev" || readTime(path).After(time.Now()) {
		return false
	}
	wait := 24*time.Hour + randDuration(24*time.Hour)
	return writeTime(path, time.Now().Add(wait))
}

func (u *Updater) update() error {
	filename := "hk"
	if runtime.GOOS == "windows" {
		filename += ".exe"
	}
	path, err := exec.LookPath(filename)
	if err != nil {
		return err
	}
	old, err := os.Open(path)
	if err != nil {
		return err
	}
	err = u.fetchInfo()
	if err != nil {
		return err
	}
	if u.info.Version == Version {
		return nil
	}
	bin, err := u.fetchAndApplyPatch(old)
	if err != nil {
		bin, err = u.fetchBin()
		if err != nil {
			return err
		}
	}
	h := sha256.New()
	h.Write(bin)
	if !bytes.Equal(h.Sum(nil), u.info.Sha256) {
		return errors.New("new file hash mismatch after patch")
	}
	return install(old.Name(), bin)
}

func (u *Updater) fetchInfo() error {
	r, err := fetch(u.hkURL + "hk-current-" + plat + ".json")
	if err != nil {
		return err
	}
	defer r.Close()
	err = json.NewDecoder(r).Decode(&u.info)
	if err != nil {
		return err
	}
	if len(u.info.Sha256) != sha256.Size {
		return errors.New("bad cmd hash in info")
	}
	return nil
}

func (u *Updater) fetchAndApplyPatch(old io.Reader) ([]byte, error) {
	r, err := fetch(u.diffURL + slug(Version) + "-to-" + u.info.Version)
	if err != nil {
		return nil, err
	}
	defer r.Close()
	var buf bytes.Buffer
	err = binarydist.Patch(old, &buf, r)
	return buf.Bytes(), err
}

func (u *Updater) fetchBin() ([]byte, error) {
	r, err := fetch(u.binURL + slug(u.info.Version) + ".gz")
	if err != nil {
		return nil, err
	}
	defer r.Close()
	buf := new(bytes.Buffer)
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(buf, gz); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func install(name string, p []byte) error {
	execDir := filepath.Dir(name)
	part := filepath.Join(execDir, "hk.part")
	err := ioutil.WriteFile(part, p, 0755)
	if err != nil {
		return err
	}
	defer os.Remove(part)

	// move the existing executable to a new file in the same directory
	oldExecPath := fmt.Sprintf("%s.old", name)
	err = os.Rename(name, oldExecPath)
	if err != nil {
		return err
	}

	// move the new executable in to become the new program
	err = os.Rename(part, name)

	if err != nil {
		// copy unsuccessful
		_ := os.Rename(oldExecPath, name)
		return err
	} else {
		// copy successful, remove the old binary
		_ = os.Remove(oldExecPath)
	}

	return nil
}

// returns a random duration in [0,n).
func randDuration(n time.Duration) time.Duration {
	return time.Duration(rand.Int63n(int64(n)))
}

func slug(ver string) string {
	return "hk-" + ver + "-" + plat
}

func fetch(url string) (io.ReadCloser, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("bad http status from %s: %v", url, resp.Status)
	}
	return resp.Body, nil
}

func readTime(path string) time.Time {
	p, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		return time.Time{}
	}
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	t, err := time.Parse(time.RFC3339, string(p))
	if err != nil {
		return time.Now().Add(1000 * time.Hour)
	}
	return t
}

func writeTime(path string, t time.Time) bool {
	return ioutil.WriteFile(path, []byte(t.Format(time.RFC3339)), 0644) == nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/dickeyxxx/golock"
	"github.com/franela/goreq"
	"github.com/heroku/heroku-cli/gode"
	"github.com/kardianos/osext"
	"github.com/ulikunitz/xz"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:            "update",
	Hidden:           true,
	Description:      "updates heroku-cli",
	DisableAnalytics: true,
	Args:             []Arg{{Name: "channel", Optional: true}},
	Flags:            []Flag{{Name: "background", Hidden: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		t := "foreground"
		if ctx.Flags["background"] == true {
			t = "background"
		}
		Update(channel, t)
	},
}

var binPath string
var updateLockPath = filepath.Join(AppDir(), "updating.lock")
var autoupdateFile = filepath.Join(AppDir(), "autoupdate")
var tmpPath = filepath.Join(AppDir(), "tmp")

func init() {
	binPath, _ = osext.Executable()
}

// Update updates the CLI and plugins
func Update(channel string, t string) {
	if !IsUpdateNeeded(t) {
		return
	}
	touchAutoupdateFile()
	SubmitAnalytics()
	updateCLI(channel)
	SetupNode()
	SetupBuiltinPlugins()
	updatePlugins()
	truncateErrorLog()
	cleanTmpDir()
}

func updatePlugins() {
	action("heroku-cli: Updating plugins", "", func() {
		plugins := PluginNamesNotSymlinked()
		if len(plugins) == 0 {
			return
		}
		packages, err := gode.OutdatedPackages(plugins...)
		WarnIfError(err)
		if len(packages) > 0 {
			for name, version := range packages {
				lockPlugin(name)
				WarnIfError(gode.InstallPackages(name + "@" + version))
				plugin, err := ParsePlugin(name)
				WarnIfError(err)
				AddPluginsToCache(plugin)
				unlockPlugin(name)
			}
			Errf(" done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
		} else {
			Errln(" no plugins to update.")
		}
	})
}

func updateCLI(channel string) {
	if channel == "?" {
		// do not update dev version
		return
	}
	manifest, err := getUpdateManifest(channel)
	if err != nil {
		Warn("Error updating CLI")
		WarnIfError(err)
		return
	}
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	locked, err := golock.IsLocked(updateLockPath)
	LogIfError(err)
	if locked {
		Warn("Update in progress")
		return
	}
	LogIfError(golock.Lock(updateLockPath))
	unlock := func() {
		golock.Unlock(updateLockPath)
	}
	defer unlock()
	msg := fmt.Sprintf("heroku-cli: Updating to %s", manifest.Version)
	if manifest.Channel != "master" {
		msg = fmt.Sprintf("%s (%s)", msg, manifest.Channel)
	}
	action(msg, "done", func() {
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		// on windows we can't remove an existing file or remove the running binary
		// so we download the file to binName.new
		// move the running binary to binName.old (deleting any existing file first)
		// rename the downloaded file to binName
		tmpBinPathNew := binPath + ".new"
		tmpBinPathOld := binPath + ".old"
		if err := downloadBin(tmpBinPathNew, build.URL); err != nil {
			panic(err)
		}
		if fileSha1(tmpBinPathNew) != build.Sha1 {
			panic("SHA mismatch")
		}
		os.Remove(tmpBinPathOld)
		os.Rename(binPath, tmpBinPathOld)
		if err := os.Rename(tmpBinPathNew, binPath); err != nil {
			panic(err)
		}
		os.Remove(tmpBinPathOld)
		unlock()
		clearAutoupdateFile() // force full update
	})
	reexec() // reexec to finish updating with new code
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "background" {
		return time.Since(f.ModTime()) > 4*time.Hour
	} else if t == "block" {
		return time.Since(f.ModTime()) > 2160*time.Hour // 90 days
	}
	return true
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
	out.Close()
}

// forces a full update on the next run
func clearAutoupdateFile() {
	WarnIfError(os.Remove(autoupdateFile))
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) (*manifest, error) {
	res, err := goreq.Request{
		Uri:       "https://cli-assets.heroku.com/" + channel + "/manifest.json",
		Timeout:   30 * time.Minute,
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return nil, err
	}
	var m manifest
	res.Body.FromJsonTo(&m)
	return &m, nil
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer out.Close()
	res, err := goreq.Request{
		Uri:       url + ".xz",
		Timeout:   30 * time.Minute,
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return err
	}
	if res.StatusCode != 200 {
		b, _ := res.Body.ToString()
		return errors.New(b)
	}
	defer res.Body.Close()
	uncompressed, err := xz.NewReader(res.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	return err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	wg.Add(1)
	go func() {
		wg.Done()
		if IsUpdateNeeded("background") {
			exec.Command(binPath, "update", "--background").Start()
		}
	}()
}

// restarts the CLI with the same arguments
func reexec() {
	Debugln("reexecing new CLI...")
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	Exit(getExitCode(cmd.Run()))
}

func truncateErrorLog() {
	Debugln("truncating error log...")
	body, err := ioutil.ReadFile(ErrLogPath)
	if err != nil {
		if !os.IsNotExist(err) {
			WarnIfError(err)
		}
		return
	}
	lines := strings.Split(string(body), "\n")
	lines = lines[maxint(len(lines)-1000, 0) : len(lines)-1]
	err = ioutil.WriteFile(ErrLogPath, []byte(strings.Join(lines, "\n")+"\n"), 0644)
	WarnIfError(err)
}

func maxint(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func cleanTmpDir() {
	Debugln("cleaning up tmp dirs...")
	dirs, err := ioutil.ReadDir(tmpPath)
	if err != nil {
		WarnIfError(err)
		return
	}
	for _, dir := range dirs {
		if time.Since(dir.ModTime()) > 24*time.Hour {
			path := filepath.Join(tmpPath, dir.Name())
			Debugln("deleting " + path)
			WarnIfError(os.RemoveAll(path))
		}
	}
}
<commit_msg>submit analytis after updating plugin versions<commit_after>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/dickeyxxx/golock"
	"github.com/franela/goreq"
	"github.com/heroku/heroku-cli/gode"
	"github.com/kardianos/osext"
	"github.com/ulikunitz/xz"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:            "update",
	Hidden:           true,
	Description:      "updates heroku-cli",
	DisableAnalytics: true,
	Args:             []Arg{{Name: "channel", Optional: true}},
	Flags:            []Flag{{Name: "background", Hidden: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = Channel
		}
		t := "foreground"
		if ctx.Flags["background"] == true {
			t = "background"
		}
		Update(channel, t)
	},
}

var binPath string
var updateLockPath = filepath.Join(AppDir(), "updating.lock")
var autoupdateFile = filepath.Join(AppDir(), "autoupdate")
var tmpPath = filepath.Join(AppDir(), "tmp")

func init() {
	binPath, _ = osext.Executable()
}

// Update updates the CLI and plugins
func Update(channel string, t string) {
	if !IsUpdateNeeded(t) {
		return
	}
	touchAutoupdateFile()
	updateCLI(channel)
	SetupNode()
	SetupBuiltinPlugins()
	updatePlugins()
	SubmitAnalytics()
	truncateErrorLog()
	cleanTmpDir()
}

func updatePlugins() {
	action("heroku-cli: Updating plugins", "", func() {
		plugins := PluginNamesNotSymlinked()
		if len(plugins) == 0 {
			return
		}
		packages, err := gode.OutdatedPackages(plugins...)
		WarnIfError(err)
		if len(packages) > 0 {
			for name, version := range packages {
				lockPlugin(name)
				WarnIfError(gode.InstallPackages(name + "@" + version))
				plugin, err := ParsePlugin(name)
				WarnIfError(err)
				AddPluginsToCache(plugin)
				unlockPlugin(name)
			}
			Errf(" done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
		} else {
			Errln(" no plugins to update.")
		}
	})
}

func updateCLI(channel string) {
	if channel == "?" {
		// do not update dev version
		return
	}
	manifest, err := getUpdateManifest(channel)
	if err != nil {
		Warn("Error updating CLI")
		WarnIfError(err)
		return
	}
	if manifest.Version == Version && manifest.Channel == Channel {
		return
	}
	locked, err := golock.IsLocked(updateLockPath)
	LogIfError(err)
	if locked {
		Warn("Update in progress")
		return
	}
	LogIfError(golock.Lock(updateLockPath))
	unlock := func() {
		golock.Unlock(updateLockPath)
	}
	defer unlock()
	msg := fmt.Sprintf("heroku-cli: Updating to %s", manifest.Version)
	if manifest.Channel != "master" {
		msg = fmt.Sprintf("%s (%s)", msg, manifest.Channel)
	}
	action(msg, "done", func() {
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		// on windows we can't remove an existing file or remove the running binary
		// so we download the file to binName.new
		// move the running binary to binName.old (deleting any existing file first)
		// rename the downloaded file to binName
		tmpBinPathNew := binPath + ".new"
		tmpBinPathOld := binPath + ".old"
		if err := downloadBin(tmpBinPathNew, build.URL); err != nil {
			panic(err)
		}
		if fileSha1(tmpBinPathNew) != build.Sha1 {
			panic("SHA mismatch")
		}
		os.Remove(tmpBinPathOld)
		os.Rename(binPath, tmpBinPathOld)
		if err := os.Rename(tmpBinPathNew, binPath); err != nil {
			panic(err)
		}
		os.Remove(tmpBinPathOld)
		unlock()
		clearAutoupdateFile() // force full update
	})
	reexec() // reexec to finish updating with new code
}

// IsUpdateNeeded checks if an update is available
func IsUpdateNeeded(t string) bool {
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	if t == "background" {
		return time.Since(f.ModTime()) > 4*time.Hour
	} else if t == "block" {
		return time.Since(f.ModTime()) > 2160*time.Hour // 90 days
	}
	return true
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
	out.Close()
}

// forces a full update on the next run
func clearAutoupdateFile() {
	WarnIfError(os.Remove(autoupdateFile))
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) (*manifest, error) {
	res, err := goreq.Request{
		Uri:       "https://cli-assets.heroku.com/" + channel + "/manifest.json",
		Timeout:   30 * time.Minute,
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return nil, err
	}
	var m manifest
	res.Body.FromJsonTo(&m)
	return &m, nil
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer out.Close()
	res, err := goreq.Request{
		Uri:       url + ".xz",
		Timeout:   30 * time.Minute,
		ShowDebug: debugging,
	}.Do()
	if err != nil {
		return err
	}
	if res.StatusCode != 200 {
		b, _ := res.Body.ToString()
		return errors.New(b)
	}
	defer res.Body.Close()
	uncompressed, err := xz.NewReader(res.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	return err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

// TriggerBackgroundUpdate will trigger an update to the client in the background
func TriggerBackgroundUpdate() {
	wg.Add(1)
	go func() {
		wg.Done()
		if IsUpdateNeeded("background") {
			exec.Command(binPath, "update", "--background").Start()
		}
	}()
}

// restarts the CLI with the same arguments
func reexec() {
	Debugln("reexecing new CLI...")
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	Exit(getExitCode(cmd.Run()))
}

func truncateErrorLog() {
	Debugln("truncating error log...")
	body, err := ioutil.ReadFile(ErrLogPath)
	if err != nil {
		if !os.IsNotExist(err) {
			WarnIfError(err)
		}
		return
	}
	lines := strings.Split(string(body), "\n")
	lines = lines[maxint(len(lines)-1000, 0) : len(lines)-1]
	err = ioutil.WriteFile(ErrLogPath, []byte(strings.Join(lines, "\n")+"\n"), 0644)
	WarnIfError(err)
}

func maxint(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func cleanTmpDir() {
	Debugln("cleaning up tmp dirs...")
	dirs, err := ioutil.ReadDir(tmpPath)
	if err != nil {
		WarnIfError(err)
		return
	}
	for _, dir := range dirs {
		if time.Since(dir.ModTime()) > 24*time.Hour {
			path := filepath.Join(tmpPath, dir.Name())
			Debugln("deleting " + path)
			WarnIfError(os.RemoveAll(path))
		}
	}
}
<|endoftext|>