<commit_before>package main

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

	"github.com/clusterit/orca/timebuffer"

	"github.com/hashicorp/logutils"

	"github.com/clusterit/orca/cmd"
	"github.com/clusterit/orca/common"
	"github.com/clusterit/orca/config"
	"github.com/clusterit/orca/etcd"
	"github.com/clusterit/orca/logging"
	"github.com/clusterit/orca/users"

	"github.com/spf13/viper"

	"golang.org/x/crypto/ssh"
)

const (
	timeoutFor2FAinSeconds = 30
)

var (
	fetcher       UserFetcher
	configuration *config.Gateway
	sshConfig     ssh.ServerConfig
	configer      config.Configer
	zone          string
	lock          sync.Mutex
	revision      = "latest"
)

func init() {
	viper.SetEnvPrefix(common.OrcaPrefix)
	viper.AutomaticEnv()
	viper.SetDefault("bind", ":2022")
	viper.SetDefault("etcd", "http://localhost:4001")
	viper.SetDefault("zone", "intranet")

	zone = viper.GetString("zone")
	etcds := strings.Split(viper.GetString("etcd"), ",")
	cc, err := etcd.Init(etcds)
	if err != nil {
		panic(err)
	}
	fetcher, err = NewHttpFetcher(cc)
	if err != nil {
		panic(err)
	}
	cfger, err := config.New(cc)
	if err != nil {
		panic(err)
	}
	configer = cfger

}

func initWithConfig(gw *config.Gateway) error {
	lock.Lock()
	defer lock.Unlock()

	signer, err := ssh.ParsePrivateKey([]byte(gw.HostKey))
	if err != nil {
		return err
	}
	filter := &logutils.LevelFilter{
		Levels:   logging.Levels,
		MinLevel: logging.ByName(gw.LogLevel),
		Writer:   os.Stderr,
	}
	log.SetOutput(filter)

	sshConfig = ssh.ServerConfig{
		PublicKeyCallback: keyAuth,
		PasswordCallback:  pwdCallback,
		ServerVersion:     fmt.Sprintf("SSH-2.0-orca_%s", revision),
	}
	sshConfig.AddHostKey(signer)
	configuration = gw
	return nil
}

func initWithSettings(zone string) error {
	cfg, _, err := cmd.ForceZone(configer, zone, true, true)
	if err != nil {
		return err
	}
	initWithConfig(cfg)

	go func() {
		ngw, stp, err := configer.Gateway(zone)
		if err != nil {
			Log(logging.Error, "cannot create watcher for gateway config: %s")
			return
		}
		for gw := range ngw {
			Log(logging.Debug, "new gateway config: %#v", gw)
			initWithConfig(&gw)
		}
		close(stp)
	}()
	return nil
}

func checkAllowed(sessid []byte, u *users.User) error {
	if u.Use2FA {
		// if the users has 2FA, check the allowance field if another
		// check is needed
		if u.Allowance == nil || u.Allowance.Until.Before(time.Now()) {
			timebuffer.Put(string(sessid), u, timeoutFor2FAinSeconds)
			return fmt.Errorf("2FA enabled, next password check")
		}

		if u.Allowance != nil && u.Allowance.Until.After(time.Now().Add(time.Duration(configuration.MaxAutologin2FA)*time.Second)) {
			timebuffer.Put(string(sessid), u, timeoutFor2FAinSeconds)
			return fmt.Errorf("allowance too long, next password check")
		}
		// there is a successfull allowance
		return nil
	}
	if configuration.Force2FA {
		return fmt.Errorf("you must use 2fa!")
	}
	if !configuration.CheckAllow {
		return nil
	}
	if u.Allowance == nil {
		return fmt.Errorf("please activate your account")
	}
	if u.Allowance.Until.Before(time.Now()) {
		return fmt.Errorf("your activation timed out")
	}

	return nil
}

func keyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
	pubk := string(ssh.MarshalAuthorizedKey(key))
	usr, err := fetcher.UserByKey(zone, strings.TrimSpace(pubk))
	if err != nil {
		Log(logging.Debug, "remote: %s: cannot fetch key for user '%s': %s", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}
	if err := checkAllowed(conn.SessionID(), usr); err != nil {
		Log(logging.Debug, "remote: %s: not allowed to login for user '%s': %s", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}
	Log(logging.Info, "remote: %s: login by %+v", conn.RemoteAddr().String(), usr)
	return &ssh.Permissions{Extensions: map[string]string{
		"user_id": usr.Id}}, nil
}

func pwdCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
	keyusr := timebuffer.Get(string(conn.SessionID()))
	if keyusr == nil {
		Log(logging.Debug, "remote: %s: no key auth happend before OTP check '%s'", conn.RemoteAddr().String(), conn.User())
		return nil, fmt.Errorf("no key auth happend before OTP check")
	}
	usr := keyusr.(*users.User)
	err := fetcher.CheckToken(zone, usr.Id, string(password), configuration.MaxAutologin2FA)
	if err != nil {
		Log(logging.Debug, "remote: %s: wrong token for user '%s': '%s'", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}

	return &ssh.Permissions{Extensions: map[string]string{
		"user_id": string(usr.Id)}}, nil
}

func main() {
	initWithSettings(zone)

	bind := viper.GetString("bind")
	socket, err := net.Listen("tcp", bind)
	if err != nil {
		panic(err)
	}
	Log(logging.Info, "gateway listens on %#v ...", socket.Addr().String())
	for {

		tcpConn, err := socket.Accept()
		if err != nil {
			Log(logging.Error, "failed to accept incoming connection (%s)", err)
			continue
		}

		_, err = NewSession(ssh.NewServerConn(tcpConn, &sshConfig))

		if err != nil {
			Log(logging.Error, "failed to handshake (%s)", err)
			continue
		}

	}
}
<commit_msg>support TLS also in gateway<commit_after>package main

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

	"github.com/clusterit/orca/timebuffer"

	"github.com/hashicorp/logutils"

	"github.com/clusterit/orca/cmd"
	"github.com/clusterit/orca/common"
	"github.com/clusterit/orca/config"
	"github.com/clusterit/orca/etcd"
	"github.com/clusterit/orca/logging"
	"github.com/clusterit/orca/users"

	"github.com/spf13/viper"

	"golang.org/x/crypto/ssh"
)

const (
	timeoutFor2FAinSeconds = 30
)

var (
	fetcher       UserFetcher
	configuration *config.Gateway
	sshConfig     ssh.ServerConfig
	configer      config.Configer
	zone          string
	lock          sync.Mutex
	revision      = "latest"
)

func init() {
	viper.SetEnvPrefix(common.OrcaPrefix)
	viper.AutomaticEnv()
	viper.SetDefault("bind", ":2022")
	viper.SetDefault("etcd_machines", "http://localhost:4001")

	viper.SetDefault("zone", "intranet")

	zone = viper.GetString("zone")
	etcds := strings.Split(viper.GetString("etcd_machines"), ",")
	etcdKey := viper.GetString("etcd_key")
	etcdCert := viper.GetString("etcd_cert")
	etcdCa := viper.GetString("etcd_ca")

	cc, err := etcd.InitTLS(etcds, etcdKey, etcdCert, etcdCa)
	if err != nil {
		panic(err)
	}
	fetcher, err = NewHttpFetcher(cc)
	if err != nil {
		panic(err)
	}
	cfger, err := config.New(cc)
	if err != nil {
		panic(err)
	}
	configer = cfger

}

func initWithConfig(gw *config.Gateway) error {
	lock.Lock()
	defer lock.Unlock()

	signer, err := ssh.ParsePrivateKey([]byte(gw.HostKey))
	if err != nil {
		return err
	}
	filter := &logutils.LevelFilter{
		Levels:   logging.Levels,
		MinLevel: logging.ByName(gw.LogLevel),
		Writer:   os.Stderr,
	}
	log.SetOutput(filter)

	sshConfig = ssh.ServerConfig{
		PublicKeyCallback: keyAuth,
		PasswordCallback:  pwdCallback,
		ServerVersion:     fmt.Sprintf("SSH-2.0-orca_%s", revision),
	}
	sshConfig.AddHostKey(signer)
	configuration = gw
	return nil
}

func initWithSettings(zone string) error {
	cfg, _, err := cmd.ForceZone(configer, zone, true, true)
	if err != nil {
		return err
	}
	initWithConfig(cfg)

	go func() {
		ngw, stp, err := configer.Gateway(zone)
		if err != nil {
			Log(logging.Error, "cannot create watcher for gateway config: %s")
			return
		}
		for gw := range ngw {
			Log(logging.Debug, "new gateway config: %#v", gw)
			initWithConfig(&gw)
		}
		close(stp)
	}()
	return nil
}

func checkAllowed(sessid []byte, u *users.User) error {
	if u.Use2FA {
		// if the users has 2FA, check the allowance field if another
		// check is needed
		if u.Allowance == nil || u.Allowance.Until.Before(time.Now()) {
			timebuffer.Put(string(sessid), u, timeoutFor2FAinSeconds)
			return fmt.Errorf("2FA enabled, next password check")
		}

		if u.Allowance != nil && u.Allowance.Until.After(time.Now().Add(time.Duration(configuration.MaxAutologin2FA)*time.Second)) {
			timebuffer.Put(string(sessid), u, timeoutFor2FAinSeconds)
			return fmt.Errorf("allowance too long, next password check")
		}
		// there is a successfull allowance
		return nil
	}
	if configuration.Force2FA {
		return fmt.Errorf("you must use 2fa!")
	}
	if !configuration.CheckAllow {
		return nil
	}
	if u.Allowance == nil {
		return fmt.Errorf("please activate your account")
	}
	if u.Allowance.Until.Before(time.Now()) {
		return fmt.Errorf("your activation timed out")
	}

	return nil
}

func keyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
	pubk := string(ssh.MarshalAuthorizedKey(key))
	usr, err := fetcher.UserByKey(zone, strings.TrimSpace(pubk))
	if err != nil {
		Log(logging.Debug, "remote: %s: cannot fetch key for user '%s': %s", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}
	if err := checkAllowed(conn.SessionID(), usr); err != nil {
		Log(logging.Debug, "remote: %s: not allowed to login for user '%s': %s", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}
	Log(logging.Info, "remote: %s: login by %+v", conn.RemoteAddr().String(), usr)
	return &ssh.Permissions{Extensions: map[string]string{
		"user_id": usr.Id}}, nil
}

func pwdCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
	keyusr := timebuffer.Get(string(conn.SessionID()))
	if keyusr == nil {
		Log(logging.Debug, "remote: %s: no key auth happend before OTP check '%s'", conn.RemoteAddr().String(), conn.User())
		return nil, fmt.Errorf("no key auth happend before OTP check")
	}
	usr := keyusr.(*users.User)
	err := fetcher.CheckToken(zone, usr.Id, string(password), configuration.MaxAutologin2FA)
	if err != nil {
		Log(logging.Debug, "remote: %s: wrong token for user '%s': '%s'", conn.RemoteAddr().String(), conn.User(), err)
		return nil, err
	}

	return &ssh.Permissions{Extensions: map[string]string{
		"user_id": string(usr.Id)}}, nil
}

func main() {
	initWithSettings(zone)

	bind := viper.GetString("bind")
	socket, err := net.Listen("tcp", bind)
	if err != nil {
		panic(err)
	}
	Log(logging.Info, "gateway listens on %#v ...", socket.Addr().String())
	for {

		tcpConn, err := socket.Accept()
		if err != nil {
			Log(logging.Error, "failed to accept incoming connection (%s)", err)
			continue
		}

		_, err = NewSession(ssh.NewServerConn(tcpConn, &sshConfig))

		if err != nil {
			Log(logging.Error, "failed to handshake (%s)", err)
			continue
		}

	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/zerowidth/gh-shorthand/internal/pkg/completion"
	"github.com/zerowidth/gh-shorthand/internal/pkg/server"
)

var rootCmd = &cobra.Command{
	Use: "gh-shorthand",
	Run: func(cmd *cobra.Command, args []string) {
		_ = cmd.Usage()
	},
}

var completeCommand = &cobra.Command{
	Use: "complete 'input string'",
	Run: func(cmd *cobra.Command, args []string) {
		input := strings.Join(args, " ")
		env := completion.AlfredEnvironment(input)
		result := completion.Complete(env)
		if err := json.NewEncoder(os.Stdout).Encode(result); err != nil {
			fmt.Fprintf(os.Stderr, "could not generate JSON: %s\n", err)
		}
	},
}

var serverCommand = &cobra.Command{
	Use: "server",
	Run: func(cmd *cobra.Command, args []string) {
		server.Run()
	},
}

func init() {
	rootCmd.AddCommand(completeCommand)
	rootCmd.AddCommand(serverCommand)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>Add markdown-link and issue-reference subcommands<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/zerowidth/gh-shorthand/internal/pkg/completion"
	"github.com/zerowidth/gh-shorthand/internal/pkg/server"
	"github.com/zerowidth/gh-shorthand/internal/pkg/snippets"
)

var rootCmd = &cobra.Command{
	Use: "gh-shorthand",
	Run: func(cmd *cobra.Command, args []string) {
		_ = cmd.Usage()
	},
}

var completeCommand = &cobra.Command{
	Use: "complete 'input string'",
	Run: func(cmd *cobra.Command, args []string) {
		input := strings.Join(args, " ")
		env := completion.AlfredEnvironment(input)
		result := completion.Complete(env)
		if err := json.NewEncoder(os.Stdout).Encode(result); err != nil {
			fmt.Fprintf(os.Stderr, "could not generate JSON: %s\n", err)
		}
	},
}

var serverCommand = &cobra.Command{
	Use: "server",
	Run: func(cmd *cobra.Command, args []string) {
		server.Run()
	},
}

var markdownCommand = &cobra.Command{
	Use: "markdown-link",
	Run: func(cmd *cobra.Command, args []string) {
		link := snippets.MarkdownLink(strings.Join(args, " "))
		fmt.Fprintf(os.Stdout, link)
	},
}

var issueReferenceCommand = &cobra.Command{
	Use: "issue-reference",
	Run: func(cmd *cobra.Command, args []string) {
		ref := snippets.IssueReference(strings.Join(args, " "))
		fmt.Fprintf(os.Stdout, ref)
	},
}

func init() {
	rootCmd.AddCommand(completeCommand)
	rootCmd.AddCommand(serverCommand)
	rootCmd.AddCommand(markdownCommand)
	rootCmd.AddCommand(issueReferenceCommand)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main // import "vimagination.zapto.org/httpdir/cmd/httpdir"

import (
	"compress/flate"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/google/brotli/go/cbrotli"
	"vimagination.zapto.org/memio"
)

var (
	pkg      = flag.String("p", "main", "package name")
	in       = flag.String("i", "", "input filename")
	out      = flag.String("o", "", "output filename")
	odate    = flag.String("d", "", "modified date [seconds since epoch]")
	path     = flag.String("w", "", "http path")
	varname  = flag.String("v", "httpdir.Default", "http dir variable name")
	cvarname = flag.String("c", "httpdir.Default", "http dir compressed variable name")
	help     = flag.Bool("h", false, "show help")
	gzcomp   = flag.Bool("g", false, "compress using gzip")
	brcomp   = flag.Bool("b", false, "compress using brotli")
	flcomp   = flag.Bool("f", false, "compress using flate/deflate")
	single   = flag.Bool("s", false, "use single source var and decompress/compress for others")
)

type replacer struct {
	f *os.File
}

var hexArr = [16]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}

func (r replacer) Write(p []byte) (int, error) {
	n := 0
	toWrite := make([]byte, 0, 5)
	for len(p) > 0 {
		rn, s := utf8.DecodeRune(p)
		if rn == utf8.RuneError {
			s = 1
		}
		if s > 1 || (p[0] > 0 && p[0] < 0x7f && p[0] != '\n' && p[0] != '\\' && p[0] != '"') {
			toWrite = append(toWrite[:0], p[:s]...)
		} else {
			switch p[0] {
			case '\n':
				toWrite = append(toWrite[:0], '\\', 'n')
			case '\\':
				toWrite = append(toWrite[:0], '\\', '\\')
			case '"':
				toWrite = append(toWrite[:0], '\\', '"')
			default:
				toWrite = append(toWrite[:0], '\\', 'x', hexArr[p[0]>>4], hexArr[p[0]&15])
			}
		}
		_, err := r.f.Write(toWrite)
		n += s
		if err != nil {
			return n, err
		}
		p = p[s:]
	}
	return n, nil
}

type tickReplacer struct {
	f *os.File
}

func (r tickReplacer) Write(p []byte) (int, error) {
	var (
		m, n int
		err  error
	)
	hexes := make([]byte, 0, 32)
	toWrite := make([]byte, 0, 1024)
	for len(p) > 0 {
		dr, s := utf8.DecodeRune(p)
		if dr == utf8.RuneError || dr == '`' || dr == '\r' {
			hexes = append(hexes, p[0])
		} else {
			if len(hexes) > 0 {
				toWrite = toWrite[:0]
				toWrite = append(toWrite, '`', '+', '"')
				for _, b := range hexes {
					if b == '`' {
						toWrite = append(toWrite, '`')
					} else if b == '\r' {
						toWrite = append(toWrite, '\\', 'r')
					} else {
						toWrite = append(toWrite, '\\', 'x', hexArr[b>>4], hexArr[b&15])
					}
				}
				toWrite = append(toWrite, '"', '+', '`')
				_, err = r.f.Write(toWrite)
				n += len(hexes)
				if err != nil {
					break
				}
				hexes = hexes[:0]
			}
			m, err = r.f.Write(p[:s])
			n += m
			if err != nil {
				break
			}
		}
		p = p[s:]
	}
	return n, err
}

func e(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}
}

func ne(_ int, err error) {
	e(err)
}

type imports []string

func (im imports) Len() int {
	return len(im)
}

func (im imports) Less(i, j int) bool {
	si := strings.HasPrefix(im[i], "\"github.com")
	sj := strings.HasPrefix(im[j], "\"github.com")
	vi := strings.HasPrefix(im[i], "\"vimagination.zapto.org")
	vj := strings.HasPrefix(im[j], "\"vimagination.zapto.org")
	if si == sj && vi == vj {
		return im[i] < im[j]
	}
	return !si || !vi
}

func (im imports) Swap(i, j int) {
	im[i], im[j] = im[j], im[i]
}

type encoding struct {
	Buffer                    []byte
	Compress, Decompress, Ext string
}

type encodings []encoding

func (e encodings) Len() int {
	return len(e)
}

func (e encodings) Less(i, j int) bool {
	return len(e[i].Buffer) < len(e[j].Buffer)
}

func (e encodings) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	var (
		f    *os.File
		err  error
		date int64
	)
	if *in == "-" || *in == "" {
		f = os.Stdin
		date = time.Now().Unix()
	} else {
		f, err = os.Open(*in)
		e(err)
		fi, err := f.Stat()
		e(err)
		date = fi.ModTime().Unix()
	}
	if *odate != "" {
		date, err = strconv.ParseInt(*odate, 10, 64)
		e(err)
	}

	data := make(memio.Buffer, 0, 1<<20)
	_, err = io.Copy(&data, f)
	e(err)
	e(f.Close())

	im := imports{"\"vimagination.zapto.org/httpdir\"", "\"time\""}

	encs := make(encodings, 1, 4)
	encs[0] = encoding{
		Buffer:     data,
		Compress:   identCompress,
		Decompress: identDecompress,
		Ext:        "",
	}

	if *brcomp {
		var b memio.Buffer
		br := cbrotli.NewWriter(&b, cbrotli.WriterOptions{Quality: 11})
		br.Write(data)
		br.Close()
		if *single {
			im = append(im, brotliImport)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   brotliCompress,
			Decompress: brotliDecompress,
			Ext:        ".br",
		})
	}
	if *flcomp {
		var b memio.Buffer
		fl, _ := flate.NewWriter(&b, flate.BestCompression)
		fl.Write(data)
		fl.Close()
		if *single {
			im = append(im, strings.Split(flateImport, "\n")...)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   flateCompress,
			Decompress: flateDecompress,
			Ext:        ".fl",
		})
	}
	if *gzcomp {
		var b memio.Buffer
		gz, _ := gzip.NewWriterLevel(&b, gzip.BestCompression)
		gz.Write(data)
		gz.Close()
		if *single {
			im = append(im, gzipImport)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   gzipCompress,
			Decompress: gzipDecompress,
			Ext:        ".gz",
		})
	}

	sort.Sort(encs)

	if *single && (*gzcomp || *brcomp || *flcomp) {
		im = append(im, "\"vimagination.zapto.org/memio\"")
		if encs[0].Ext != "" {
			im = append(im, "\"strings\"")
		}
	}
	if encs[0].Ext == ".fl" {
		im = append(im, "\"io\"")
	}

	sort.Sort(im)
	var (
		imports string
		ext     bool
	)
	for _, i := range im {
		if !ext && strings.HasPrefix(i, "\"github.com") {
			imports += "\n"
			ext = true
		}
		imports += "	" + i + "\n"
	}
	if *out == "-" || *out == "" {
		f = os.Stdout
	} else {
		f, err = os.Create(*out)
		e(err)
	}
	fmt.Fprintf(f, packageStart, *pkg, imports, date)
	if *single {
		f.WriteString(stringStart)
		if encs[0].Ext == "" {
			ne(f.WriteString("`"))
			ne(tickReplacer{f}.Write(encs[0].Buffer))
			ne(f.WriteString("`"))
		} else {
			ne(f.WriteString("\""))
			ne(replacer{f}.Write(encs[0].Buffer))
			ne(f.WriteString("\""))
		}
		f.WriteString(stringEnd)
		for n, enc := range encs {
			var (
				templ string
				vars  = []interface{}{0, *cvarname, *path + enc.Ext}
			)
			if enc.Ext == "" {
				vars = vars[1:]
				vars[0] = *varname
				if n == 0 {
					templ = identDecompress
				} else {
					templ = identCompress
				}
			} else {
				if n == 0 {
					vars[0] = len(data)
					templ = enc.Decompress
				} else {
					vars[0] = len(enc.Buffer)
					templ = enc.Compress
				}
			}
			fmt.Fprintf(f, templ, vars...)
		}
	} else {
		for _, enc := range encs {
			filename := *path + enc.Ext
			ne(fmt.Fprintf(f, soloStart, *varname, filename))
			if enc.Ext == "" {
				ne(f.WriteString("`"))
				ne(tickReplacer{f}.Write(enc.Buffer))
				ne(f.WriteString("`"))
			} else {
				ne(f.WriteString("\""))
				ne(replacer{f}.Write(enc.Buffer))
				ne(f.WriteString("\""))
			}
			ne(f.WriteString(soloEnd))
		}
	}
	ne(fmt.Fprintf(f, packageEnd))
	e(f.Close())
}

const (
	packageStart = `package %s

import (
%s)

func init() {
	date := time.Unix(%d, 0)
`
	stringStart = `	s := `
	stringEnd   = `
`
	packageEnd = `}
`
	soloStart = `	%s.Create(%q, httpdir.FileString(`
	soloEnd   = `, date))
`
	identDecompress = `	b := []byte(s)
	%s.Create(%q, httpdir.FileString(s, date))
`
	identCompress = `	%s.Create(%q, httpdir.FileBytes(b, date))
`

	brotliImport     = "\"github.com/google/brotli/go/cbrotli\""
	brotliDecompress = `	b := make([]byte, %d)
	br := cbrotli.NewReader(strings.NewReader(s))
	br.Read(b)
	br.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	brotliCompress = `	brb := make(memio.Buffer, 0, %d)
	br := cbrotli.NewWriter(&brb, cbrotli.WriterOptions{Quality: 11})
	br.Write(b)
	br.Close()
	%s.Create(%q, httpdir.FileBytes(brb, date))
`
	flateImport     = "\"compress/flate\""
	flateDecompress = `	b := make([]byte, %d)
	fl := flate.NewReader(strings.NewReader(s))
	io.ReadFull(fl, b)
	fl.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	flateCompress = `	flb := make(memio.Buffer, 0, %d)
	fl, _ := flate.NewWriter(&flb, flate.BestCompression)
	fl.Write(b)
	fl.Close()
	%s.Create(%q, httpdir.FileBytes(flb, date))
`
	gzipImport     = "\"compress/gzip\""
	gzipDecompress = `	b := make([]byte, %d)
	gz := gzip.NewReader(strings.NewReader(s))
	gz.Read(b)
	gz.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	gzipCompress = `	gzb := make(memio.Buffer, 0, %d)
	gz, _ := gzip.NewWriterLevel(&gzb, gzip.BestCompression)
	gz.Write(b)
	gz.Close()
	%s.Create(%q, httpdir.FileBytes(gzb, date))
`
)
<commit_msg>corrected import ordering<commit_after>package main // import "vimagination.zapto.org/httpdir/cmd/httpdir"

import (
	"compress/flate"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/google/brotli/go/cbrotli"
	"vimagination.zapto.org/memio"
)

var (
	pkg      = flag.String("p", "main", "package name")
	in       = flag.String("i", "", "input filename")
	out      = flag.String("o", "", "output filename")
	odate    = flag.String("d", "", "modified date [seconds since epoch]")
	path     = flag.String("w", "", "http path")
	varname  = flag.String("v", "httpdir.Default", "http dir variable name")
	cvarname = flag.String("c", "httpdir.Default", "http dir compressed variable name")
	help     = flag.Bool("h", false, "show help")
	gzcomp   = flag.Bool("g", false, "compress using gzip")
	brcomp   = flag.Bool("b", false, "compress using brotli")
	flcomp   = flag.Bool("f", false, "compress using flate/deflate")
	single   = flag.Bool("s", false, "use single source var and decompress/compress for others")
)

type replacer struct {
	f *os.File
}

var hexArr = [16]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}

func (r replacer) Write(p []byte) (int, error) {
	n := 0
	toWrite := make([]byte, 0, 5)
	for len(p) > 0 {
		rn, s := utf8.DecodeRune(p)
		if rn == utf8.RuneError {
			s = 1
		}
		if s > 1 || (p[0] > 0 && p[0] < 0x7f && p[0] != '\n' && p[0] != '\\' && p[0] != '"') {
			toWrite = append(toWrite[:0], p[:s]...)
		} else {
			switch p[0] {
			case '\n':
				toWrite = append(toWrite[:0], '\\', 'n')
			case '\\':
				toWrite = append(toWrite[:0], '\\', '\\')
			case '"':
				toWrite = append(toWrite[:0], '\\', '"')
			default:
				toWrite = append(toWrite[:0], '\\', 'x', hexArr[p[0]>>4], hexArr[p[0]&15])
			}
		}
		_, err := r.f.Write(toWrite)
		n += s
		if err != nil {
			return n, err
		}
		p = p[s:]
	}
	return n, nil
}

type tickReplacer struct {
	f *os.File
}

func (r tickReplacer) Write(p []byte) (int, error) {
	var (
		m, n int
		err  error
	)
	hexes := make([]byte, 0, 32)
	toWrite := make([]byte, 0, 1024)
	for len(p) > 0 {
		dr, s := utf8.DecodeRune(p)
		if dr == utf8.RuneError || dr == '`' || dr == '\r' {
			hexes = append(hexes, p[0])
		} else {
			if len(hexes) > 0 {
				toWrite = toWrite[:0]
				toWrite = append(toWrite, '`', '+', '"')
				for _, b := range hexes {
					if b == '`' {
						toWrite = append(toWrite, '`')
					} else if b == '\r' {
						toWrite = append(toWrite, '\\', 'r')
					} else {
						toWrite = append(toWrite, '\\', 'x', hexArr[b>>4], hexArr[b&15])
					}
				}
				toWrite = append(toWrite, '"', '+', '`')
				_, err = r.f.Write(toWrite)
				n += len(hexes)
				if err != nil {
					break
				}
				hexes = hexes[:0]
			}
			m, err = r.f.Write(p[:s])
			n += m
			if err != nil {
				break
			}
		}
		p = p[s:]
	}
	return n, err
}

func e(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}
}

func ne(_ int, err error) {
	e(err)
}

type imports []string

func (im imports) Len() int {
	return len(im)
}

func (im imports) Less(i, j int) bool {
	si := strings.HasPrefix(im[i], "\"github.com")
	sj := strings.HasPrefix(im[j], "\"github.com")
	vi := strings.HasPrefix(im[i], "\"vimagination.zapto.org")
	vj := strings.HasPrefix(im[j], "\"vimagination.zapto.org")
	if si == sj && vi == vj {
		return im[i] < im[j]
	}
	return !si && !vi || si && vj
}

func (im imports) Swap(i, j int) {
	im[i], im[j] = im[j], im[i]
}

type encoding struct {
	Buffer                    []byte
	Compress, Decompress, Ext string
}

type encodings []encoding

func (e encodings) Len() int {
	return len(e)
}

func (e encodings) Less(i, j int) bool {
	return len(e[i].Buffer) < len(e[j].Buffer)
}

func (e encodings) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	var (
		f    *os.File
		err  error
		date int64
	)
	if *in == "-" || *in == "" {
		f = os.Stdin
		date = time.Now().Unix()
	} else {
		f, err = os.Open(*in)
		e(err)
		fi, err := f.Stat()
		e(err)
		date = fi.ModTime().Unix()
	}
	if *odate != "" {
		date, err = strconv.ParseInt(*odate, 10, 64)
		e(err)
	}

	data := make(memio.Buffer, 0, 1<<20)
	_, err = io.Copy(&data, f)
	e(err)
	e(f.Close())

	im := imports{"\"vimagination.zapto.org/httpdir\"", "\"time\""}

	encs := make(encodings, 1, 4)
	encs[0] = encoding{
		Buffer:     data,
		Compress:   identCompress,
		Decompress: identDecompress,
		Ext:        "",
	}

	if *brcomp {
		var b memio.Buffer
		br := cbrotli.NewWriter(&b, cbrotli.WriterOptions{Quality: 11})
		br.Write(data)
		br.Close()
		if *single {
			im = append(im, brotliImport)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   brotliCompress,
			Decompress: brotliDecompress,
			Ext:        ".br",
		})
	}
	if *flcomp {
		var b memio.Buffer
		fl, _ := flate.NewWriter(&b, flate.BestCompression)
		fl.Write(data)
		fl.Close()
		if *single {
			im = append(im, strings.Split(flateImport, "\n")...)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   flateCompress,
			Decompress: flateDecompress,
			Ext:        ".fl",
		})
	}
	if *gzcomp {
		var b memio.Buffer
		gz, _ := gzip.NewWriterLevel(&b, gzip.BestCompression)
		gz.Write(data)
		gz.Close()
		if *single {
			im = append(im, gzipImport)
		}
		encs = append(encs, encoding{
			Buffer:     b,
			Compress:   gzipCompress,
			Decompress: gzipDecompress,
			Ext:        ".gz",
		})
	}

	sort.Sort(encs)

	if *single && (*gzcomp || *brcomp || *flcomp) {
		im = append(im, "\"vimagination.zapto.org/memio\"")
		if encs[0].Ext != "" {
			im = append(im, "\"strings\"")
		}
	}
	if encs[0].Ext == ".fl" {
		im = append(im, "\"io\"")
	}

	sort.Sort(im)
	var (
		imports string
		ext     bool
	)
	for _, i := range im {
		if !ext && (strings.HasPrefix(i, "\"github.com") || strings.HasPrefix(i, "\"vimagination")) {
			imports += "\n"
			ext = true
		}
		imports += "	" + i + "\n"
	}
	if *out == "-" || *out == "" {
		f = os.Stdout
	} else {
		f, err = os.Create(*out)
		e(err)
	}
	fmt.Fprintf(f, packageStart, *pkg, imports, date)
	if *single {
		f.WriteString(stringStart)
		if encs[0].Ext == "" {
			ne(f.WriteString("`"))
			ne(tickReplacer{f}.Write(encs[0].Buffer))
			ne(f.WriteString("`"))
		} else {
			ne(f.WriteString("\""))
			ne(replacer{f}.Write(encs[0].Buffer))
			ne(f.WriteString("\""))
		}
		f.WriteString(stringEnd)
		for n, enc := range encs {
			var (
				templ string
				vars  = []interface{}{0, *cvarname, *path + enc.Ext}
			)
			if enc.Ext == "" {
				vars = vars[1:]
				vars[0] = *varname
				if n == 0 {
					templ = identDecompress
				} else {
					templ = identCompress
				}
			} else {
				if n == 0 {
					vars[0] = len(data)
					templ = enc.Decompress
				} else {
					vars[0] = len(enc.Buffer)
					templ = enc.Compress
				}
			}
			fmt.Fprintf(f, templ, vars...)
		}
	} else {
		for _, enc := range encs {
			filename := *path + enc.Ext
			ne(fmt.Fprintf(f, soloStart, *varname, filename))
			if enc.Ext == "" {
				ne(f.WriteString("`"))
				ne(tickReplacer{f}.Write(enc.Buffer))
				ne(f.WriteString("`"))
			} else {
				ne(f.WriteString("\""))
				ne(replacer{f}.Write(enc.Buffer))
				ne(f.WriteString("\""))
			}
			ne(f.WriteString(soloEnd))
		}
	}
	ne(fmt.Fprintf(f, packageEnd))
	e(f.Close())
}

const (
	packageStart = `package %s

import (
%s)

func init() {
	date := time.Unix(%d, 0)
`
	stringStart = `	s := `
	stringEnd   = `
`
	packageEnd = `}
`
	soloStart = `	%s.Create(%q, httpdir.FileString(`
	soloEnd   = `, date))
`
	identDecompress = `	b := []byte(s)
	%s.Create(%q, httpdir.FileString(s, date))
`
	identCompress = `	%s.Create(%q, httpdir.FileBytes(b, date))
`

	brotliImport     = "\"github.com/google/brotli/go/cbrotli\""
	brotliDecompress = `	b := make([]byte, %d)
	br := cbrotli.NewReader(strings.NewReader(s))
	br.Read(b)
	br.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	brotliCompress = `	brb := make(memio.Buffer, 0, %d)
	br := cbrotli.NewWriter(&brb, cbrotli.WriterOptions{Quality: 11})
	br.Write(b)
	br.Close()
	%s.Create(%q, httpdir.FileBytes(brb, date))
`
	flateImport     = "\"compress/flate\""
	flateDecompress = `	b := make([]byte, %d)
	fl := flate.NewReader(strings.NewReader(s))
	io.ReadFull(fl, b)
	fl.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	flateCompress = `	flb := make(memio.Buffer, 0, %d)
	fl, _ := flate.NewWriter(&flb, flate.BestCompression)
	fl.Write(b)
	fl.Close()
	%s.Create(%q, httpdir.FileBytes(flb, date))
`
	gzipImport     = "\"compress/gzip\""
	gzipDecompress = `	b := make([]byte, %d)
	gz := gzip.NewReader(strings.NewReader(s))
	gz.Read(b)
	gz.Close()
	%s.Create(%q, httpdir.FileString(s, date))
`
	gzipCompress = `	gzb := make(memio.Buffer, 0, %d)
	gz, _ := gzip.NewWriterLevel(&gzb, gzip.BestCompression)
	gz.Write(b)
	gz.Close()
	%s.Create(%q, httpdir.FileBytes(gzb, date))
`
)
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-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 (
	"context"
	"flag"
	"fmt"
	logpkg "log"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"cloud.google.com/go/errorreporting"
	"contrib.go.opencensus.io/exporter/stackdriver"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/trace"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"golang.org/x/sync/errgroup"

	"github.com/zchee/nvim-go/pkg/autocmd"
	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/command"
	"github.com/zchee/nvim-go/pkg/config"
	"github.com/zchee/nvim-go/pkg/logger"
	"github.com/zchee/nvim-go/pkg/server"
)

const (
	appName = "nvim-go"
)

// flags
var (
	fVersion    = flag.Bool("version", false, "Show the version information.")
	pluginHost  = flag.String("manifest", "", "Write plugin manifest for `host` to stdout")
	vimFilePath = flag.String("location", "", "Manifest is automatically written to `.vim file`")
)

func init() {
	flag.Parse()
	logpkg.SetPrefix("nvim-go: ")
}

func main() {
	if *fVersion {
		fmt.Printf("%s:\n  version: %s\n", appName, version)
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if *pluginHost != "" {
		os.Unsetenv("NVIM_GO_DEBUG")
		fn := func(p *plugin.Plugin) error {
			return func(ctx context.Context, p *plugin.Plugin) error {
				ctx, cancel := context.WithCancel(ctx)
				defer cancel()
				bctxt := buildctxt.NewContext()
				c := command.Register(ctx, p, bctxt)
				autocmd.Register(ctx, cancel, p, bctxt, c)
				return nil
			}(ctx, p)
		}
		if err := Plugin(fn); err != nil {
			logpkg.Fatal(err)
		}
		return
	}

	env, err := config.Process()
	if err != nil {
		logpkg.Fatalf("env.Process: %+v", err)
	}

	var lv zapcore.Level
	if err := lv.UnmarshalText([]byte(env.LogLevel)); err != nil {
		logpkg.Fatalf("failed to parse log level: %s, err: %v", env.LogLevel, err)
	}
	zapLogger, undo := logger.NewRedirectZapLogger(lv)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	if gcpProjectID := env.GCPProjectID; gcpProjectID != "" {
		// Stackdriver Profiler
		// profCfg := profiler.Config{
		// 	Service:        appName,
		// 	ServiceVersion: tag,
		// 	MutexProfiling: true,
		// 	ProjectID:      gcpProjectID,
		// }
		// if err := profiler.Start(profCfg); err != nil {
		// 	logpkg.Fatalf("failed to start stackdriver profiler: %v", err)
		// }

		// OpenCensus tracing
		sdOpts := stackdriver.Options{
			ProjectID: gcpProjectID,
			OnError: func(err error) {
				zapLogger.Error("stackdriver.Exporter", zap.Error(fmt.Errorf("could not log error: %v", err)))
			},
			MetricPrefix: appName,
			Context:      ctx,
		}
		sd, err := stackdriver.NewExporter(sdOpts)
		if err != nil {
			logpkg.Fatalf("failed to create stackdriver exporter: %v", err)
		}
		defer sd.Flush()
		trace.RegisterExporter(sd)
		trace.ApplyConfig(trace.Config{
			DefaultSampler: trace.AlwaysSample(),
		})
		view.RegisterExporter(sd)

		// Stackdriver Error Reporting
		errReportCfg := errorreporting.Config{
			ServiceName:    appName,
			ServiceVersion: tag,
			OnError: func(err error) {
				zapLogger.Error("errorreporting", zap.Error(fmt.Errorf("could not log error: %v", err)))
			},
		}
		errClient, err := errorreporting.NewClient(ctx, gcpProjectID, errReportCfg)
		if err != nil {
			logpkg.Fatalf("failed to create errorreporting client: %v", err)
		}
		defer errClient.Close()
		ctx = context.WithValue(ctx, &errorreporting.Client{}, errClient)
	}

	zapLogger.Info("starting "+appName+" server", zap.Object("env", env))

	eg := new(errgroup.Group)
	eg, ctx = errgroup.WithContext(ctx)
	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		return Plugin(fn)
	})
	eg.Go(func() error {
		return Child(ctx)
	})

	go func() {
		if err := eg.Wait(); err != nil {
			zapLogger.Fatal("eg.Wait", zap.Error(err))
		}
	}()

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-ctx.Done():
		zapLogger.Error("ctx.Done()", zap.Error(ctx.Err()))
		return
	case sig := <-sigc:
		switch sig {
		case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Info("catch signal", zap.String("name", sig.String()))
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	ctx, cancel := context.WithCancel(ctx)
	ctx = logger.NewContext(ctx, logger.FromContext(ctx).Named("main"))

	bctxt := buildctxt.NewContext()
	autocmd.Register(ctx, cancel, p, bctxt, command.Register(ctx, p, bctxt))

	// switch to unix socket rpc-connection
	if n, err := server.Dial(ctx); err == nil {
		p.Nvim = n
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")
	ctx = logger.NewContext(ctx, log)

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	go s.Serve()
	defer func() {
		if err := s.Close(); err != nil {
			log.Fatal("Close", zap.Error(err))
		}
	}()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}

	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("buffer", zap.String("name", name))
	}

	return nil
}
<commit_msg>cmd/nvim-go: fix nil panic if generate manifest file<commit_after>// Copyright 2016 The nvim-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 (
	"context"
	"flag"
	"fmt"
	logpkg "log"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"

	"cloud.google.com/go/errorreporting"
	"contrib.go.opencensus.io/exporter/stackdriver"
	"github.com/neovim/go-client/nvim/plugin"
	"github.com/pkg/errors"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/trace"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"golang.org/x/sync/errgroup"

	"github.com/zchee/nvim-go/pkg/autocmd"
	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/command"
	"github.com/zchee/nvim-go/pkg/config"
	"github.com/zchee/nvim-go/pkg/logger"
	"github.com/zchee/nvim-go/pkg/server"
)

const (
	appName = "nvim-go"
)

// flags
var (
	fVersion    = flag.Bool("version", false, "Show the version information.")
	pluginHost  = flag.String("manifest", "", "Write plugin manifest for `host` to stdout")
	vimFilePath = flag.String("location", "", "Manifest is automatically written to `.vim file`")
)

func init() {
	flag.Parse()
	logpkg.SetPrefix("nvim-go: ")
}

func main() {
	if *fVersion {
		fmt.Printf("%s:\n  version: %s\n", appName, version)
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if *pluginHost != "" {
		os.Unsetenv("NVIM_GO_DEBUG")               // disable zap output
		ctx = logger.NewContext(ctx, zap.NewNop()) // avoid nil panic on logger.FromContext

		fn := func(p *plugin.Plugin) error {
			return func(ctx context.Context, p *plugin.Plugin) error {
				ctx, cancel := context.WithCancel(ctx)
				defer cancel()
				bctxt := buildctxt.NewContext()
				c := command.Register(ctx, p, bctxt)
				autocmd.Register(ctx, cancel, p, bctxt, c)
				return nil
			}(ctx, p)
		}
		if err := Plugin(fn); err != nil {
			logpkg.Fatal(err)
		}
		return
	}

	env, err := config.Process()
	if err != nil {
		logpkg.Fatalf("env.Process: %+v", err)
	}

	var lv zapcore.Level
	if err := lv.UnmarshalText([]byte(env.LogLevel)); err != nil {
		logpkg.Fatalf("failed to parse log level: %s, err: %v", env.LogLevel, err)
	}
	zapLogger, undo := logger.NewRedirectZapLogger(lv)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	if gcpProjectID := env.GCPProjectID; gcpProjectID != "" {
		// Stackdriver Profiler
		// profCfg := profiler.Config{
		// 	Service:        appName,
		// 	ServiceVersion: tag,
		// 	MutexProfiling: true,
		// 	ProjectID:      gcpProjectID,
		// }
		// if err := profiler.Start(profCfg); err != nil {
		// 	logpkg.Fatalf("failed to start stackdriver profiler: %v", err)
		// }

		// OpenCensus tracing
		sdOpts := stackdriver.Options{
			ProjectID: gcpProjectID,
			OnError: func(err error) {
				zapLogger.Error("stackdriver.Exporter", zap.Error(fmt.Errorf("could not log error: %v", err)))
			},
			MetricPrefix: appName,
			Context:      ctx,
		}
		sd, err := stackdriver.NewExporter(sdOpts)
		if err != nil {
			logpkg.Fatalf("failed to create stackdriver exporter: %v", err)
		}
		defer sd.Flush()
		trace.RegisterExporter(sd)
		trace.ApplyConfig(trace.Config{
			DefaultSampler: trace.AlwaysSample(),
		})
		view.RegisterExporter(sd)

		// Stackdriver Error Reporting
		errReportCfg := errorreporting.Config{
			ServiceName:    appName,
			ServiceVersion: tag,
			OnError: func(err error) {
				zapLogger.Error("errorreporting", zap.Error(fmt.Errorf("could not log error: %v", err)))
			},
		}
		errClient, err := errorreporting.NewClient(ctx, gcpProjectID, errReportCfg)
		if err != nil {
			logpkg.Fatalf("failed to create errorreporting client: %v", err)
		}
		defer errClient.Close()
		ctx = context.WithValue(ctx, &errorreporting.Client{}, errClient)
	}

	zapLogger.Info("starting "+appName+" server", zap.Object("env", env))

	eg := new(errgroup.Group)
	eg, ctx = errgroup.WithContext(ctx)
	eg.Go(func() error {
		fn := func(p *plugin.Plugin) error {
			return Main(ctx, p)
		}
		return Plugin(fn)
	})
	eg.Go(func() error {
		return Child(ctx)
	})

	go func() {
		if err := eg.Wait(); err != nil {
			zapLogger.Fatal("eg.Wait", zap.Error(err))
		}
	}()

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-ctx.Done():
		zapLogger.Error("ctx.Done()", zap.Error(ctx.Err()))
		return
	case sig := <-sigc:
		switch sig {
		case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Info("catch signal", zap.String("name", sig.String()))
			return
		}
	}
}

func Main(ctx context.Context, p *plugin.Plugin) error {
	ctx, cancel := context.WithCancel(ctx)
	ctx = logger.NewContext(ctx, logger.FromContext(ctx).Named("main"))

	bctxt := buildctxt.NewContext()
	autocmd.Register(ctx, cancel, p, bctxt, command.Register(ctx, p, bctxt))

	// switch to unix socket rpc-connection
	if n, err := server.Dial(ctx); err == nil {
		p.Nvim = n
	}

	return nil
}

func Child(ctx context.Context) error {
	log := logger.FromContext(ctx).Named("child")
	ctx = logger.NewContext(ctx, log)

	s, err := server.NewServer(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to create NewServer")
	}
	go s.Serve()
	defer func() {
		if err := s.Close(); err != nil {
			log.Fatal("Close", zap.Error(err))
		}
	}()

	bufs, err := s.Buffers()
	if err != nil {
		return errors.Wrap(err, "failed to get buffers")
	}

	// Get the names using a single atomic call to Nvim.
	names := make([]string, len(bufs))
	b := s.NewBatch()
	for i, buf := range bufs {
		b.BufferName(buf, &names[i])
	}

	if err := b.Execute(); err != nil {
		return errors.Wrap(err, "failed to execute batch")
	}

	for _, name := range names {
		log.Info("buffer", zap.String("name", name))
	}

	return nil
}
<|endoftext|>


<commit_before>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/

// rlpdump is a pretty-printer for RLP data.
package main

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/ethereum/go-ethereum/rlp"
)

var (
	hexMode = flag.String("hex", "", "dump given hex data")
	noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
)

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, `
Dumps RLP data from the given file in readable form.
If the filename is omitted, data is read from stdin.`)
	}
}

func main() {
	flag.Parse()

	var r io.Reader
	switch {
	case *hexMode != "":
		data, err := hex.DecodeString(*hexMode)
		if err != nil {
			die(err)
		}
		r = bytes.NewReader(data)

	case flag.NArg() == 0:
		r = os.Stdin

	case flag.NArg() == 1:
		fd, err := os.Open(flag.Arg(0))
		if err != nil {
			die(err)
		}
		defer fd.Close()
		r = bufio.NewReader(fd)

	default:
		fmt.Fprintln(os.Stderr, "Error: too many arguments")
		flag.Usage()
		os.Exit(2)
	}

	s := rlp.NewStream(r)
	for {
		if err := dump(s, 0); err != nil {
			if err != io.EOF {
				die(err)
			}
			break
		}
		fmt.Println()
	}
}

func dump(s *rlp.Stream, depth int) error {
	kind, size, err := s.Kind()
	if err != nil {
		return err
	}
	switch kind {
	case rlp.Byte, rlp.String:
		str, err := s.Bytes()
		if err != nil {
			return err
		}
		if len(str) == 0 || !*noASCII && isASCII(str) {
			fmt.Printf("%s%q", ws(depth), str)
		} else {
			fmt.Printf("%s%x", ws(depth), str)
		}
	case rlp.List:
		s.List()
		defer s.ListEnd()
		if size == 0 {
			fmt.Printf(ws(depth) + "[]")
			return nil
		} else {
			fmt.Println(ws(depth) + "[")
			for i := 0; ; i++ {
				if i > 0 {
					fmt.Print(",\n")
				}
				if err := dump(s, depth+1); err == rlp.EOL {
					break
				} else if err != nil {
					return err
				}
			}
			fmt.Print(ws(depth) + "]")
		}
	}
	return nil
}

func isASCII(b []byte) bool {
	for _, c := range b {
		if c < 32 || c > 126 {
			return false
		}
	}
	return true
}

func ws(n int) string {
	return strings.Repeat("  ", n)
}

func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}
<commit_msg>cmd/rlpdump: remove extra buffer<commit_after>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/

// rlpdump is a pretty-printer for RLP data.
package main

import (
	"bytes"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/ethereum/go-ethereum/rlp"
)

var (
	hexMode = flag.String("hex", "", "dump given hex data")
	noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
)

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr, `
Dumps RLP data from the given file in readable form.
If the filename is omitted, data is read from stdin.`)
	}
}

func main() {
	flag.Parse()

	var r io.Reader
	switch {
	case *hexMode != "":
		data, err := hex.DecodeString(*hexMode)
		if err != nil {
			die(err)
		}
		r = bytes.NewReader(data)

	case flag.NArg() == 0:
		r = os.Stdin

	case flag.NArg() == 1:
		fd, err := os.Open(flag.Arg(0))
		if err != nil {
			die(err)
		}
		defer fd.Close()
		r = fd

	default:
		fmt.Fprintln(os.Stderr, "Error: too many arguments")
		flag.Usage()
		os.Exit(2)
	}

	s := rlp.NewStream(r)
	for {
		if err := dump(s, 0); err != nil {
			if err != io.EOF {
				die(err)
			}
			break
		}
		fmt.Println()
	}
}

func dump(s *rlp.Stream, depth int) error {
	kind, size, err := s.Kind()
	if err != nil {
		return err
	}
	switch kind {
	case rlp.Byte, rlp.String:
		str, err := s.Bytes()
		if err != nil {
			return err
		}
		if len(str) == 0 || !*noASCII && isASCII(str) {
			fmt.Printf("%s%q", ws(depth), str)
		} else {
			fmt.Printf("%s%x", ws(depth), str)
		}
	case rlp.List:
		s.List()
		defer s.ListEnd()
		if size == 0 {
			fmt.Printf(ws(depth) + "[]")
			return nil
		} else {
			fmt.Println(ws(depth) + "[")
			for i := 0; ; i++ {
				if i > 0 {
					fmt.Print(",\n")
				}
				if err := dump(s, depth+1); err == rlp.EOL {
					break
				} else if err != nil {
					return err
				}
			}
			fmt.Print(ws(depth) + "]")
		}
	}
	return nil
}

func isASCII(b []byte) bool {
	for _, c := range b {
		if c < 32 || c > 126 {
			return false
		}
	}
	return true
}

func ws(n int) string {
	return strings.Repeat("  ", n)
}

func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>/*
rqlite -- a replicated SQLite database.

rqlite is a distributed system that provides a replicated SQLite database.
rqlite is written in Go and uses Raft to achieve consensus across all the
instances of the SQLite databases. rqlite ensures that every change made to
the database is made to a majority of underlying SQLite files, or none-at-all.
*/

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/pprof"

	sql "github.com/otoolep/rqlite/db"
	httpd "github.com/otoolep/rqlite/http"
	"github.com/otoolep/rqlite/store"
)

const sqliteDSN = "db.sqlite"

var httpAddr string
var raftAddr string
var joinAddr string
var expvar bool
var dsn string
var inMem bool
var disRedirect bool
var cpuprofile string

const desc = `rqlite is a distributed system that provides a replicated SQLite database.`

func init() {
	flag.StringVar(&httpAddr, "http", "localhost:4001", "HTTP query server bind address")
	flag.StringVar(&raftAddr, "raft", "localhost:4002", "Raft communication bind address")
	flag.StringVar(&joinAddr, "join", "", "host:port of leader to join")
	flag.BoolVar(&expvar, "expvar", true, "Serve expvar data on HTTP server")
	flag.StringVar(&dsn, "dsn", "", `SQLite DSN parameters. E.g. "cache=shared&mode=memory"`)
	flag.BoolVar(&inMem, "mem", false, "Use an in-memory database")
	flag.BoolVar(&disRedirect, "noredir", true, "Disable leader-redirect")
	flag.StringVar(&cpuprofile, "cpuprofile", "", "Write CPU profile to file")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%s\n\n", desc)
		fmt.Fprintf(os.Stderr, "Usage: %s [arguments] <data directory>\n", os.Args[0])
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()

	// Ensure the data path is set.
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	dataPath := flag.Arg(0)

	// Set up profiling, if requested.
	if cpuprofile != "" {
		log.Println("profiling enabled")
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Printf("unable to create path: %s", err.Error())
		}
		defer f.Close()

		err = pprof.StartCPUProfile(f)
		if err != nil {
			log.Printf("unable to start CPU Profile: %s", err.Error())
		}

		defer pprof.StopCPUProfile()
	}

	// Create and open the store.
	dataPath, err := filepath.Abs(dataPath)
	if err != nil {
		log.Fatalf("failed to determine absolute data path: %s", err.Error())
	}
	dbConf := sql.NewConfig()
	dbConf.DSN = dsn
	dbConf.Memory = inMem
	store := store.New(dbConf, dataPath, raftAddr)
	if err := store.Open(joinAddr == ""); err != nil {
		log.Fatalf("failed to open store: %s", err.Error())
	}

	// If join was specified, make the join request.
	if joinAddr != "" {
		if err := join(joinAddr, raftAddr); err != nil {
			log.Fatalf("failed to join node at %s: %s", joinAddr, err.Error())
		}
	}

	// Create the HTTP query server.
	s := httpd.New(httpAddr, store)
	s.DisableRedirect = disRedirect
	s.Expvar = expvar
	if err := s.Start(); err != nil {
		log.Fatalf("failed to start HTTP server: %s", err.Error())

	}

	terminate := make(chan os.Signal, 1)
	signal.Notify(terminate, os.Interrupt)
	<-terminate
	if err := store.Close(); err != nil {
		log.Printf("failed to close store: %s", err.Error())
	}
	log.Println("rqlite server stopped")
}

func join(joinAddr, raftAddr string) error {
	b, err := json.Marshal(map[string]string{"addr": raftAddr})
	if err != nil {
		return err
	}
	resp, err := http.Post(fmt.Sprintf("http://%s/join", joinAddr), "application-type/json", bytes.NewReader(b))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}
<commit_msg>Allow build version to be set<commit_after>/*
rqlite -- a replicated SQLite database.

rqlite is a distributed system that provides a replicated SQLite database.
rqlite is written in Go and uses Raft to achieve consensus across all the
instances of the SQLite databases. rqlite ensures that every change made to
the database is made to a majority of underlying SQLite files, or none-at-all.
*/

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/pprof"

	sql "github.com/otoolep/rqlite/db"
	httpd "github.com/otoolep/rqlite/http"
	"github.com/otoolep/rqlite/store"
)

const sqliteDSN = "db.sqlite"

// These variables are populated via the Go linker.
var (
	version = "2.1"
	commit  string
	branch  string
)

var httpAddr string
var raftAddr string
var joinAddr string
var expvar bool
var dsn string
var inMem bool
var disRedirect bool
var cpuprofile string

const desc = `rqlite is a distributed system that provides a replicated SQLite database.`

func init() {
	flag.StringVar(&httpAddr, "http", "localhost:4001", "HTTP query server bind address")
	flag.StringVar(&raftAddr, "raft", "localhost:4002", "Raft communication bind address")
	flag.StringVar(&joinAddr, "join", "", "host:port of leader to join")
	flag.BoolVar(&expvar, "expvar", true, "Serve expvar data on HTTP server")
	flag.StringVar(&dsn, "dsn", "", `SQLite DSN parameters. E.g. "cache=shared&mode=memory"`)
	flag.BoolVar(&inMem, "mem", false, "Use an in-memory database")
	flag.BoolVar(&disRedirect, "noredir", true, "Disable leader-redirect")
	flag.StringVar(&cpuprofile, "cpuprofile", "", "Write CPU profile to file")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "\n%s\n\n", desc)
		fmt.Fprintf(os.Stderr, "Usage: %s [arguments] <data directory>\n", os.Args[0])
		flag.PrintDefaults()
	}

	// If commit, branch, or build time are not set, make that clear.
	if commit == "" {
		commit = "unknown"
	}
	if branch == "" {
		branch = "unknown"
	}

}

func main() {
	flag.Parse()

	// Ensure the data path is set.
	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	dataPath := flag.Arg(0)

	// Configure logging and pump out initial message.
	log.SetFlags(log.LstdFlags)
	log.SetPrefix("[rqlited] ")
	log.Printf("rqlited starting, version %s, commit %s, branch %s", version, commit, branch)

	// Set up profiling, if requested.
	if cpuprofile != "" {
		log.Println("profiling enabled")
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Printf("unable to create path: %s", err.Error())
		}
		defer f.Close()

		err = pprof.StartCPUProfile(f)
		if err != nil {
			log.Printf("unable to start CPU Profile: %s", err.Error())
		}

		defer pprof.StopCPUProfile()
	}

	// Create and open the store.
	dataPath, err := filepath.Abs(dataPath)
	if err != nil {
		log.Fatalf("failed to determine absolute data path: %s", err.Error())
	}
	dbConf := sql.NewConfig()
	dbConf.DSN = dsn
	dbConf.Memory = inMem
	store := store.New(dbConf, dataPath, raftAddr)
	if err := store.Open(joinAddr == ""); err != nil {
		log.Fatalf("failed to open store: %s", err.Error())
	}

	// If join was specified, make the join request.
	if joinAddr != "" {
		if err := join(joinAddr, raftAddr); err != nil {
			log.Fatalf("failed to join node at %s: %s", joinAddr, err.Error())
		}
	}

	// Create the HTTP query server.
	s := httpd.New(httpAddr, store)
	s.DisableRedirect = disRedirect
	s.Expvar = expvar
	if err := s.Start(); err != nil {
		log.Fatalf("failed to start HTTP server: %s", err.Error())

	}

	terminate := make(chan os.Signal, 1)
	signal.Notify(terminate, os.Interrupt)
	<-terminate
	if err := store.Close(); err != nil {
		log.Printf("failed to close store: %s", err.Error())
	}
	log.Println("rqlite server stopped")
}

func join(joinAddr, raftAddr string) error {
	b, err := json.Marshal(map[string]string{"addr": raftAddr})
	if err != nil {
		return err
	}
	resp, err := http.Post(fmt.Sprintf("http://%s/join", joinAddr), "application-type/json", bytes.NewReader(b))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"math/rand"
	"os"
	"runtime/pprof"
	"strconv"
	"time"

	"golang.org/x/image/colornames"

	//	_ "image/png"

	"github.com/aerth/rpg"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/imdraw"
	"github.com/faiface/pixel/pixelgl"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

var (
	flagenemies   = flag.Int("e", 2, "number of enemies to begin with")
	flaglevel     = flag.Int("lvl", 1, "starting level (1)")
	flagcustomlvl = flag.String("test", "", "custom world test (filename)")
	debug         = flag.Bool("v", false, "extra logs")
)

const (
	LEFT  = rpg.LEFT
	RIGHT = rpg.RIGHT
	UP    = rpg.UP
	DOWN  = rpg.DOWN
)

var (
	ZV = pixel.ZV
	IM = pixel.IM
	V  = pixel.V
	R  = pixel.R
)

var (
	defaultzoom  = 1.0
	camZoomSpeed = 1.20
)

func run() {
	f, err := os.Create("p.debug")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	rand.Seed(time.Now().UnixNano())

	winbounds := pixel.R(0, 0, 800, 600)

	fmt.Println("which screen resolution?")
	fmt.Println("1. 800x600")
	fmt.Println("2. 1024x768")
	fmt.Println("3. 1280x720")
	var screenres int
	_, err = fmt.Scanf("%d", &screenres)
	if err != nil {
		screenres = 0
	}
	switch screenres {
	default:
	case 2:
		winbounds = pixel.R(0, 0, 1024, 768)
	case 3:
		winbounds = pixel.R(0, 0, 1280, 720)
	}
	// window options
	cfg := pixelgl.WindowConfig{
		Title:  "AERPG",
		Bounds: winbounds,
		//Undecorated: true,
		VSync: false,
	}

	// create window
	win, err := pixelgl.NewWindow(cfg)
	if err != nil {
		panic(err)
	}
	win.SetSmooth(false)
	buttons := []rpg.Button{
		{Name: "manastorm", Frame: pixel.R(10, 10, 42, 42)},
		{Name: "magicbullet", Frame: pixel.R(42, 10, 42+42, 42)},
	}
	// START
	//world.Char.Rect = world.Char.Rect.Moved(V(33, 33))
	// load world
	worldbounds := pixel.R(float64(-3000), float64(-3000), float64(3000), float64(3000))
	//	worldbounds = pixel.R(float64(-4000), float64(-4000), float64(4000), float64(4000))
	world := rpg.NewWorld(strconv.Itoa(*flaglevel), worldbounds, *flagcustomlvl)
	// sprite sheet
	spritesheet, spritemap := rpg.LoadSpriteSheet("tileset.png")

	// layers (TODO: slice?)
	// batch sprite drawing
	globebatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	animbatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)

	// water world 67 wood, 114 117 182 special, 121 135 dirt, 128 blank, 20 grass
	//	rpg.DrawPattern(batch, spritemap[53], pixel.R(-3000, -3000, 3000, 3000), 100)

	// draw menu bar
	menubatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	rpg.DrawPattern(menubatch, spritemap[67], pixel.R(0, 0, win.Bounds().W()+20, 60), 100)
	for _, btn := range buttons {
		spritemap[200].Draw(menubatch, IM.Moved(btn.Frame.Center()))
	}

	redrawWorld := func(w *rpg.World) {
		globebatch.Clear()
		// draw it on to canvasglobe
		for _, v := range w.Tiles {
			v.Draw(globebatch, spritesheet, spritemap)
		}
		for _, v := range w.Blocks {
			v.Draw(globebatch, spritesheet, spritemap)
		}

		if *debug {
			world.HighlightPaths(globebatch)
		}
	}

	// create NPC

	world.NewMobs(*flagenemies)
	l := time.Now()
	var last = &l
	second := time.Tick(time.Second)
	tick := time.Tick(time.Second)

	frames := 0
	var delda float64 = 0.00
	var camZoom = &defaultzoom
	var dt *float64 = &delda
	t1 := time.Now()
	//text := rpg.NewText(36)
	// start loop
	imd := imdraw.New(nil)
	rand.Seed(time.Now().UnixNano())
	//var latest string
	redrawWorld(world)
	for !win.Closed() {
		rpg.TitleMenu(world, win)
		world.Reset()
		world.Char.Health = 255
		for !win.Closed() {

			if world.Char.Health < 1 {
				log.Println("GAME OVER")
				break
			}
			*dt = time.Since(*last).Seconds()
			*last = time.Now()
			// zoom with mouse scroll

			*camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)

			// drawing
			//win.Clear(rpg.RandomColor())
			win.Clear(colornames.Black)
			animbatch.Clear()
			// if key
			if win.JustPressed(pixelgl.KeyQ) {
				break
			}
			if win.JustReleased(pixelgl.KeyI) {
				rpg.InventoryLoop(win, world)
			}

			if win.JustPressed(pixelgl.KeyEqual) {
				*debug = !*debug
			}

			dir := controlswitch(dt, world, win, buttons, win)
			world.Char.Update(*dt, dir, world)
			world.Update(*dt)

			world.Char.Matrix = pixel.IM.Scaled(pixel.ZV, *camZoom).Moved(win.Bounds().Center())
			cam := world.Char.Matrix.Moved(world.Char.Rect.Center().Scaled(-*camZoom))
			win.SetMatrix(cam)

			// draw map (tiles and blocks) (never updated for now)
			globebatch.Draw(win)

			// draw entities and objects (not tiles and blocks)
			world.Draw(win)

			// animations such as magic spells
			imd.Clear()
			world.CleanAnimations()
			world.ShowAnimations(imd)
			imd.Draw(win)

			// back to window cam
			win.SetMatrix(pixel.IM)
			world.Char.Draw(win)

			// draw score board
			//text.Clear()
			//rpg.DrawScore(winbounds, text, win,
			//	"[%vHP·%vMP·%sGP LVL%v %v/%vXP %vKills] %s", world.Char.Health, world.Char.Mana, world.Char.CountGold(), world.Char.Level, world.Char.Stats.XP, world.Char.NextLevel(), world.Char.Stats.Kills, latest)

			// draw menubar
			menubatch.Draw(win)
			if win.JustPressed(pixelgl.Key6) {
				redrawWorld(world)
			}

			// draw health, mana, xp bars
			world.Char.DrawBars(win, win.Bounds())

			mouseloc := win.MousePosition()
			if win.JustPressed(pixelgl.MouseButtonLeft) {

				b, f, ok := world.IsButton(buttons, mouseloc)
				if ok {
					if *debug {
						log.Printf("Clicked button: %q", b.Name)
					}
					f(win, world)
				} else {
					tile := world.Tile(cam.Unproject(mouseloc))
					log.Println(tile)
				}

			}
			select {
			default: //
			case <-tick:
			}

			//spritemap[20].Draw(menubar, pixel.IM.Scaled(ZV, 10).Moved(pixel.V(30, 30)))
			//menubar.Draw(win, pixel.IM)
			win.Update()
			world.Clean()

			// fps, gps
			frames++
			gps := world.Char.Rect.Center()
			select {
			default: //keep going
			case <-second:
				//redrawWorld(world)
				//latest = ""
				str := fmt.Sprintf(""+
					"FPS: %d | GPS: (%v,%v) | VEL: (%v) | HP: (%v) ",
					frames, int(gps.X), int(gps.Y), int(world.Char.Phys.Vel.Len()), world.Char.Health)
				win.SetTitle(str)
				frames = 0
			}

		}
		log.Printf("You survived for %s.\nYou acquired %s gold", time.Now().Sub(t1), world.Char.CountGold())
		log.Println("Inventory:", rpg.FormatItemList(world.Char.Inventory))
		log.Printf("Skeletons killed: %v", world.Char.Stats.Kills)
	}

}

func controlswitch(dt *float64, w *rpg.World, win *pixelgl.Window, buttons []rpg.Button, buf pixel.Target) rpg.Direction {
	if win.JustPressed(pixelgl.KeySpace) {
		if w.Char.Mana > 0 {
			w.Action(w.Char, w.Char.Rect.Center(), rpg.ManaStorm)
		} else {
			log.Println("Not enough mana")
		}
	}

	// slow motion with tab
	if win.Pressed(pixelgl.KeyTab) {
		*dt /= 8
	}
	// speed motion with tab
	if win.Pressed(pixelgl.KeyLeftShift) {
		*dt *= 15
	}
	if win.Pressed(pixelgl.Key1) {
		w.Char.Mana += 1
		if w.Char.Mana > 255 {
			w.Char.Mana = 255
		}
	}
	if win.Pressed(pixelgl.KeyCapsLock) {
		w.Char.Phys.CanFly = !w.Char.Phys.CanFly
	}
	dir := w.Char.Dir

	// disable momentum
	if win.JustPressed(pixelgl.KeyS) {
		w.Char.Phys.Vel = pixel.ZV
	}

	if win.JustPressed(pixelgl.MouseButtonLeft) {
		mouseloc := win.MousePosition()
		log.Println(mouseloc)
		if b, f, ok := w.IsButton(buttons, mouseloc); ok {
			log.Printf("Clicked button: %q", b.Name)
			f(win, w)

		}
	}

	if win.Pressed(pixelgl.KeyLeft) || win.Pressed(pixelgl.KeyH) || win.Pressed(pixelgl.KeyA) {
		dir = LEFT
		w.Char.Phys.Vel.X = -w.Char.Phys.RunSpeed * (1 + *dt)
	}
	if win.Pressed(pixelgl.KeyRight) || win.Pressed(pixelgl.KeyL) || win.Pressed(pixelgl.KeyD) {
		w.Char.Phys.Vel.X = +w.Char.Phys.RunSpeed * (1 + *dt)
		dir = RIGHT
	}
	if win.Pressed(pixelgl.KeyDown) || win.Pressed(pixelgl.KeyJ) || win.Pressed(pixelgl.KeyS) {
		w.Char.Phys.Vel.Y = -w.Char.Phys.RunSpeed * (1 + *dt)
		dir = DOWN

	}
	if win.Pressed(pixelgl.KeyUp) || win.Pressed(pixelgl.KeyK) || win.Pressed(pixelgl.KeyW) {

		w.Char.Phys.Vel.Y = +w.Char.Phys.RunSpeed * (1 + *dt)
		dir = UP
	}

	// restart the level on pressing enter
	//	if win.JustPressed(pixelgl.KeyEnter) {
	//		rpg.InventoryLoop(win, w)
	//	}
	return dir
}
func main() {
	flag.Parse()
	pixelgl.Run(run)
}
<commit_msg>show version on boot<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"math/rand"
	"os"
	"runtime/pprof"
	"strconv"
	"time"

	"golang.org/x/image/colornames"

	//	_ "image/png"

	"github.com/aerth/rpg"
	"github.com/faiface/pixel"
	"github.com/faiface/pixel/imdraw"
	"github.com/faiface/pixel/pixelgl"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

var (
	flagenemies   = flag.Int("e", 2, "number of enemies to begin with")
	flaglevel     = flag.Int("lvl", 1, "starting level (1)")
	flagcustomlvl = flag.String("test", "", "custom world test (filename)")
	debug         = flag.Bool("v", false, "extra logs")
)

const (
	LEFT  = rpg.LEFT
	RIGHT = rpg.RIGHT
	UP    = rpg.UP
	DOWN  = rpg.DOWN
)

var (
	ZV = pixel.ZV
	IM = pixel.IM
	V  = pixel.V
	R  = pixel.R
)

var (
	defaultzoom  = 1.0
	camZoomSpeed = 1.20
)

func run() {
	f, err := os.Create("p.debug")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	rand.Seed(time.Now().UnixNano())

	winbounds := pixel.R(0, 0, 800, 600)

	fmt.Println("which screen resolution?")
	fmt.Println("1. 800x600")
	fmt.Println("2. 1024x768")
	fmt.Println("3. 1280x720")
	var screenres int
	_, err = fmt.Scanf("%d", &screenres)
	if err != nil {
		screenres = 0
	}
	switch screenres {
	default:
	case 2:
		winbounds = pixel.R(0, 0, 1024, 768)
	case 3:
		winbounds = pixel.R(0, 0, 1280, 720)
	}
	// window options
	cfg := pixelgl.WindowConfig{
		Title:  rpg.Version(),
		Bounds: winbounds,
		//Undecorated: true,
		VSync: false,
	}

	// create window
	win, err := pixelgl.NewWindow(cfg)
	if err != nil {
		panic(err)
	}
	win.SetSmooth(false)
	buttons := []rpg.Button{
		{Name: "manastorm", Frame: pixel.R(10, 10, 42, 42)},
		{Name: "magicbullet", Frame: pixel.R(42, 10, 42+42, 42)},
	}
	// START
	//world.Char.Rect = world.Char.Rect.Moved(V(33, 33))
	// load world
	worldbounds := pixel.R(float64(-3000), float64(-3000), float64(3000), float64(3000))
	//	worldbounds = pixel.R(float64(-4000), float64(-4000), float64(4000), float64(4000))
	world := rpg.NewWorld(strconv.Itoa(*flaglevel), worldbounds, *flagcustomlvl)
	// sprite sheet
	spritesheet, spritemap := rpg.LoadSpriteSheet("tileset.png")

	// layers (TODO: slice?)
	// batch sprite drawing
	globebatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	animbatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)

	// water world 67 wood, 114 117 182 special, 121 135 dirt, 128 blank, 20 grass
	//	rpg.DrawPattern(batch, spritemap[53], pixel.R(-3000, -3000, 3000, 3000), 100)

	// draw menu bar
	menubatch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
	rpg.DrawPattern(menubatch, spritemap[67], pixel.R(0, 0, win.Bounds().W()+20, 60), 100)
	for _, btn := range buttons {
		spritemap[200].Draw(menubatch, IM.Moved(btn.Frame.Center()))
	}

	redrawWorld := func(w *rpg.World) {
		globebatch.Clear()
		// draw it on to canvasglobe
		for _, v := range w.Tiles {
			v.Draw(globebatch, spritesheet, spritemap)
		}
		for _, v := range w.Blocks {
			v.Draw(globebatch, spritesheet, spritemap)
		}

	}

	// create NPC

	world.NewMobs(*flagenemies)
	l := time.Now()
	var last = &l
	second := time.Tick(time.Second)
	tick := time.Tick(time.Second)

	frames := 0
	var delda float64 = 0.00
	var camZoom = &defaultzoom
	var dt *float64 = &delda
	t1 := time.Now()
	//text := rpg.NewText(36)
	// start loop
	imd := imdraw.New(nil)
	rand.Seed(time.Now().UnixNano())
	//var latest string
	redrawWorld(world)
	for !win.Closed() {
		rpg.TitleMenu(world, win)
		world.Reset()
		world.Char.Health = 255
		for !win.Closed() {

			if world.Char.Health < 1 {
				log.Println("GAME OVER")
				break
			}
			*dt = time.Since(*last).Seconds()
			*last = time.Now()
			// zoom with mouse scroll

			*camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)

			// drawing
			//win.Clear(rpg.RandomColor())
			win.Clear(colornames.Black)
			animbatch.Clear()
			// if key
			if win.JustPressed(pixelgl.KeyQ) {
				break
			}
			if win.JustReleased(pixelgl.KeyI) {
				rpg.InventoryLoop(win, world)
			}

			if win.JustPressed(pixelgl.KeyEqual) {
				*debug = !*debug
			}

			dir := controlswitch(dt, world, win, buttons, win)
			world.Char.Update(*dt, dir, world)
			world.Update(*dt)
			world.Clean()

			world.Char.Matrix = pixel.IM.Scaled(pixel.ZV, *camZoom).Moved(win.Bounds().Center())
			cam := world.Char.Matrix.Moved(world.Char.Rect.Center().Scaled(-*camZoom))
			win.SetMatrix(cam)

			// draw map (tiles and blocks) (never updated for now)
			globebatch.Draw(win)

			if *debug {
				world.HighlightPaths(win)
			}
			// draw entities and objects (not tiles and blocks)
			world.Draw(win)

			// animations such as magic spells
			imd.Clear()
			world.CleanAnimations()
			world.ShowAnimations(imd)
			imd.Draw(win)

			// back to window cam
			win.SetMatrix(pixel.IM)
			world.Char.Draw(win)

			// draw score board
			//text.Clear()
			//rpg.DrawScore(winbounds, text, win,
			//	"[%vHP·%vMP·%sGP LVL%v %v/%vXP %vKills] %s", world.Char.Health, world.Char.Mana, world.Char.CountGold(), world.Char.Level, world.Char.Stats.XP, world.Char.NextLevel(), world.Char.Stats.Kills, latest)

			// draw menubar
			menubatch.Draw(win)
			if win.JustPressed(pixelgl.Key6) {
				redrawWorld(world)
			}

			// draw health, mana, xp bars
			world.Char.DrawBars(win, win.Bounds())

			mouseloc := win.MousePosition()
			if win.JustPressed(pixelgl.MouseButtonLeft) {

				b, f, ok := world.IsButton(buttons, mouseloc)
				if ok {
					if *debug {
						log.Printf("Clicked button: %q", b.Name)
					}
					f(win, world)
				} else {
					tile := world.Tile(cam.Unproject(mouseloc))
					log.Println(tile)
				}

			}
			select {
			default: //
			case <-tick:
			}

			//spritemap[20].Draw(menubar, pixel.IM.Scaled(ZV, 10).Moved(pixel.V(30, 30)))
			//menubar.Draw(win, pixel.IM)
			win.Update()

			// fps, gps
			frames++
			gps := world.Char.Rect.Center()
			select {
			default: //keep going
			case <-second:
				//redrawWorld(world)
				//latest = ""
				str := fmt.Sprintf(""+
					"FPS: %d | GPS: (%v,%v) | VEL: (%v) | HP: (%v) ",
					frames, int(gps.X), int(gps.Y), int(world.Char.Phys.Vel.Len()), world.Char.Health)
				win.SetTitle(str)
				frames = 0
			}

		}
		log.Printf("You survived for %s.\nYou acquired %s gold", time.Now().Sub(t1), world.Char.CountGold())
		log.Println("Inventory:", rpg.FormatItemList(world.Char.Inventory))
		log.Printf("Skeletons killed: %v", world.Char.Stats.Kills)
	}

}

func controlswitch(dt *float64, w *rpg.World, win *pixelgl.Window, buttons []rpg.Button, buf pixel.Target) rpg.Direction {
	if win.JustPressed(pixelgl.KeySpace) {
		if w.Char.Mana > 0 {
			w.Action(w.Char, w.Char.Rect.Center(), rpg.ManaStorm)
		} else {
			log.Println("Not enough mana")
		}
	}

	// slow motion with tab
	if win.Pressed(pixelgl.KeyTab) {
		*dt /= 8
	}
	// speed motion with tab
	if win.Pressed(pixelgl.KeyLeftShift) {
		*dt *= 15
	}
	if win.Pressed(pixelgl.Key1) {
		w.Char.Mana += 1
		if w.Char.Mana > 255 {
			w.Char.Mana = 255
		}
	}
	if win.Pressed(pixelgl.KeyCapsLock) {
		w.Char.Phys.CanFly = !w.Char.Phys.CanFly
	}
	dir := w.Char.Dir

	// disable momentum
	if win.JustPressed(pixelgl.KeyS) {
		w.Char.Phys.Vel = pixel.ZV
	}

	if win.JustPressed(pixelgl.MouseButtonLeft) {
		mouseloc := win.MousePosition()
		log.Println(mouseloc)
		if b, f, ok := w.IsButton(buttons, mouseloc); ok {
			log.Printf("Clicked button: %q", b.Name)
			f(win, w)

		}
	}

	if win.Pressed(pixelgl.KeyLeft) || win.Pressed(pixelgl.KeyH) || win.Pressed(pixelgl.KeyA) {
		dir = LEFT
		w.Char.Phys.Vel.X = -w.Char.Phys.RunSpeed * (1 + *dt)
	}
	if win.Pressed(pixelgl.KeyRight) || win.Pressed(pixelgl.KeyL) || win.Pressed(pixelgl.KeyD) {
		w.Char.Phys.Vel.X = +w.Char.Phys.RunSpeed * (1 + *dt)
		dir = RIGHT
	}
	if win.Pressed(pixelgl.KeyDown) || win.Pressed(pixelgl.KeyJ) || win.Pressed(pixelgl.KeyS) {
		w.Char.Phys.Vel.Y = -w.Char.Phys.RunSpeed * (1 + *dt)
		dir = DOWN

	}
	if win.Pressed(pixelgl.KeyUp) || win.Pressed(pixelgl.KeyK) || win.Pressed(pixelgl.KeyW) {

		w.Char.Phys.Vel.Y = +w.Char.Phys.RunSpeed * (1 + *dt)
		dir = UP
	}

	// restart the level on pressing enter
	//	if win.JustPressed(pixelgl.KeyEnter) {
	//		rpg.InventoryLoop(win, w)
	//	}
	return dir
}
func main() {
	flag.Parse()
	pixelgl.Run(run)
}
<|endoftext|>


<commit_before>package task

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/spf13/pflag"
	"github.com/taskcluster/slugid-go/slugid"
	tcclient "github.com/taskcluster/taskcluster-client-go"
	"github.com/taskcluster/taskcluster-client-go/queue"
)

// runCancel cancels the runs of a given task.
func runCancel(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	if noop {
		displayNoopMsg("Would cancel", credentials, args)
		return nil
	}

	if confirm {
		confirmMsg("Cancels", credentials, args)
		if true {
			c, err := q.CancelTask(taskID)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return fmt.Errorf("could not cancel the task %s: %v", taskID, err)
			}
			run := c.Status.Runs[len(c.Status.Runs)-1]

			fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
			return nil
		}
		return nil
	}

	c, err := q.CancelTask(taskID)
	run := c.Status.Runs[len(c.Status.Runs)-1]

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return fmt.Errorf("could not cancel the task %s: %v", taskID, err)
	}

	fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
	return nil
}

// runRerun re-runs a given task.
func runRerun(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	if noop {
		displayNoopMsg("Would re-run", credentials, args)
		return nil
	}

	if confirm {
		confirmMsg("Will re-run", credentials, args)
		if true {
			c, err := q.RerunTask(taskID)
			run := c.Status.Runs[len(c.Status.Runs)-1]
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return fmt.Errorf("could not rerun the task %s: %v", taskID, err)
			}
			fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
			return nil

		}
	}

	c, err := q.RerunTask(taskID)

	run := c.Status.Runs[len(c.Status.Runs)-1]

	if err != nil {
		return fmt.Errorf("could not rerun the task %s: %v", taskID, err)

	}

	fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
	return nil

}

// runRetrigger re-triggers a given task.
// It will generate a new taskId, update timestamps and retries to 0
// Optionnally, you can pass '--exact' to keep stuff like:
//  - routes,
//  - dependencies,
//  - ...
//
// Otherwise, default behavior is to omit those as taskcluster-tools does:
// https://github.com/taskcluster/taskcluster-tools/blob/e8b6d45f10e7520f717b7a9f5db87d550c74d15e/src/views/UnifiedInspector/ActionsMenu.jsx#L141-L158
func runRetrigger(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	q := makeQueue(credentials)
	taskID := args[0]

	t, err := q.Task(taskID)
	if err != nil {
		return fmt.Errorf("could not get the task %s: %v", taskID, err)
	}

	exactRetrigger, _ := flagSet.GetBool("exact")

	newTaskID := slugid.Nice()
	now := time.Now().UTC()

	origCreated, err := time.Parse(time.RFC3339, t.Created.String())
	if err != nil {
		return fmt.Errorf("could not parse created date: %s", t.Created)
	}

	origDeadline, err := time.Parse(time.RFC3339, t.Deadline.String())
	if err != nil {
		return fmt.Errorf("could not parse deadline date: %s", t.Deadline)
	}

	origExpires, err := time.Parse(time.RFC3339, t.Expires.String())
	if err != nil {
		return fmt.Errorf("could not parse created date: %s", t.Expires)
	}

	// TaskDefinitionRequest: https://github.com/taskcluster/taskcluster-client-go/blob/88cfe471bfe2eb8fc9bc22d9cde6a65e74a9f3e5/tcqueue/types.go#L1368-L1549
	// TaskDefinitionResponse: https://github.com/taskcluster/taskcluster-client-go/blob/88cfe471bfe2eb8fc9bc22d9cde6a65e74a9f3e5/tcqueue/types.go#L1554-L1716

	newDependencies := []string{}
	newRoutes       := []string{}
	if exactRetrigger {
		newDependencies = t.Dependencies
		newRoutes       = t.Routes
	}

	newT := &queue.TaskDefinitionRequest{
		Created:       tcclient.Time(now),
		Deadline:      tcclient.Time(now.Add(origDeadline.Sub(origCreated))),
		Expires:       tcclient.Time(now.Add(origExpires.Sub(origCreated))),
		TaskGroupID:   t.TaskGroupID,
		SchedulerID:   t.SchedulerID,
		WorkerType:    t.WorkerType,
		ProvisionerID: t.ProvisionerID,
		Priority:      t.Priority,
		Dependencies:  newDependencies,
		Extra:         t.Extra,
		Metadata:      t.Metadata,
		Payload:       t.Payload,
		Requires:      t.Requires,
		Retries:       0,
		Routes:        newRoutes,
		Scopes:        t.Scopes,
		Tags:          t.Tags,
	}

	c, err := q.CreateTask(newTaskID, newT)
	if err != nil {
		return fmt.Errorf("could not create task: %v", err)
	}

	// If we got no error, that means the task was successfully submitted
	fmt.Fprintf(out, "Task %s created\n", c.Status.TaskID)
	return nil
}

// runComplete completes a given task.
func runComplete(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	s, err := q.Status(taskID)
	if err != nil {
		return fmt.Errorf("could not get the status of the task %s: %v", taskID, err)
	}

	if noop {
		displayNoopMsg("Would complete", credentials, args)
		return nil
	}

	if confirm {
		confirmMsg("Will complete", credentials, args)
		if true {
			c, err := q.ClaimTask(taskID, fmt.Sprint(len(s.Status.Runs)-1), &queue.TaskClaimRequest{
				WorkerGroup: s.Status.WorkerType,
				WorkerID:    "taskcluster-cli",
			})
			if err != nil {
				return fmt.Errorf("could not claim the task %s: %v", taskID, err)
			}

			wq := makeQueue(&tcclient.Credentials{
				ClientID:    c.Credentials.ClientID,
				AccessToken: c.Credentials.AccessToken,
				Certificate: c.Credentials.Certificate,
			})
			r, err := wq.ReportCompleted(taskID, fmt.Sprint(c.RunID))
			if err != nil {
				return fmt.Errorf("could not complete the task %s: %v", taskID, err)
			}
			fmt.Fprintln(out, getRunStatusString(r.Status.Runs[c.RunID].State, r.Status.Runs[c.RunID].ReasonResolved))
			return nil

		}
	}

	c, err := q.ClaimTask(taskID, fmt.Sprint(len(s.Status.Runs)-1), &queue.TaskClaimRequest{
		WorkerGroup: s.Status.WorkerType,
		WorkerID:    "taskcluster-cli",
	})
	if err != nil {
		return fmt.Errorf("could not claim the task %s: %v", taskID, err)
	}

	wq := makeQueue(&tcclient.Credentials{
		ClientID:    c.Credentials.ClientID,
		AccessToken: c.Credentials.AccessToken,
		Certificate: c.Credentials.Certificate,
	})
	r, err := wq.ReportCompleted(taskID, fmt.Sprint(c.RunID))
	if err != nil {
		return fmt.Errorf("could not complete the task %s: %v", taskID, err)
	}

	fmt.Fprintln(out, getRunStatusString(r.Status.Runs[c.RunID].State, r.Status.Runs[c.RunID].ReasonResolved))
	return nil
}
<commit_msg>Make required modifications<commit_after>package task

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/spf13/pflag"
	"github.com/taskcluster/slugid-go/slugid"
	tcclient "github.com/taskcluster/taskcluster-client-go"
	"github.com/taskcluster/taskcluster-client-go/queue"
)

// runCancel cancels the runs of a given task.
func runCancel(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	if noop {
		displayNoopMsg("Would cancel", credentials, args)
		return nil
	}

	if confirm {
		var confirm = confirmMsg("Cancels", credentials, args)
		if ! confirm {
			return nil
		}

	}

	c, err := q.CancelTask(taskID)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return fmt.Errorf("could not cancel the task %s: %v", taskID, err)
	}

	run := c.Status.Runs[len(c.Status.Runs)-1]

	fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
	return nil
}

// runRerun re-runs a given task.
func runRerun(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	if noop {
		displayNoopMsg("Would re-run", credentials, args)
		return nil
	}

	if confirm {
		var confirm = confirmMsg("Will re-run", credentials, args)
		if ! confirm {
			return nil

		}
	}

	c, err := q.RerunTask(taskID)
	if err != nil {
		return fmt.Errorf("could not rerun the task %s: %v", taskID, err)

	}
	run := c.Status.Runs[len(c.Status.Runs)-1]

	fmt.Fprintln(out, getRunStatusString(run.State, run.ReasonResolved))
	return nil

}

// runRetrigger re-triggers a given task.
// It will generate a new taskId, update timestamps and retries to 0
// Optionnally, you can pass '--exact' to keep stuff like:
//  - routes,
//  - dependencies,
//  - ...
//
// Otherwise, default behavior is to omit those as taskcluster-tools does:
// https://github.com/taskcluster/taskcluster-tools/blob/e8b6d45f10e7520f717b7a9f5db87d550c74d15e/src/views/UnifiedInspector/ActionsMenu.jsx#L141-L158
func runRetrigger(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	q := makeQueue(credentials)
	taskID := args[0]

	t, err := q.Task(taskID)
	if err != nil {
		return fmt.Errorf("could not get the task %s: %v", taskID, err)
	}

	exactRetrigger, _ := flagSet.GetBool("exact")

	newTaskID := slugid.Nice()
	now := time.Now().UTC()

	origCreated, err := time.Parse(time.RFC3339, t.Created.String())
	if err != nil {
		return fmt.Errorf("could not parse created date: %s", t.Created)
	}

	origDeadline, err := time.Parse(time.RFC3339, t.Deadline.String())
	if err != nil {
		return fmt.Errorf("could not parse deadline date: %s", t.Deadline)
	}

	origExpires, err := time.Parse(time.RFC3339, t.Expires.String())
	if err != nil {
		return fmt.Errorf("could not parse created date: %s", t.Expires)
	}

	// TaskDefinitionRequest: https://github.com/taskcluster/taskcluster-client-go/blob/88cfe471bfe2eb8fc9bc22d9cde6a65e74a9f3e5/tcqueue/types.go#L1368-L1549
	// TaskDefinitionResponse: https://github.com/taskcluster/taskcluster-client-go/blob/88cfe471bfe2eb8fc9bc22d9cde6a65e74a9f3e5/tcqueue/types.go#L1554-L1716

	newDependencies := []string{}
	newRoutes       := []string{}
	if exactRetrigger {
		newDependencies = t.Dependencies
		newRoutes       = t.Routes
	}

	newT := &queue.TaskDefinitionRequest{
		Created:       tcclient.Time(now),
		Deadline:      tcclient.Time(now.Add(origDeadline.Sub(origCreated))),
		Expires:       tcclient.Time(now.Add(origExpires.Sub(origCreated))),
		TaskGroupID:   t.TaskGroupID,
		SchedulerID:   t.SchedulerID,
		WorkerType:    t.WorkerType,
		ProvisionerID: t.ProvisionerID,
		Priority:      t.Priority,
		Dependencies:  newDependencies,
		Extra:         t.Extra,
		Metadata:      t.Metadata,
		Payload:       t.Payload,
		Requires:      t.Requires,
		Retries:       0,
		Routes:        newRoutes,
		Scopes:        t.Scopes,
		Tags:          t.Tags,
	}

	c, err := q.CreateTask(newTaskID, newT)
	if err != nil {
		return fmt.Errorf("could not create task: %v", err)
	}

	// If we got no error, that means the task was successfully submitted
	fmt.Fprintf(out, "Task %s created\n", c.Status.TaskID)
	return nil
}

// runComplete completes a given task.
func runComplete(credentials *tcclient.Credentials, args []string, out io.Writer, flagSet *pflag.FlagSet) error {
	noop, _ := flagSet.GetBool("noop")
	confirm, _ := flagSet.GetBool("confirm")

	q := makeQueue(credentials)
	taskID := args[0]

	s, err := q.Status(taskID)
	if err != nil {
		return fmt.Errorf("could not get the status of the task %s: %v", taskID, err)
	}

	if noop {
		displayNoopMsg("Would complete", credentials, args)
		return nil
	}

	if confirm {
		var confirm = confirmMsg("Will complete", credentials, args)
		if ! confirm {
			return nil
		}
	}

	c, err := q.ClaimTask(taskID, fmt.Sprint(len(s.Status.Runs)-1), &queue.TaskClaimRequest{
		WorkerGroup: s.Status.WorkerType,
		WorkerID:    "taskcluster-cli",
	})
	if err != nil {
		return fmt.Errorf("could not claim the task %s: %v", taskID, err)
	}

	wq := makeQueue(&tcclient.Credentials{
		ClientID:    c.Credentials.ClientID,
		AccessToken: c.Credentials.AccessToken,
		Certificate: c.Credentials.Certificate,
	})
	r, err := wq.ReportCompleted(taskID, fmt.Sprint(c.RunID))
	if err != nil {
		return fmt.Errorf("could not complete the task %s: %v", taskID, err)
	}

	fmt.Fprintln(out, getRunStatusString(r.Status.Runs[c.RunID].State, r.Status.Runs[c.RunID].ReasonResolved))
	return nil
}
<|endoftext|>


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

package libkbfs

import (
	"sync"

	"github.com/keybase/client/go/logger"
	"github.com/keybase/kbfs/kbfssync"
	"github.com/keybase/kbfs/tlf"

	"golang.org/x/net/context"
)

const (
	numRekeyWorkers = 64
	rekeyQueueSize  = 1024 // 24 KB
)

// CtxRekeyTagKey is the type used for unique context tags within an
// enqueued Rekey.
type CtxRekeyTagKey int

const (
	// CtxRekeyIDKey is the type of the tag for unique operation IDs
	// within an enqueued Rekey.
	CtxRekeyIDKey CtxRekeyTagKey = iota
)

// CtxRekeyOpID is the display name for the unique operation
// enqueued rekey ID tag.
const CtxRekeyOpID = "REKEYID"

type rekeyQueueEntry struct {
	id tlf.ID
	ch chan error
}

// RekeyQueueStandard implements the RekeyQueue interface.
type RekeyQueueStandard struct {
	config Config
	log    logger.Logger
	queue  chan rekeyQueueEntry

	wg kbfssync.RepeatedWaitGroup

	lock sync.RWMutex // protects all of the below
	// pendings tracks TLFs that are in the rekey queue, but doesn't include
	// those that a worker has already picked up and begun working on.
	pendings map[tlf.ID]<-chan error
	// cancel, if non-nil, is for all spawned workers. If nil, no workers should
	// be running. Calling cancel would cause all workers to stop.
	cancel context.CancelFunc
}

// Test that RekeyQueueStandard fully implements the RekeyQueue interface.
var _ RekeyQueue = (*RekeyQueueStandard)(nil)

// NewRekeyQueueStandard instantiates a new rekey worker.
func NewRekeyQueueStandard(config Config) *RekeyQueueStandard {
	log := config.MakeLogger("RQ")
	rkq := &RekeyQueueStandard{
		config:   config,
		log:      log,
		queue:    make(chan rekeyQueueEntry, rekeyQueueSize),
		pendings: make(map[tlf.ID]<-chan error),
	}
	return rkq
}

// workingOn removes entry from pendings, so that if fbo.Rekey() needs to Enqueue
// again, it wouldn't be ignored.
func (rkq *RekeyQueueStandard) workingOn(entry rekeyQueueEntry) {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	delete(rkq.pendings, entry.id)
}

func (rkq *RekeyQueueStandard) doneLocked(entry rekeyQueueEntry, err error) {
	entry.ch <- err
	close(entry.ch)
	delete(rkq.pendings, entry.id)
	rkq.wg.Done()
}

func (rkq *RekeyQueueStandard) done(entry rekeyQueueEntry, err error) {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	rkq.doneLocked(entry, err)
}

func (rkq *RekeyQueueStandard) work(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case entry := <-rkq.queue:
			func() {
				var err error
				defer rkq.done(entry, err) // deferred in case a panic happens below
				rkq.workingOn(entry)
				newCtx := ctxWithRandomIDReplayable(ctx, CtxRekeyIDKey,
					CtxRekeyOpID, nil)
				rkq.log.CDebugf(newCtx, "Processing rekey for %s", entry.id)
				err = rkq.config.KBFSOps().Rekey(newCtx, entry.id)
			}()
		}
	}
}

func (rkq *RekeyQueueStandard) ensureRunningLocked() {
	if rkq.cancel != nil {
		return
	}
	var ctx context.Context
	ctx, rkq.cancel = context.WithCancel(context.Background())
	for i := 0; i < numRekeyWorkers; i++ {
		go rkq.work(ctx)
	}
}

func (rkq *RekeyQueueStandard) getRekeyChannelRLocked(id tlf.ID) <-chan error {
	if ch, ok := rkq.pendings[id]; ok {
		return ch
	}
	return nil
}

func (rkq *RekeyQueueStandard) getRekeyChannel(id tlf.ID) <-chan error {
	rkq.lock.RLock()
	defer rkq.lock.RUnlock()
	return rkq.getRekeyChannelRLocked(id)
}

// Enqueue implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Enqueue(id tlf.ID) <-chan error {
	rkq.log.Debug("Enqueueing %s for rekey", id)

	if ch := rkq.getRekeyChannel(id); ch != nil {
		return ch
	}

	rkq.lock.Lock()
	defer rkq.lock.Unlock()

	// Now we are locked, check again in case another one slips in.
	if ch := rkq.getRekeyChannelRLocked(id); ch != nil {
		return ch
	}

	rkq.ensureRunningLocked()

	rkq.wg.Add(1)

	ch := make(chan error, 1)
	rkq.pendings[id] = ch

	select {
	case rkq.queue <- rekeyQueueEntry{id: id, ch: ch}:
	default:
		// The queue is full; avoid blocking by spawning a goroutine.
		rkq.log.Debug("Rekey queue is full; enqueuing %s in the background", id)
		go func() { rkq.queue <- rekeyQueueEntry{id: id, ch: ch} }()
	}

	return ch
}

// IsRekeyPending implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) IsRekeyPending(id tlf.ID) bool {
	rkq.lock.RLock()
	defer rkq.lock.RUnlock()
	_, ok := rkq.pendings[id]
	return ok
}

// Clear implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Clear() {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	if rkq.cancel == nil {
		return
	}

	rkq.cancel()
	for more := true; more; { // drain rkq.queue
		select {
		case e := <-rkq.queue:
			rkq.doneLocked(e, context.Canceled)
		default:
			more = false
		}
	}
	rkq.cancel = nil
}

// Wait implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Wait(ctx context.Context) error {
	return rkq.wg.Wait(ctx)
}
<commit_msg>add comment explaining the rekey process (#759)<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"sync"

	"github.com/keybase/client/go/logger"
	"github.com/keybase/kbfs/kbfssync"
	"github.com/keybase/kbfs/tlf"

	"golang.org/x/net/context"
)

// When provisioning a new device from an existing device, the provisionee
// needs one of the existing devices to rekey for it, or it has to use paperkey
// for the rekey. For the case where an existing device does the rekey, there
// are three routines which eventually all go through this rekey queue. These
// three rekey routines are:
//
// 1. When a new device is added, the service on provisioner calls an RPC into
// KBFS, notifying the latter about the new device (provisionee) and that it
// needs rekey.
// 2. On KBFS client, a background routine runs once per hour. It asks the
// mdserver to check for TLFs that needs rekey. Note that this happens on all
// KBFS devices, no matter it has rekey capability or now.
//
// Both 1 and 2 do this by calling MDServerRemote.CheckForRekeys to send back a
// FoldersNeedRekey request.
//
// 3. When the provisionee gets provisioned, it goes through all TLFs and sends
// a MD update for each one of them, by merely copying (since it doesn't have
// access to the key yet) the existing MD revision while setting the rekey bit
// in the flag.

const (
	numRekeyWorkers = 64
	rekeyQueueSize  = 1024 // 24 KB
)

// CtxRekeyTagKey is the type used for unique context tags within an
// enqueued Rekey.
type CtxRekeyTagKey int

const (
	// CtxRekeyIDKey is the type of the tag for unique operation IDs
	// within an enqueued Rekey.
	CtxRekeyIDKey CtxRekeyTagKey = iota
)

// CtxRekeyOpID is the display name for the unique operation
// enqueued rekey ID tag.
const CtxRekeyOpID = "REKEYID"

type rekeyQueueEntry struct {
	id tlf.ID
	ch chan error
}

// RekeyQueueStandard implements the RekeyQueue interface.
type RekeyQueueStandard struct {
	config Config
	log    logger.Logger
	queue  chan rekeyQueueEntry

	wg kbfssync.RepeatedWaitGroup

	lock sync.RWMutex // protects all of the below
	// pendings tracks TLFs that are in the rekey queue, but doesn't include
	// those that a worker has already picked up and begun working on.
	pendings map[tlf.ID]<-chan error
	// cancel, if non-nil, is for all spawned workers. If nil, no workers should
	// be running. Calling cancel would cause all workers to stop.
	cancel context.CancelFunc
}

// Test that RekeyQueueStandard fully implements the RekeyQueue interface.
var _ RekeyQueue = (*RekeyQueueStandard)(nil)

// NewRekeyQueueStandard instantiates a new rekey worker.
func NewRekeyQueueStandard(config Config) *RekeyQueueStandard {
	log := config.MakeLogger("RQ")
	rkq := &RekeyQueueStandard{
		config:   config,
		log:      log,
		queue:    make(chan rekeyQueueEntry, rekeyQueueSize),
		pendings: make(map[tlf.ID]<-chan error),
	}
	return rkq
}

// workingOn removes entry from pendings, so that if fbo.Rekey() needs to Enqueue
// again, it wouldn't be ignored.
func (rkq *RekeyQueueStandard) workingOn(entry rekeyQueueEntry) {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	delete(rkq.pendings, entry.id)
}

func (rkq *RekeyQueueStandard) doneLocked(entry rekeyQueueEntry, err error) {
	entry.ch <- err
	close(entry.ch)
	delete(rkq.pendings, entry.id)
	rkq.wg.Done()
}

func (rkq *RekeyQueueStandard) done(entry rekeyQueueEntry, err error) {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	rkq.doneLocked(entry, err)
}

func (rkq *RekeyQueueStandard) work(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case entry := <-rkq.queue:
			func() {
				var err error
				defer rkq.done(entry, err) // deferred in case a panic happens below
				rkq.workingOn(entry)
				newCtx := ctxWithRandomIDReplayable(ctx, CtxRekeyIDKey,
					CtxRekeyOpID, nil)
				rkq.log.CDebugf(newCtx, "Processing rekey for %s", entry.id)
				err = rkq.config.KBFSOps().Rekey(newCtx, entry.id)
			}()
		}
	}
}

func (rkq *RekeyQueueStandard) ensureRunningLocked() {
	if rkq.cancel != nil {
		return
	}
	var ctx context.Context
	ctx, rkq.cancel = context.WithCancel(context.Background())
	for i := 0; i < numRekeyWorkers; i++ {
		go rkq.work(ctx)
	}
}

func (rkq *RekeyQueueStandard) getRekeyChannelRLocked(id tlf.ID) <-chan error {
	if ch, ok := rkq.pendings[id]; ok {
		return ch
	}
	return nil
}

func (rkq *RekeyQueueStandard) getRekeyChannel(id tlf.ID) <-chan error {
	rkq.lock.RLock()
	defer rkq.lock.RUnlock()
	return rkq.getRekeyChannelRLocked(id)
}

// Enqueue implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Enqueue(id tlf.ID) <-chan error {
	rkq.log.Debug("Enqueueing %s for rekey", id)

	if ch := rkq.getRekeyChannel(id); ch != nil {
		return ch
	}

	rkq.lock.Lock()
	defer rkq.lock.Unlock()

	// Now we are locked, check again in case another one slips in.
	if ch := rkq.getRekeyChannelRLocked(id); ch != nil {
		return ch
	}

	rkq.ensureRunningLocked()

	rkq.wg.Add(1)

	ch := make(chan error, 1)
	rkq.pendings[id] = ch

	select {
	case rkq.queue <- rekeyQueueEntry{id: id, ch: ch}:
	default:
		// The queue is full; avoid blocking by spawning a goroutine.
		rkq.log.Debug("Rekey queue is full; enqueuing %s in the background", id)
		go func() { rkq.queue <- rekeyQueueEntry{id: id, ch: ch} }()
	}

	return ch
}

// IsRekeyPending implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) IsRekeyPending(id tlf.ID) bool {
	rkq.lock.RLock()
	defer rkq.lock.RUnlock()
	_, ok := rkq.pendings[id]
	return ok
}

// Clear implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Clear() {
	rkq.lock.Lock()
	defer rkq.lock.Unlock()
	if rkq.cancel == nil {
		return
	}

	rkq.cancel()
	for more := true; more; { // drain rkq.queue
		select {
		case e := <-rkq.queue:
			rkq.doneLocked(e, context.Canceled)
		default:
			more = false
		}
	}
	rkq.cancel = nil
}

// Wait implements the RekeyQueue interface for RekeyQueueStandard.
func (rkq *RekeyQueueStandard) Wait(ctx context.Context) error {
	return rkq.wg.Wait(ctx)
}
<|endoftext|>