<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bgentry/speakeasy"
	"github.com/miquella/vaulted/vault"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"sort"
)

type CommandMode int

const (
	NoMode CommandMode = iota
	SpawnEnvironmentMode
	MutateEnvironmentMode
	ListEnvironmentsMode
)

var (
	// vault flags
	filename string

	// environment flags
	environment string
	commandMode CommandMode = NoMode

	listEnvironments    bool
	dumpJsonEnvironment bool

	interactiveAdd    bool
	deleteEnvironment bool
	interactiveShell  bool

	charDevice bool

	// vault
	vaultKey  []byte
	vaultData []byte
	v         vault.Vault
	envs      vault.Environments
)

func init() {
	environment = os.Getenv("VAULTED_ENV")

	u, err := user.Current()
	if err != nil {
		panic(err)
	}

	flag.StringVar(&filename, "f", filepath.Join(u.HomeDir, ".vaulted"), "vault filename")
	flag.StringVar(&environment, "n", environment, "name of the environment")
	flag.BoolVar(&listEnvironments, "L", false, "list environments in vault")
	flag.BoolVar(&dumpJsonEnvironment, "j", false, "dump json version of environment")
	flag.BoolVar(&interactiveAdd, "a", false, "add to environment interactively")
	flag.BoolVar(&deleteEnvironment, "D", false, "delete environment from vault")
	flag.BoolVar(&interactiveShell, "i", false, "spawn a new shell populated with the environment")
	flag.Parse()

	// figure out which mode the user wants
	spawnMode := interactiveShell || flag.NArg() > 0
	mutateMode := interactiveAdd || deleteEnvironment
	listMode := listEnvironments || dumpJsonEnvironment

	if spawnMode {
		if listMode || mutateMode {
			fmt.Fprint(os.Stderr, "ERROR: cannot list/dump or update environments while spawning\n")
			os.Exit(2)
		}

		if os.Getenv("SHELL") == "" {
			fmt.Fprint(os.Stderr, "ERROR: SHELL environment variable not set, cannot spawn shell")
			os.Exit(2)
		}

		commandMode = SpawnEnvironmentMode

	} else if mutateMode {
		if listMode {
			fmt.Fprintf(os.Stderr, "ERROR: cannot update environments while listing/dumping\n")
			os.Exit(2)
		}
		commandMode = MutateEnvironmentMode

	} else if listMode {
		commandMode = ListEnvironmentsMode
	}

	stat, _ := os.Stdin.Stat()
	charDevice = (stat.Mode() & os.ModeCharDevice) != 0
}

func main() {
	if commandMode == NoMode {
		fmt.Fprint(os.Stderr, "invalid command mode\n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	err := loadVault()
	if err != nil {
		println(err.Error())
		os.Exit(3)
	}

	switch commandMode {
	case ListEnvironmentsMode:
		listEnvironmentsMode()
	case SpawnEnvironmentMode:
		spawnEnvironmentMode()
	case MutateEnvironmentMode:
		mutateEnvironmentMode()
		err = saveVault()
		if err != nil {
			println(err.Error())
			os.Exit(3)
		}
	}
}

func getPassword() string {
	password, err := speakeasy.Ask("Vault password: ")
	if err != nil {
		fmt.Printf("ERROR: %s\n", err)
		os.Exit(4)
	}

	return password
}

func loadVault() error {
	var err error

	// skip loading if the file doesn't exist
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		envs = make(vault.Environments)
		return nil
	}

	// read the vault data
	vaultData, err = ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	// deserialize the vault data
	err = json.Unmarshal(vaultData, &v)
	if err != nil {
		return err
	}

	// decrypt the vault
	if vaultKey == nil {
		vaultKey, err = v.GenerateKey(getPassword())
		if err != nil {
			return err
		}
	}

	envs, err = v.DecryptEnvironments(vaultKey)
	return err
}

func saveVault() error {
	var err error

	// encrypt the vault
	if vaultKey == nil {
		vaultKey, err = v.GenerateKey(getPassword())
		if err != nil {
			return err
		}
	}

	err = v.EncryptEnvironments(vaultKey, envs)
	if err != nil {
		return err
	}

	// serialize the vault data
	tmpVaultData, err := json.Marshal(&v)
	if err != nil {
		return err
	}

	// write the vault data
	if !bytes.Equal(tmpVaultData, vaultData) {
		return ioutil.WriteFile(filename, tmpVaultData, 0600)
	}

	return nil
}

func listEnvironmentsMode() {
	if listEnvironments {
		envList := make([]string, 0, len(envs))
		for name := range envs {
			envList = append(envList, name)
		}

		sort.Strings(envList)
		for _, name := range envList {
			fmt.Fprintf(os.Stdout, "%s\n", name)
		}
	} else {
		env, exists := envs[environment]
		if !exists {
			fmt.Fprintf(os.Stderr, "ERROR: environment '%s' does not exist\n", environment)
			os.Exit(2)
		}

		data, err := json.MarshalIndent(&env, "", "  ")
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stdout, "%s\n", data)
	}
}

func spawnEnvironmentMode() {
	var spawnArgs []string
	if interactiveShell {
		spawnArgs = append(spawnArgs, os.Getenv("SHELL"), "--login")
	}
	spawnArgs = append(spawnArgs, flag.Args()...)

	env, exists := envs[environment]
	if !exists {
		fmt.Fprintf(os.Stderr, "ERROR: environment '%s' does not exist\n", environment)
		os.Exit(2)
	}

	if len(spawnArgs) < 1 {
		fmt.Fprint(os.Stderr, "ERROR: invalid spawn arguments\n")
		os.Exit(2)
	}

	vars := ParseEnviron(os.Environ())
	for key, val := range env.Vars {
		vars[key] = val
	}

	cmd := exec.Command(spawnArgs[0], spawnArgs[1:]...)
	cmd.Env = CreateEnviron(vars)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: failed to execute command: %s\n", err)
		os.Exit(3)
	}
	if !cmd.ProcessState.Success() {
		os.Exit(3)
	}
}

func mutateEnvironmentMode() {
	if deleteEnvironment {
		delete(envs, environment)
	} else if interactiveAdd {
		env := envs[environment]
		env.Name = environment
		if env.Vars == nil {
			env.Vars = make(map[string]string)
		}

		if charDevice {
			fmt.Printf("input vars (VAR=VALUE); end with ctrl+d\n")
		}
		for {
			if charDevice {
				fmt.Print("var> ")
			}

			var varLine string
			_, err := fmt.Scanln(&varLine)
			if err == io.EOF || varLine == "" {
				break
			}
			if err != nil {
				fmt.Fprintf(os.Stderr, "ERROR: Failed to read env vars - %s\n", err)
				os.Exit(1)
			}

			key, value := ParseVar(varLine)
			env.Vars[key] = value
		}
		envs[environment] = env
	}
}
<commit_msg>change to use os.StartProcess to launch processes<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bgentry/speakeasy"
	"github.com/miquella/vaulted/vault"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"sort"
)

type CommandMode int

const (
	NoMode CommandMode = iota
	SpawnEnvironmentMode
	MutateEnvironmentMode
	ListEnvironmentsMode
)

var (
	// vault flags
	filename string

	// environment flags
	environment string
	commandMode CommandMode = NoMode

	listEnvironments    bool
	dumpJsonEnvironment bool

	interactiveAdd    bool
	deleteEnvironment bool
	interactiveShell  bool

	charDevice bool

	// vault
	vaultKey  []byte
	vaultData []byte
	v         vault.Vault
	envs      vault.Environments
)

func init() {
	environment = os.Getenv("VAULTED_ENV")

	u, err := user.Current()
	if err != nil {
		panic(err)
	}

	flag.StringVar(&filename, "f", filepath.Join(u.HomeDir, ".vaulted"), "vault filename")
	flag.StringVar(&environment, "n", environment, "name of the environment")
	flag.BoolVar(&listEnvironments, "L", false, "list environments in vault")
	flag.BoolVar(&dumpJsonEnvironment, "j", false, "dump json version of environment")
	flag.BoolVar(&interactiveAdd, "a", false, "add to environment interactively")
	flag.BoolVar(&deleteEnvironment, "D", false, "delete environment from vault")
	flag.BoolVar(&interactiveShell, "i", false, "spawn a new shell populated with the environment")
	flag.Parse()

	// figure out which mode the user wants
	spawnMode := interactiveShell || flag.NArg() > 0
	mutateMode := interactiveAdd || deleteEnvironment
	listMode := listEnvironments || dumpJsonEnvironment

	if spawnMode {
		if listMode || mutateMode {
			fmt.Fprint(os.Stderr, "ERROR: cannot list/dump or update environments while spawning\n")
			os.Exit(2)
		}

		if os.Getenv("SHELL") == "" {
			fmt.Fprint(os.Stderr, "ERROR: SHELL environment variable not set, cannot spawn shell")
			os.Exit(2)
		}

		commandMode = SpawnEnvironmentMode

	} else if mutateMode {
		if listMode {
			fmt.Fprintf(os.Stderr, "ERROR: cannot update environments while listing/dumping\n")
			os.Exit(2)
		}
		commandMode = MutateEnvironmentMode

	} else if listMode {
		commandMode = ListEnvironmentsMode
	}

	stat, _ := os.Stdin.Stat()
	charDevice = (stat.Mode() & os.ModeCharDevice) != 0
}

func main() {
	if commandMode == NoMode {
		fmt.Fprint(os.Stderr, "invalid command mode\n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	err := loadVault()
	if err != nil {
		println(err.Error())
		os.Exit(3)
	}

	switch commandMode {
	case ListEnvironmentsMode:
		listEnvironmentsMode()
	case SpawnEnvironmentMode:
		spawnEnvironmentMode()
	case MutateEnvironmentMode:
		mutateEnvironmentMode()
		err = saveVault()
		if err != nil {
			println(err.Error())
			os.Exit(3)
		}
	}
}

func getPassword() string {
	password, err := speakeasy.Ask("Vault password: ")
	if err != nil {
		fmt.Printf("ERROR: %s\n", err)
		os.Exit(4)
	}

	return password
}

func loadVault() error {
	var err error

	// skip loading if the file doesn't exist
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		envs = make(vault.Environments)
		return nil
	}

	// read the vault data
	vaultData, err = ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	// deserialize the vault data
	err = json.Unmarshal(vaultData, &v)
	if err != nil {
		return err
	}

	// decrypt the vault
	if vaultKey == nil {
		vaultKey, err = v.GenerateKey(getPassword())
		if err != nil {
			return err
		}
	}

	envs, err = v.DecryptEnvironments(vaultKey)
	return err
}

func saveVault() error {
	var err error

	// encrypt the vault
	if vaultKey == nil {
		vaultKey, err = v.GenerateKey(getPassword())
		if err != nil {
			return err
		}
	}

	err = v.EncryptEnvironments(vaultKey, envs)
	if err != nil {
		return err
	}

	// serialize the vault data
	tmpVaultData, err := json.Marshal(&v)
	if err != nil {
		return err
	}

	// write the vault data
	if !bytes.Equal(tmpVaultData, vaultData) {
		return ioutil.WriteFile(filename, tmpVaultData, 0600)
	}

	return nil
}

func listEnvironmentsMode() {
	if listEnvironments {
		envList := make([]string, 0, len(envs))
		for name := range envs {
			envList = append(envList, name)
		}

		sort.Strings(envList)
		for _, name := range envList {
			fmt.Fprintf(os.Stdout, "%s\n", name)
		}
	} else {
		env, exists := envs[environment]
		if !exists {
			fmt.Fprintf(os.Stderr, "ERROR: environment '%s' does not exist\n", environment)
			os.Exit(2)
		}

		data, err := json.MarshalIndent(&env, "", "  ")
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stdout, "%s\n", data)
	}
}

func spawnEnvironmentMode() {
	// validate that the environment exists
	env, exists := envs[environment]
	if !exists {
		fmt.Fprintf(os.Stderr, "ERROR: environment '%s' does not exist\n", environment)
		os.Exit(2)
	}

	// compile command line arguments
	var spawnArgs []string
	if interactiveShell {
		spawnArgs = append(spawnArgs, os.Getenv("SHELL"), "--login")
	}
	spawnArgs = append(spawnArgs, flag.Args()...)

	if len(spawnArgs) < 1 {
		fmt.Fprint(os.Stderr, "ERROR: invalid spawn arguments\n")
		os.Exit(2)
	}

	// build the environment
	vars := ParseEnviron(os.Environ())
	for key, val := range env.Vars {
		vars[key] = val
	}

	// locate the executable
	fullpath, err := exec.LookPath(spawnArgs[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: cannot find executable: %s - %s\n", spawnArgs[0], err)
		os.Exit(2)
	}

	// start the process
	var attr os.ProcAttr
	attr.Env = CreateEnviron(vars)
	attr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
	proc, err := os.StartProcess(fullpath, spawnArgs, &attr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: failed to execute command: %s\n", err)
		os.Exit(3)
	}

	// wait for the process to exit
	state, err := proc.Wait()
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: failed to execute command: %s\n", err)
		os.Exit(3)
	}
	if !state.Success() {
		os.Exit(3)
	}
}

func mutateEnvironmentMode() {
	if deleteEnvironment {
		delete(envs, environment)
	} else if interactiveAdd {
		env := envs[environment]
		env.Name = environment
		if env.Vars == nil {
			env.Vars = make(map[string]string)
		}

		if charDevice {
			fmt.Printf("input vars (VAR=VALUE); end with ctrl+d\n")
		}
		for {
			if charDevice {
				fmt.Print("var> ")
			}

			var varLine string
			_, err := fmt.Scanln(&varLine)
			if err == io.EOF || varLine == "" {
				break
			}
			if err != nil {
				fmt.Fprintf(os.Stderr, "ERROR: Failed to read env vars - %s\n", err)
				os.Exit(1)
			}

			key, value := ParseVar(varLine)
			env.Vars[key] = value
		}
		envs[environment] = env
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/stacktic/dropbox"
)

var (
	dirName   = flag.String("dir", "", "Directory name (required, in env: DROPBOX_DIR)")
	doMkdir   = flag.Bool("mkdir", false, "Create the directory")
	fileName  = flag.String("filename", "", "File name to create (required)")
	inputFile = flag.String("input", "", "Input file to upload create (stdin used by default)")
	appId     = flag.String("appId", "", "Dropbox App ID (required, in env: DROPBOX_APP_ID)")
	appSecret = flag.String("appSecret", "", "Dropbox App Secret (required, in env: DROPBOX_APP_SECRET)")
	appToken  = flag.String("token", "", "Dropbox App Token (in env: DROPBOX_APP_TOKEN)")
	chunkSize = flag.Int("chunk-size", 64, "Upload chunk size in megabytes")
)

func printUsage() {
	fmt.Println("upload-to-dropbox")
	fmt.Println()
	flag.PrintDefaults()
}

func main() {
	flag.Parse()
	setFromEnv()

	if *appId == "" || *appSecret == "" {
		printUsage()
		return
	}

	dbox := dropbox.NewDropbox()
	dbox.SetAppInfo(*appId, *appSecret)

	if *appToken == "" {
		if err := dbox.Auth(); err != nil {
			fmt.Println(err)
			return
		}
	} else {
		dbox.SetAccessToken(*appToken)
	}

	if *dirName == "" || *fileName == "" {
		printUsage()
		return
	}

	if *doMkdir {
		if _, err := dbox.CreateFolder(*dirName); err != nil && strings.Contains(err.Error(), "already exists") {
			fmt.Printf("Folder %s already exists\n", *dirName)
		} else if err != nil {
			fmt.Printf("Error creating folder %s: %s\n", *dirName, err)
			return
		}
		fmt.Printf("Folder %s successfully created\n", *dirName)
	}

	var err error
	fname := fmt.Sprintf("%s/%s", *dirName, *fileName)
	if *inputFile != "" {
		_, err = dbox.UploadFile(*inputFile, fname, true, "")
	} else {
		_, err = dbox.UploadByChunk(os.Stdin, *chunkSize*1024, fname, false, "")
	}

	if err != nil {
		fmt.Printf("Error uploading file %s: %s\n", fname, err)
		return
	}

	fmt.Printf("File %s successfully created\n", fname)

}

func setFromEnv() {
	if v := os.Getenv("DROPBOX_DIR"); *dirName == "" && v != "" {
		*dirName = v
	}
	if v := os.Getenv("DROPBOX_APP_ID"); *appId == "" && v != "" {
		*appId = v
	}
	if v := os.Getenv("DROPBOX_APP_SECRET"); *appSecret == "" && v != "" {
		*appSecret = v
	}
	if v := os.Getenv("DROPBOX_APP_TOKEN"); *appToken == "" && v != "" {
		*appToken = v
	}
}
<commit_msg>Progress bar<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/cheggaaa/pb"
	"github.com/mitchellh/ioprogress"
	"github.com/stacktic/dropbox"
)

var (
	dirName   = flag.String("dir", "", "Directory name (required, in env: DROPBOX_DIR)")
	doMkdir   = flag.Bool("mkdir", false, "Create the directory")
	fileName  = flag.String("filename", "", "File name to create (required)")
	inputFile = flag.String("input", "", "Input file to upload create (stdin used by default)")
	appID     = flag.String("appID", "", "Dropbox App ID (required, in env: DROPBOX_APP_ID)")
	appSecret = flag.String("appSecret", "", "Dropbox App Secret (required, in env: DROPBOX_APP_SECRET)")
	appToken  = flag.String("token", "", "Dropbox App Token (in env: DROPBOX_APP_TOKEN)")
	chunkSize = flag.Int("chunk-size", 64, "Upload chunk size in megabytes")
)

func printUsage() {
	fmt.Println("upload-to-dropbox")
	fmt.Println()
	flag.PrintDefaults()
}

func main() {
	flag.Parse()
	setFromEnv()

	if *appID == "" || *appSecret == "" {
		fatalf("Application ID and Secret is required.")
	}

	dbox := dropbox.NewDropbox()
	dbox.SetAppInfo(*appID, *appSecret)

	if *appToken == "" {
		if err := dbox.Auth(); err != nil {
			fmt.Println(err)
			return
		}
	} else {
		dbox.SetAccessToken(*appToken)
	}

	if *fileName == "" && *inputFile == "" {
		fmt.Println("Filename is required.")
		os.Exit(1)
		return
	} else if *fileName == "" {
		*fileName = *inputFile
	}

	if *doMkdir && *dirName != "" {
		if _, err := dbox.CreateFolder(*dirName); err != nil && strings.Contains(err.Error(), "already exists") {
			fmt.Printf("Folder %s already exists\n", *dirName)
		} else if err != nil {
			fatalf("Error creating folder %s: %s", *dirName, err)
		}
		fmt.Printf("Folder %s successfully created\n", *dirName)
	}

	fname := strings.Trim(strings.Join([]string{*dirName, *fileName}, "/"), "/")
	fmt.Printf("Uploading file %s => %s\n", *inputFile, fname)

	var input io.ReadCloser
	if *inputFile != "" {
		// _, err = dbox.UploadFile(*inputFile, fname, true, "")
		info, err := os.Stat(*inputFile)
		if err != nil {
			fatalf("%v", err)
		}

		file, err := os.Open(*inputFile)
		if err != nil {
			fatalf("Error opening file %q: %v", *inputFile, err)
		}
		defer file.Close()
		bar := pb.StartNew(int(info.Size()))
		bar.Units = pb.U_BYTES

		input = ioutil.NopCloser(&ioprogress.Reader{
			Reader: file,
			Size:   info.Size(),
			DrawFunc: func(progress int64, total int64) error {
				bar.Set(int(progress))
				if progress >= total {
					bar.FinishPrint("Done")
				}
				return nil
			},
		})
	} else {
		input = os.Stdin
	}

	_, err := dbox.UploadByChunk(input, *chunkSize*1024, fname, false, "")
	if err != nil {
		fatalf("Error uploading file %s: %v", fname, err)
	}

	fmt.Printf("File %s successfully created\n", fname)
}

func fatalf(format string, a ...interface{}) {
	fmt.Printf(format+"\n", a...)
	os.Exit(1)
}

func setFromEnv() {
	if v := os.Getenv("DROPBOX_DIR"); *dirName == "" && v != "" {
		*dirName = v
	}
	if v := os.Getenv("DROPBOX_APP_ID"); *appID == "" && v != "" {
		*appID = v
	}
	if v := os.Getenv("DROPBOX_APP_SECRET"); *appSecret == "" && v != "" {
		*appSecret = v
	}
	if v := os.Getenv("DROPBOX_APP_TOKEN"); *appToken == "" && v != "" {
		*appToken = v
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	flag "github.com/ogier/pflag"
	"github.com/yarbelk/refasta/formats"
	"github.com/yarbelk/refasta/sequence"
)

type FakeReadCloser struct {
	io.Reader
}

func (f FakeReadCloser) Close() error {
	return nil
}

type FakeWriteCloser struct {
	io.Writer
}

func (f FakeWriteCloser) Close() error {
	return nil
}

func getOutputFilePointer(filename string) (io.WriteCloser, error) {
	if filename == "--" {
		return FakeWriteCloser{os.Stdout}, nil
	}
	return os.Create(filename)
}

func getInputFilePointer(filename string) (io.ReadCloser, error) {
	if filename == "--" {
		return FakeReadCloser{os.Stdin}, nil
	}
	return os.Open(filename)
}

func IsDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	return fileInfo.IsDir(), err
}

func isFormat(file, format string) bool {
	switch path.Ext(file) {
	case ".fas", ".fasta":
		return format == formats.FASTA_FORMAT
	default:
		return false
	}
}

func dirInput(dir, format string, recurse bool) ([]string, error) {
	absDir, err := filepath.Abs(dir)
	if err != nil {
		return nil, err
	}
	files, err := ioutil.ReadDir(absDir)
	if err != nil {
		return nil, err
	}
	filteredFiles := make([]string, 0, 10)
	for _, file := range files {
		if file.IsDir() && recurse {
			f, err := dirInput(filepath.Join(absDir, file.Name()), format, true)
			if err != nil {
				return nil, err
			}
			filteredFiles = append(filteredFiles, f...)
		} else if isFormat(file.Name(), format) {
			filteredFiles = append(filteredFiles, filepath.Join(absDir, file.Name()))
		}
	}
	return filteredFiles, nil
}

func isDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	return fileInfo.IsDir(), err
}

func handleFastaInput(input string) ([]sequence.Sequence, error) {
	var files []string
	var sequences []sequence.Sequence

	if isDir, err := isDirectory(input); isDir && err == nil {
		files, err = dirInput(input, formats.FASTA_FORMAT, true)
		if err != nil {
			// Some error in walking the directory tree
			return nil, err
		}
	} else {
		files = []string{input}
	}
	fmt.Println("files", files)

	for _, file := range files {
		ext := path.Ext(file)
		geneName := file[:len(file)-len(ext)]
		fasta := formats.Fasta{SpeciesFromID: true}
		fd, err := os.Open(file)
		if err != nil {
			// probably an Access Control issue, or race condition
			return nil, err
		}
		defer fd.Close()
		err = fasta.Parse(fd, geneName)
		if err != nil {
			// Some parsing error...
			return nil, err
		}
		sequences = append(sequences, fasta.Sequences...)
	}
	return sequences, nil
}

func handleFastaOutput(sequences []sequence.Sequence, output string) error {
	fasta := formats.Fasta{}
	fmt.Println(sequences)
	fasta.AddSequence(sequences...)
	fd, err := os.Create(output)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Issue opening output file;\n%s", err.Error())
	}
	defer fd.Close()
	return fasta.WriteSequences(fd)
}

func handleTNTOutput(sequences []sequence.Sequence, output string) error {
	return nil
}

func main() {
	inputFormat := flag.StringP("input-format", "f", formats.FASTA_FORMAT, "intput format, must be 'fasta'")
	outputFormat := flag.StringP("outpu-format", "F", formats.TNT_FORMAT, "output format, must be [fasta|tnt]")
	input := flag.StringP("input-file", "i", "--", "input file, it must be a valid input, or '--', or blank.  if blank. or '--', will read from stdin")
	output := flag.StringP("output-file", "o", "--", "output file, it must be a valid input, or '--', or blank.  if blank. or '--', will write to stdout")
	flag.Parse()

	var sequences []sequence.Sequence
	var err error

	switch *inputFormat {
	case formats.FASTA_FORMAT:
		fmt.Fprintf(os.Stderr, "intput format is fasta; parsing\n")
		sequences, err = handleFastaInput(*input)
	default:
		fmt.Fprintf(os.Stderr, "Unknown intput format '%s'", inputFormat)
		os.Exit(1)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		os.Exit(1)
	}

	switch *outputFormat {
	case formats.FASTA_FORMAT:
		fmt.Fprintf(os.Stderr, "Output format is fasta; serializing\n")
		if err := handleFastaOutput(sequences, *output); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			os.Exit(1)
		}
	case formats.TNT_FORMAT:
		fmt.Fprintf(os.Stderr, "Output format is TNT; serializing\n")
		if err := handleTNTOutput(sequences, *output); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			os.Exit(1)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown output format '%s'", inputFormat)
		os.Exit(1)
	}

}
<commit_msg>TNT output, with customizable title<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	flag "github.com/ogier/pflag"
	"github.com/yarbelk/refasta/formats"
	"github.com/yarbelk/refasta/sequence"
)

type FakeReadCloser struct {
	io.Reader
}

func (f FakeReadCloser) Close() error {
	return nil
}

type FakeWriteCloser struct {
	io.Writer
}

type TNTContext struct {
	Title string
}

func (f FakeWriteCloser) Close() error {
	return nil
}

func getOutputFilePointer(filename string) (io.WriteCloser, error) {
	if filename == "--" {
		return FakeWriteCloser{os.Stdout}, nil
	}
	return os.Create(filename)
}

func getInputFilePointer(filename string) (io.ReadCloser, error) {
	if filename == "--" {
		return FakeReadCloser{os.Stdin}, nil
	}
	return os.Open(filename)
}

func IsDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	return fileInfo.IsDir(), err
}

func isFormat(file, format string) bool {
	switch path.Ext(file) {
	case ".fas", ".fasta":
		return format == formats.FASTA_FORMAT
	default:
		return false
	}
}

func dirInput(dir, format string, recurse bool) ([]string, error) {
	absDir, err := filepath.Abs(dir)
	if err != nil {
		return nil, err
	}
	files, err := ioutil.ReadDir(absDir)
	if err != nil {
		return nil, err
	}
	filteredFiles := make([]string, 0, 10)
	for _, file := range files {
		if file.IsDir() && recurse {
			f, err := dirInput(filepath.Join(absDir, file.Name()), format, true)
			if err != nil {
				return nil, err
			}
			filteredFiles = append(filteredFiles, f...)
		} else if isFormat(file.Name(), format) {
			filteredFiles = append(filteredFiles, filepath.Join(absDir, file.Name()))
		}
	}
	return filteredFiles, nil
}

func isDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	return fileInfo.IsDir(), err
}

func handleFastaInput(input string) ([]sequence.Sequence, error) {
	var files []string
	var sequences []sequence.Sequence

	if isDir, err := isDirectory(input); isDir && err == nil {
		files, err = dirInput(input, formats.FASTA_FORMAT, true)
		if err != nil {
			// Some error in walking the directory tree
			return nil, err
		}
	} else {
		files = []string{input}
	}
	fmt.Println("files", files)

	for _, file := range files {
		ext := path.Ext(file)
		geneName := file[:len(file)-len(ext)]
		fasta := formats.Fasta{SpeciesFromID: true}
		fd, err := os.Open(file)
		if err != nil {
			// probably an Access Control issue, or race condition
			return nil, err
		}
		defer fd.Close()
		err = fasta.Parse(fd, geneName)
		if err != nil {
			// Some parsing error...
			return nil, err
		}
		sequences = append(sequences, fasta.Sequences...)
	}
	return sequences, nil
}

func handleFastaOutput(sequences []sequence.Sequence, output string) error {
	fasta := formats.Fasta{}
	fmt.Println(sequences)
	fasta.AddSequence(sequences...)
	fd, err := os.Create(output)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Issue opening output file;\n%s", err.Error())
	}
	defer fd.Close()
	return fasta.WriteSequences(fd)
}

func handleTNTOutput(context TNTContext, sequences []sequence.Sequence, output string) error {
	tnt := formats.TNT{Title: context.Title}
	fmt.Println(sequences)
	tnt.AddSequence(sequences...)
	fd, err := os.Create(output)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Issue opening output file;\n%s", err.Error())
	}
	defer fd.Close()
	return tnt.WriteSequences(fd)
}

func main() {
	inputFormat := flag.StringP("input-format", "f", formats.FASTA_FORMAT, "intput format, must be 'fasta'")
	outputFormat := flag.StringP("outpu-format", "F", formats.TNT_FORMAT, "output format, must be [fasta|tnt]")
	input := flag.StringP("input-file", "i", "--", "input file, it must be a valid input, or '--', or blank.  if blank. or '--', will read from stdin")
	output := flag.StringP("output-file", "o", "--", "output file, it must be a valid input, or '--', or blank.  if blank. or '--', will write to stdout")
	tntTitle := flag.StringP("tnt-title", "t", "", "title for TNT output")
	flag.Parse()

	var sequences []sequence.Sequence
	var err error

	switch *inputFormat {
	case formats.FASTA_FORMAT:
		fmt.Fprintf(os.Stderr, "intput format is fasta; parsing\n")
		sequences, err = handleFastaInput(*input)
	default:
		fmt.Fprintf(os.Stderr, "Unknown intput format '%s'", inputFormat)
		os.Exit(1)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		os.Exit(1)
	}

	switch *outputFormat {
	case formats.FASTA_FORMAT:
		fmt.Fprintf(os.Stderr, "Output format is fasta; serializing\n")
		if err := handleFastaOutput(sequences, *output); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			os.Exit(1)
		}
	case formats.TNT_FORMAT:
		fmt.Fprintf(os.Stderr, "Output format is TNT; serializing\n")
		context := TNTContext{
			Title: *tntTitle,
		}
		if err := handleTNTOutput(context, sequences, *output); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			os.Exit(1)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown output format '%s'", inputFormat)
		os.Exit(1)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"github.com/ActiveState/tail"
	"github.com/prometheus/client_golang/prometheus"
	"log"
	"net/http"
	"strconv"
	"strings"
)

var (
	loadEvents = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: "datalogger",
			Name:      "rows_loaded",
			Help:      "the number of rows loaded into the database",
		},
		[]string{"site"},
	)
	batteryVoltage = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: "datalogger_battery_voltage",
		Help: "The current battery voltage",
	},
		[]string{"site"},
	)
)

func init() {
	prometheus.MustRegister(loadEvents)
	prometheus.MustRegister(batteryVoltage)
}

type stringSlice []string

func (slice stringSlice) pos(value string) int {
	for p, v := range slice {
		if v == value {
			return p
		}
	}
	return -1
}

func readCSVLine(text string) []string {
	reader := csv.NewReader(strings.NewReader(text))
	fields, err := reader.Read()
	if err != nil {
		log.Fatal(err)
	}
	return fields
}

func loadData(fileName string, site string, batteryVoltName string) {

	t, err := tail.TailFile(fileName, tail.Config{
		Follow: true,
		ReOpen: true,
	})
	if err != nil {
		log.Fatal(err)
	}

	battery := 1

	// Read title and variables and units
	skip := 0
	for line := range t.Lines {
		if strings.Contains(line.Text, "TOA5") {
			skip = 4
		}
		if skip == 3 {
			// decode headers
			fields := readCSVLine(line.Text)
			variates := stringSlice(fields)
			battery = variates.pos(batteryVoltName)
		}
		if skip > 0 {
			// skip the rest
			skip = skip - 1
			continue
		}
		// Read data
		fields := readCSVLine(line.Text)
		voltage, err := strconv.ParseFloat(fields[battery], 64)
		if err != nil {
			log.Fatal(err)
		}
		// log.Println(voltage)
		batteryVoltage.WithLabelValues(site).Set(voltage)
		loadEvents.WithLabelValues(site).Inc()
	}
}

func main() {

	go loadData("raingauge_Table1.dat", "lter-raingauge", "GageMinV")
	go loadData("MarginalLand_five_min.dat", "luxarbor", "BattV_Min")

	http.Handle("/metrics", prometheus.Handler())
	http.ListenAndServe(":9094", nil)

}
<commit_msg>read json data from stdin to set up loggers<commit_after>package main

import (
	"encoding/csv"
	"encoding/json"
	"github.com/ActiveState/tail"
	"github.com/prometheus/client_golang/prometheus"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var (
	loadEvents = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: "datalogger",
			Name:      "rows_loaded",
			Help:      "the number of rows loaded into the database",
		},
		[]string{"site"},
	)
	batteryVoltage = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: "datalogger_battery_voltage",
		Help: "The current battery voltage",
	},
		[]string{"site"},
	)
)

func init() {
	prometheus.MustRegister(loadEvents)
	prometheus.MustRegister(batteryVoltage)
}

type stringSlice []string

func (slice stringSlice) pos(value string) int {
	for p, v := range slice {
		if v == value {
			return p
		}
	}
	return -1
}

type logger struct {
	FileName           string `json:"file-name"`
	Site               string `json:"site"`
	BatteryVariateName string `json:"battery-variate-name"`
}

func readCSVLine(text string) []string {
	reader := csv.NewReader(strings.NewReader(text))
	fields, err := reader.Read()
	if err != nil {
		log.Fatal(err)
	}
	return fields
}

func loadData(logger logger) {

	t, err := tail.TailFile(logger.FileName, tail.Config{
		Follow: true,
		ReOpen: true,
	})
	if err != nil {
		log.Fatal(err)
	}

	var batteryFieldNumber int

	// Read title and variables and units
	skip := 0
	for line := range t.Lines {
		if strings.Contains(line.Text, "TOA5") {
			skip = 4
		}
		if skip == 3 {
			// decode headers
			fields := readCSVLine(line.Text)
			variates := stringSlice(fields)
			batteryFieldNumber = variates.pos(logger.BatteryVariateName)
		}
		if skip > 0 {
			// skip the rest
			skip = skip - 1
			continue
		}
		// Read data
		fields := readCSVLine(line.Text)
		voltage, err := strconv.ParseFloat(fields[batteryFieldNumber], 64)
		if err != nil {
			log.Fatal(err)
		}
		// log.Println(voltage)
		batteryVoltage.WithLabelValues(logger.Site).Set(voltage)
		loadEvents.WithLabelValues(logger.Site).Inc()
	}
}

func main() {

	dec := json.NewDecoder(os.Stdin)

	var loggers []logger
	if err := dec.Decode(&loggers); err != nil {
		log.Println(err)
		return
	}
	for _, lg := range loggers {
		go loadData(lg)
	}

	http.Handle("/metrics", prometheus.Handler())
	http.ListenAndServe(":9094", nil)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/drivers/gl"
	"github.com/nelsam/gxui/themes/dark"
	"github.com/nelsam/gxui/themes/light"
)

const (
	version = "1.1.0.0"
	defIni  = "[basic]\ntheme=dk"
)

var (
	dr        gxui.Driver
	th        gxui.Theme
	master    map[string][]app
	linmaster map[string][]app
	cats      []string
	lin       []string
	wine      bool
	comEnbld  bool
	darkTheme = true
)

func main() {
	updated := false
	os.MkdirAll("PortableApps/LinuxPACom", 0777)
	stat, err := versionDL()
	if stat {
		res := getVersionFileInfo()
		if res != "Error!" {
			stat, err = checkForUpdate(res)
			if stat {
				downloadUpdate(res)
				updated = true
			} else {
				fmt.Println(err)
			}
		} else {
			fmt.Println(err)
		}
	} else {
		fmt.Println(err)
	}
	if updated {
		cmd := exec.Command("./LinuxPA")
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Start()
	} else {
		master = make(map[string][]app)
		linmaster = make(map[string][]app)
		gl.StartDriver(appMain)
	}
}

func appMain(dri gxui.Driver) {
	dr = dri
	setup()
	if darkTheme {
		th = dark.CreateTheme(dr)
	} else {
		th = light.CreateTheme(dr)
	}
	th = dark.CreateTheme(dr)
	ui()
}

func contains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}
<commit_msg>Last push before GUI redo<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/drivers/gl"
	"github.com/nelsam/gxui/themes/dark"
	"github.com/nelsam/gxui/themes/light"
)

const (
	version = "1.1.0.0"
	defIni  = "[basic]\ntheme=dk"
)

var (
	dr        gxui.Driver
	th        gxui.Theme
	master    map[string][]app
	linmaster map[string][]app
	cats      []string
	lin       []string
	wine      bool
	comEnbld  bool
	darkTheme = true
)

func main() {
	updated := false
	os.MkdirAll("PortableApps/LinuxPACom", 0777)
	stat, err := versionDL()
	if stat {
		res := getVersionFileInfo()
		if res != "Error!" {
			stat, err = checkForUpdate(res)
			if stat {
				downloadUpdate(res)
				updated = true
			} else {
				fmt.Println(err)
			}
		} else {
			fmt.Println("Failed Version File Info")
		}
	} else {
		fmt.Println(err)
	}
	if updated {
		cmd := exec.Command("./LinuxPA")
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Start()
	} else {
		master = make(map[string][]app)
		linmaster = make(map[string][]app)
		gl.StartDriver(appMain)
	}
}

func appMain(dri gxui.Driver) {
	dr = dri
	setup()
	if darkTheme {
		th = dark.CreateTheme(dr)
	} else {
		th = light.CreateTheme(dr)
	}
	th = dark.CreateTheme(dr)
	ui()
}

func contains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import "log"
import "github.com/robfig/cron"

func main() {
	FailOnError(CheckAndGetConfigs())
	if ShouldRunOnStart() {
		run()
	}
	c := cron.New()
	err := c.AddFunc(GetRunSchedule(), func() {
		run()
	})
	if err != nil {
		log.Fatalf("Unable to start cron due to error - %s", err.Error())
	}
	c.Start()
}

func run() {
	tagsDeleted := 0
	// Getting all repositories from a private docker hub
	allRepos, err := GetAllReposInRegistry()
	FailOnError(err)
	for _, singleRepo := range allRepos {
		allTags, err := GetListOfTagsForRepo(singleRepo)
		if err != nil {
			log.Printf("Unable to get list of tags for repo %s", singleRepo)
			continue
		}
		for _, singleTag := range allTags {
			log.Printf("%s:%s", singleRepo, singleTag)
			isExcempt, err := IsTagExcemptedFromDeletion(singleRepo, singleTag)
			if err != nil {
				log.Printf("Unable to determine if tag %s is excempt for repo %s. Error : %s", singleTag, singleRepo, err.Error())
				continue
			}
			if !isExcempt {
				isOutdate, err := IsImageTagOutdated(singleRepo, singleTag)
				if err != nil {
					log.Printf("Unable to determine if tag %s is outdate for repo %s", singleTag, singleRepo)
					continue
				}
				if isOutdate {
					contentDigest, err := GetContentDigest(singleRepo, singleTag)
					if err != nil {
						log.Printf("Unable to get content digest for tag %s in repo %s", singleTag, singleRepo)
					}
					log.Println(DeleteDigest(singleRepo, contentDigest))
					log.Printf(">>>>>> Deletion Done <<<<<<")
					tagsDeleted++
					log.Printf("Total number of tags deleted: %d", tagsDeleted)
				}
			}
		}
	}
	// Run garbage-collect on registry
	FailOnError(RunRegistryGarbageCollection())
}

// FailOnError ...
func FailOnError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>wait for cron<commit_after>package main

import "log"
import "github.com/robfig/cron"

func main() {
	FailOnError(CheckAndGetConfigs())
	if ShouldRunOnStart() {
		run()
	}
	c := cron.New()
	err := c.AddFunc(GetRunSchedule(), func() {
		run()
	})
	if err != nil {
		log.Fatalf("Unable to start cron due to error - %s", err.Error())
	}
	c.Start()
	<-make(chan struct{})
}

func run() {
	tagsDeleted := 0
	// Getting all repositories from a private docker hub
	allRepos, err := GetAllReposInRegistry()
	FailOnError(err)
	for _, singleRepo := range allRepos {
		allTags, err := GetListOfTagsForRepo(singleRepo)
		if err != nil {
			log.Printf("Unable to get list of tags for repo %s", singleRepo)
			continue
		}
		for _, singleTag := range allTags {
			log.Printf("%s:%s", singleRepo, singleTag)
			isExcempt, err := IsTagExcemptedFromDeletion(singleRepo, singleTag)
			if err != nil {
				log.Printf("Unable to determine if tag %s is excempt for repo %s. Error : %s", singleTag, singleRepo, err.Error())
				continue
			}
			if !isExcempt {
				isOutdate, err := IsImageTagOutdated(singleRepo, singleTag)
				if err != nil {
					log.Printf("Unable to determine if tag %s is outdate for repo %s", singleTag, singleRepo)
					continue
				}
				if isOutdate {
					contentDigest, err := GetContentDigest(singleRepo, singleTag)
					if err != nil {
						log.Printf("Unable to get content digest for tag %s in repo %s", singleTag, singleRepo)
					}
					log.Println(DeleteDigest(singleRepo, contentDigest))
					log.Printf(">>>>>> Deletion Done <<<<<<")
					tagsDeleted++
					log.Printf("Total number of tags deleted: %d", tagsDeleted)
				}
			}
		}
	}
	// Run garbage-collect on registry
	FailOnError(RunRegistryGarbageCollection())
}

// FailOnError ...
func FailOnError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package otoshi defines a middleware abstraction for the composition of
// BitTorrent tracker functionality.
package otoshi

import (
	"net"
	"time"

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

// Peer represents one instance of a BitTorrent client currently participating
// in a Swarm.
type Peer struct {
	ID   string
	IP   net.IP
	Port uint32
	Left uint64
}

// PeerIterator represents a stream of Peers.
type PeerIterator interface {
	Next() bool
	Peer() Peer
}

// Event represents an event done by a BitTorrent client.
type Event string

const (
	// Started is the event sent by a BitTorrent client when it joins a Swarm.
	Started Event = "started"

	// Stopped is the event sent by a BitTorrent client when it leaves a Swarm.
	Stopped Event = "stopped"

	// Completed is the event sent by a BitTorrent client when it finishes
	// downloading all of the required chunks.
	Completed Event = "completed"
)

// Infohash is the hash of a set of files that are to be downloaded by an
// individual Swarm.
type Infohash string

// Swarm represents the metadata of collection of Peers sharing a torrent.
type Swarm struct{ Complete, Incomplete, Downloaded uint64 }

// AnnounceIntervals represents the intervals at which a BitTorrent client is
// intended to announce to a Tracker.
type AnnounceIntervals struct{ AnnounceInterval, MinAnnounceInterval time.Duration }

// AnnounceRequest represents the transport-agnostic Announce request sent
// from a BitTorrent client to a Tracker.
type AnnounceRequest struct {
	Peer       Peer
	Infohash   Infohash
	Event      Event
	URL        string
	Downloaded uint64
	Uploaded   uint64
	Compact    bool
	NumWant    uint16
}

// AnnounceResponse represents the transport-agnostic Announce response sent
// from a Tracker to a BitTorrent client.
type AnnounceResponse struct {
	IPv4Peers, IPv6Peers PeerIterator
	AnnounceIntervals
	Swarm
}

// AnnounceResponseWriter is used by an AnnounceHandler to construct a response.
type AnnounceResponseWriter interface {
	WriteAnnounceResponse(*AnnounceResponse)
	WriteError(error)
}

// AnnounceHandler is a function that operates on an Announce before a response
// has been written.
type AnnounceHandler func(context.Context, *AnnounceRequest, AnnounceResponseWriter) error

// AnnounceMiddleware enables the extension of an AnnounceHandler via a closure.
type AnnounceMiddleware func(AnnounceHandler) AnnounceHandler

// ScrapeRequest represents a transport-agnostic Scrape request sent from a
// BitTorrent client to a tracker.
type ScrapeRequest []Infohash

// ScrapeResponse represents the transport-agnostic Scrape response sent from
// a tracker to a BitTorrent client.
type ScrapeResponse map[Infohash]Swarm

// ScrapeResponseWriter is used by a ScrapeHandler to construct a response.
type ScrapeResponseWriter interface {
	WriteScrapeResponse(*ScrapeResponse)
	WriteError(error)
}

// ScrapeHandler is a function that operates on a Scrape before a response
// has been written.
type ScrapeHandler func(context.Context, *ScrapeRequest, ScrapeResponseWriter) error

// ScrapeMiddleware enables the extension of an ScrapeHandler via a closure.
type ScrapeMiddleware func(ScrapeHandler) ScrapeHandler

// AnnounceChain represents a composition of AnnounceMiddleware.
type AnnounceChain struct{ mw []AnnounceMiddleware }

// Append creates a new AnnounceChain by appending the provided middleware to
// the current chain.
func (c AnnounceChain) Append(mw ...AnnounceMiddleware) (nc AnnounceChain) {
	nc.mw = make([]AnnounceMiddleware, len(c.mw)+len(mw))
	copy(nc.mw[:len(c.mw)], c.mw)
	copy(nc.mw[len(c.mw):], mw)
	return
}

// Finalize creates an AnnounceHandler that is the composition of the chain.
func (c AnnounceChain) Finalize(final AnnounceHandler) AnnounceHandler {
	for i := len(c.mw) - 1; i >= 0; i-- {
		final = c.mw[i](final)
	}
	return final
}

// ScrapeChain represents a composition of ScrapeMiddleware.
type ScrapeChain struct{ mw []ScrapeMiddleware }

// Append creates a new ScrapeChain by appending the provided middleware to the
// current chain.
func (c ScrapeChain) Append(mw ...ScrapeMiddleware) (nc ScrapeChain) {
	nc.mw = make([]ScrapeMiddleware, len(c.mw)+len(mw))
	copy(nc.mw[:len(c.mw)], c.mw)
	copy(nc.mw[len(c.mw):], mw)
	return
}

// Finalize creates an ScrapeHandler that is the composition of the chain.
func (c ScrapeChain) Finalize(final ScrapeHandler) ScrapeHandler {
	for i := len(c.mw) - 1; i >= 0; i-- {
		final = c.mw[i](final)
	}
	return final
}

// ServeAnnounce creates an empty context and calls itself.
func (h AnnounceHandler) ServeAnnounce(req *AnnounceRequest, resp AnnounceResponseWriter) {
	ctx := context.TODO()
	h(ctx, req, resp)
}

// ServeScrape creates an empty context and calls itself.
func (h ScrapeHandler) ServeScrape(req *ScrapeRequest, resp ScrapeResponseWriter) {
	ctx := context.TODO()
	h(ctx, req, resp)
}

// NewTracker returns a Tracker given an AnnounceHandler and a ScrapeHandler.
func NewTracker(ah AnnounceHandler, sh ScrapeHandler) Tracker {
	return struct {
		AnnounceHandler
		ScrapeHandler
	}{
		ac,
		sc,
	}
}

// Tracker represents a BitTorrent tracker.
type Tracker interface {
	ServeAnnounce(*AnnounceRequest, AnnounceResponseWriter)
	ServeScrape(*ScrapeRequest, ScrapeResponseWriter)
}
<commit_msg>reverse handler parameters to match net/HTTP<commit_after>// Copyright 2015 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package otoshi defines a middleware abstraction for the composition of
// BitTorrent tracker functionality.
package otoshi

import (
	"net"
	"time"

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

// Peer represents one instance of a BitTorrent client currently participating
// in a Swarm.
type Peer struct {
	ID   string
	IP   net.IP
	Port uint32
	Left uint64
}

// PeerIterator represents a stream of Peers.
type PeerIterator interface {
	Next() bool
	Peer() Peer
}

// Event represents an event done by a BitTorrent client.
type Event string

const (
	// Started is the event sent by a BitTorrent client when it joins a Swarm.
	Started Event = "started"

	// Stopped is the event sent by a BitTorrent client when it leaves a Swarm.
	Stopped Event = "stopped"

	// Completed is the event sent by a BitTorrent client when it finishes
	// downloading all of the required chunks.
	Completed Event = "completed"
)

// Infohash is the hash of a set of files that are to be downloaded by an
// individual Swarm.
type Infohash string

// Swarm represents the metadata of collection of Peers sharing a torrent.
type Swarm struct{ Complete, Incomplete, Downloaded uint64 }

// AnnounceIntervals represents the intervals at which a BitTorrent client is
// intended to announce to a Tracker.
type AnnounceIntervals struct{ AnnounceInterval, MinAnnounceInterval time.Duration }

// AnnounceRequest represents the transport-agnostic Announce request sent
// from a BitTorrent client to a Tracker.
type AnnounceRequest struct {
	Peer       Peer
	Infohash   Infohash
	Event      Event
	URL        string
	Downloaded uint64
	Uploaded   uint64
	Compact    bool
	NumWant    uint16
}

// AnnounceResponse represents the transport-agnostic Announce response sent
// from a Tracker to a BitTorrent client.
type AnnounceResponse struct {
	IPv4Peers, IPv6Peers PeerIterator
	AnnounceIntervals
	Swarm
}

// AnnounceResponseWriter is used by an AnnounceHandler to construct a response.
type AnnounceResponseWriter interface {
	WriteAnnounceResponse(*AnnounceResponse)
	WriteError(error)
}

// AnnounceHandler is a function that operates on an Announce before a response
// has been written.
type AnnounceHandler func(context.Context, AnnounceResponseWriter, *AnnounceRequest) error

// AnnounceMiddleware enables the extension of an AnnounceHandler via a closure.
type AnnounceMiddleware func(AnnounceHandler) AnnounceHandler

// ScrapeRequest represents a transport-agnostic Scrape request sent from a
// BitTorrent client to a tracker.
type ScrapeRequest []Infohash

// ScrapeResponse represents the transport-agnostic Scrape response sent from
// a tracker to a BitTorrent client.
type ScrapeResponse map[Infohash]Swarm

// ScrapeResponseWriter is used by a ScrapeHandler to construct a response.
type ScrapeResponseWriter interface {
	WriteScrapeResponse(*ScrapeResponse)
	WriteError(error)
}

// ScrapeHandler is a function that operates on a Scrape before a response
// has been written.
type ScrapeHandler func(context.Context, ScrapeResponseWriter, *ScrapeRequest) error

// ScrapeMiddleware enables the extension of an ScrapeHandler via a closure.
type ScrapeMiddleware func(ScrapeHandler) ScrapeHandler

// AnnounceChain represents a composition of AnnounceMiddleware.
type AnnounceChain struct{ mw []AnnounceMiddleware }

// Append creates a new AnnounceChain by appending the provided middleware to
// the current chain.
func (c AnnounceChain) Append(mw ...AnnounceMiddleware) (nc AnnounceChain) {
	nc.mw = make([]AnnounceMiddleware, len(c.mw)+len(mw))
	copy(nc.mw[:len(c.mw)], c.mw)
	copy(nc.mw[len(c.mw):], mw)
	return
}

// Finalize creates an AnnounceHandler that is the composition of the chain.
func (c AnnounceChain) Finalize(final AnnounceHandler) AnnounceHandler {
	for i := len(c.mw) - 1; i >= 0; i-- {
		final = c.mw[i](final)
	}
	return final
}

// ScrapeChain represents a composition of ScrapeMiddleware.
type ScrapeChain struct{ mw []ScrapeMiddleware }

// Append creates a new ScrapeChain by appending the provided middleware to the
// current chain.
func (c ScrapeChain) Append(mw ...ScrapeMiddleware) (nc ScrapeChain) {
	nc.mw = make([]ScrapeMiddleware, len(c.mw)+len(mw))
	copy(nc.mw[:len(c.mw)], c.mw)
	copy(nc.mw[len(c.mw):], mw)
	return
}

// Finalize creates an ScrapeHandler that is the composition of the chain.
func (c ScrapeChain) Finalize(final ScrapeHandler) ScrapeHandler {
	for i := len(c.mw) - 1; i >= 0; i-- {
		final = c.mw[i](final)
	}
	return final
}

// ServeAnnounce creates an empty context and calls itself.
func (h AnnounceHandler) ServeAnnounce(w AnnounceResponseWriter, r *AnnounceRequest) {
	ctx := context.TODO()
	h(ctx, w, r)
}

// ServeScrape creates an empty context and calls itself.
func (h ScrapeHandler) ServeScrape(w ScrapeResponseWriter, r *ScrapeRequest) {
	ctx := context.TODO()
	h(ctx, w, r)
}

// NewTracker returns a Tracker given an AnnounceHandler and a ScrapeHandler.
func NewTracker(ah AnnounceHandler, sh ScrapeHandler) Tracker {
	return struct {
		AnnounceHandler
		ScrapeHandler
	}{
		ac,
		sc,
	}
}

// Tracker represents a BitTorrent tracker.
type Tracker interface {
	ServeAnnounce(AnnounceResponseWriter, *AnnounceRequest)
	ServeScrape(AnnounceResponseWriter, *ScrapeRequest)
}
<|endoftext|>


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

package main

import (
	"gopkg.in/fsnotify.v1"
	"github.com/BurntSushi/toml"
	"log"
	"os"
	"path/filepath"
	"flag"
	"net"
	"container/list"
	"encoding/json"
	"os/exec"
	"regexp"
	"fmt"
	"io"
	"time"
	"runtime"
)

const (
	FORMAT_GEM = "gem-listen"
	FORMAT_GO_JSON = "go-json"
)

var (
	configuration = &Configuration{}
	exclude *regexp.Regexp
	include *regexp.Regexp
)

func debug(message string) {
	if (configuration.Verbose == false) {
		return
	}

	log.Print(message, "\n")
}

func info(message string) {
	log.Print(message, "\n")
}

type Operation struct {
	Path      string
	Type      string
	Dir       string
	Filename  string
	Op        fsnotify.Op
	Operation string
}

func (o Operation) Name() string {
	if o.Op&fsnotify.Create == fsnotify.Create {
		return "CREATE"
	}
	if o.Op&fsnotify.Remove == fsnotify.Remove {
		return "REMOVE"
	}
	if o.Op&fsnotify.Write == fsnotify.Write {
		return "WRITE"
	}
	if o.Op&fsnotify.Rename == fsnotify.Rename {
		return "RENAME"
	}
	if o.Op&fsnotify.Chmod == fsnotify.Chmod {
		return "CHMOD"
	}

	return "WRITE"
}

func (o Operation) GemName() string {
	if o.Op&fsnotify.Create == fsnotify.Create {
		return "added"
	}
	if o.Op&fsnotify.Remove == fsnotify.Remove {
		return "removed"
	}
	if o.Op&fsnotify.Write == fsnotify.Write {
		return "modified"
	}
	if o.Op&fsnotify.Rename == fsnotify.Rename {
		return "removed"
	}
	if o.Op&fsnotify.Chmod == fsnotify.Chmod {
		return "modified"
	}

	return "modified"
}

type Configuration struct {
	Path                string
	Server              string
	Command             string
	Exclude             string
	Include             string
	ServerFormat        string
	ParallelCommand     string
	ServerMaxConnection int
	Verbose             bool
	FileConfiguration   string
	PrintConfiguration  bool
}

func NewServer(conf *Configuration) *Server {
	return &Server{
		listeners: list.New(),
		maxConnection: conf.ServerMaxConnection,
	}
}

type Server struct {
	listeners     *list.List
	maxConnection int
}

func (s *Server) SendMessage(message []byte) {
	for e := s.listeners.Front(); e != nil; e = e.Next() {
		conn := e.Value.(net.Conn)
		_, err := conn.Write(message)

		if err != nil {
			info(fmt.Sprintf("Error writing to: %+v, removing connection from the stacks", err))
			s.listeners.Remove(e)
			conn.Close()

			continue
		}

		conn.Write([]byte("\n"))
	}
}

func (s *Server) AddListener(l net.Conn) {

	if s.listeners.Len() + 1 > s.maxConnection {
		log.Printf("Drop client connection, too much connections\n")

		l.Write([]byte("Cannot register your client, too much connections\n"))
		l.Close()

		return
	}

	s.listeners.PushBack(l)
}

// Add folder recursively in the watcher scope
// The configuration object is used to get the root path and exclude/include information
func AddFolder(watcher *fsnotify.Watcher, conf *Configuration) error {
	path, _ := filepath.Abs(conf.Path)

	cpt := 0

	info(fmt.Sprintf("Scanning folders",))

	err := filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
		if err != nil {
			debug(fmt.Sprintf("Folder does not exist: ", err))
			panic(err)
		}

		if f.IsDir() {
			if include.Match([]byte(path)) == true && exclude.Match([]byte(path)) == false {

				debug(fmt.Sprintf("Add folder: %s", path))
				watcher.Add(path)

				cpt += 1
			}
		}

		return nil
	})

	info(fmt.Sprintf("%d folders added", cpt))
	info(fmt.Sprintf("End scanning."))

	return err
}

func StartServer(server *Server, conf *Configuration) {
	if conf.Server == "" {
		info("Server disabled")

		return
	}

	// Listen for incoming connections.
	l, err := net.Listen("tcp", conf.Server)

	if err != nil {
		log.Println("Error listening:", err.Error())
		os.Exit(1)
	}

	// Close the listener when the application closes.
	defer l.Close()

	info(fmt.Sprintf("Listening on %s", conf.Server))

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			log.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}

		server.AddListener(conn)
	}
}

func GetCommand(command string) *exec.Cmd {
	if runtime.GOOS == "windows" {
		return exec.Command("cmd", "/C %s", configuration.ParallelCommand)
	}

	return exec.Command("sh", "-c", configuration.ParallelCommand)
}

func init() {
	flag.StringVar(&configuration.Path, "path", "", "The path to watch")
	flag.BoolVar(&configuration.Verbose, "verbose", false, "Display verbose information")
	flag.StringVar(&configuration.Server, "server", "", "Open a TCP server with local modification")
	flag.StringVar(&configuration.Command, "command", "", "The command to start, use {file} as placeholder for the file")
	flag.StringVar(&configuration.Exclude, "exclude", "", "Folder pattern to ignore")
	flag.StringVar(&configuration.Include, "include", "", "Folder pattern to include (all by default)")
	flag.IntVar(&configuration.ServerMaxConnection, "server-max-connection", 8, "The number of maximun connection, default=8")
	flag.StringVar(&configuration.ServerFormat, "server-format", "", fmt.Sprintf("Output format, default to: %s (also: %s compatible with gem listen)", FORMAT_GO_JSON, FORMAT_GEM ))
	flag.StringVar(&configuration.ParallelCommand, "parallel-command", "", fmt.Sprintf("Run a command as a child process"))
	flag.StringVar(&configuration.FileConfiguration, "c", "", fmt.Sprintf("Configuration file to use"))
	flag.BoolVar(&configuration.PrintConfiguration, "p", false, fmt.Sprintf("Print the current configuration into stdout"))
}

func PrintConfiguration(conf *Configuration) {
	debug("")
	debug(fmt.Sprintf("> Configuration"))
	debug(fmt.Sprintf(">> Path: %s ", conf.Path))
	debug(fmt.Sprintf(">> Verbose: %b ", conf.Verbose))
	debug(fmt.Sprintf(">> Server: %s ", conf.Server))
	debug(fmt.Sprintf(">> Command: %s ", conf.Command))
	debug(fmt.Sprintf(">> Exclude: %s ", conf.Exclude))
	debug(fmt.Sprintf(">> Include: %s ", conf.Include))
	debug(fmt.Sprintf(">> ServerMaxConnection: %s ", conf.ServerMaxConnection))
	debug(fmt.Sprintf(">> ServerFormat: %s ", conf.ServerFormat))
	debug(fmt.Sprintf(">> ParallelCommand: %s ", conf.ParallelCommand))
	debug("")
	debug("")
}

// configure basic variable and check if the command can run properly
func configure() {
	var err error

	// parse command line argument
	flag.Parse()

	if configuration.FileConfiguration != "" {
		var fileConf = Configuration{}
		path, _ := filepath.Abs(configuration.FileConfiguration)

		if _, err := toml.DecodeFile(path, &fileConf); err != nil {
			info(fmt.Sprintf("Error while reading configuration file, %s", err))
		}

		if configuration.Command == "" {
			configuration.Command = fileConf.Command
		}

		if configuration.Path == "" {
			configuration.Path = fileConf.Path
		}

		if configuration.Server == "" {
			configuration.Server = fileConf.Server
		}

		if configuration.Exclude == "" {
			configuration.Exclude = fileConf.Exclude
		}

		if configuration.Include == "" {
			configuration.Include = fileConf.Include
		}

		if configuration.ServerMaxConnection == 0 {
			configuration.ServerMaxConnection = fileConf.ServerMaxConnection
		}

		if configuration.ServerFormat == "" {
			configuration.ServerFormat = fileConf.ServerFormat
		}

		if configuration.ParallelCommand == "" {
			configuration.ParallelCommand = fileConf.ParallelCommand
		}
	}

	// fix default value
	if configuration.Exclude == "" {
		configuration.Exclude = "((.*)/\\.git|\\.svn|node_modules|bower_components|/dist)"
	}

	if configuration.Include == "" {
		configuration.Include = "*"
	}

	if configuration.Path == "" {
		configuration.Path = "."
	}

	if configuration.ServerFormat == "" {
		configuration.ServerFormat = "127.0.0.1:4000"
	}

	if configuration.ServerFormat == "" {
		configuration.ServerFormat = FORMAT_GO_JSON
	}

	configuration.Path, _ = filepath.Abs(configuration.Path)

	if configuration.PrintConfiguration {
		encoder := toml.NewEncoder(os.Stdout)
		encoder.Encode(configuration)

		os.Exit(0)
	}

	// check if the command can run
	if configuration.Server == "" && configuration.Command == "" {
		log.Fatal("You need to set either a -server option or a -command option")
	}

	// parse Exclude and Include parameter
	exclude, err = regexp.Compile(fmt.Sprintf("^(.*)%s(.*)$", configuration.Exclude))

	if err != nil {
		panic(err)
	}

	include, err = regexp.Compile(fmt.Sprintf("^(.*)%s(.*)$", configuration.Include))

	if err != nil {
		panic(err)
	}

	PrintConfiguration(configuration)
}

func getWatcher() *fsnotify.Watcher {
	// start the watcher
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	AddFolder(watcher, configuration)

	return watcher
}

func startParallelCommand() {
//	var err error

	if configuration.ParallelCommand == "" {
		return;
	}

	for {
		info(fmt.Sprintf("Running command: %s", configuration.ParallelCommand))

		c := GetCommand(configuration.ParallelCommand)

		errReader, err := c.StderrPipe()
		outReader, err := c.StdoutPipe()

		err = c.Start()

		if err != nil {
			panic(err)
		}

		go io.Copy(os.Stdout, errReader)
		go io.Copy(os.Stdout, outReader)

		c.Wait()

		info(fmt.Sprintf("Parallel command exited, start a new one in 2s"))

		time.Sleep(2 * time.Second)
	}

}

func main() {
	var err error

	configure()

	info("golisten is a development tools and not suitable for production usage")
	info("   more information can be found at https://github.com/ekino/golisten")
	info("                                                Thomas Rabaix @ Ekino")
	info("")

	// start the watcher
	watcher := getWatcher()

	defer watcher.Close()

	done := make(chan bool)

	server := NewServer(configuration)

	go StartServer(server, configuration)

	running := false

	go func() {

		for {
			select {
			case event := <-watcher.Events:
				path, _ := filepath.Abs(event.Name)

				// move this into a NewOperation function
				op := Operation{
					Type: "file",
					Path: path,
					Dir: filepath.Dir(path),
					Filename: filepath.Base(path),
					Op: event.Op,
				}

				op.Operation = op.Name()

				debug(fmt.Sprintf("Operation: %s", op.Name()))

				if include.Match([]byte(path)) == false  {
					debug(fmt.Sprintf("Skipping: does not match include path: %s", op.Path))

					continue
				}

				if exclude.Match([]byte(path)) == true {
					debug(fmt.Sprintf("Skipping: does match exclude path: %s", op.Path))

					continue
				}

				debug(fmt.Sprintf("Event path: %s", op.Path))

				if configuration.Server != "" {
					// format compatible with gem listen
					// d["file","added","/Users/rande/Projects/go/gonode/src/github.com/rande/gonodeexplorer","test",{}]

					var raw []byte
					if configuration.ServerFormat == FORMAT_GEM {
						raw = []byte(fmt.Sprintf("d[\"%s\",\"%s\",\"%s\",\"%s\",{}]", op.Type, op.GemName(), op.Dir, op.Filename ))
					} else {
						raw, _ = json.Marshal(op)
					}

					debug(fmt.Sprintf("Raw message: %s", raw))
					server.SendMessage(raw)
				}

				if configuration.Command != "" {
					if running {
						debug(fmt.Sprintf("SKIPPING: Command already running", configuration.Command))
					} else {
						go func() {
							running = true

							debug(fmt.Sprintf("Running command: %s", configuration.Command))

							c := GetCommand(configuration.Command)
							output, err := c.CombinedOutput()
							if err != nil {
								info(fmt.Sprintf("Fail to run the command: %s", err))
							}

							info(fmt.Sprintf("Output command\n%s", output))

							running = false
						}()
					}
				}

				info(fmt.Sprintf("Operation: %s => %s", op.Name(), op.Filename))
			case err := <-watcher.Errors:
				info(fmt.Sprint("error: %s", err))
			}
		}
	}()

	go startParallelCommand()

	if err != nil {
		log.Fatal(err)
	}

	<-done
}
<commit_msg>Move to notify package rather than fsnotify<commit_after>// Copyright 2014-2015 Ekino. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/rjeczalik/notify"
)

const (
	FORMAT_GEM     = "gem-listen"
	FORMAT_GO_JSON = "go-json"
)

var (
	configuration = &Configuration{}
	exclude       *regexp.Regexp
	include       *regexp.Regexp
)

func debug(message string) {
	if configuration.Verbose == false {
		return
	}

	log.Print(message, "\n")
}

func info(message string) {
	log.Print(message, "\n")
}

type Operation struct {
	Path      string
	EventInfo notify.EventInfo
}

func (o Operation) Name() string {
	switch o.EventInfo.Event() {
	case notify.Create:
		return "CREATE"
	case notify.Remove:
		return "REMOVE"
	case notify.Rename:
		return "RENAME"
	default:
		return "WRITE"
	}
}

func (o Operation) GemName() string {
	switch o.EventInfo.Event() {
	case notify.Create:
		return "added"
	case notify.Remove:
		return "removed"
	case notify.Rename:
		return "removed"
	default:
		return "modified"
	}
}

type watcher struct {
	events chan notify.EventInfo
	ops    chan Operation
}

func newWatcher(path string, include, exclude *regexp.Regexp) (*watcher, error) {
	events := make(chan notify.EventInfo, 10)
	ops := make(chan Operation, 10)

	if err := notify.Watch(path+"/...", events, notify.All); err != nil {
		return nil, err
	}

	go func() {
		for ev := range events {
			if include.MatchString(ev.Path()) == false {
				debug(fmt.Sprintf("Skipping: does not match include path: %s", ev.Path()))
				continue
			}

			if exclude.MatchString(ev.Path()) == true {
				debug(fmt.Sprintf("Skipping: does match exclude path: %s", ev.Path()))
				continue
			}

			ops <- Operation{
				Path:      ev.Path(),
				EventInfo: ev,
			}
		}
	}()

	return &watcher{ops: ops, events: events}, nil
}

func (w *watcher) Watch() chan Operation {
	return w.ops
}

func (w *watcher) Close() {
	notify.Stop(w.events)
}

type Configuration struct {
	Path                string
	Server              string
	Command             string
	Exclude             string
	Include             string
	ServerFormat        string
	ParallelCommand     string
	ServerMaxConnection int
	Verbose             bool
	FileConfiguration   string
	PrintConfiguration  bool
}

func NewServer(conf *Configuration) *Server {
	return &Server{
		listeners:     list.New(),
		maxConnection: conf.ServerMaxConnection,
	}
}

type Server struct {
	listeners     *list.List
	maxConnection int
}

func (s *Server) SendMessage(message []byte) {
	for e := s.listeners.Front(); e != nil; e = e.Next() {
		conn := e.Value.(net.Conn)
		_, err := conn.Write(message)

		if err != nil {
			info(fmt.Sprintf("Error writing to: %+v, removing connection from the stacks", err))
			s.listeners.Remove(e)
			conn.Close()

			continue
		}

		conn.Write([]byte("\n"))
	}
}

func (s *Server) AddListener(l net.Conn) {
	if s.listeners.Len()+1 > s.maxConnection {
		log.Printf("Drop client connection, too much connections\n")

		l.Write([]byte("Cannot register your client, too much connections\n"))
		l.Close()

		return
	}

	s.listeners.PushBack(l)
}

func StartServer(server *Server, conf *Configuration) {
	if conf.Server == "" {
		info("Server disabled")
		return
	}

	// Listen for incoming connections.
	l, err := net.Listen("tcp", conf.Server)

	if err != nil {
		log.Println("Error listening:", err.Error())
		os.Exit(1)
	}

	// Close the listener when the application closes.
	defer l.Close()

	info(fmt.Sprintf("Listening on %s", conf.Server))

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			log.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}

		server.AddListener(conn)
	}
}

func GetCommand(command string) *exec.Cmd {
	if runtime.GOOS == "windows" {
		return exec.Command("cmd", "/C %s", configuration.ParallelCommand)
	}

	return exec.Command("sh", "-c", configuration.ParallelCommand)
}

func init() {
	flag.StringVar(&configuration.Path, "path", "", "The path to watch")
	flag.BoolVar(&configuration.Verbose, "verbose", false, "Display verbose information")
	flag.StringVar(&configuration.Server, "server", "", "Open a TCP server with local modification")
	flag.StringVar(&configuration.Command, "command", "", "The command to start, use {file} as placeholder for the file")
	flag.StringVar(&configuration.Exclude, "exclude", "", "Folder pattern to ignore")
	flag.StringVar(&configuration.Include, "include", "", "Folder pattern to include (all by default)")
	flag.IntVar(&configuration.ServerMaxConnection, "server-max-connection", 8, "The number of maximun connection, default=8")
	flag.StringVar(&configuration.ServerFormat, "server-format", "", fmt.Sprintf("Output format, default to: %s (also: %s compatible with gem listen)", FORMAT_GO_JSON, FORMAT_GEM))
	flag.StringVar(&configuration.ParallelCommand, "parallel-command", "", fmt.Sprintf("Run a command as a child process"))
	flag.StringVar(&configuration.FileConfiguration, "c", "", fmt.Sprintf("Configuration file to use"))
	flag.BoolVar(&configuration.PrintConfiguration, "p", false, fmt.Sprintf("Print the current configuration into stdout"))
}

func PrintConfiguration(conf *Configuration) {
	debug("")
	debug(fmt.Sprintf("> Configuration"))
	debug(fmt.Sprintf(">> Path: %s ", conf.Path))
	debug(fmt.Sprintf(">> Verbose: %b ", conf.Verbose))
	debug(fmt.Sprintf(">> Server: %s ", conf.Server))
	debug(fmt.Sprintf(">> Command: %s ", conf.Command))
	debug(fmt.Sprintf(">> Exclude: %s ", conf.Exclude))
	debug(fmt.Sprintf(">> Include: %s ", conf.Include))
	debug(fmt.Sprintf(">> ServerMaxConnection: %s ", conf.ServerMaxConnection))
	debug(fmt.Sprintf(">> ServerFormat: %s ", conf.ServerFormat))
	debug(fmt.Sprintf(">> ParallelCommand: %s ", conf.ParallelCommand))
	debug("")
	debug("")
}

// configure basic variable and check if the command can run properly
func configure() {
	var err error

	// parse command line argument
	flag.Parse()

	if configuration.FileConfiguration != "" {
		var fileConf = Configuration{}
		path, _ := filepath.Abs(configuration.FileConfiguration)

		if _, err := toml.DecodeFile(path, &fileConf); err != nil {
			info(fmt.Sprintf("Error while reading configuration file, %s", err))
		}

		if configuration.Command == "" {
			configuration.Command = fileConf.Command
		}

		if configuration.Path == "" {
			configuration.Path = fileConf.Path
		}

		if configuration.Server == "" {
			configuration.Server = fileConf.Server
		}

		if configuration.Exclude == "" {
			configuration.Exclude = fileConf.Exclude
		}

		if configuration.Include == "" {
			configuration.Include = fileConf.Include
		}

		if configuration.ServerMaxConnection == 0 {
			configuration.ServerMaxConnection = fileConf.ServerMaxConnection
		}

		if configuration.ServerFormat == "" {
			configuration.ServerFormat = fileConf.ServerFormat
		}

		if configuration.ParallelCommand == "" {
			configuration.ParallelCommand = fileConf.ParallelCommand
		}
	}

	// fix default value
	if configuration.Exclude == "" {
		configuration.Exclude = "((.*)/\\.git|\\.svn|node_modules|bower_components|/dist)"
	}

	if configuration.Include == "" {
		configuration.Include = "*"
	}

	if configuration.Path == "" {
		configuration.Path = "."
	}

	if configuration.ServerFormat == "" {
		configuration.ServerFormat = "127.0.0.1:4000"
	}

	if configuration.ServerFormat == "" {
		configuration.ServerFormat = FORMAT_GO_JSON
	}

	configuration.Path, _ = filepath.Abs(configuration.Path)

	if configuration.PrintConfiguration {
		encoder := toml.NewEncoder(os.Stdout)
		encoder.Encode(configuration)

		os.Exit(0)
	}

	// check if the command can run
	if configuration.Server == "" && configuration.Command == "" {
		log.Fatal("You need to set either a -server option or a -command option")
	}

	// parse Exclude and Include parameter
	exclude, err = regexp.Compile(fmt.Sprintf("^(.*)%s(.*)$", configuration.Exclude))

	if err != nil {
		panic(err)
	}

	include, err = regexp.Compile(fmt.Sprintf("^(.*)%s(.*)$", configuration.Include))

	if err != nil {
		panic(err)
	}

	PrintConfiguration(configuration)
}

func startParallelCommand() {
	//	var err error

	if configuration.ParallelCommand == "" {
		return
	}

	for {
		info(fmt.Sprintf("Running command: %s", configuration.ParallelCommand))

		c := GetCommand(configuration.ParallelCommand)

		errReader, err := c.StderrPipe()
		outReader, err := c.StdoutPipe()

		err = c.Start()

		if err != nil {
			panic(err)
		}

		go io.Copy(os.Stdout, errReader)
		go io.Copy(os.Stdout, outReader)

		c.Wait()

		info(fmt.Sprintf("Parallel command exited, start a new one in 2s"))

		time.Sleep(2 * time.Second)
	}
}

func formatMessage(op Operation, format string) (raw []byte, err error) {
	switch format {
	case FORMAT_GEM:
		buf := new(bytes.Buffer)

		// uint32(length) + json encoded array
		// https://github.com/guard/listen/blob/master/lib/listen/tcp/message.rb
		payload := []byte(fmt.Sprintf(
			`["file","%s","%s","%s",{}]`,
			op.GemName(), filepath.Dir(op.Path), filepath.Base(op.Path),
		))

		if err := binary.Write(buf, binary.BigEndian, uint32(len(payload))); err != nil {
			panic(err)
		}

		buf.Write(payload)
		return buf.Bytes(), nil

	case FORMAT_GO_JSON:
		return json.Marshal(struct{ Path, Type, Dir, Filename, Operation string }{
			Path:      op.Path,
			Type:      "file",
			Dir:       filepath.Dir(op.Path),
			Filename:  filepath.Base(op.Path),
			Operation: op.Name(),
		})
	}
	return nil, fmt.Errorf("Unrecognized format %s", format)
}

func main() {
	configure()

	info(fmt.Sprintf("Watching %s", configuration.Path))
	watcher, err := newWatcher(configuration.Path, include, exclude)
	if err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	done := make(chan bool)
	server := NewServer(configuration)

	go StartServer(server, configuration)
	running := false

	go func() {
		for {
			select {
			case op := <-watcher.Watch():
				info(fmt.Sprintf("Operation: %s %s", op.Name(), op.Path))

				if configuration.Server != "" {
					raw, err := formatMessage(op, configuration.ServerFormat)
					if err != nil {
						panic(err)
					}
					debug(fmt.Sprintf("Raw message: %s", raw))
					server.SendMessage(raw)
				}

				if configuration.Command != "" {
					if running {
						debug(fmt.Sprintf("SKIPPING: Command already running", configuration.Command))
					} else {
						go func() {
							running = true

							debug(fmt.Sprintf("Running command: %s", configuration.Command))

							c := GetCommand(configuration.Command)
							output, err := c.CombinedOutput()
							if err != nil {
								info(fmt.Sprintf("Fail to run the command: %s", err))
							}

							info(fmt.Sprintf("Output command\n%s", output))

							running = false
						}()
					}
				}
			}
		}
	}()

	go startParallelCommand()

	if err != nil {
		log.Fatal(err)
	}

	<-done
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/drone/drone-go/drone"
	"github.com/drone/drone-go/plugin"
)

var (
	build             string
	buildDate         string
	sshKeyPath        string = "/root/.ssh"
	sshPrivateKeyPath string = path.Join(sshKeyPath, "id_rsa")
	sshPublicKeyPath  string = path.Join(sshKeyPath, "id_rsa.pub")
)

func main() {
	fmt.Printf("Drone Capistrano Plugin built at %s\n", buildDate)

	workspace := drone.Workspace{}
	repo := drone.Repo{}
	build := drone.Build{}
	vargs := Params{}

	plugin.Param("workspace", &workspace)
	plugin.Param("repo", &repo)
	plugin.Param("build", &build)
	plugin.Param("vargs", &vargs)
	plugin.MustParse()

	fmt.Printf("Installing your deploy key to %s\n", sshKeyPath)
	os.MkdirAll(sshKeyPath, 0700)
	ioutil.WriteFile(sshPrivateKeyPath, []byte(workspace.Keys.Private), 0600)
	ioutil.WriteFile(sshPublicKeyPath, []byte(workspace.Keys.Public), 0644)

	// set private key to use with $GIT_SSH wrapper
	os.Setenv("GIT_SSH", "/git_ssh.sh")
	os.Setenv("GIT_SSH_KEY", sshPrivateKeyPath)

	tasks := strings.Fields(vargs.Tasks)

	if len(tasks) == 0 {
		fmt.Println("Please provide Capistrano tasks to execute")
		os.Exit(1)
		return
	}

	bundle := command(workspace, "bundle", "install")
	bundle.Run()

	capistrano := command(workspace, "bundle exec cap", tasks...)
	if err := capistrano.Run(); err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}
}

func command(w drone.Workspace, cmd string, args ...string) *exec.Cmd {
	c := exec.Command(cmd, args...)
	c.Dir = w.Path
	c.Env = os.Environ()
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	return c
}
<commit_msg>trying to get bundler not to reinstall everything that already exists in the cache folder<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/drone/drone-go/drone"
	"github.com/drone/drone-go/plugin"
)

var (
	build             string
	buildDate         string
	sshKeyPath        string = "/root/.ssh"
	sshPrivateKeyPath string = path.Join(sshKeyPath, "id_rsa")
	sshPublicKeyPath  string = path.Join(sshKeyPath, "id_rsa.pub")
)

func main() {
	fmt.Printf("Drone Capistrano Plugin built at %s\n", buildDate)

	workspace := drone.Workspace{}
	repo := drone.Repo{}
	build := drone.Build{}
	vargs := Params{}

	plugin.Param("workspace", &workspace)
	plugin.Param("repo", &repo)
	plugin.Param("build", &build)
	plugin.Param("vargs", &vargs)
	plugin.MustParse()

	fmt.Printf("Installing your deploy key to %s\n", sshKeyPath)
	os.MkdirAll(sshKeyPath, 0700)
	ioutil.WriteFile(sshPrivateKeyPath, []byte(workspace.Keys.Private), 0600)
	ioutil.WriteFile(sshPublicKeyPath, []byte(workspace.Keys.Public), 0644)

	// set private key to use with $GIT_SSH wrapper
	os.Setenv("GIT_SSH", "/git_ssh.sh")
	os.Setenv("GIT_SSH_KEY", sshPrivateKeyPath)

	tasks := strings.Fields(vargs.Tasks)

	if len(tasks) == 0 {
		fmt.Println("Please provide Capistrano tasks to execute")
		os.Exit(1)
		return
	}

	bundle := command(workspace, "bundle", "install --path build/bundle")
	bundle.Run()

	capistrano := command(workspace, "bundle exec cap", tasks...)
	if err := capistrano.Run(); err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}
}

func command(w drone.Workspace, cmd string, args ...string) *exec.Cmd {
	c := exec.Command(cmd, args...)
	c.Dir = w.Path
	c.Env = os.Environ()
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	return c
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"github.com/christophwitzko/github-release-download/release"
	"github.com/julienschmidt/httprouter"
	"log"
	"net/http"
	"os"
	"time"
)

var Client *release.GithubClient

func init() {
	Client = release.NewClient(os.Getenv("GITHUB_TOKEN"))
}

func logger(router http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s - %s %s", r.RemoteAddr, r.Method, r.URL.EscapedPath())
		router.ServeHTTP(w, r)
	})
}

func doRedirect(w http.ResponseWriter, r *http.Request, url string, err error) {
	if err != nil {
		log.Println(err)
		http.Error(w, "server error", http.StatusInternalServerError)
		return
	}
	if url == "" {
		http.NotFound(w, r)
		return
	}
	http.Redirect(w, r, url, 302)
}

func GetLatestDownload(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	url, err := Client.GetLatestDownloadUrl(ctx, ps[0].Value, ps[1].Value, ps[2].Value, ps[3].Value)
	doRedirect(w, r, url, err)
}

func GetMatchingDownload(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	url, err := Client.GetMatchingDownloadUrl(ctx, ps[0].Value, ps[1].Value, ps[2].Value, ps[3].Value, ps[4].Value)
	doRedirect(w, r, url, err)
}

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprintln(w, "https://get-release.xyz/:owner/:repo/:os/:arch{/:constraint}")
}

func main() {
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/:owner/:repo/:os/:arch", GetLatestDownload)
	router.GET("/:owner/:repo/:os/:arch/:constraint", GetMatchingDownload)
	log.Println("starting server on port 5000...")
	log.Fatal(http.ListenAndServe(":5000", logger(router)))
}
<commit_msg>fix: use correct request context<commit_after>package main

import (
	"context"
	"fmt"
	"github.com/christophwitzko/github-release-download/release"
	"github.com/julienschmidt/httprouter"
	"log"
	"net/http"
	"os"
	"time"
)

var Client *release.GithubClient

func init() {
	Client = release.NewClient(os.Getenv("GITHUB_TOKEN"))
}

func logger(router http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s - %s %s", r.RemoteAddr, r.Method, r.URL.EscapedPath())
		router.ServeHTTP(w, r)
	})
}

func doRedirect(w http.ResponseWriter, r *http.Request, url string, err error) {
	if err != nil {
		log.Println(err)
		http.Error(w, "server error", http.StatusInternalServerError)
		return
	}
	if url == "" {
		http.NotFound(w, r)
		return
	}
	http.Redirect(w, r, url, 302)
}

func GetLatestDownload(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
	defer cancel()
	url, err := Client.GetLatestDownloadUrl(ctx, ps[0].Value, ps[1].Value, ps[2].Value, ps[3].Value)
	doRedirect(w, r, url, err)
}

func GetMatchingDownload(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
	defer cancel()
	url, err := Client.GetMatchingDownloadUrl(ctx, ps[0].Value, ps[1].Value, ps[2].Value, ps[3].Value, ps[4].Value)
	doRedirect(w, r, url, err)
}

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprintln(w, "https://get-release.xyz/:owner/:repo/:os/:arch{/:constraint}")
}

func main() {
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/:owner/:repo/:os/:arch", GetLatestDownload)
	router.GET("/:owner/:repo/:os/:arch/:constraint", GetMatchingDownload)
	log.Println("starting server on port 5000...")
	log.Fatal(http.ListenAndServe(":5000", logger(router)))
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/go-kit/kit/log/level"
	"github.com/goph/emperror"
	"github.com/goph/healthz"
	"github.com/goph/serverz"
	"github.com/goph/stdlib/ext"
	"github.com/kelseyhightower/envconfig"
)

func main() {
	config := &configuration{}

	// Load configuration from environment
	err := envconfig.Process("", config)
	if err != nil {
		panic(err)
	}

	// Load configuration from flags
	flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	config.flags(flags)
	flags.Parse(os.Args[1:])

	// Create a new logger
	logger := newLogger(config)
	defer ext.Close(logger)

	// Create a new error handler
	errorHandler := newErrorHandler(config, logger)
	defer ext.Close(errorHandler)

	// Register error handler to recover from panics
	defer emperror.HandleRecover(errorHandler)

	healthCollector := healthz.Collector{}
	tracer := newTracer(config)
	metrics := newMetrics(config)
	defer ext.Close(metrics)

	// Application context
	appCtx := &application{
		config:          config,
		logger:          logger,
		errorHandler:    errorHandler,
		healthCollector: healthCollector,
		tracer:          tracer,
		metrics:         metrics,
	}

	serverQueue := serverz.NewQueue(&serverz.Manager{Logger: logger})

	level.Info(logger).Log(
		"msg", fmt.Sprintf("Starting %s", FriendlyServiceName),
		"version", Version,
		"commitHash", CommitHash,
		"buildDate", BuildDate,
		"environment", config.Environment,
	)

	if config.Debug {
		debugServer := newDebugServer(logger)
		serverQueue.Append(debugServer, config.DebugAddr)
		defer debugServer.Close()
	}

	server := newServer(appCtx)
	serverQueue.Prepend(server, config.ServiceAddr)
	defer server.Close()

	healthServer, status := newHealthServer(appCtx)
	serverQueue.Prepend(healthServer, config.HealthAddr)
	defer healthServer.Close()

	errChan := serverQueue.Start()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

MainLoop:
	for {
		select {
		case err := <-errChan:
			status.SetStatus(healthz.Unhealthy)
			level.Debug(logger).Log("msg", "Error received from error channel")
			emperror.HandleIfErr(errorHandler, err)

			// Break the loop, proceed with regular shutdown
			break MainLoop
		case s := <-signalChan:
			level.Info(logger).Log("msg", fmt.Sprintf("Captured %v", s))
			status.SetStatus(healthz.Unhealthy)

			level.Debug(logger).Log(
				"msg", "Shutting down with timeout",
				"timeout", config.ShutdownTimeout,
			)

			ctx, cancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

			err := serverQueue.Stop(ctx)
			if err != nil {
				errorHandler.Handle(err)
			}

			// Cancel context if shutdown completed earlier
			cancel()

			// Break the loop, proceed with regular shutdown
			break MainLoop
		}
	}
}
<commit_msg>Simplify application context creation<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/go-kit/kit/log/level"
	"github.com/goph/emperror"
	"github.com/goph/healthz"
	"github.com/goph/serverz"
	"github.com/goph/stdlib/ext"
	"github.com/kelseyhightower/envconfig"
)

func main() {
	config := &configuration{}

	// Load configuration from environment
	err := envconfig.Process("", config)
	if err != nil {
		panic(err)
	}

	// Load configuration from flags
	flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	config.flags(flags)
	flags.Parse(os.Args[1:])

	// Create a new logger
	logger := newLogger(config)
	defer ext.Close(logger)

	// Create a new error handler
	errorHandler := newErrorHandler(config, logger)
	defer ext.Close(errorHandler)

	// Register error handler to recover from panics
	defer emperror.HandleRecover(errorHandler)

	metrics := newMetrics(config)
	defer ext.Close(metrics)

	// Application context
	appCtx := &application{
		config:          config,
		logger:          logger,
		errorHandler:    errorHandler,
		healthCollector: healthz.Collector{},
		tracer:          newTracer(config),
		metrics:         metrics,
	}

	serverQueue := serverz.NewQueue(&serverz.Manager{Logger: logger})

	level.Info(logger).Log(
		"msg", fmt.Sprintf("Starting %s", FriendlyServiceName),
		"version", Version,
		"commitHash", CommitHash,
		"buildDate", BuildDate,
		"environment", config.Environment,
	)

	if config.Debug {
		debugServer := newDebugServer(logger)
		serverQueue.Append(debugServer, config.DebugAddr)
		defer debugServer.Close()
	}

	server := newServer(appCtx)
	serverQueue.Prepend(server, config.ServiceAddr)
	defer server.Close()

	healthServer, status := newHealthServer(appCtx)
	serverQueue.Prepend(healthServer, config.HealthAddr)
	defer healthServer.Close()

	errChan := serverQueue.Start()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

MainLoop:
	for {
		select {
		case err := <-errChan:
			status.SetStatus(healthz.Unhealthy)
			level.Debug(logger).Log("msg", "Error received from error channel")
			emperror.HandleIfErr(errorHandler, err)

			// Break the loop, proceed with regular shutdown
			break MainLoop
		case s := <-signalChan:
			level.Info(logger).Log("msg", fmt.Sprintf("Captured %v", s))
			status.SetStatus(healthz.Unhealthy)

			level.Debug(logger).Log(
				"msg", "Shutting down with timeout",
				"timeout", config.ShutdownTimeout,
			)

			ctx, cancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

			err := serverQueue.Stop(ctx)
			if err != nil {
				errorHandler.Handle(err)
			}

			// Cancel context if shutdown completed earlier
			cancel()

			// Break the loop, proceed with regular shutdown
			break MainLoop
		}
	}
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client
var PetDB *Pets

func main() {
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	PetDB = NewPets()
	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()

		//Log detail receive content
		if content != nil {
			log.Println("RECEIVE Msg:", content.IsMessage, " OP:", content.IsOperation, " type:", content.ContentType, " from:", content.From, "to:", content.To, " ID:", content.ID)
		}
		//Add with new friend.
		if content != nil && content.IsOperation {
			opContent, err := content.OperationContent()
			if err != nil {
				log.Println("OP retreival error:", err)
			} else {
				log.Println("OP detail from:", opContent.From, " ID:", opContent.ID, " TO:", opContent.To)
				log.Println("OP rawdata:", result.RawContent, " from:", result.RawContent.From, " to:", result.RawContent.To, " id:", result.RawContent.ID, " param:", result.RawContent.Params)
			}

			out := fmt.Sprintf("您好，感謝你加入成為好友一起幫助流浪動物找到新的家．輸入任何文字後，會隨機得到一個流浪動物，你可以不斷重複輸入文字然後查看目前所有的流浪動物．")
			_, err = bot.SendText(result.RawContent.Params, out)
			if err != nil {
				log.Println(err)
			}
			log.Println("New friend add, send cue to new friend.")
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {

			pet := PetDB.GetNextPet()
			out := fmt.Sprintf("您好，目前的動物：名為%s, 所在地為:%s, 敘述: %s 電話為:%s", pet.Name, pet.Resettlement, pet.Note, pet.Phone)

			// text, err := content.TextContent()
			_, err = bot.SendText([]string{content.From}, out)
			if err != nil {
				log.Println(err)
			}

			_, err = bot.SendImage([]string{content.From}, pet.ImageName, pet.ImageName)
			if err != nil {
				log.Println(err)
			}

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

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client
var PetDB *Pets

func main() {
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	PetDB = NewPets()
	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()

		//Log detail receive content
		if content != nil {
			log.Println("RECEIVE Msg:", content.IsMessage, " OP:", content.IsOperation, " type:", content.ContentType, " from:", content.From, "to:", content.To, " ID:", content.ID)
		}
		//Add with new friend.
		if content != nil && content.IsOperation {
			opContent, err := content.OperationContent()
			if err != nil {
				log.Println("OP retreival error:", err)
			} else {
				log.Println("OP detail from:", opContent.From, " ID:", opContent.ID, " TO:", opContent.To)
				log.Println("OP rawdata:", result.RawContent, " from:", result.RawContent.From, " to:", result.RawContent.To, " id:", result.RawContent.ID, " param:", result.RawContent.Params)
			}

			out := fmt.Sprintf("您好，感謝你加入成為好友一起幫助流浪動物找到新的家．輸入任何文字後，會隨機得到一個流浪動物，你可以不斷重複輸入文字然後查看目前所有的流浪動物．")
			_, err = bot.SendText([]string{result.RawContent.Params[0]}, out)
			if err != nil {
				log.Println(err)
			}
			log.Println("New friend add, send cue to new friend.")
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {

			pet := PetDB.GetNextPet()
			out := fmt.Sprintf("您好，目前的動物：名為%s, 所在地為:%s, 敘述: %s 電話為:%s", pet.Name, pet.Resettlement, pet.Note, pet.Phone)

			// text, err := content.TextContent()
			_, err = bot.SendText([]string{content.From}, out)
			if err != nil {
				log.Println(err)
			}

			_, err = bot.SendImage([]string{content.From}, pet.ImageName, pet.ImageName)
			if err != nil {
				log.Println(err)
			}

		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/skratchdot/open-golang/open"
)

func in(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

var acceptedImageExt = []string{".jpg", ".jpeg"}
var dirThumbs = fmt.Sprintf("%s%s", os.Getenv("HOME"), "/.cache/lk")
var dirPath = "."
var gitVersion string
var showVersionFlag = flag.Bool("version", false, "Show version")
var port = flag.Int("port", 0, "listen port")

func main() {

	flag.Parse()

	if *showVersionFlag {
		log.Println("lk", gitVersion, "https://github.com/kaihendry/lk")
		os.Exit(0)
	}

	directory := flag.Arg(0)
	dirPath, _ = filepath.Abs(directory)

	// Don't allow path under dirPath to be viewed
	http.Handle("/o/", http.StripPrefix(path.Join("/o", dirPath), http.FileServer(http.Dir(dirPath))))
	http.HandleFunc("/favicon.ico", http.NotFound)

	http.HandleFunc("/", lk)
	http.HandleFunc("/t/", thumb)

	// http://stackoverflow.com/a/33985208/4534
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Panic(err)
	}

	hostname, _ := os.Hostname()
	if a, ok := ln.Addr().(*net.TCPAddr); ok {
		host := fmt.Sprintf("http://%s:%d", hostname, a.Port)
		log.Println("Serving from", host)
		open.Start(host)
	}
	if err := http.Serve(ln, nil); err != nil {
		log.Panic(err)
	}

}

func thumb(w http.ResponseWriter, r *http.Request) {

	// Make sure you can't go under the dirPath
	if !strings.HasPrefix(r.URL.Path[2:], dirPath) {
		http.NotFound(w, r)
		return
	}

	thumbPath := filepath.Join(dirThumbs, r.URL.Path[2:])
	if _, err := os.Stat(thumbPath); err != nil {
		log.Println("THUMB:", thumbPath, "does not exist")
		srcPath := r.URL.Path[2:]
		if _, err := os.Stat(srcPath); err != nil {
			log.Println("ORIGINAL", srcPath, "does not exist")
			http.NotFound(w, r)
			return
		} else {
			log.Println("Must generate thumb for", srcPath)
			err := genthumb(srcPath, thumbPath)
			if err != nil {
				http.Error(w, err.Error(), 400)
				return
			}
			log.Println("Created thumb", thumbPath)
		}
	}
	log.Println("Serving thumb", thumbPath)
	http.ServeFile(w, r, thumbPath)
}

func lk(w http.ResponseWriter, r *http.Request) {

	// log.Println("dirPath", dirPath, "Web path", r.URL.Path)
	srcPath := filepath.Join(dirPath, r.URL.Path)
	// log.Println("Combined", srcPath)

	files, err := ioutil.ReadDir(srcPath)
	// log.Println(files)

	if err != nil {
		panic(err)
	}

	dirs := []string{}
	images := []string{}

	for _, f := range files {

		if strings.HasPrefix(filepath.Base(f.Name()), ".") {
			continue
		}

		// log.Println("Filename", f.Name())
		if f.IsDir() {
			// log.Println(f.Name(), "is a directory")
			// TODO check they have a JPG in them?
			dirs = append(dirs, filepath.Join(r.URL.Path, f.Name()))
		}
		// Only append jpg images
		if in(acceptedImageExt, strings.ToLower(path.Ext(f.Name()))) {
			log.Printf("Appending %s", f.Name())
			images = append(images, filepath.Join(srcPath, f.Name()))
		}
	}

	t, err := template.New("foo").Parse(`<!DOCTYPE html>

<meta charset="utf-8" />

{{ range .Dirs }}
{{ . }}

{{ end }}

{{ range .Images }}
{{ end }}

By lk {{ .Version }}

`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := struct {
		Images  []string
		Dirs    []string
		Version string
	}{
		images,
		dirs,
		gitVersion,
	}

	t.Execute(w, data)

	log.Printf("%s %s %s %s\n", r.RemoteAddr, r.Method, r.URL, r.UserAgent())

}
<commit_msg>Return .local address if hostname isn't fully qualified (assuming zeroconf/avahi)<commit_after>package main

import (
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/skratchdot/open-golang/open"
)

func in(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

var acceptedImageExt = []string{".jpg", ".jpeg"}
var dirThumbs = fmt.Sprintf("%s%s", os.Getenv("HOME"), "/.cache/lk")
var dirPath = "."
var gitVersion string
var showVersionFlag = flag.Bool("version", false, "Show version")
var port = flag.Int("port", 0, "listen port")

func hostname() string {
	hostname, _ := os.Hostname()
	if strings.Split(hostname, ".")[0] == hostname {
		return hostname + ".local"
	}
	return hostname
}

func main() {

	flag.Parse()

	if *showVersionFlag {
		log.Println("lk", gitVersion, "https://github.com/kaihendry/lk")
		os.Exit(0)
	}

	directory := flag.Arg(0)
	dirPath, _ = filepath.Abs(directory)

	// Don't allow path under dirPath to be viewed
	http.Handle("/o/", http.StripPrefix(path.Join("/o", dirPath), http.FileServer(http.Dir(dirPath))))
	http.HandleFunc("/favicon.ico", http.NotFound)

	http.HandleFunc("/", lk)
	http.HandleFunc("/t/", thumb)

	// http://stackoverflow.com/a/33985208/4534
	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Panic(err)
	}

	if a, ok := ln.Addr().(*net.TCPAddr); ok {
		hostname := hostname()
		host := fmt.Sprintf("http://%s:%d", hostname, a.Port)
		log.Println("Serving from", host)
		open.Start(host)
	}
	if err := http.Serve(ln, nil); err != nil {
		log.Panic(err)
	}

}

func thumb(w http.ResponseWriter, r *http.Request) {

	// Make sure you can't go under the dirPath
	if !strings.HasPrefix(r.URL.Path[2:], dirPath) {
		http.NotFound(w, r)
		return
	}

	thumbPath := filepath.Join(dirThumbs, r.URL.Path[2:])
	if _, err := os.Stat(thumbPath); err != nil {
		log.Println("THUMB:", thumbPath, "does not exist")
		srcPath := r.URL.Path[2:]
		if _, err := os.Stat(srcPath); err != nil {
			log.Println("ORIGINAL", srcPath, "does not exist")
			http.NotFound(w, r)
			return
		} else {
			log.Println("Must generate thumb for", srcPath)
			err := genthumb(srcPath, thumbPath)
			if err != nil {
				http.Error(w, err.Error(), 400)
				return
			}
			log.Println("Created thumb", thumbPath)
		}
	}
	log.Println("Serving thumb", thumbPath)
	http.ServeFile(w, r, thumbPath)
}

func lk(w http.ResponseWriter, r *http.Request) {

	// log.Println("dirPath", dirPath, "Web path", r.URL.Path)
	srcPath := filepath.Join(dirPath, r.URL.Path)
	// log.Println("Combined", srcPath)

	files, err := ioutil.ReadDir(srcPath)
	// log.Println(files)

	if err != nil {
		panic(err)
	}

	dirs := []string{}
	images := []string{}

	for _, f := range files {

		if strings.HasPrefix(filepath.Base(f.Name()), ".") {
			continue
		}

		// log.Println("Filename", f.Name())
		if f.IsDir() {
			// log.Println(f.Name(), "is a directory")
			// TODO check they have a JPG in them?
			dirs = append(dirs, filepath.Join(r.URL.Path, f.Name()))
		}
		// Only append jpg images
		if in(acceptedImageExt, strings.ToLower(path.Ext(f.Name()))) {
			log.Printf("Appending %s", f.Name())
			images = append(images, filepath.Join(srcPath, f.Name()))
		}
	}

	t, err := template.New("foo").Parse(`<!DOCTYPE html>

<meta charset="utf-8" />

{{ range .Dirs }}
{{ . }}

{{ end }}

{{ range .Images }}
{{ end }}

By lk {{ .Version }}

`)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := struct {
		Images  []string
		Dirs    []string
		Version string
	}{
		images,
		dirs,
		gitVersion,
	}

	t.Execute(w, data)

	log.Printf("%s %s %s %s\n", r.RemoteAddr, r.Method, r.URL, r.UserAgent())

}
<|endoftext|>