<commit_before>package apply2

import (
	"encoding/gob"
	"os"
	"path"

	"github.com/dchest/safefile"
	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	"github.com/itchio/savior/seeksource"
	"github.com/itchio/wharf/eos"
	"github.com/itchio/wharf/eos/option"
	"github.com/itchio/wharf/pools/fspool"
	"github.com/itchio/wharf/pwr/bowl"
	"github.com/itchio/wharf/pwr/patcher"
	"github.com/itchio/wharf/state"
	"github.com/pkg/errors"
)

var args = struct {
	patch *string
	dir   *string
	old   *string
}{}

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("apply2", "(Advanced) Use a patch to resumably patch a directory to a new version")
	args.patch = cmd.Arg("patch", "Patch file (.pwr), previously generated with the `diff` command.").Required().String()
	args.old = cmd.Arg("old", "Directory with old files").Required().String()
	args.dir = cmd.Flag("dir", "Directory for patched files and checkpoints").Required().String()
	ctx.Register(cmd, do)
}

func do(ctx *mansion.Context) {
	ctx.Must(Do(&Params{
		Patch: *args.patch,
		Old:   *args.old,
		Dir:   *args.dir,
	}))
}

type Params struct {
	Patch string
	Old   string
	Dir   string
}

func Do(params *Params) error {
	patch := params.Patch
	old := params.Old
	dir := params.Dir

	consumer := &state.Consumer{
		OnMessage: func(level string, message string) {
			comm.Logf("[%s] %s", level, message)
		},
	}

	patchReader, err := eos.Open(patch, option.WithConsumer(comm.NewStateConsumer()))
	if err != nil {
		return errors.Wrap(err, "opening patch")
	}

	patchSource := seeksource.FromFile(patchReader)
	_, err = patchSource.Resume(nil)
	if err != nil {
		return errors.Wrap(err, "creating patch source")
	}

	p, err := patcher.New(patchSource, consumer)
	if err != nil {
		return errors.Wrap(err, "creating patcher")
	}

	// comm.StartProgressWithTotalBytes(patchSource.Size())

	var checkpoint *patcher.Checkpoint
	checkpointPath := path.Join(dir, "checkpoint.bwl")

	checkpointFile, err := os.Open(checkpointPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return errors.Wrap(err, "opening checkpoint")
		}
	} else {
		defer checkpointFile.Close()

		checkpoint = &patcher.Checkpoint{}

		dec := gob.NewDecoder(checkpointFile)
		err := dec.Decode(checkpoint)
		if err != nil {
			return errors.Wrap(err, "decoding checkpoint")
		}

		// yay, we have a checkpoint!
	}

	p.SetSaveConsumer(&patcherSaveConsumer{
		shouldSave: func() bool {
			// TODO: patcher checkpoints are big. how often do we actually wanna do this?
			return true
		},
		save: func(c *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
			checkpointFile, err := safefile.Create(checkpointPath, 0644)
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "creating checkpoint file")
			}
			defer checkpointFile.Close()

			enc := gob.NewEncoder(checkpointFile)
			err = enc.Encode(c)
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "encoding checkpoint")
			}

			err = checkpointFile.Commit()
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "committing checkpoint file")
			}

			return patcher.AfterSaveContinue, nil
		},
	})

	targetPool := fspool.New(p.GetTargetContainer(), old)

	bowl, err := bowl.NewFreshBowl(&bowl.FreshBowlParams{
		SourceContainer: p.GetSourceContainer(),
		TargetContainer: p.GetTargetContainer(),
		TargetPool:      targetPool,
		OutputFolder:    dir,
	})
	if err != nil {
		return errors.Wrap(err, "creating fresh bowl")
	}

	err = p.Resume(checkpoint, targetPool, bowl)
	if err != nil {
		return errors.Wrap(err, "patching")
	}
	comm.EndProgress()

	return nil
}

type patcherSaveConsumer struct {
	shouldSave func() bool
	save       func(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error)
}

var _ patcher.SaveConsumer = (*patcherSaveConsumer)(nil)

func (psc *patcherSaveConsumer) ShouldSave() bool {
	return psc.shouldSave()
}

func (psc *patcherSaveConsumer) Save(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
	return psc.save(checkpoint)
}
<commit_msg>Add '-d' as short option for apply2<commit_after>package apply2

import (
	"encoding/gob"
	"os"
	"path"

	"github.com/dchest/safefile"
	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	"github.com/itchio/savior/seeksource"
	"github.com/itchio/wharf/eos"
	"github.com/itchio/wharf/eos/option"
	"github.com/itchio/wharf/pools/fspool"
	"github.com/itchio/wharf/pwr/bowl"
	"github.com/itchio/wharf/pwr/patcher"
	"github.com/itchio/wharf/state"
	"github.com/pkg/errors"
)

var args = struct {
	patch *string
	dir   *string
	old   *string
}{}

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("apply2", "(Advanced) Use a patch to resumably patch a directory to a new version")
	args.patch = cmd.Arg("patch", "Patch file (.pwr), previously generated with the `diff` command.").Required().String()
	args.old = cmd.Arg("old", "Directory with old files").Required().String()
	args.dir = cmd.Flag("dir", "Directory for patched files and checkpoints").Short('d').Required().String()
	ctx.Register(cmd, do)
}

func do(ctx *mansion.Context) {
	ctx.Must(Do(&Params{
		Patch: *args.patch,
		Old:   *args.old,
		Dir:   *args.dir,
	}))
}

type Params struct {
	Patch string
	Old   string
	Dir   string
}

func Do(params *Params) error {
	patch := params.Patch
	old := params.Old
	dir := params.Dir

	consumer := &state.Consumer{
		OnMessage: func(level string, message string) {
			comm.Logf("[%s] %s", level, message)
		},
	}

	patchReader, err := eos.Open(patch, option.WithConsumer(comm.NewStateConsumer()))
	if err != nil {
		return errors.Wrap(err, "opening patch")
	}

	patchSource := seeksource.FromFile(patchReader)
	_, err = patchSource.Resume(nil)
	if err != nil {
		return errors.Wrap(err, "creating patch source")
	}

	p, err := patcher.New(patchSource, consumer)
	if err != nil {
		return errors.Wrap(err, "creating patcher")
	}

	// comm.StartProgressWithTotalBytes(patchSource.Size())

	var checkpoint *patcher.Checkpoint
	checkpointPath := path.Join(dir, "checkpoint.bwl")

	checkpointFile, err := os.Open(checkpointPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return errors.Wrap(err, "opening checkpoint")
		}
	} else {
		defer checkpointFile.Close()

		checkpoint = &patcher.Checkpoint{}

		dec := gob.NewDecoder(checkpointFile)
		err := dec.Decode(checkpoint)
		if err != nil {
			return errors.Wrap(err, "decoding checkpoint")
		}

		// yay, we have a checkpoint!
	}

	p.SetSaveConsumer(&patcherSaveConsumer{
		shouldSave: func() bool {
			// TODO: patcher checkpoints are big. how often do we actually wanna do this?
			return true
		},
		save: func(c *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
			checkpointFile, err := safefile.Create(checkpointPath, 0644)
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "creating checkpoint file")
			}
			defer checkpointFile.Close()

			enc := gob.NewEncoder(checkpointFile)
			err = enc.Encode(c)
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "encoding checkpoint")
			}

			err = checkpointFile.Commit()
			if err != nil {
				return patcher.AfterSaveStop, errors.Wrap(err, "committing checkpoint file")
			}

			return patcher.AfterSaveContinue, nil
		},
	})

	targetPool := fspool.New(p.GetTargetContainer(), old)

	bowl, err := bowl.NewFreshBowl(&bowl.FreshBowlParams{
		SourceContainer: p.GetSourceContainer(),
		TargetContainer: p.GetTargetContainer(),
		TargetPool:      targetPool,
		OutputFolder:    dir,
	})
	if err != nil {
		return errors.Wrap(err, "creating fresh bowl")
	}

	err = p.Resume(checkpoint, targetPool, bowl)
	if err != nil {
		return errors.Wrap(err, "patching")
	}
	comm.EndProgress()

	return nil
}

type patcherSaveConsumer struct {
	shouldSave func() bool
	save       func(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error)
}

var _ patcher.SaveConsumer = (*patcherSaveConsumer)(nil)

func (psc *patcherSaveConsumer) ShouldSave() bool {
	return psc.shouldSave()
}

func (psc *patcherSaveConsumer) Save(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
	return psc.save(checkpoint)
}
<|endoftext|>


<commit_before>package locking

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/git-lfs/git-lfs/api"
	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools/kv"
)

var (
	// ErrNoMatchingLocks is an error returned when no matching locks were
	// able to be resolved
	ErrNoMatchingLocks = errors.New("lfs: no matching locks found")
	// ErrLockAmbiguous is an error returned when multiple matching locks
	// were found
	ErrLockAmbiguous = errors.New("lfs: multiple locks found; ambiguous")
)

// Client is the main interface object for the locking package
type Client struct {
	cfg       *config.Configuration
	apiClient *api.Client
	cache     *LockCache
}

// NewClient creates a new locking client with the given configuration
// You must call the returned object's `Close` method when you are finished with
// it
func NewClient(cfg *config.Configuration) (*Client, error) {

	apiClient := api.NewClient(api.NewHttpLifecycle(cfg))

	lockDir := filepath.Join(config.LocalGitStorageDir, "lfs")
	err := os.MkdirAll(lockDir, 0755)
	if err != nil {
		return nil, err
	}
	lockFile := filepath.Join(lockDir, "lockcache.db")
	cache, err := NewLockCache(lockFile)
	if err != nil {
		return nil, err
	}
	return &Client{cfg, apiClient, cache}, nil
}

// Close this client instance; must be called to dispose of resources
func (c *Client) Close() error {
	return c.cache.Save()
}

// LockFile attempts to lock a file on the current remote
// path must be relative to the root of the repository
// Returns the lock id if successful, or an error
func (c *Client) LockFile(path string) (Lock, error) {

	// TODO: this is not really the constraint we need to avoid merges, improve as per proposal
	latest, err := git.CurrentRemoteRef()
	if err != nil {
		return Lock{}, err
	}

	s, resp := c.apiClient.Locks.Lock(&api.LockRequest{
		Path:               path,
		Committer:          api.NewCommitter(c.cfg.CurrentCommitter()),
		LatestRemoteCommit: latest.Sha,
	})

	if _, err := c.apiClient.Do(s); err != nil {
		return Lock{}, fmt.Errorf("Error communicating with LFS API: %v", err)
	}

	if len(resp.Err) > 0 {
		return Lock{}, fmt.Errorf("Server unable to create lock: %v", resp.Err)
	}

	lock := c.newLockFromApi(*resp.Lock)

	if err := c.cache.Add(lock); err != nil {
		return Lock{}, fmt.Errorf("Error caching lock information: %v", err)
	}

	return lock, nil
}

// UnlockFile attempts to unlock a file on the current remote
// path must be relative to the root of the repository
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFile(path string, force bool) error {

	id, err := c.lockIdFromPath(path)
	if err != nil {
		return fmt.Errorf("Unable to get lock id: %v", err)
	}

	return c.UnlockFileById(id, force)

}

// UnlockFileById attempts to unlock a lock with a given id on the current remote
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFileById(id string, force bool) error {
	s, resp := c.apiClient.Locks.Unlock(id, force)

	if _, err := c.apiClient.Do(s); err != nil {
		return fmt.Errorf("Error communicating with LFS API: %v", err)
	}

	if len(resp.Err) > 0 {
		return fmt.Errorf("Server unable to unlock lock: %v", resp.Err)
	}

	if err := c.cache.RemoveById(id); err != nil {
		return fmt.Errorf("Error caching unlock information: %v", err)
	}

	return nil
}

// Lock is a record of a locked file
type Lock struct {
	// Id is the unique identifier corresponding to this particular Lock. It
	// must be consistent with the local copy, and the server's copy.
	Id string
	// Path is an absolute path to the file that is locked as a part of this
	// lock.
	Path string
	// Name is the name of the person holding this lock
	Name string
	// Email address of the person holding this lock
	Email string
	// LockedAt is the time at which this lock was acquired.
	LockedAt time.Time
}

func (c *Client) newLockFromApi(a api.Lock) Lock {
	return Lock{
		Id:       a.Id,
		Path:     a.Path,
		Name:     a.Committer.Name,
		Email:    a.Committer.Email,
		LockedAt: a.LockedAt,
	}
}

// SearchLocks returns a channel of locks which match the given name/value filter
// If limit > 0 then search stops at that number of locks
// If localOnly = true, don't query the server & report only own local locks
func (c *Client) SearchLocks(filter map[string]string, limit int, localOnly bool) (locks []Lock, err error) {

	if localOnly {
		return c.searchCachedLocks(filter, limit)
	} else {
		return c.searchRemoteLocks(filter, limit)
	}
}

func (c *Client) searchCachedLocks(filter map[string]string, limit int) ([]Lock, error) {
	cachedlocks := c.cache.Locks()
	path, filterByPath := filter["path"]
	id, filterById := filter["id"]
	lockCount := 0
	locks := make([]Lock, 0, len(cachedlocks))
	for _, l := range cachedlocks {
		// Manually filter by Path/Id
		if (filterByPath && path != l.Path) ||
			(filterById && id != l.Id) {
			continue
		}
		locks = append(locks, l)
		lockCount++
		if limit > 0 && lockCount >= limit {
			break
		}
	}
	return locks, nil
}

func (c *Client) searchRemoteLocks(filter map[string]string, limit int) ([]Lock, error) {
	locks := make([]Lock, 0, limit)

	apifilters := make([]api.Filter, 0, len(filter))
	for k, v := range filter {
		apifilters = append(apifilters, api.Filter{k, v})
	}
	query := &api.LockSearchRequest{Filters: apifilters}
	for {
		s, resp := c.apiClient.Locks.Search(query)
		if _, err := c.apiClient.Do(s); err != nil {
			return locks, fmt.Errorf("Error communicating with LFS API: %v", err)
		}

		if resp.Err != "" {
			return locks, fmt.Errorf("Error response from LFS API: %v", resp.Err)
		}

		for _, l := range resp.Locks {
			locks = append(locks, c.newLockFromApi(l))
			if limit > 0 && len(locks) >= limit {
				// Exit outer loop too
				return locks, nil
			}
		}

		if resp.NextCursor != "" {
			query.Cursor = resp.NextCursor
		} else {
			break
		}
	}

	return locks, nil

}

// lockIdFromPath makes a call to the LFS API and resolves the ID for the locked
// locked at the given path.
//
// If the API call failed, an error will be returned. If multiple locks matched
// the given path (should not happen during real-world usage), an error will be
// returnd. If no locks matched the given path, an error will be returned.
//
// If the API call is successful, and only one lock matches the given filepath,
// then its ID will be returned, along with a value of "nil" for the error.
func (c *Client) lockIdFromPath(path string) (string, error) {
	s, resp := c.apiClient.Locks.Search(&api.LockSearchRequest{
		Filters: []api.Filter{
			{"path", path},
		},
	})

	if _, err := c.apiClient.Do(s); err != nil {
		return "", err
	}

	switch len(resp.Locks) {
	case 0:
		return "", ErrNoMatchingLocks
	case 1:
		return resp.Locks[0].Id, nil
	default:
		return "", ErrLockAmbiguous
	}
}

// Fetch locked files for the current committer and cache them locally
// This can be used to sync up locked files when moving machines
func (c *Client) refreshLockCache() error {
	// TODO: filters don't seem to currently define how to search for a
	// committer's email. Is it "committer.email"? For now, just iterate
	locks, err := c.SearchLocks(nil, 0, false)
	if err != nil {
		return err
	}

	// We're going to overwrite the entire local cache
	c.cache.Clear()

	_, email := c.cfg.CurrentCommitter()
	for _, l := range locks {
		if l.Email == email {
			c.cache.Add(l)
		}
	}

	return nil
}

func init() {
	kv.RegisterTypeForStorage(&Lock{})
}
<commit_msg>locking: add `json:""` tags to Lock fields for `--json` support<commit_after>package locking

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/git-lfs/git-lfs/api"
	"github.com/git-lfs/git-lfs/config"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools/kv"
)

var (
	// ErrNoMatchingLocks is an error returned when no matching locks were
	// able to be resolved
	ErrNoMatchingLocks = errors.New("lfs: no matching locks found")
	// ErrLockAmbiguous is an error returned when multiple matching locks
	// were found
	ErrLockAmbiguous = errors.New("lfs: multiple locks found; ambiguous")
)

// Client is the main interface object for the locking package
type Client struct {
	cfg       *config.Configuration
	apiClient *api.Client
	cache     *LockCache
}

// NewClient creates a new locking client with the given configuration
// You must call the returned object's `Close` method when you are finished with
// it
func NewClient(cfg *config.Configuration) (*Client, error) {

	apiClient := api.NewClient(api.NewHttpLifecycle(cfg))

	lockDir := filepath.Join(config.LocalGitStorageDir, "lfs")
	err := os.MkdirAll(lockDir, 0755)
	if err != nil {
		return nil, err
	}
	lockFile := filepath.Join(lockDir, "lockcache.db")
	cache, err := NewLockCache(lockFile)
	if err != nil {
		return nil, err
	}
	return &Client{cfg, apiClient, cache}, nil
}

// Close this client instance; must be called to dispose of resources
func (c *Client) Close() error {
	return c.cache.Save()
}

// LockFile attempts to lock a file on the current remote
// path must be relative to the root of the repository
// Returns the lock id if successful, or an error
func (c *Client) LockFile(path string) (Lock, error) {

	// TODO: this is not really the constraint we need to avoid merges, improve as per proposal
	latest, err := git.CurrentRemoteRef()
	if err != nil {
		return Lock{}, err
	}

	s, resp := c.apiClient.Locks.Lock(&api.LockRequest{
		Path:               path,
		Committer:          api.NewCommitter(c.cfg.CurrentCommitter()),
		LatestRemoteCommit: latest.Sha,
	})

	if _, err := c.apiClient.Do(s); err != nil {
		return Lock{}, fmt.Errorf("Error communicating with LFS API: %v", err)
	}

	if len(resp.Err) > 0 {
		return Lock{}, fmt.Errorf("Server unable to create lock: %v", resp.Err)
	}

	lock := c.newLockFromApi(*resp.Lock)

	if err := c.cache.Add(lock); err != nil {
		return Lock{}, fmt.Errorf("Error caching lock information: %v", err)
	}

	return lock, nil
}

// UnlockFile attempts to unlock a file on the current remote
// path must be relative to the root of the repository
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFile(path string, force bool) error {

	id, err := c.lockIdFromPath(path)
	if err != nil {
		return fmt.Errorf("Unable to get lock id: %v", err)
	}

	return c.UnlockFileById(id, force)

}

// UnlockFileById attempts to unlock a lock with a given id on the current remote
// Force causes the file to be unlocked from other users as well
func (c *Client) UnlockFileById(id string, force bool) error {
	s, resp := c.apiClient.Locks.Unlock(id, force)

	if _, err := c.apiClient.Do(s); err != nil {
		return fmt.Errorf("Error communicating with LFS API: %v", err)
	}

	if len(resp.Err) > 0 {
		return fmt.Errorf("Server unable to unlock lock: %v", resp.Err)
	}

	if err := c.cache.RemoveById(id); err != nil {
		return fmt.Errorf("Error caching unlock information: %v", err)
	}

	return nil
}

// Lock is a record of a locked file
type Lock struct {
	// Id is the unique identifier corresponding to this particular Lock. It
	// must be consistent with the local copy, and the server's copy.
	Id string `json:"id"`
	// Path is an absolute path to the file that is locked as a part of this
	// lock.
	Path string `json:"path"`
	// Name is the name of the person holding this lock
	Name string `json:"name"`
	// Email address of the person holding this lock
	Email string `json:"email"`
	// LockedAt is the time at which this lock was acquired.
	LockedAt time.Time `json:"locked_at"`
}

func (c *Client) newLockFromApi(a api.Lock) Lock {
	return Lock{
		Id:       a.Id,
		Path:     a.Path,
		Name:     a.Committer.Name,
		Email:    a.Committer.Email,
		LockedAt: a.LockedAt,
	}
}

// SearchLocks returns a channel of locks which match the given name/value filter
// If limit > 0 then search stops at that number of locks
// If localOnly = true, don't query the server & report only own local locks
func (c *Client) SearchLocks(filter map[string]string, limit int, localOnly bool) (locks []Lock, err error) {

	if localOnly {
		return c.searchCachedLocks(filter, limit)
	} else {
		return c.searchRemoteLocks(filter, limit)
	}
}

func (c *Client) searchCachedLocks(filter map[string]string, limit int) ([]Lock, error) {
	cachedlocks := c.cache.Locks()
	path, filterByPath := filter["path"]
	id, filterById := filter["id"]
	lockCount := 0
	locks := make([]Lock, 0, len(cachedlocks))
	for _, l := range cachedlocks {
		// Manually filter by Path/Id
		if (filterByPath && path != l.Path) ||
			(filterById && id != l.Id) {
			continue
		}
		locks = append(locks, l)
		lockCount++
		if limit > 0 && lockCount >= limit {
			break
		}
	}
	return locks, nil
}

func (c *Client) searchRemoteLocks(filter map[string]string, limit int) ([]Lock, error) {
	locks := make([]Lock, 0, limit)

	apifilters := make([]api.Filter, 0, len(filter))
	for k, v := range filter {
		apifilters = append(apifilters, api.Filter{k, v})
	}
	query := &api.LockSearchRequest{Filters: apifilters}
	for {
		s, resp := c.apiClient.Locks.Search(query)
		if _, err := c.apiClient.Do(s); err != nil {
			return locks, fmt.Errorf("Error communicating with LFS API: %v", err)
		}

		if resp.Err != "" {
			return locks, fmt.Errorf("Error response from LFS API: %v", resp.Err)
		}

		for _, l := range resp.Locks {
			locks = append(locks, c.newLockFromApi(l))
			if limit > 0 && len(locks) >= limit {
				// Exit outer loop too
				return locks, nil
			}
		}

		if resp.NextCursor != "" {
			query.Cursor = resp.NextCursor
		} else {
			break
		}
	}

	return locks, nil

}

// lockIdFromPath makes a call to the LFS API and resolves the ID for the locked
// locked at the given path.
//
// If the API call failed, an error will be returned. If multiple locks matched
// the given path (should not happen during real-world usage), an error will be
// returnd. If no locks matched the given path, an error will be returned.
//
// If the API call is successful, and only one lock matches the given filepath,
// then its ID will be returned, along with a value of "nil" for the error.
func (c *Client) lockIdFromPath(path string) (string, error) {
	s, resp := c.apiClient.Locks.Search(&api.LockSearchRequest{
		Filters: []api.Filter{
			{"path", path},
		},
	})

	if _, err := c.apiClient.Do(s); err != nil {
		return "", err
	}

	switch len(resp.Locks) {
	case 0:
		return "", ErrNoMatchingLocks
	case 1:
		return resp.Locks[0].Id, nil
	default:
		return "", ErrLockAmbiguous
	}
}

// Fetch locked files for the current committer and cache them locally
// This can be used to sync up locked files when moving machines
func (c *Client) refreshLockCache() error {
	// TODO: filters don't seem to currently define how to search for a
	// committer's email. Is it "committer.email"? For now, just iterate
	locks, err := c.SearchLocks(nil, 0, false)
	if err != nil {
		return err
	}

	// We're going to overwrite the entire local cache
	c.cache.Clear()

	_, email := c.cfg.CurrentCommitter()
	for _, l := range locks {
		if l.Email == email {
			c.cache.Add(l)
		}
	}

	return nil
}

func init() {
	kv.RegisterTypeForStorage(&Lock{})
}
<|endoftext|>


<commit_before>package logger

import (
  "log"
  "time"
  "net/http"

  sentry "github.com/getsentry/sentry-go"
)

type RecoveredError struct {
  ErrorMessage string
}

func (re RecoveredError) Error() string {
  return re.ErrorMessage
}

type ReportableError struct {
  Error    error
  Request  *http.Request
  Response *http.Response
}

func (re ReportableError) hint() *sentry.EventHint {
  return &sentry.EventHint{
    Request:  re.Request,
    Response: re.Response,
  }
}

func (re ReportableError) scope() *sentry.Scope {
  scope := sentry.NewScope()
  if re.hint().Request != nil {
    request := sentry.Request{}.FromHTTPRequest(re.hint().Request)
    scope.SetRequest(request)
  }
  if re.hint().Response != nil {
    scope.SetExtra("Response Status", re.hint().Response.Status);
  }
  return scope
}

func NotifySentry(re ReportableError) {
  // We don't need to set SENTRY_ENVIRONMENT, SENTRY_DSN or SENTRY_RELEASE
  // in ClientOptions as they are automatically picked up as env vars.
  // https://docs.sentry.io/platforms/go/config/
  client, err := sentry.NewClient(sentry.ClientOptions{})

  if err != nil {
    log.Printf("router: Sentry initialization failed: %v\n", err)
    return
  }

  hub := sentry.NewHub(client, re.scope())
  hub.CaptureException(re.Error)
  sentry.Flush(time.Second * 5)
}
<commit_msg>Ignore timeout errors<commit_after>package logger

import (
  "log"
  "time"
  "net"
  "net/http"

  sentry "github.com/getsentry/sentry-go"
)

type RecoveredError struct {
  ErrorMessage string
}

func (re RecoveredError) Error() string {
  return re.ErrorMessage
}

type ReportableError struct {
  Error    error
  Request  *http.Request
  Response *http.Response
}

func (re ReportableError) hint() *sentry.EventHint {
  return &sentry.EventHint{
    Request:  re.Request,
    Response: re.Response,
  }
}

func (re ReportableError) scope() *sentry.Scope {
  scope := sentry.NewScope()
  if re.hint().Request != nil {
    request := sentry.Request{}.FromHTTPRequest(re.hint().Request)
    scope.SetRequest(request)
  }
  if re.hint().Response != nil {
    scope.SetExtra("Response Status", re.hint().Response.Status);
  }
  return scope
}

func (re ReportableError) timeoutError() bool {
  opErr, ok := re.Error.(*net.OpError)
  return ok && opErr.Timeout()
}

func (re ReportableError) ignorableError() bool {
  // We don't want to hear about timeouts. These get visibility elsewhere.
  return re.timeoutError()
}

func NotifySentry(re ReportableError) {
  if re.ignorableError() { return }

  // We don't need to set SENTRY_ENVIRONMENT, SENTRY_DSN or SENTRY_RELEASE
  // in ClientOptions as they are automatically picked up as env vars.
  // https://docs.sentry.io/platforms/go/config/
  client, err := sentry.NewClient(sentry.ClientOptions{})

  if err != nil {
    log.Printf("router: Sentry initialization failed: %v\n", err)
    return
  }

  hub := sentry.NewHub(client, re.scope())
  hub.CaptureException(re.Error)
  sentry.Flush(time.Second * 5)
}
<|endoftext|>


<commit_before>package loggly

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

const (
	adapterName         = "loggly"
	logglyTokenEnvVar   = "LOGGLY_TOKEN"
	logglyTagsEnvVar    = "LOGGLY_TAGS"
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/inputs"
)

// TODO: consider logging all fatals to loggly

func init() {
	router.AdapterFactories.Register(NewLogglyAdapter, adapterName)

	r := &router.Route{
		Adapter: "loggly",
	}

	// It's not documented in the logspout repo but if you want to use an adapter without
	// going through the routesapi you must add at #init or via #New...
	err := router.Routes.Add(r)
	if err != nil {
		log.Fatal("Could not add route: ", err.Error())
	}
}

// NewLogglyAdapter returns an Adapter with that uses a loggly token taken from
// the LOGGLY_TOKEN environment variable
func NewLogglyAdapter(route *router.Route) (router.LogAdapter, error) {
	token := os.Getenv(logglyTokenEnvVar)

	if token == "" {
		return nil, errors.New("Could not find environment variable LOGGLY_TOKEN")
	}

	return &Adapter{
		token:  token,
		client: http.Client{},
	}, nil
}

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token  string
	client http.Client
}

// Stream satisfies the router.LogAdapter interface and passes all messages to Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		msg := logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}

		err := l.SendMessage(msg)

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

// SendMessage handles creating and sending a request to Loggly. Any errors that occur during that
// process are bubbled up to the caller
func (l *Adapter) SendMessage(msg logglyMessage) error {
	js, err := json.Marshal(msg)

	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token, "tag", os.Getenv(logglyTagsEnvVar))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(js))

	req.Header.Add("X-LOGGLY-TAG", os.Getenv(logglyTagsEnvVar))

	if err != nil {
		return err
	}

	// TODO: possibly use pool of workers to send requests?
	resp, err := l.client.Do(req)

	if err != nil {
		errMsg := fmt.Sprintf("Error from client: %s", err.Error())
		return errors.New(errMsg)
	}

	if resp.StatusCode != http.StatusOK {
		errMsg := fmt.Sprintf("Received a non 200 status code: %s", err.Error())
		return errors.New(errMsg)
	}

	return nil
}

type logglyMessage struct {
	Message           string `json:"message"`
	ContainerName     string `json:"container_name"`
	ContainerID       string `json:"container_id"`
	ContainerImage    string `json:"container_image"`
	ContainerHostname string `json:"hostname"`
}
<commit_msg>some print statements<commit_after>package loggly

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

const (
	adapterName         = "loggly"
	logglyTokenEnvVar   = "LOGGLY_TOKEN"
	logglyTagsEnvVar    = "LOGGLY_TAGS"
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/inputs"
)

// TODO: consider logging all fatals to loggly

func init() {
	router.AdapterFactories.Register(NewLogglyAdapter, adapterName)

	r := &router.Route{
		Adapter: "loggly",
	}

	// It's not documented in the logspout repo but if you want to use an adapter without
	// going through the routesapi you must add at #init or via #New...
	err := router.Routes.Add(r)
	if err != nil {
		log.Fatal("Could not add route: ", err.Error())
	}
}

// NewLogglyAdapter returns an Adapter with that uses a loggly token taken from
// the LOGGLY_TOKEN environment variable
func NewLogglyAdapter(route *router.Route) (router.LogAdapter, error) {
	token := os.Getenv(logglyTokenEnvVar)

	if token == "" {
		return nil, errors.New("Could not find environment variable LOGGLY_TOKEN")
	}

	return &Adapter{
		token:  token,
		client: http.Client{},
	}, nil
}

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token  string
	client http.Client
}

// Stream satisfies the router.LogAdapter interface and passes all messages to Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		msg := logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}

		err := l.SendMessage(msg)

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

// SendMessage handles creating and sending a request to Loggly. Any errors that occur during that
// process are bubbled up to the caller
func (l *Adapter) SendMessage(msg logglyMessage) error {
	js, err := json.Marshal(msg)

	if err != nil {
		return err
	}

  os.Stdout.Write(logglyTagsEnvVar)
	url := fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token, "tag", os.Getenv(logglyTagsEnvVar))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(js))

	req.Header.Add("X-LOGGLY-TAG", os.Getenv(logglyTagsEnvVar))
	os.Stdout.Write(req.Header)

	if err != nil {
		return err
	}

	// TODO: possibly use pool of workers to send requests?
	resp, err := l.client.Do(req)

	if err != nil {
		errMsg := fmt.Sprintf("Error from client: %s", err.Error())
		return errors.New(errMsg)
	}

	if resp.StatusCode != http.StatusOK {
		errMsg := fmt.Sprintf("Received a non 200 status code: %s", err.Error())
		return errors.New(errMsg)
	}

	return nil
}

type logglyMessage struct {
	Message           string `json:"message"`
	ContainerName     string `json:"container_name"`
	ContainerID       string `json:"container_id"`
	ContainerImage    string `json:"container_image"`
	ContainerHostname string `json:"hostname"`
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"github.com/drud/ddev/pkg/ddevapp"
	"github.com/drud/ddev/pkg/dockerutil"
	"github.com/drud/ddev/pkg/globalconfig"
	"github.com/drud/ddev/pkg/output"
	"github.com/drud/ddev/pkg/updatecheck"
	"github.com/drud/ddev/pkg/util"
	"github.com/drud/ddev/pkg/version"
	"github.com/rogpeppe/go-internal/semver"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"gopkg.in/segmentio/analytics-go.v3"
	"os"
	"path/filepath"

	"time"
)

var (
	updateInterval     = time.Hour * 24 * 7 // One week interval between updates
	serviceType        string
	updateDocURL       = "https://ddev.readthedocs.io/en/stable/#installation"
	instrumentationApp *ddevapp.DdevApp
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "ddev",
	Short: "DDEV-Local local development environment",
	Long: `Create and maintain a local web development environment.
Docs: https://ddev.readthedocs.io
Support: https://ddev.readthedocs.io/en/stable/#support`,
	Version: version.DdevVersion,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		command := os.Args[1]

		// LogSetup() has already been done, but now needs to be done
		// again *after* --json flag is parsed.
		output.LogSetUp()

		// Skip docker and other validation for most commands
		if command != "start" && command != "restart" {
			return
		}

		err := dockerutil.CheckDockerVersion(version.DockerVersionConstraint)
		if err != nil {
			if err.Error() == "no docker" {
				if os.Args[1] != "version" {
					util.Failed("Could not connect to docker. Please ensure Docker is installed and running.")
				}
			} else {
				util.Warning("The docker version currently installed does not seem to meet ddev's requirements: %v", err)
			}
		}

		updateFile := filepath.Join(globalconfig.GetGlobalDdevDir(), ".update")

		// Do periodic detection of whether an update is available for ddev users.
		timeToCheckForUpdates, err := updatecheck.IsUpdateNeeded(updateFile, updateInterval)
		if err != nil {
			util.Warning("Could not perform update check: %v", err)
		}

		if timeToCheckForUpdates && globalconfig.IsInternetActive() {
			// Recreate the updatefile with current time so we won't do this again soon.
			err = updatecheck.ResetUpdateTime(updateFile)
			if err != nil {
				util.Warning("Failed to update updatecheck file %s", updateFile)
				return // Do not continue as we'll end up with github api violations.
			}

			updateNeeded, updateVersion, updateURL, err := updatecheck.AvailableUpdates("drud", "ddev", version.DdevVersion)

			if err != nil {
				util.Warning("Could not check for updates. This is most often caused by a networking issue.")
				return
			}

			if updateNeeded {
				output.UserOut.Printf(util.ColorizeText(fmt.Sprintf("\n\nUpgraded DDEV %s is available!\nPlease visit %s to get the upgrade.\nFor upgrade help see %s\n\n", updateVersion, updateURL, updateDocURL), "green"))
			}
		}
	},
	PersistentPostRun: func(cmd *cobra.Command, args []string) {
		// Do not report these commands
		ignores := map[string]bool{"auth": true, "exec": true, "help": true, "hostname": true, "list": true, "ssh": true, "version": true}
		if _, ok := ignores[cmd.CalledAs()]; ok {
			return
		}
		instrumentationNotSetUpWarning()

		// All this nonsense is to capture the official usage we used for this command.
		// Unfortunately cobra doesn't seem to provide this easily.
		// We use the first word of Use: to get it.
		cmdCopy := cmd
		var fullCommand = make([]string, 0)
		fullCommand = append(fullCommand, util.GetFirstWord(cmdCopy.Use))
		for cmdCopy.HasParent() {
			fullCommand = append(fullCommand, util.GetFirstWord(cmdCopy.Parent().Use))
			cmdCopy = cmdCopy.Parent()
		}
		for i := 0; i < len(fullCommand)/2; i++ {
			j := len(fullCommand) - i - 1
			fullCommand[i], fullCommand[j] = fullCommand[j], fullCommand[i]
		}

		event := ""
		if len(fullCommand) > 1 {
			event = fullCommand[1]
		}

		if globalconfig.DdevGlobalConfig.InstrumentationOptIn && version.SegmentKey != "" && globalconfig.IsInternetActive() && len(fullCommand) > 1 {
			runTime := util.TimeTrack(time.Now(), "Instrumentation")
			// Try to get default instrumentationApp from current directory if not already set
			if instrumentationApp == nil {
				app, err := ddevapp.NewApp("", false)
				if err == nil {
					instrumentationApp = app
				}
			}
			// If it has been set, provide the tags, otherwise no app tags
			if instrumentationApp != nil {
				instrumentationApp.SetInstrumentationAppTags()
			}
			ddevapp.SetInstrumentationBaseTags()
			ddevapp.SendInstrumentationEvents(event)
			runTime()
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	// bind flags to viper config values...allows override by flag
	viper.AutomaticEnv() // read in environment variables that match

	if err := RootCmd.Execute(); err != nil {
		os.Exit(-1)
	}
}

func init() {
	RootCmd.PersistentFlags().BoolVarP(&output.JSONOutput, "json-output", "j", false, "If true, user-oriented output will be in JSON format.")

	output.LogSetUp()

	// Populate custom/script commands so they're visible
	// We really don't want ~/.ddev or .ddev/homeadditions or .ddev/.globalcommands to have root ownership, breaks things.
	if os.Geteuid() == 0 {
		output.UserOut.Warning("Not populating custom commands or hostadditions because running with root privileges")
	} else {
		err := ddevapp.PopulateExamplesCommandsHomeadditions("")
		if err != nil {
			util.Warning("populateExamplesAndCommands() failed: %v", err)
		}

		err = addCustomCommands(RootCmd)
		if err != nil {
			util.Warning("Adding custom/shell commands failed: %v", err)
		}
	}
}

func instrumentationNotSetUpWarning() {
	if version.SegmentKey == "" && globalconfig.DdevGlobalConfig.InstrumentationOptIn {
		output.UserOut.Warning("Instrumentation is opted in, but SegmentKey is not available.")
	}
}

// checkDdevVersionAndOptInInstrumentation() reads global config and checks to see if current version is different
// from the last saved version. If it is, prompt to request anon ddev usage stats
// and update the info.
func checkDdevVersionAndOptInInstrumentation(skipConfirmation bool) error {
	if !output.JSONOutput && semver.Compare(version.DdevVersion, globalconfig.DdevGlobalConfig.LastStartedVersion) > 0 && globalconfig.DdevGlobalConfig.InstrumentationOptIn == false && !globalconfig.DdevNoInstrumentation && !skipConfirmation {
		allowStats := util.Confirm("It looks like you have a new ddev release.\nMay we send anonymous ddev usage statistics and errors?\nTo know what we will see please take a look at\nhttps://ddev.readthedocs.io/en/stable/users/cli-usage/#opt-in-usage-information\nPermission to beam up?")
		if allowStats {
			globalconfig.DdevGlobalConfig.InstrumentationOptIn = true
			client, _ := analytics.NewWithConfig(version.SegmentKey, analytics.Config{
				Logger: &ddevapp.SegmentNoopLogger{},
			})
			defer func() {
				_ = client.Close()
			}()

			err := ddevapp.SegmentUser(client, ddevapp.GetInstrumentationUser())
			if err != nil {
				output.UserOut.Debugf("error in SegmentUser: %v", err)
			}
		}
	}
	if globalconfig.DdevGlobalConfig.LastStartedVersion != version.DdevVersion && !skipConfirmation {
		globalconfig.DdevGlobalConfig.LastStartedVersion = version.DdevVersion
		err := globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
		if err != nil {
			return err
		}

		okPoweroff := util.Confirm("It looks like you have a new DDEV version. During an upgrade it's important to `ddev poweroff`. May I do `ddev poweroff` before continuing? This does no harm and loses no data.")
		if okPoweroff {
			powerOff()
		}
		return nil
	}

	return nil
}
<commit_msg>Remove warning about missing SegmentKey in json mode (#3574)<commit_after>package cmd

import (
	"fmt"
	"github.com/drud/ddev/pkg/ddevapp"
	"github.com/drud/ddev/pkg/dockerutil"
	"github.com/drud/ddev/pkg/globalconfig"
	"github.com/drud/ddev/pkg/output"
	"github.com/drud/ddev/pkg/updatecheck"
	"github.com/drud/ddev/pkg/util"
	"github.com/drud/ddev/pkg/version"
	"github.com/rogpeppe/go-internal/semver"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"gopkg.in/segmentio/analytics-go.v3"
	"os"
	"path/filepath"

	"time"
)

var (
	updateInterval     = time.Hour * 24 * 7 // One week interval between updates
	serviceType        string
	updateDocURL       = "https://ddev.readthedocs.io/en/stable/#installation"
	instrumentationApp *ddevapp.DdevApp
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "ddev",
	Short: "DDEV-Local local development environment",
	Long: `Create and maintain a local web development environment.
Docs: https://ddev.readthedocs.io
Support: https://ddev.readthedocs.io/en/stable/#support`,
	Version: version.DdevVersion,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		command := os.Args[1]

		// LogSetup() has already been done, but now needs to be done
		// again *after* --json flag is parsed.
		output.LogSetUp()

		// Skip docker and other validation for most commands
		if command != "start" && command != "restart" {
			return
		}

		err := dockerutil.CheckDockerVersion(version.DockerVersionConstraint)
		if err != nil {
			if err.Error() == "no docker" {
				if os.Args[1] != "version" {
					util.Failed("Could not connect to docker. Please ensure Docker is installed and running.")
				}
			} else {
				util.Warning("The docker version currently installed does not seem to meet ddev's requirements: %v", err)
			}
		}

		updateFile := filepath.Join(globalconfig.GetGlobalDdevDir(), ".update")

		// Do periodic detection of whether an update is available for ddev users.
		timeToCheckForUpdates, err := updatecheck.IsUpdateNeeded(updateFile, updateInterval)
		if err != nil {
			util.Warning("Could not perform update check: %v", err)
		}

		if timeToCheckForUpdates && globalconfig.IsInternetActive() {
			// Recreate the updatefile with current time so we won't do this again soon.
			err = updatecheck.ResetUpdateTime(updateFile)
			if err != nil {
				util.Warning("Failed to update updatecheck file %s", updateFile)
				return // Do not continue as we'll end up with github api violations.
			}

			updateNeeded, updateVersion, updateURL, err := updatecheck.AvailableUpdates("drud", "ddev", version.DdevVersion)

			if err != nil {
				util.Warning("Could not check for updates. This is most often caused by a networking issue.")
				return
			}

			if updateNeeded {
				output.UserOut.Printf(util.ColorizeText(fmt.Sprintf("\n\nUpgraded DDEV %s is available!\nPlease visit %s to get the upgrade.\nFor upgrade help see %s\n\n", updateVersion, updateURL, updateDocURL), "green"))
			}
		}
	},
	PersistentPostRun: func(cmd *cobra.Command, args []string) {
		// Do not report these commands
		ignores := map[string]bool{"auth": true, "exec": true, "help": true, "hostname": true, "list": true, "ssh": true, "version": true}
		if _, ok := ignores[cmd.CalledAs()]; ok {
			return
		}
		instrumentationNotSetUpWarning()

		// All this nonsense is to capture the official usage we used for this command.
		// Unfortunately cobra doesn't seem to provide this easily.
		// We use the first word of Use: to get it.
		cmdCopy := cmd
		var fullCommand = make([]string, 0)
		fullCommand = append(fullCommand, util.GetFirstWord(cmdCopy.Use))
		for cmdCopy.HasParent() {
			fullCommand = append(fullCommand, util.GetFirstWord(cmdCopy.Parent().Use))
			cmdCopy = cmdCopy.Parent()
		}
		for i := 0; i < len(fullCommand)/2; i++ {
			j := len(fullCommand) - i - 1
			fullCommand[i], fullCommand[j] = fullCommand[j], fullCommand[i]
		}

		event := ""
		if len(fullCommand) > 1 {
			event = fullCommand[1]
		}

		if globalconfig.DdevGlobalConfig.InstrumentationOptIn && version.SegmentKey != "" && globalconfig.IsInternetActive() && len(fullCommand) > 1 {
			runTime := util.TimeTrack(time.Now(), "Instrumentation")
			// Try to get default instrumentationApp from current directory if not already set
			if instrumentationApp == nil {
				app, err := ddevapp.NewApp("", false)
				if err == nil {
					instrumentationApp = app
				}
			}
			// If it has been set, provide the tags, otherwise no app tags
			if instrumentationApp != nil {
				instrumentationApp.SetInstrumentationAppTags()
			}
			ddevapp.SetInstrumentationBaseTags()
			ddevapp.SendInstrumentationEvents(event)
			runTime()
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	// bind flags to viper config values...allows override by flag
	viper.AutomaticEnv() // read in environment variables that match

	if err := RootCmd.Execute(); err != nil {
		os.Exit(-1)
	}
}

func init() {
	RootCmd.PersistentFlags().BoolVarP(&output.JSONOutput, "json-output", "j", false, "If true, user-oriented output will be in JSON format.")

	output.LogSetUp()

	// Populate custom/script commands so they're visible
	// We really don't want ~/.ddev or .ddev/homeadditions or .ddev/.globalcommands to have root ownership, breaks things.
	if os.Geteuid() == 0 {
		output.UserOut.Warning("Not populating custom commands or hostadditions because running with root privileges")
	} else {
		err := ddevapp.PopulateExamplesCommandsHomeadditions("")
		if err != nil {
			util.Warning("populateExamplesAndCommands() failed: %v", err)
		}

		err = addCustomCommands(RootCmd)
		if err != nil {
			util.Warning("Adding custom/shell commands failed: %v", err)
		}
	}
}

func instrumentationNotSetUpWarning() {
	if !output.JSONOutput && version.SegmentKey == "" && globalconfig.DdevGlobalConfig.InstrumentationOptIn {
		output.UserOut.Warning("Instrumentation is opted in, but SegmentKey is not available. This usually means you have a locally-built ddev binary or one from a PR build. It's not an error. Please report it if you're using an official release build.")
	}
}

// checkDdevVersionAndOptInInstrumentation() reads global config and checks to see if current version is different
// from the last saved version. If it is, prompt to request anon ddev usage stats
// and update the info.
func checkDdevVersionAndOptInInstrumentation(skipConfirmation bool) error {
	if !output.JSONOutput && semver.Compare(version.DdevVersion, globalconfig.DdevGlobalConfig.LastStartedVersion) > 0 && globalconfig.DdevGlobalConfig.InstrumentationOptIn == false && !globalconfig.DdevNoInstrumentation && !skipConfirmation {
		allowStats := util.Confirm("It looks like you have a new ddev release.\nMay we send anonymous ddev usage statistics and errors?\nTo know what we will see please take a look at\nhttps://ddev.readthedocs.io/en/stable/users/cli-usage/#opt-in-usage-information\nPermission to beam up?")
		if allowStats {
			globalconfig.DdevGlobalConfig.InstrumentationOptIn = true
			client, _ := analytics.NewWithConfig(version.SegmentKey, analytics.Config{
				Logger: &ddevapp.SegmentNoopLogger{},
			})
			defer func() {
				_ = client.Close()
			}()

			err := ddevapp.SegmentUser(client, ddevapp.GetInstrumentationUser())
			if err != nil {
				output.UserOut.Debugf("error in SegmentUser: %v", err)
			}
		}
	}
	if globalconfig.DdevGlobalConfig.LastStartedVersion != version.DdevVersion && !skipConfirmation {
		globalconfig.DdevGlobalConfig.LastStartedVersion = version.DdevVersion
		err := globalconfig.WriteGlobalConfig(globalconfig.DdevGlobalConfig)
		if err != nil {
			return err
		}

		okPoweroff := util.Confirm("It looks like you have a new DDEV version. During an upgrade it's important to `ddev poweroff`. May I do `ddev poweroff` before continuing? This does no harm and loses no data.")
		if okPoweroff {
			powerOff()
		}
		return nil
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

// describe-certCmd represents the describe-cert command
var describeCertCmd = &cobra.Command{
	Use:   "describe-cert",
	Short: "display important details of an x509 certificate",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: func(cmd *cobra.Command, args []string) {
		// TODO: Work your own magic here
		fmt.Println("describe-cert called")
	},
}

func init() {
	RootCmd.AddCommand(describeCertCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// describe-certCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// describe-certCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}
<commit_msg>add describe-cert<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"io"
	"os"

	"io/ioutil"

	"encoding/pem"

	"crypto/x509"

	"github.com/spf13/cobra"
)

type CertificateDescription struct {
	Subject          string
	AlternativeNames []string
	Issuer           string
}

func (cd *CertificateDescription) ToString() string {
	return fmt.Sprintf("%s, %s, %s", cd.Subject, cd.AlternativeNames, cd.Issuer)
}

// describe-certCmd represents the describe-cert command
var describeCertCmd = &cobra.Command{
	Use:   "describe-cert [certificate filenames]",
	Short: "display important details of an x509 certificate",
	Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
	Run: func(cmd *cobra.Command, args []string) {
		var fileList []*os.File

		filecount := len(args)
		if filecount == 0 {
			fileList = make([]*os.File, 1, 1)
			fileList[0] = os.Stdin
		} else {
			fileList = make([]*os.File, filecount, filecount)
			for i, filename := range args {
				var err error
				fileList[i], err = os.OpenFile(filename, os.O_RDONLY, 0)
				defer fileList[i].Close()
				if err != nil {
					fmt.Fprintf(cmd.OutOrStderr(), "%s while attempting to open %s.\n", err, filename)
				}
			}
		}

		for i, file := range fileList {
			cd, err := describe(file)
			if err != nil {
				fmt.Fprint(cmd.OutOrStderr(), "while accessing %s -- %s", args[i], err)
			}
			fmt.Fprintf(cmd.OutOrStdout(), "%s, %s\n", args[i], cd.ToString())
		}

	},
}

func describe(file io.Reader) (*CertificateDescription, error) {
	buf, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}

	p, _ := pem.Decode(buf)
	if p == nil {
		panic("failed to decode certificate")
	}
	cert, err := x509.ParseCertificate(p.Bytes)
	if err != nil {
		panic(err)
	}

	return &CertificateDescription{
		Subject:          cert.Subject.CommonName,
		AlternativeNames: cert.DNSNames,
		Issuer:           cert.Issuer.CommonName,
	}, nil
}

func init() {
	RootCmd.AddCommand(describeCertCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// describe-certCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// describe-certCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}
<|endoftext|>


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

import (
	"bufio"
	"context"
	"flag"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/profiler"
	"contrib.go.opencensus.io/integrations/ocsql"
	"github.com/go-redis/redis/v7"
	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/database"
	"golang.org/x/pkgsite/internal/dcensus"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/postgres"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/proxydatasource"
	"golang.org/x/pkgsite/internal/queue"
	"golang.org/x/pkgsite/internal/source"
)

var (
	queueName      = config.GetEnv("GO_DISCOVERY_FRONTEND_TASK_QUEUE", "")
	workers        = flag.Int("workers", 10, "number of concurrent requests to the fetch service, when running locally")
	_              = flag.String("static", "content/static", "path to folder containing static files served")
	thirdPartyPath = flag.String("third_party", "third_party", "path to folder containing third-party libraries")
	devMode        = flag.Bool("dev", false, "enable developer mode (reload templates on each page load, serve non-minified JS/CSS, etc.)")
	proxyURL       = flag.String("proxy_url", "https://proxy.golang.org", "Uses the module proxy referred to by this URL "+
		"for direct proxy mode and frontend fetches")
	directProxy = flag.Bool("direct_proxy", false, "if set to true, uses the module proxy referred to by this URL "+
		"as a direct backend, bypassing the database")
)

func main() {
	flag.Parse()
	ctx := context.Background()
	cfg, err := config.Init(ctx)
	if err != nil {
		log.Fatal(ctx, err)
	}
	cfg.Dump(os.Stderr)
	if cfg.UseProfiler {
		if err := profiler.Start(profiler.Config{}); err != nil {
			log.Fatalf(ctx, "profiler.Start: %v", err)
		}
	}

	log.SetLevel(cfg.LogLevel)

	var (
		dsg        func(context.Context) internal.DataSource
		expg       func(context.Context) internal.ExperimentSource
		fetchQueue queue.Queue
	)
	proxyClient, err := proxy.New(*proxyURL)
	if err != nil {
		log.Fatal(ctx, err)
	}
	if *directProxy {
		pds := proxydatasource.New(proxyClient)
		dsg = func(context.Context) internal.DataSource { return pds }
		expg = func(context.Context) internal.ExperimentSource {
			return internal.NewLocalExperimentSource(readLocalExperiments(ctx))
		}
	} else {
		// Wrap the postgres driver with OpenCensus instrumentation.
		ocDriver, err := ocsql.Register("postgres", ocsql.WithAllTraceOptions())
		if err != nil {
			log.Fatalf(ctx, "unable to register the ocsql driver: %v\n", err)
		}
		ddb, err := openDB(ctx, cfg, ocDriver)
		if err != nil {
			log.Fatal(ctx, err)
		}
		db := postgres.New(ddb)
		defer db.Close()
		dsg = func(context.Context) internal.DataSource { return db }
		expg = func(context.Context) internal.ExperimentSource { return db }
		sourceClient := source.NewClient(config.SourceTimeout)
		// queue.New uses the db argument only while it is constructing the queue.Queue.
		// The closure passed to it is only used for testing and local execution, not in production.
		// So it's okay that in neither case do we use a per-request connection.
		fetchQueue, err = queue.New(ctx, cfg, queueName, *workers, db,
			func(ctx context.Context, modulePath, version string) (int, error) {
				return frontend.FetchAndUpdateState(ctx, modulePath, version, proxyClient, sourceClient, db)
			})
		if err != nil {
			log.Fatalf(ctx, "queue.New: %v", err)
		}
	}
	var haClient *redis.Client
	if cfg.RedisHAHost != "" {
		haClient = redis.NewClient(&redis.Options{
			Addr: cfg.RedisHAHost + ":" + cfg.RedisHAPort,
		})
	}
	server, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter:     dsg,
		Queue:                fetchQueue,
		CompletionClient:     haClient,
		TaskIDChangeInterval: config.TaskIDChangeIntervalFrontend,
		StaticPath:           template.TrustedSourceFromFlag(flag.Lookup("static").Value),
		ThirdPartyPath:       *thirdPartyPath,
		DevMode:              *devMode,
		AppVersionLabel:      cfg.AppVersionLabel(),
		GoogleTagManagerID:   cfg.GoogleTagManagerID,
	})
	if err != nil {
		log.Fatalf(ctx, "frontend.NewServer: %v", err)
	}
	router := dcensus.NewRouter(frontend.TagRoute)
	var cacheClient *redis.Client
	if cfg.RedisCacheHost != "" {
		cacheClient = redis.NewClient(&redis.Options{
			Addr: cfg.RedisCacheHost + ":" + cfg.RedisCachePort,
		})
	}
	server.Install(router.Handle, cacheClient, cfg.AuthValues)
	views := append(dcensus.ServerViews,
		postgres.SearchLatencyDistribution,
		postgres.SearchResponseCount,
		frontend.FetchLatencyDistribution,
		frontend.FetchResponseCount,
		frontend.PlaygroundShareRequestCount,
		frontend.VersionTypeCount,
		middleware.CacheResultCount,
		middleware.CacheErrorCount,
		middleware.CacheLatency,
		middleware.QuotaResultCount,
	)
	if err := dcensus.Init(cfg, views...); err != nil {
		log.Fatal(ctx, err)
	}
	// We are not currently forwarding any ports on AppEngine, so serving debug
	// information is broken.
	if !cfg.OnAppEngine() {
		dcensusServer, err := dcensus.NewServer()
		if err != nil {
			log.Fatal(ctx, err)
		}
		go http.ListenAndServe(cfg.DebugAddr("localhost:8081"), dcensusServer)
	}
	panicHandler, err := server.PanicHandler()
	if err != nil {
		log.Fatal(ctx, err)
	}
	requestLogger := getLogger(ctx, cfg)
	experimenter, err := middleware.NewExperimenter(ctx, 1*time.Minute, expg)
	if err != nil {
		log.Fatal(ctx, err)
	}
	mw := middleware.Chain(
		middleware.RequestLog(requestLogger),
		middleware.AcceptRequests(http.MethodGet, http.MethodPost), // accept only GETs and POSTs
		middleware.Quota(cfg.Quota),
		middleware.GodocURL(),                          // potentially redirects so should be early in chain
		middleware.SecureHeaders(),                     // must come before any caching for nonces to work
		middleware.LatestVersion(server.LatestVersion), // must come before caching for version badge to work
		middleware.Panic(panicHandler),
		middleware.Timeout(54*time.Second),
		middleware.Experiment(experimenter),
	)
	addr := cfg.HostAddr("localhost:8080")
	log.Infof(ctx, "Listening on addr %s", addr)
	log.Fatal(ctx, http.ListenAndServe(addr, mw(router)))
}

// TODO(https://github.com/golang/go/issues/40097): factor out to reduce
// duplication with cmd/worker/main.go.

// openDB opens a connection to a database with the given driver, using connection info from
// the given config.
// It first tries the main connection info (DBConnInfo), and if that fails, it uses backup
// connection info it if exists (DBSecondaryConnInfo).
func openDB(ctx context.Context, cfg *config.Config, driver string) (_ *database.DB, err error) {
	derrors.Wrap(&err, "openDB(ctx, cfg, %q)", driver)
	log.Infof(ctx, "opening database on host %s", cfg.DBHost)
	ddb, err := database.Open(driver, cfg.DBConnInfo(), cfg.InstanceID)
	if err == nil {
		return ddb, nil
	}
	ci := cfg.DBSecondaryConnInfo()
	if ci == "" {
		log.Infof(ctx, "no secondary DB host")
		return nil, err
	}
	log.Errorf(ctx, "database.Open for primary host %s failed with %v; trying secondary host %s ",
		cfg.DBHost, err, cfg.DBSecondaryHost)
	return database.Open(driver, ci, cfg.InstanceID)
}
func getLogger(ctx context.Context, cfg *config.Config) middleware.Logger {
	if cfg.OnAppEngine() {
		logger, err := log.UseStackdriver(ctx, cfg, "frontend-log")
		if err != nil {
			log.Fatal(ctx, err)
		}
		return logger
	}
	return middleware.LocalLogger{}
}

// Read a file of experiments used to initialize the local experiment source
// for use in direct proxy mode.
// Format of the file: each line is
//     name,rollout
// For each experiment.
func readLocalExperiments(ctx context.Context) []*internal.Experiment {
	filename := config.GetEnv("GO_DISCOVERY_LOCAL_EXPERIMENTS", "")
	if filename == "" {
		return nil
	}
	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(ctx, err)
	}
	defer f.Close()
	scan := bufio.NewScanner(f)
	var experiments []*internal.Experiment
	log.Infof(ctx, "reading experiments from %q for local development", filename)
	for scan.Scan() {
		line := strings.TrimSpace(scan.Text())
		parts := strings.SplitN(line, ",", 3)
		if len(parts) != 2 {
			log.Fatalf(ctx, "invalid experiment in file: %q", line)
		}
		name := parts[0]
		if name == "" {
			log.Fatalf(ctx, "invalid experiment in file (name cannot be empty): %q", line)
		}
		rollout, err := strconv.ParseUint(parts[1], 10, 0)
		if err != nil {
			log.Fatalf(ctx, "invalid experiment in file (invalid rollout): %v", err)
		}
		if rollout > 100 {
			log.Fatalf(ctx, "invalid experiment in file (rollout must be between 0 - 100): %q", line)
		}
		experiments = append(experiments, &internal.Experiment{
			Name:    name,
			Rollout: uint(rollout),
		})
		log.Infof(ctx, "experiment %q: rollout = %d", name, rollout)
	}
	if err := scan.Err(); err != nil {
		log.Fatalf(ctx, "scanning %s: %v", filename, err)
	}
	log.Infof(ctx, "found %d experiment(s)", len(experiments))
	return experiments
}
<commit_msg>cmd/frontend: add flag for bypassing license check<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main

import (
	"bufio"
	"context"
	"flag"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/profiler"
	"contrib.go.opencensus.io/integrations/ocsql"
	"github.com/go-redis/redis/v7"
	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/config"
	"golang.org/x/pkgsite/internal/database"
	"golang.org/x/pkgsite/internal/dcensus"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/postgres"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/proxydatasource"
	"golang.org/x/pkgsite/internal/queue"
	"golang.org/x/pkgsite/internal/source"
)

var (
	queueName      = config.GetEnv("GO_DISCOVERY_FRONTEND_TASK_QUEUE", "")
	workers        = flag.Int("workers", 10, "number of concurrent requests to the fetch service, when running locally")
	_              = flag.String("static", "content/static", "path to folder containing static files served")
	thirdPartyPath = flag.String("third_party", "third_party", "path to folder containing third-party libraries")
	devMode        = flag.Bool("dev", false, "enable developer mode (reload templates on each page load, serve non-minified JS/CSS, etc.)")
	proxyURL       = flag.String("proxy_url", "https://proxy.golang.org", "Uses the module proxy referred to by this URL "+
		"for direct proxy mode and frontend fetches")
	directProxy = flag.Bool("direct_proxy", false, "if set to true, uses the module proxy referred to by this URL "+
		"as a direct backend, bypassing the database")
	bypassLicenseCheck = flag.Bool("bypass_license_check", false, "display all information, even for non-redistributable paths")
)

func main() {
	flag.Parse()
	ctx := context.Background()
	cfg, err := config.Init(ctx)
	if err != nil {
		log.Fatal(ctx, err)
	}
	cfg.Dump(os.Stderr)
	if cfg.UseProfiler {
		if err := profiler.Start(profiler.Config{}); err != nil {
			log.Fatalf(ctx, "profiler.Start: %v", err)
		}
	}

	log.SetLevel(cfg.LogLevel)

	var (
		dsg        func(context.Context) internal.DataSource
		expg       func(context.Context) internal.ExperimentSource
		fetchQueue queue.Queue
	)
	proxyClient, err := proxy.New(*proxyURL)
	if err != nil {
		log.Fatal(ctx, err)
	}
	if *bypassLicenseCheck {
		log.Info(ctx, "BYPASSING LICENSE CHECKING: DISPLAYING NON-REDISTRIBUTABLE INFORMATION")
	}
	if *directProxy {
		var pds *proxydatasource.DataSource
		if *bypassLicenseCheck {
			pds = proxydatasource.NewBypassingLicenseCheck(proxyClient)
		} else {
			pds = proxydatasource.New(proxyClient)
		}
		dsg = func(context.Context) internal.DataSource { return pds }
		expg = func(context.Context) internal.ExperimentSource {
			return internal.NewLocalExperimentSource(readLocalExperiments(ctx))
		}
	} else {
		// Wrap the postgres driver with OpenCensus instrumentation.
		ocDriver, err := ocsql.Register("postgres", ocsql.WithAllTraceOptions())
		if err != nil {
			log.Fatalf(ctx, "unable to register the ocsql driver: %v\n", err)
		}
		ddb, err := openDB(ctx, cfg, ocDriver)
		if err != nil {
			log.Fatal(ctx, err)
		}
		var db *postgres.DB
		if *bypassLicenseCheck {
			db = postgres.NewBypassingLicenseCheck(ddb)
		} else {
			db = postgres.New(ddb)
		}
		defer db.Close()
		dsg = func(context.Context) internal.DataSource { return db }
		expg = func(context.Context) internal.ExperimentSource { return db }
		sourceClient := source.NewClient(config.SourceTimeout)
		// queue.New uses the db argument only while it is constructing the queue.Queue.
		// The closure passed to it is only used for testing and local execution, not in production.
		// So it's okay that in neither case do we use a per-request connection.
		fetchQueue, err = queue.New(ctx, cfg, queueName, *workers, db,
			func(ctx context.Context, modulePath, version string) (int, error) {
				return frontend.FetchAndUpdateState(ctx, modulePath, version, proxyClient, sourceClient, db)
			})
		if err != nil {
			log.Fatalf(ctx, "queue.New: %v", err)
		}
	}
	var haClient *redis.Client
	if cfg.RedisHAHost != "" {
		haClient = redis.NewClient(&redis.Options{
			Addr: cfg.RedisHAHost + ":" + cfg.RedisHAPort,
		})
	}
	server, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter:     dsg,
		Queue:                fetchQueue,
		CompletionClient:     haClient,
		TaskIDChangeInterval: config.TaskIDChangeIntervalFrontend,
		StaticPath:           template.TrustedSourceFromFlag(flag.Lookup("static").Value),
		ThirdPartyPath:       *thirdPartyPath,
		DevMode:              *devMode,
		AppVersionLabel:      cfg.AppVersionLabel(),
		GoogleTagManagerID:   cfg.GoogleTagManagerID,
	})
	if err != nil {
		log.Fatalf(ctx, "frontend.NewServer: %v", err)
	}
	router := dcensus.NewRouter(frontend.TagRoute)
	var cacheClient *redis.Client
	if cfg.RedisCacheHost != "" {
		cacheClient = redis.NewClient(&redis.Options{
			Addr: cfg.RedisCacheHost + ":" + cfg.RedisCachePort,
		})
	}
	server.Install(router.Handle, cacheClient, cfg.AuthValues)
	views := append(dcensus.ServerViews,
		postgres.SearchLatencyDistribution,
		postgres.SearchResponseCount,
		frontend.FetchLatencyDistribution,
		frontend.FetchResponseCount,
		frontend.PlaygroundShareRequestCount,
		frontend.VersionTypeCount,
		middleware.CacheResultCount,
		middleware.CacheErrorCount,
		middleware.CacheLatency,
		middleware.QuotaResultCount,
	)
	if err := dcensus.Init(cfg, views...); err != nil {
		log.Fatal(ctx, err)
	}
	// We are not currently forwarding any ports on AppEngine, so serving debug
	// information is broken.
	if !cfg.OnAppEngine() {
		dcensusServer, err := dcensus.NewServer()
		if err != nil {
			log.Fatal(ctx, err)
		}
		go http.ListenAndServe(cfg.DebugAddr("localhost:8081"), dcensusServer)
	}
	panicHandler, err := server.PanicHandler()
	if err != nil {
		log.Fatal(ctx, err)
	}
	requestLogger := getLogger(ctx, cfg)
	experimenter, err := middleware.NewExperimenter(ctx, 1*time.Minute, expg)
	if err != nil {
		log.Fatal(ctx, err)
	}
	mw := middleware.Chain(
		middleware.RequestLog(requestLogger),
		middleware.AcceptRequests(http.MethodGet, http.MethodPost), // accept only GETs and POSTs
		middleware.Quota(cfg.Quota),
		middleware.GodocURL(),                          // potentially redirects so should be early in chain
		middleware.SecureHeaders(),                     // must come before any caching for nonces to work
		middleware.LatestVersion(server.LatestVersion), // must come before caching for version badge to work
		middleware.Panic(panicHandler),
		middleware.Timeout(54*time.Second),
		middleware.Experiment(experimenter),
	)
	addr := cfg.HostAddr("localhost:8080")
	log.Infof(ctx, "Listening on addr %s", addr)
	log.Fatal(ctx, http.ListenAndServe(addr, mw(router)))
}

// TODO(https://github.com/golang/go/issues/40097): factor out to reduce
// duplication with cmd/worker/main.go.

// openDB opens a connection to a database with the given driver, using connection info from
// the given config.
// It first tries the main connection info (DBConnInfo), and if that fails, it uses backup
// connection info it if exists (DBSecondaryConnInfo).
func openDB(ctx context.Context, cfg *config.Config, driver string) (_ *database.DB, err error) {
	derrors.Wrap(&err, "openDB(ctx, cfg, %q)", driver)
	log.Infof(ctx, "opening database on host %s", cfg.DBHost)
	ddb, err := database.Open(driver, cfg.DBConnInfo(), cfg.InstanceID)
	if err == nil {
		return ddb, nil
	}
	ci := cfg.DBSecondaryConnInfo()
	if ci == "" {
		log.Infof(ctx, "no secondary DB host")
		return nil, err
	}
	log.Errorf(ctx, "database.Open for primary host %s failed with %v; trying secondary host %s ",
		cfg.DBHost, err, cfg.DBSecondaryHost)
	return database.Open(driver, ci, cfg.InstanceID)
}
func getLogger(ctx context.Context, cfg *config.Config) middleware.Logger {
	if cfg.OnAppEngine() {
		logger, err := log.UseStackdriver(ctx, cfg, "frontend-log")
		if err != nil {
			log.Fatal(ctx, err)
		}
		return logger
	}
	return middleware.LocalLogger{}
}

// Read a file of experiments used to initialize the local experiment source
// for use in direct proxy mode.
// Format of the file: each line is
//     name,rollout
// For each experiment.
func readLocalExperiments(ctx context.Context) []*internal.Experiment {
	filename := config.GetEnv("GO_DISCOVERY_LOCAL_EXPERIMENTS", "")
	if filename == "" {
		return nil
	}
	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(ctx, err)
	}
	defer f.Close()
	scan := bufio.NewScanner(f)
	var experiments []*internal.Experiment
	log.Infof(ctx, "reading experiments from %q for local development", filename)
	for scan.Scan() {
		line := strings.TrimSpace(scan.Text())
		parts := strings.SplitN(line, ",", 3)
		if len(parts) != 2 {
			log.Fatalf(ctx, "invalid experiment in file: %q", line)
		}
		name := parts[0]
		if name == "" {
			log.Fatalf(ctx, "invalid experiment in file (name cannot be empty): %q", line)
		}
		rollout, err := strconv.ParseUint(parts[1], 10, 0)
		if err != nil {
			log.Fatalf(ctx, "invalid experiment in file (invalid rollout): %v", err)
		}
		if rollout > 100 {
			log.Fatalf(ctx, "invalid experiment in file (rollout must be between 0 - 100): %q", line)
		}
		experiments = append(experiments, &internal.Experiment{
			Name:    name,
			Rollout: uint(rollout),
		})
		log.Infof(ctx, "experiment %q: rollout = %d", name, rollout)
	}
	if err := scan.Err(); err != nil {
		log.Fatalf(ctx, "scanning %s: %v", filename, err)
	}
	log.Infof(ctx, "found %d experiment(s)", len(experiments))
	return experiments
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Implements go/build buildpack.
// The build buildpack runs go build.
package main

import (
	"os"
	"path/filepath"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/devmode"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/golang"
	"github.com/buildpack/libbuildpack/layers"
)

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) error {
	if !ctx.HasAtLeastOne(ctx.ApplicationRoot(), "*.go") {
		ctx.OptOut("No *.go files found")
	}
	return nil
}

func buildFn(ctx *gcp.Context) error {
	bl := ctx.Layer("bin")
	ctx.PrependPathLaunchEnv(bl, "PATH", bl.Root)
	ctx.WriteMetadata(bl, nil, layers.Launch)

	var flags []string
	if ctx.FileExists("go.mod") && !useVendor(ctx) {
		flags = append(flags, "-mod=readonly")
	}

	pkg, ok := os.LookupEnv(env.Buildable)
	if !ok {
		pkg = "."
	}
	flags = append(flags, pkg)

	// Build the application.
	ctx.ExecUser(append([]string{"go", "build", "-o", filepath.Join(bl.Root, golang.OutBin)}, flags...))

	// Configure the entrypoint for production.
	if !devmode.Enabled(ctx) {
		ctx.AddWebProcess([]string{golang.OutBin})
		return nil
	}

	// Configure the entrypoint and metadata for dev mode.
	devmode.AddFileWatcherProcess(ctx, devmode.Config{
		Cmd: append([]string{"go", "run"}, flags...),
		Ext: devmode.GoWatchedExtensions,
	})
	devmode.AddSyncMetadata(ctx, devmode.GoSyncRules)

	return nil
}

func useVendor(ctx *gcp.Context) bool {
	return ctx.FileExists("vendor") && golang.SupportsAutoVendor(ctx, ctx.ApplicationRoot())
}
<commit_msg>Remove `go build` flags<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Implements go/build buildpack.
// The build buildpack runs go build.
package main

import (
	"os"
	"path/filepath"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/devmode"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/golang"
	"github.com/buildpack/libbuildpack/layers"
)

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) error {
	if !ctx.HasAtLeastOne(ctx.ApplicationRoot(), "*.go") {
		ctx.OptOut("No *.go files found")
	}
	return nil
}

func buildFn(ctx *gcp.Context) error {
	bl := ctx.Layer("bin")
	ctx.PrependPathLaunchEnv(bl, "PATH", bl.Root)
	ctx.WriteMetadata(bl, nil, layers.Launch)

	pkg, ok := os.LookupEnv(env.Buildable)
	if !ok {
		pkg = "."
	}

	// Build the application.
	ctx.ExecUser(append([]string{"go", "build", "-o", filepath.Join(bl.Root, golang.OutBin)}, pkg))

	// Configure the entrypoint for production.
	if !devmode.Enabled(ctx) {
		ctx.AddWebProcess([]string{golang.OutBin})
		return nil
	}

	// Configure the entrypoint and metadata for dev mode.
	devmode.AddFileWatcherProcess(ctx, devmode.Config{
		Cmd: append([]string{"go", "run"}, pkg),
		Ext: devmode.GoWatchedExtensions,
	})
	devmode.AddSyncMetadata(ctx, devmode.GoSyncRules)

	return nil
}
<|endoftext|>


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

/*
The gomote command is a client for the Go builder infrastructure.
It's a remote control for remote Go builder machines.

See https://golang.org/wiki/Gomote

Usage:

	gomote [global-flags] cmd [cmd-flags]

	For example,
	$ gomote create openbsd-amd64-60
	user-username-openbsd-amd64-60-0
	$ gomote push user-username-openbsd-amd64-60-0
	$ gomote run user-username-openbsd-amd64-60-0 go/src/make.bash
	$ gomote run user-username-openbsd-amd64-60-0 go/bin/go test -v -short os

To list the subcommands, run "gomote" without arguments:

	Commands:

	  create     create a buildlet; with no args, list types of buildlets
	  destroy    destroy a buildlet
	  gettar     extract a tar.gz from a buildlet
	  list       list active buildlets
	  ls         list the contents of a directory on a buildlet
	  ping       test whether a buildlet is alive and reachable
	  push       sync your GOROOT directory to the buildlet
	  put        put files on a buildlet
	  put14      put Go 1.4 in place
	  puttar     extract a tar.gz to a buildlet
	  rm         delete files or directories
	  rdp        RDP (Remote Desktop Protocol) to a Windows buildlet
	  run        run a command on a buildlet
	  ssh        ssh to a buildlet

To list all the builder types available, run "create" with no arguments:

	$ gomote create
	(list tons of buildlet types)

The "gomote run" command has many of its own flags:

	$ gomote run -h
	run usage: gomote run [run-opts] <instance> <cmd> [args...]
	  -builderenv string
	        Optional alternate builder to act like. Must share the same
	        underlying buildlet host type, or it's an error. For
	        instance, linux-amd64-race is compatible
	        with linux-amd64, but openbsd-amd64 and openbsd-386 are
	        different hosts.
	  -debug
	        write debug info about the command's execution before it begins
	  -dir string
	        Directory to run from. Defaults to the directory of the
	        command, or the work directory if -system is true.
	  -e value
	        Environment variable KEY=value. The -e flag may be repeated
	        multiple times to add multiple things to the environment.
	  -path string
	        Comma-separated list of ExecOpts.Path elements. The special
	        string 'EMPTY' means to run without any $PATH. The empty
	        string (default) does not modify the $PATH. Otherwise, the
	        following expansions apply: the string '$PATH' expands to
	        the current PATH element(s), the substring '$WORKDIR'
	        expands to the buildlet's temp workdir.
	  -system
	        run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'

# Debugging buildlets directly

Using "gomote create" contacts the build coordinator
(farmer.golang.org) and requests that it create the buildlet on your
behalf. All subsequent commands (such as "gomote run" or "gomote ls")
then proxy your request via the coordinator.  To access a buildlet
directly (for example, when working on the buildlet code), you can
skip the "gomote create" step and use the special builder name
"<build-config-name>@ip[:port>", such as "windows-amd64-2008@10.1.5.3".
*/
package main

import (
	"flag"
	"fmt"
	"os"
	"sort"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
)

var (
	buildEnv *buildenv.Environment
)

type command struct {
	name string
	des  string
	run  func([]string) error
}

var commands = map[string]command{}

func sortedCommands() []string {
	s := make([]string, 0, len(commands))
	for name := range commands {
		s = append(s, name)
	}
	sort.Strings(s)
	return s
}

func usage() {
	fmt.Fprintf(os.Stderr, `Usage of gomote: gomote [global-flags] <cmd> [cmd-flags]

Global flags:
`)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "Commands:\n\n")
	for _, name := range sortedCommands() {
		fmt.Fprintf(os.Stderr, "  %-10s %s\n", name, commands[name].des)
	}
	os.Exit(1)
}

func registerCommand(name, des string, run func([]string) error) {
	if _, dup := commands[name]; dup {
		panic("duplicate registration of " + name)
	}
	commands[name] = command{
		name: name,
		des:  des,
		run:  run,
	}
}

func registerCommands() {
	registerCommand("create", "create a buildlet; with no args, list types of buildlets", create)
	registerCommand("destroy", "destroy a buildlet", destroy)
	registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
	registerCommand("ls", "list the contents of a directory on a buildlet", ls)
	registerCommand("list", "list active buildlets", list)
	registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
	registerCommand("push", "sync your GOROOT directory to the buildlet", push)
	registerCommand("put", "put files on a buildlet", put)
	registerCommand("put14", "put Go 1.4 in place", put14)
	registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
	registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
	registerCommand("rm", "delete files or directories", rm)
	registerCommand("run", "run a command on a buildlet", run)
	registerCommand("ssh", "ssh to a buildlet", ssh)
}

func main() {
	buildlet.RegisterFlags()
	registerCommands()
	flag.Usage = usage
	flag.Parse()
	buildEnv = buildenv.FromFlags()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}
	cmdName := args[0]
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmdName)
		usage()
	}
	err := cmd.run(args[1:])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error running %s: %v\n", cmdName, err)
		os.Exit(1)
	}
}
<commit_msg>cmd/gomote: add gomote GRPC client and authentication<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
The gomote command is a client for the Go builder infrastructure.
It's a remote control for remote Go builder machines.

See https://golang.org/wiki/Gomote

Usage:

	gomote [global-flags] cmd [cmd-flags]

	For example,
	$ gomote create openbsd-amd64-60
	user-username-openbsd-amd64-60-0
	$ gomote push user-username-openbsd-amd64-60-0
	$ gomote run user-username-openbsd-amd64-60-0 go/src/make.bash
	$ gomote run user-username-openbsd-amd64-60-0 go/bin/go test -v -short os

To list the subcommands, run "gomote" without arguments:

	Commands:

	  create     create a buildlet; with no args, list types of buildlets
	  destroy    destroy a buildlet
	  gettar     extract a tar.gz from a buildlet
	  list       list active buildlets
	  ls         list the contents of a directory on a buildlet
	  ping       test whether a buildlet is alive and reachable
	  push       sync your GOROOT directory to the buildlet
	  put        put files on a buildlet
	  put14      put Go 1.4 in place
	  puttar     extract a tar.gz to a buildlet
	  rm         delete files or directories
	  rdp        RDP (Remote Desktop Protocol) to a Windows buildlet
	  run        run a command on a buildlet
	  ssh        ssh to a buildlet

To list all the builder types available, run "create" with no arguments:

	$ gomote create
	(list tons of buildlet types)

The "gomote run" command has many of its own flags:

	$ gomote run -h
	run usage: gomote run [run-opts] <instance> <cmd> [args...]
	  -builderenv string
	        Optional alternate builder to act like. Must share the same
	        underlying buildlet host type, or it's an error. For
	        instance, linux-amd64-race is compatible
	        with linux-amd64, but openbsd-amd64 and openbsd-386 are
	        different hosts.
	  -debug
	        write debug info about the command's execution before it begins
	  -dir string
	        Directory to run from. Defaults to the directory of the
	        command, or the work directory if -system is true.
	  -e value
	        Environment variable KEY=value. The -e flag may be repeated
	        multiple times to add multiple things to the environment.
	  -path string
	        Comma-separated list of ExecOpts.Path elements. The special
	        string 'EMPTY' means to run without any $PATH. The empty
	        string (default) does not modify the $PATH. Otherwise, the
	        following expansions apply: the string '$PATH' expands to
	        the current PATH element(s), the substring '$WORKDIR'
	        expands to the buildlet's temp workdir.
	  -system
	        run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'

# Debugging buildlets directly

Using "gomote create" contacts the build coordinator
(farmer.golang.org) and requests that it create the buildlet on your
behalf. All subsequent commands (such as "gomote run" or "gomote ls")
then proxy your request via the coordinator.  To access a buildlet
directly (for example, when working on the buildlet code), you can
skip the "gomote create" step and use the special builder name
"<build-config-name>@ip[:port>", such as "windows-amd64-2008@10.1.5.3".
*/
package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"os"
	"sort"
	"strings"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
	"golang.org/x/build/internal/gomote/protos"
	"golang.org/x/build/internal/iapclient"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/oauth"
)

var (
	buildEnv *buildenv.Environment
)

type command struct {
	name string
	des  string
	run  func([]string) error
}

var commands = map[string]command{}

func sortedCommands() []string {
	s := make([]string, 0, len(commands))
	for name := range commands {
		s = append(s, name)
	}
	sort.Strings(s)
	return s
}

func usage() {
	fmt.Fprintf(os.Stderr, `Usage of gomote: gomote [global-flags] <cmd> [cmd-flags]

Global flags:
`)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "Commands:\n\n")
	for _, name := range sortedCommands() {
		fmt.Fprintf(os.Stderr, "  %-10s %s\n", name, commands[name].des)
	}
	os.Exit(1)
}

func registerCommand(name, des string, run func([]string) error) {
	if _, dup := commands[name]; dup {
		panic("duplicate registration of " + name)
	}
	commands[name] = command{
		name: name,
		des:  des,
		run:  run,
	}
}

func registerCommands() {
	registerCommand("create", "create a buildlet; with no args, list types of buildlets", create)
	registerCommand("destroy", "destroy a buildlet", destroy)
	registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
	registerCommand("ls", "list the contents of a directory on a buildlet", ls)
	registerCommand("list", "list active buildlets", list)
	registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
	registerCommand("push", "sync your GOROOT directory to the buildlet", push)
	registerCommand("put", "put files on a buildlet", put)
	registerCommand("put14", "put Go 1.4 in place", put14)
	registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
	registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
	registerCommand("rm", "delete files or directories", rm)
	registerCommand("run", "run a command on a buildlet", run)
	registerCommand("ssh", "ssh to a buildlet", ssh)
}

var (
	serverAddr = flag.String("server", "build.golang.org:443", "Address for GRPC server")
)

func main() {
	buildlet.RegisterFlags()
	registerCommands()
	flag.Usage = usage
	flag.Parse()
	buildEnv = buildenv.FromFlags()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}
	cmdName := args[0]
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmdName)
		usage()
	}
	err := cmd.run(args[1:])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error running %s: %v\n", cmdName, err)
		os.Exit(1)
	}
}

// gomoteServerClient returns a gomote server client which can be used to interact with the gomote GRPC server.
// It will either retrieve a previously created authentication token or attempt to create a new one.
func gomoteServerClient(ctx context.Context) protos.GomoteServiceClient {
	ts, err := iapclient.TokenSource(ctx)
	if err != nil {
		logAndExitf("failed to retrieve oauth token: %s", err)
	}
	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: strings.HasPrefix(*serverAddr, "localhost:")})),
		grpc.WithDefaultCallOptions(grpc.PerRPCCredentials(oauth.TokenSource{TokenSource: ts})),
		grpc.WithBlock(),
	}
	grpcClient, err := grpc.DialContext(ctx, *serverAddr, opts...)
	if err != nil {
		logAndExitf("dialing the server=%s failed with: %s", *serverAddr, err)
	}
	return protos.NewGomoteServiceClient(grpcClient)
}

// logAndExitf is equivalent to Printf to Stderr followed by a call to os.Exit(1).
func logAndExitf(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, format, v...)
	os.Exit(1)
}
<|endoftext|>


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

// heapview is a tool for viewing Go heap dumps.
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
)

var host = flag.String("host", "", "host addr to listen on")
var port = flag.Int("port", 8080, "service port")

var index = `<!DOCTYPE html>
<script src="js/customelements.js"></script>
<script src="js/typescript.js"></script>
<script src="js/moduleloader.js"></script>
<script>
  System.transpiler = 'typescript';
  System.typescriptOptions = {target: ts.ScriptTarget.ES2015};
  System.locate = (load) => load.name + '.ts';
</script>
<script type="module">
  import {main} from './client/main';
  main();
</script>
`

func toolsDir() string {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		log.Println("error: GOPATH not set. Can't find client files")
		os.Exit(1)
	}
	return filepath.Join(filepath.SplitList(gopath)[0], "src/golang.org/x/tools")
}

var parseFlags = func() {
	flag.Parse()
}

var addHandlers = func() {
	// Directly serve typescript code in client directory for development.
	http.Handle("/client/", http.StripPrefix("/client",
		http.FileServer(http.Dir(filepath.Join(toolsDir(), "cmd/heapview/client")))))

	// Serve typescript.js and moduleloader.js for development.
	http.HandleFunc("/js/typescript.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir(), "third_party/typescript/typescript.js"))
	})
	http.HandleFunc("/js/moduleloader.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir(), "third_party/moduleloader/moduleloader.js"))
	})
	http.HandleFunc("/js/customelements.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir(), "third_party/webcomponents/customelements.js"))
	})

	// Serve index.html using html string above.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		io.WriteString(w, index)
	})
}

var listenAndServe = func() error {
	return http.ListenAndServe(fmt.Sprintf("%s:%d", *host, *port), nil)
}

func main() {
	parseFlags()
	addHandlers()
	log.Fatal(listenAndServe())
}
<commit_msg>cmd/heapview: look for tools repository in all GOPATH entries<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// heapview is a tool for viewing Go heap dumps.
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
)

var host = flag.String("host", "", "host addr to listen on")
var port = flag.Int("port", 8080, "service port")

var index = `<!DOCTYPE html>
<script src="js/customelements.js"></script>
<script src="js/typescript.js"></script>
<script src="js/moduleloader.js"></script>
<script>
  System.transpiler = 'typescript';
  System.typescriptOptions = {target: ts.ScriptTarget.ES2015};
  System.locate = (load) => load.name + '.ts';
</script>
<script type="module">
  import {main} from './client/main';
  main();
</script>
`

func toolsDir() string {
	p, err := build.Import("golang.org/x/tools", "", build.FindOnly)
	if err != nil {
		log.Println("error: can't find client files:", err)
		os.Exit(1)
	}
	return p.Dir
}

var parseFlags = func() {
	flag.Parse()
}

var addHandlers = func() {
	toolsDir := toolsDir()

	// Directly serve typescript code in client directory for development.
	http.Handle("/client/", http.StripPrefix("/client",
		http.FileServer(http.Dir(filepath.Join(toolsDir, "cmd/heapview/client")))))

	// Serve typescript.js and moduleloader.js for development.
	http.HandleFunc("/js/typescript.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/typescript/typescript.js"))
	})
	http.HandleFunc("/js/moduleloader.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/moduleloader/moduleloader.js"))
	})
	http.HandleFunc("/js/customelements.js", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filepath.Join(toolsDir, "third_party/webcomponents/customelements.js"))
	})

	// Serve index.html using html string above.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		io.WriteString(w, index)
	})
}

var listenAndServe = func() error {
	return http.ListenAndServe(fmt.Sprintf("%s:%d", *host, *port), nil)
}

func main() {
	parseFlags()
	addHandlers()
	log.Fatal(listenAndServe())
}
<|endoftext|>