<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"github.com/schollz/closestmatch"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/calendar/v3"
)

func main() {
	srv := getService()

	var calendar string
	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "--help":
			fmt.Printf("Usage: galendar [calendar]\n")
			os.Exit(0)
		case "-h":
			fmt.Printf("Usage: galendar [calendar]\n")
			os.Exit(0)
		default:
			calendar = os.Args[1]
		}
	}

	query(srv, calendar)
}

// getService does the oauth dance and creates a service from the provided credentials
func getService() *calendar.Service {
	ctx := context.Background()

	// Get client secret
	b, err := getClientSecret()
	if err != nil {
		log.Fatalf("Unable to get client secret file: %v", err)
	}

	// If modifying these scopes, delete your previously saved credentials
	// at ~/.credentials/calendar-go-quickstart.json
	config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope)
	if err != nil {
		log.Fatalf("Unable to parse client secret file to config: %v", err)
	}
	client := getClient(ctx, config)

	srv, err := calendar.New(client)
	if err != nil {
		log.Fatalf("Unable to retrieve calendar Client %v", err)
	}

	return srv
}

// getClientSecret read the json file in ~/.credentials
func getClientSecret() ([]byte, error) {
	usr, err := user.Current()
	if err != nil {
		return nil, err
	}

	clientSecretFile := filepath.Join(usr.HomeDir, ".credentials/galendar_client_secret.json")
	return ioutil.ReadFile(clientSecretFile)
}

// getClient uses a Context and Config to retrieve a Token
// then generate a Client. It returns the generated Client.
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
	cacheFile, err := tokenCacheFile()
	if err != nil {
		log.Fatalf("Unable to get path to cached credential file. %v", err)
	}

	tok, err := tokenFromFile(cacheFile)
	if err != nil {
		tok = getTokenFromWeb(config)
		saveToken(cacheFile, tok)
	}
	return config.Client(ctx, tok)
}

// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func tokenCacheFile() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
	return filepath.Join(tokenCacheDir, "galendar_client_token.json"), err
}

// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}

	t := &oauth2.Token{}
	err = json.NewDecoder(f).Decode(t)
	defer f.Close()
	return t, err
}

// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
	authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
	fmt.Printf("Go to the following link in your browser then type the "+
		"authorization code: \n%v\n", authURL)

	var code string
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatalf("Unable to read authorization code %v", err)
	}

	tok, err := config.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatalf("Unable to retrieve token from web %v", err)
	}
	return tok
}

// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) {
	fmt.Printf("Saving credential file to: %s\n", file)
	f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to cache oauth token: %v", err)
	}

	defer f.Close()
	json.NewEncoder(f).Encode(token)
}

func query(srv *calendar.Service, calendar string) {
	t := time.Now()
	tMin := t.Format(time.RFC3339)
	tMax := t.Add(24 * time.Hour).Format(time.RFC3339)

	var calendarID string
	var err error
	if calendar != "" {
		calendarID, err = getIDFromList(srv, calendar)
		if err != nil {
			log.Fatalf("Unable to find a calendar from the provided calendar %s: %v", calendarID, err)
		}

		if calendarID == "" {
			log.Fatalf("No matching calendar from the provided calendar: %s", calendar)
		}

		if calendar != calendarID {
			fmt.Printf("No exact match for %s, but found %s\n\n", calendar, calendarID)
		}
	} else {
		calendarID = "primary"
	}

	events, err := srv.Events.List(calendarID).ShowDeleted(false).
		SingleEvents(true).TimeMin(tMin).TimeMax(tMax).MaxResults(10).OrderBy("startTime").Do()
	if err != nil {
		log.Fatalf("Unable to retrieve next ten of the user's events: %v for calendar %s", err, calendarID)
	}

	fmt.Printf("Upcoming events for %s:\n\n", calendarID)
	if len(events.Items) > 0 {
		for _, i := range events.Items {
			var start string
			var end string
			// If the DateTime is an empty string the Event is an all-day Event.
			// So only Date is available.
			if i.Start.DateTime != "" {
				start = i.Start.DateTime
				end = i.End.DateTime
				startTime, err := time.Parse(time.RFC3339, start)
				if err != nil {
					log.Fatalf("Failed to parse event's time: %v", err)
				}

				start = onlyShowTime(start)
				end = onlyShowTime(end)
				if t.After(startTime) {
					fmt.Printf("Happening now: %s (%s-%s)\n", i.Summary, start, end)
				} else {
					fmt.Printf("%s (%s-%s)\n", i.Summary, start, end)
				}
			} else {
				start = i.Start.Date
				fmt.Printf("Full-day: %s (%s)\n", i.Summary, start)
			}
		}
	} else {
		fmt.Printf("No upcoming events found.\n")
	}
}

func onlyShowTime(dateTime string) string {
	time := strings.Split(strings.Split(dateTime, "T")[1], "Z")[0]
	return time
}

func getIDFromList(srv *calendar.Service, calendarID string) (string, error) {
	list, err := srv.CalendarList.List().Do()
	if err != nil {
		return "", err
	}

	infos := []string{}
	bagSizes := []int{3}
	for _, calendar := range list.Items {
		infos = append(infos, calendar.Id)
	}

	cm := closestmatch.New(infos, bagSizes)

	return cm.Closest(calendarID), nil
}
<commit_msg>Set max results to 20<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"github.com/schollz/closestmatch"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/calendar/v3"
)

func main() {
	srv := getService()

	var calendar string
	if len(os.Args) > 1 {
		switch os.Args[1] {
		case "--help":
			fmt.Printf("Usage: galendar [calendar]\n")
			os.Exit(0)
		case "-h":
			fmt.Printf("Usage: galendar [calendar]\n")
			os.Exit(0)
		default:
			calendar = os.Args[1]
		}
	}

	query(srv, calendar)
}

// getService does the oauth dance and creates a service from the provided credentials
func getService() *calendar.Service {
	ctx := context.Background()

	// Get client secret
	b, err := getClientSecret()
	if err != nil {
		log.Fatalf("Unable to get client secret file: %v", err)
	}

	// If modifying these scopes, delete your previously saved credentials
	// at ~/.credentials/calendar-go-quickstart.json
	config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope)
	if err != nil {
		log.Fatalf("Unable to parse client secret file to config: %v", err)
	}
	client := getClient(ctx, config)

	srv, err := calendar.New(client)
	if err != nil {
		log.Fatalf("Unable to retrieve calendar Client %v", err)
	}

	return srv
}

// getClientSecret read the json file in ~/.credentials
func getClientSecret() ([]byte, error) {
	usr, err := user.Current()
	if err != nil {
		return nil, err
	}

	clientSecretFile := filepath.Join(usr.HomeDir, ".credentials/galendar_client_secret.json")
	return ioutil.ReadFile(clientSecretFile)
}

// getClient uses a Context and Config to retrieve a Token
// then generate a Client. It returns the generated Client.
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
	cacheFile, err := tokenCacheFile()
	if err != nil {
		log.Fatalf("Unable to get path to cached credential file. %v", err)
	}

	tok, err := tokenFromFile(cacheFile)
	if err != nil {
		tok = getTokenFromWeb(config)
		saveToken(cacheFile, tok)
	}
	return config.Client(ctx, tok)
}

// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func tokenCacheFile() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
	return filepath.Join(tokenCacheDir, "galendar_client_token.json"), err
}

// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}

	t := &oauth2.Token{}
	err = json.NewDecoder(f).Decode(t)
	defer f.Close()
	return t, err
}

// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
	authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
	fmt.Printf("Go to the following link in your browser then type the "+
		"authorization code: \n%v\n", authURL)

	var code string
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatalf("Unable to read authorization code %v", err)
	}

	tok, err := config.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatalf("Unable to retrieve token from web %v", err)
	}
	return tok
}

// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) {
	fmt.Printf("Saving credential file to: %s\n", file)
	f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to cache oauth token: %v", err)
	}

	defer f.Close()
	json.NewEncoder(f).Encode(token)
}

func query(srv *calendar.Service, calendar string) {
	t := time.Now()
	tMin := t.Format(time.RFC3339)
	tMax := t.Add(24 * time.Hour).Format(time.RFC3339)

	var calendarID string
	var err error
	if calendar != "" {
		calendarID, err = getIDFromList(srv, calendar)
		if err != nil {
			log.Fatalf("Unable to find a calendar from the provided calendar %s: %v", calendarID, err)
		}

		if calendarID == "" {
			log.Fatalf("No matching calendar from the provided calendar: %s", calendar)
		}

		if calendar != calendarID {
			fmt.Printf("No exact match for %s, but found %s\n\n", calendar, calendarID)
		}
	} else {
		calendarID = "primary"
	}

	events, err := srv.Events.List(calendarID).ShowDeleted(false).
		SingleEvents(true).TimeMin(tMin).TimeMax(tMax).MaxResults(20).OrderBy("startTime").Do()
	if err != nil {
		log.Fatalf("Unable to retrieve next ten of the user's events: %v for calendar %s", err, calendarID)
	}

	fmt.Printf("Upcoming events for %s:\n\n", calendarID)
	if len(events.Items) > 0 {
		for _, i := range events.Items {
			var start string
			var end string
			// If the DateTime is an empty string the Event is an all-day Event.
			// So only Date is available.
			if i.Start.DateTime != "" {
				start = i.Start.DateTime
				end = i.End.DateTime
				startTime, err := time.Parse(time.RFC3339, start)
				if err != nil {
					log.Fatalf("Failed to parse event's time: %v", err)
				}

				start = onlyShowTime(start)
				end = onlyShowTime(end)
				if t.After(startTime) {
					fmt.Printf("Happening now: %s (%s-%s)\n", i.Summary, start, end)
				} else {
					fmt.Printf("%s (%s-%s)\n", i.Summary, start, end)
				}
			} else {
				start = i.Start.Date
				fmt.Printf("Full-day: %s (%s)\n", i.Summary, start)
			}
		}
	} else {
		fmt.Printf("No upcoming events found.\n")
	}
}

func onlyShowTime(dateTime string) string {
	time := strings.Split(strings.Split(dateTime, "T")[1], "Z")[0]
	return time
}

func getIDFromList(srv *calendar.Service, calendarID string) (string, error) {
	list, err := srv.CalendarList.List().Do()
	if err != nil {
		return "", err
	}

	infos := []string{}
	bagSizes := []int{3}
	for _, calendar := range list.Items {
		infos = append(infos, calendar.Id)
	}

	cm := closestmatch.New(infos, bagSizes)

	return cm.Closest(calendarID), nil
}
<|endoftext|>


<commit_before>// Package main generates web project.
package main

import (
	"flag"
	"github.com/go-bootstrap/go-bootstrap/helpers"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

func main() {
	dir := flag.String("dir", "", "Project directory relative to $GOPATH/src/")
	flag.Parse()

	if *dir == "" {
		log.Fatal("dir option is missing.")
	}

	// There can be more than one path, separated by colon.
	gopaths := strings.Split(os.ExpandEnv("$GOPATH"), ":")
	gopath := gopaths[0]

	fullpath := filepath.Join(gopath, "src", *dir)
	migrationsPath := filepath.Join(fullpath, "migrations")
	dirChunks := strings.Split(*dir, "/")
	repoName := dirChunks[len(dirChunks)-3]
	repoUser := dirChunks[len(dirChunks)-2]
	projectName := dirChunks[len(dirChunks)-1]
	dbName := projectName
	testDbName := projectName + "-test"
	currentUser, _ := user.Current()

	// 1. Create target directory
	log.Print("Creating " + fullpath + "...")
	err := os.MkdirAll(fullpath, 0755)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Copy everything under blank directory to target directory.
	log.Print("Copying a blank project to " + fullpath + "...")
	if output, err := exec.Command("cp", "-rf", "./blank/.", fullpath).CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	// 3. Interpolate placeholder variables on the new project.
	log.Print("Replacing placeholder variables on " + repoUser + "/" + projectName + "...")
	replacers := make(map[string]string)
	replacers["$GO_BOOTSTRAP_REPO_NAME"] = repoName
	replacers["$GO_BOOTSTRAP_REPO_USER"] = repoUser
	replacers["$GO_BOOTSTRAP_PROJECT_NAME"] = projectName
	replacers["$GO_BOOTSTRAP_COOKIE_SECRET"] = helpers.RandString(16)
	replacers["$GO_BOOTSTRAP_CURRENT_USER"] = currentUser.Username
	replacers["$GO_BOOTSTRAP_DOCKERFILE_DSN"] = helpers.DefaultPGDSN(dbName)
	if err := helpers.RecursiveSearchReplaceFiles(fullpath, replacers); err != nil {
		log.Fatal(err)
	}

	// 4. Create PostgreSQL databases.
	for _, name := range []string{dbName, testDbName} {
		log.Print("Creating a database named " + name + "...")
		if exec.Command("createdb", name).Run() != nil {
			log.Print("Unable to create PostgreSQL database: " + name)
		}
	}

	// 5.a. go get github.com/mattes/migrate.
	log.Print("Installing github.com/mattes/migrate...")
	if output, err := exec.Command("go", "get", "github.com/mattes/migrate").CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	// 5.b. Run migrations on localhost:5432.
	for _, name := range []string{dbName, testDbName} {
		pgDSN := helpers.DefaultPGDSN(name)

		log.Print("Running database migrations on " + pgDSN + "...")
		if output, err := exec.Command("migrate", "-url", pgDSN, "-path", migrationsPath, "up").CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}
	}

	// 6. Get all application dependencies for the first time.
	log.Print("Running go get ./...")
	cmd := exec.Command("go", "get", "./...")
	cmd.Dir = fullpath
	if output, err := cmd.CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	repoIsGit := strings.HasPrefix(repoName, "git")

	if repoIsGit {
		// Generate Godeps directory. Currently only works on git related repo.
		log.Print("Installing github.com/tools/godep...")
		if output, err := exec.Command("go", "get", "github.com/tools/godep").CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// git init.
		log.Print("Running git init")
		cmd := exec.Command("git", "init")
		cmd.Dir = fullpath
		if output, err := cmd.CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// godep save.
		log.Print("Running godep save")
		cmd = exec.Command("godep", "save")
		cmd.Dir = fullpath
		if output, err := cmd.CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// Run tests on newly generated app.
		log.Print("Running godep go test ./...")
		cmd = exec.Command("godep", "go", "test", "./...")
		cmd.Dir = fullpath
		output, _ := cmd.CombinedOutput()
		log.Print(string(output))

	} else {
		// Run tests on newly generated app.
		log.Print("Running go test ./...")
		cmd = exec.Command("go", "test", "./...")
		cmd.Dir = fullpath
		output, _ := cmd.CombinedOutput()
		log.Print(string(output))
	}
}
<commit_msg>Run godep save ./… to also save testing dependencies.<commit_after>// Package main generates web project.
package main

import (
	"flag"
	"github.com/go-bootstrap/go-bootstrap/helpers"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

func main() {
	dir := flag.String("dir", "", "Project directory relative to $GOPATH/src/")
	flag.Parse()

	if *dir == "" {
		log.Fatal("dir option is missing.")
	}

	// There can be more than one path, separated by colon.
	gopaths := strings.Split(os.ExpandEnv("$GOPATH"), ":")
	gopath := gopaths[0]

	fullpath := filepath.Join(gopath, "src", *dir)
	migrationsPath := filepath.Join(fullpath, "migrations")
	dirChunks := strings.Split(*dir, "/")
	repoName := dirChunks[len(dirChunks)-3]
	repoUser := dirChunks[len(dirChunks)-2]
	projectName := dirChunks[len(dirChunks)-1]
	dbName := projectName
	testDbName := projectName + "-test"
	currentUser, _ := user.Current()

	// 1. Create target directory
	log.Print("Creating " + fullpath + "...")
	err := os.MkdirAll(fullpath, 0755)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Copy everything under blank directory to target directory.
	log.Print("Copying a blank project to " + fullpath + "...")
	if output, err := exec.Command("cp", "-rf", "./blank/.", fullpath).CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	// 3. Interpolate placeholder variables on the new project.
	log.Print("Replacing placeholder variables on " + repoUser + "/" + projectName + "...")
	replacers := make(map[string]string)
	replacers["$GO_BOOTSTRAP_REPO_NAME"] = repoName
	replacers["$GO_BOOTSTRAP_REPO_USER"] = repoUser
	replacers["$GO_BOOTSTRAP_PROJECT_NAME"] = projectName
	replacers["$GO_BOOTSTRAP_COOKIE_SECRET"] = helpers.RandString(16)
	replacers["$GO_BOOTSTRAP_CURRENT_USER"] = currentUser.Username
	replacers["$GO_BOOTSTRAP_DOCKERFILE_DSN"] = helpers.DefaultPGDSN(dbName)
	if err := helpers.RecursiveSearchReplaceFiles(fullpath, replacers); err != nil {
		log.Fatal(err)
	}

	// 4. Create PostgreSQL databases.
	for _, name := range []string{dbName, testDbName} {
		log.Print("Creating a database named " + name + "...")
		if exec.Command("createdb", name).Run() != nil {
			log.Print("Unable to create PostgreSQL database: " + name)
		}
	}

	// 5.a. go get github.com/mattes/migrate.
	log.Print("Installing github.com/mattes/migrate...")
	if output, err := exec.Command("go", "get", "github.com/mattes/migrate").CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	// 5.b. Run migrations on localhost:5432.
	for _, name := range []string{dbName, testDbName} {
		pgDSN := helpers.DefaultPGDSN(name)

		log.Print("Running database migrations on " + pgDSN + "...")
		if output, err := exec.Command("migrate", "-url", pgDSN, "-path", migrationsPath, "up").CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}
	}

	// 6. Get all application dependencies for the first time.
	log.Print("Running go get ./...")
	cmd := exec.Command("go", "get", "./...")
	cmd.Dir = fullpath
	if output, err := cmd.CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	repoIsGit := strings.HasPrefix(repoName, "git")

	if repoIsGit {
		// Generate Godeps directory. Currently only works on git related repo.
		log.Print("Installing github.com/tools/godep...")
		if output, err := exec.Command("go", "get", "github.com/tools/godep").CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// git init.
		log.Print("Running git init")
		cmd := exec.Command("git", "init")
		cmd.Dir = fullpath
		if output, err := cmd.CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// godep save ./....
		log.Print("Running godep save ./...")
		cmd = exec.Command("godep", "save", "./...")
		cmd.Dir = fullpath
		if output, err := cmd.CombinedOutput(); err != nil {
			log.Fatal(string(output))
		}

		// Run tests on newly generated app.
		log.Print("Running godep go test ./...")
		cmd = exec.Command("godep", "go", "test", "./...")
		cmd.Dir = fullpath
		output, _ := cmd.CombinedOutput()
		log.Print(string(output))

	} else {
		// Run tests on newly generated app.
		log.Print("Running go test ./...")
		cmd = exec.Command("go", "test", "./...")
		cmd.Dir = fullpath
		output, _ := cmd.CombinedOutput()
		log.Print(string(output))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/howeyc/fsnotify"
	"github.com/levicook/go-detect"
)

var (
	watcher  *fsnotify.Watcher
	watched  = make(map[string]struct{})
	exitCode = make(chan int)
	rootPath string

	buildQueued = true

	// command line flags we pay attention to
	afterAllOk string
	afterNotOk string
	verbose    bool

	// environment variables we pay attention to
	buildArgs = detect.String(os.Getenv("BUILD_ARGS"), "./...")
	vetArgs   = detect.String(os.Getenv("VET_ARGS"), "./...")
	testArgs  = detect.String(os.Getenv("TEST_ARGS"), "./...")
)

func runCmd(name string, args ...string) (err error) {
	buf := new(commandBuffer)

	cmd := exec.Command(name, args...)
	cmd.Stdout = buf
	cmd.Stderr = buf
	cmd.Dir = rootPath

	// go vet doesn't use non zero exits  :(
	isVet := len(args) > 0 && name == "go" && args[0] == "vet"

	if err = cmd.Run(); err != nil || verbose || isVet {
		print(buf.String())
	}

	return
}

func fullBuild() {
	var err error

	log.Println("glitch: building")
	if err = runCmd("go", "build", buildArgs); err == nil {
		log.Println("glitch: build OK - vetting")

		if err = runCmd("go", "vet", vetArgs); err == nil {
			log.Println("glitch: vet OK - testing")

			if err = runCmd("go", "test", testArgs); err == nil {
				log.Println("glitch: test OK")

				if len(afterAllOk) > 0 {
					err = runCmd("bash", "-c", afterAllOk)
				}
			}
		}
	}

	if err != nil && len(afterNotOk) > 0 {
		err = runCmd("bash", "-c", afterNotOk)
	}

	if err != nil {
		log.Println("glitch: failed")
	} else {
		log.Println("glitch: all OK")
	}
}

func maybeQueueBuild(path string) {
	buildQueued = hasSuffix(path, ".go")
}

func handleCreate(path string) {
	watch(path)
	maybeQueueBuild(path)
}

func handleDelete(path string) {
	if _, watching := watched[path]; watching {
		_ = watcher.RemoveWatch(path)
		delete(watched, path)
	}
	maybeQueueBuild(path)
}

func handleModify(path string) {
	maybeQueueBuild(path)
}

func handleEvent(ev *fsnotify.FileEvent) {
	if len(ev.Name) > 0 {
		switch {
		case ev.IsCreate():
			handleCreate(ev.Name)
		case ev.IsDelete():
			handleDelete(ev.Name)
		case ev.IsModify():
			handleModify(ev.Name)
		}
	}
}

var (
	gitSuffix   = sprintf("%v.git", string(filepath.Separator))
	gitContains = sprintf("%v%v", gitSuffix, string(filepath.Separator))
)

func watch(dir string) {
	const watchFlags = fsnotify.FSN_CREATE | fsnotify.FSN_DELETE | fsnotify.FSN_MODIFY

	if _, watching := watched[dir]; watching {
		return
	}

	walker := func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		if hasSuffix(path, gitSuffix) || contains(path, gitContains) {
			return nil
		}

		if fileInfo.IsDir() {
			if err = watcher.WatchFlags(path, watchFlags); err == nil {
				watched[path] = emptyStruct
			}
		}

		return err
	}

	_ = filepath.Walk(dir, walker)
}

func periodicallyLogWatchedCount() {
	logWatchedCount := func() {
		log.Printf("glitch: watching: %v paths", len(watched))
	}

	logWatchedCount()
	for _ = range time.Tick(5 * time.Second) {
		logWatchedCount()
	}
}

func periodicallyLogWatchedPaths() {
	logWatchedPaths := func() {
		log.Printf("glitch: watching: %v paths", len(watched))
		for path, _ := range watched {
			log.Println("glitch: watching:", path)
		}
	}

	logWatchedPaths()
	for _ = range time.Tick(5 * time.Second) {
		logWatchedPaths()
	}
}

func runEventLoop() {
	for {
		select {
		case ev := <-watcher.Event:
			handleEvent(ev)
		case err := <-watcher.Error:
			panicIf(err)
		}
	}
}

func runBuildLoop() {
	consumeBuildQueue := func() {
		if buildQueued {
			buildQueued = false
			clearScrollBuffer()
			fullBuild()
		}
	}

	for _ = range time.Tick(1 * time.Second) {
		go consumeBuildQueue()
	}
}

func main() {
	flag.StringVar(&afterAllOk, "after-all-ok", "", "command to run after build, vet and test succeed")
	flag.StringVar(&afterNotOk, "after-not-ok", "", "command to run after all OK")
	flag.BoolVar(&verbose, "verbose", false, "be verbose")
	flag.Parse()

	wd, err := os.Getwd()
	panicIf(err)
	rootPath = wd

	w, err := fsnotify.NewWatcher()
	panicIf(err)
	watcher = w
	defer watcher.Close()

	//go periodicallyLogWatchedPaths()
	//go periodicallyLogWatchedCount()
	go runEventLoop()
	go runBuildLoop()

	watch(rootPath)
	os.Exit(<-exitCode)
}
<commit_msg>stop spreading lies about go vet :)<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/howeyc/fsnotify"
	"github.com/levicook/go-detect"
)

var (
	watcher  *fsnotify.Watcher
	watched  = make(map[string]struct{})
	exitCode = make(chan int)
	rootPath string

	buildQueued = true

	// command line flags we pay attention to
	afterAllOk string
	afterNotOk string
	verbose    bool

	// environment variables we pay attention to
	buildArgs = detect.String(os.Getenv("BUILD_ARGS"), "./...")
	vetArgs   = detect.String(os.Getenv("VET_ARGS"), "./...")
	testArgs  = detect.String(os.Getenv("TEST_ARGS"), "./...")
)

func runCmd(name string, args ...string) (err error) {
	buf := new(commandBuffer)

	cmd := exec.Command(name, args...)
	cmd.Stdout = buf
	cmd.Stderr = buf
	cmd.Dir = rootPath

	if err = cmd.Run(); err != nil || verbose {
		print(buf.String())
	}

	return
}

func fullBuild() {
	var err error

	log.Println("glitch: building")
	if err = runCmd("go", "build", buildArgs); err == nil {
		log.Println("glitch: build OK - vetting")

		if err = runCmd("go", "vet", vetArgs); err == nil {
			log.Println("glitch: vet OK - testing")

			if err = runCmd("go", "test", testArgs); err == nil {
				log.Println("glitch: test OK")

				if len(afterAllOk) > 0 {
					if err = runCmd("bash", "-c", afterAllOk); err == nil {
						log.Println("glitch: after-all-ok OK %v")
					}
				}
			}
		}
	}

	if err != nil && len(afterNotOk) > 0 {
		if err = runCmd("bash", "-c", afterNotOk); err == nil {
			log.Println("glitch: after-not-ok OK")
		}
	}

	if err != nil {
		log.Println("glitch: failed")
	} else {
		log.Println("glitch: all OK")
	}
}

func maybeQueueBuild(path string) {
	buildQueued = hasSuffix(path, ".go")
}

func handleCreate(path string) {
	watch(path)
	maybeQueueBuild(path)
}

func handleDelete(path string) {
	if _, watching := watched[path]; watching {
		_ = watcher.RemoveWatch(path)
		delete(watched, path)
	}
	maybeQueueBuild(path)
}

func handleModify(path string) {
	maybeQueueBuild(path)
}

func handleEvent(ev *fsnotify.FileEvent) {
	if len(ev.Name) > 0 {
		switch {
		case ev.IsCreate():
			handleCreate(ev.Name)
		case ev.IsDelete():
			handleDelete(ev.Name)
		case ev.IsModify():
			handleModify(ev.Name)
		}
	}
}

var (
	gitSuffix   = sprintf("%v.git", string(filepath.Separator))
	gitContains = sprintf("%v%v", gitSuffix, string(filepath.Separator))
)

func watch(dir string) {
	const watchFlags = fsnotify.FSN_CREATE | fsnotify.FSN_DELETE | fsnotify.FSN_MODIFY

	if _, watching := watched[dir]; watching {
		return
	}

	walker := func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		if hasSuffix(path, gitSuffix) || contains(path, gitContains) {
			return nil
		}

		if fileInfo.IsDir() {
			if err = watcher.WatchFlags(path, watchFlags); err == nil {
				watched[path] = emptyStruct
			}
		}

		return err
	}

	_ = filepath.Walk(dir, walker)
}

func periodicallyLogWatchedCount() {
	logWatchedCount := func() {
		log.Printf("glitch: watching: %v paths", len(watched))
	}

	logWatchedCount()
	for _ = range time.Tick(5 * time.Second) {
		logWatchedCount()
	}
}

func periodicallyLogWatchedPaths() {
	logWatchedPaths := func() {
		log.Printf("glitch: watching: %v paths", len(watched))
		for path, _ := range watched {
			log.Println("glitch: watching:", path)
		}
	}

	logWatchedPaths()
	for _ = range time.Tick(5 * time.Second) {
		logWatchedPaths()
	}
}

func runEventLoop() {
	for {
		select {
		case ev := <-watcher.Event:
			handleEvent(ev)
		case err := <-watcher.Error:
			panicIf(err)
		}
	}
}

func runBuildLoop() {
	consumeBuildQueue := func() {
		if buildQueued {
			buildQueued = false
			clearScrollBuffer()
			fullBuild()
		}
	}

	for _ = range time.Tick(1 * time.Second) {
		go consumeBuildQueue()
	}
}

func main() {
	flag.StringVar(&afterAllOk, "after-all-ok", "", "command to run after build, vet and test succeed")
	flag.StringVar(&afterNotOk, "after-not-ok", "", "command to run after all OK")
	flag.BoolVar(&verbose, "verbose", false, "be verbose")
	flag.Parse()

	wd, err := os.Getwd()
	panicIf(err)
	rootPath = wd

	w, err := fsnotify.NewWatcher()
	panicIf(err)
	watcher = w
	defer watcher.Close()

	//go periodicallyLogWatchedPaths()
	//go periodicallyLogWatchedCount()
	go runEventLoop()
	go runBuildLoop()

	watch(rootPath)
	os.Exit(<-exitCode)
}
<|endoftext|>


<commit_before>package proc

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	log "github.com/go-pkgz/lgr"
	"github.com/microcosm-cc/bluemonday"
	"github.com/pkg/errors"
	"golang.org/x/net/html"
	tb "gopkg.in/tucnak/telebot.v2"

	"github.com/umputun/feed-master/app/duration"
	"github.com/umputun/feed-master/app/feed"
)

// TelegramClient client
type TelegramClient struct {
	Bot             *tb.Bot
	Timeout         time.Duration
	DurationService *duration.Service
}

// NewTelegramClient init telegram client
func NewTelegramClient(token, apiURL string, timeout time.Duration, durSvc *duration.Service) (*TelegramClient, error) {
	log.Printf("[INFO] create telegram client for %s, timeout: %s", apiURL, timeout)
	if timeout == 0 {
		timeout = time.Second * 60
	}

	if token == "" {
		return &TelegramClient{
			Bot:     nil,
			Timeout: timeout,
		}, nil
	}

	bot, err := tb.NewBot(tb.Settings{
		URL:    apiURL,
		Token:  token,
		Client: &http.Client{Timeout: timeout},
	})
	if err != nil {
		return nil, err
	}

	result := TelegramClient{
		Bot:             bot,
		Timeout:         timeout,
		DurationService: durSvc,
	}
	return &result, err
}

// Send message, skip if telegram token empty
func (client TelegramClient) Send(channelID string, item feed.Item) (err error) {
	if client.Bot == nil || channelID == "" {
		return nil
	}

	message, err := client.sendAudio(channelID, item)
	if err != nil && strings.HasSuffix(err.Error(), "Request Entity Too Large") {
		message, err = client.sendText(channelID, item)
	}

	if err != nil {
		return errors.Wrapf(err, "can't send to telegram for %+v", item.Enclosure)
	}

	log.Printf("[DEBUG] telegram message sent: \n%s", message.Text)
	return nil
}

func (client TelegramClient) sendText(channelID string, item feed.Item) (*tb.Message, error) {
	message, err := client.Bot.Send(
		recipient{chatID: channelID},
		client.getMessageHTML(item, htmlMessageParams{WithMp3Link: true}),
		tb.ModeHTML,
		tb.NoPreview,
	)

	return message, err
}

func (client TelegramClient) sendAudio(channelID string, item feed.Item) (*tb.Message, error) {
	httpBody, err := item.DownloadAudio(client.Timeout)
	if err != nil {
		return nil, err
	}
	defer httpBody.Close()
	// download audio to the temp file
	tmpFile, err := ioutil.TempFile(os.TempDir(), "feed-master-*.mp3")
	if err != nil {
		return nil, err
	}
	defer os.Remove(tmpFile.Name())
	if _, err := io.Copy(tmpFile, httpBody); err != nil {
		return nil, err
	}
	if err := tmpFile.Close(); err != nil {
		return nil, err
	}

	audio := tb.Audio{
		File:      tb.FromDisk(tmpFile.Name()),
		FileName:  item.GetFilename(),
		MIME:      "audio/mpeg",
		Caption:   client.getMessageHTML(item, htmlMessageParams{TrimCaption: true}),
		Title:     item.Title,
		Performer: item.Author,
		Duration:  client.DurationService.File(tmpFile.Name()),
	}

	return audio.Send(
		client.Bot,
		recipient{chatID: channelID},
		&tb.SendOptions{
			ParseMode: tb.ModeHTML,
		},
	)
}

// https://core.telegram.org/bots/api#html-style
func (client TelegramClient) tagLinkOnlySupport(htmlText string) string {
	p := bluemonday.NewPolicy()
	p.AllowAttrs("href").OnElements("a")
	return html.UnescapeString(p.Sanitize(htmlText))
}

type htmlMessageParams struct{ WithMp3Link, TrimCaption bool }

// getMessageHTML generates HTML message from provided feed.Item
func (client TelegramClient) getMessageHTML(item feed.Item, params htmlMessageParams) string {
	var header, footer string
	title := strings.TrimSpace(item.Title)
	if title != "" && item.Link == "" {
		header = fmt.Sprintf("%s\n\n", title)
	} else if title != "" && item.Link != "" {
		header = fmt.Sprintf("%s\n\n", item.Link, title)
	}

	if params.WithMp3Link {
		footer += fmt.Sprintf("\n\n%s", item.Enclosure.URL)
	}

	description := string(item.Description)
	description = strings.TrimPrefix(description, "<![CDATA[")
	description = strings.TrimSuffix(description, "]]>")
	// apparently bluemonday doesn't remove escaped HTML tags
	description = client.tagLinkOnlySupport(html.UnescapeString(description))
	description = strings.TrimSpace(description)

	// https://limits.tginfo.me/en 1024 symbol limit for caption
	if params.TrimCaption && len(header+description+footer) > 1024 {
		description = CropText(description, 1024-len(header+footer))
	}

	return header + description + footer
}

type recipient struct {
	chatID string
}

func (r recipient) Recipient() string {
	if !strings.HasPrefix(r.chatID, "@") {
		return "@" + r.chatID
	}

	return r.chatID
}

// CropText shrinks the provided string, removing HTML tags in case it's exceeding the limit
func CropText(inp string, max int) string {
	if len([]rune(inp)) > max {
		return CleanText(inp, max)
	}
	return inp
}
<commit_msg>fix large file upload fallback<commit_after>package proc

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	log "github.com/go-pkgz/lgr"
	"github.com/microcosm-cc/bluemonday"
	"github.com/pkg/errors"
	"golang.org/x/net/html"
	tb "gopkg.in/tucnak/telebot.v2"

	"github.com/umputun/feed-master/app/duration"
	"github.com/umputun/feed-master/app/feed"
)

// TelegramClient client
type TelegramClient struct {
	Bot             *tb.Bot
	Timeout         time.Duration
	DurationService *duration.Service
}

// NewTelegramClient init telegram client
func NewTelegramClient(token, apiURL string, timeout time.Duration, durSvc *duration.Service) (*TelegramClient, error) {
	log.Printf("[INFO] create telegram client for %s, timeout: %s", apiURL, timeout)
	if timeout == 0 {
		timeout = time.Second * 60
	}

	if token == "" {
		return &TelegramClient{
			Bot:     nil,
			Timeout: timeout,
		}, nil
	}

	bot, err := tb.NewBot(tb.Settings{
		URL:    apiURL,
		Token:  token,
		Client: &http.Client{Timeout: timeout},
	})
	if err != nil {
		return nil, err
	}

	result := TelegramClient{
		Bot:             bot,
		Timeout:         timeout,
		DurationService: durSvc,
	}
	return &result, err
}

// Send message, skip if telegram token empty
func (client TelegramClient) Send(channelID string, item feed.Item) (err error) {
	if client.Bot == nil || channelID == "" {
		return nil
	}

	message, err := client.sendAudio(channelID, item)
	if err != nil && strings.Contains(err.Error(), "Request Entity Too Large") {
		message, err = client.sendText(channelID, item)
	}

	if err != nil {
		return errors.Wrapf(err, "can't send to telegram for %+v", item.Enclosure)
	}

	log.Printf("[DEBUG] telegram message sent: \n%s", message.Text)
	return nil
}

func (client TelegramClient) sendText(channelID string, item feed.Item) (*tb.Message, error) {
	message, err := client.Bot.Send(
		recipient{chatID: channelID},
		client.getMessageHTML(item, htmlMessageParams{WithMp3Link: true}),
		tb.ModeHTML,
		tb.NoPreview,
	)

	return message, err
}

func (client TelegramClient) sendAudio(channelID string, item feed.Item) (*tb.Message, error) {
	httpBody, err := item.DownloadAudio(client.Timeout)
	if err != nil {
		return nil, err
	}
	defer httpBody.Close()
	// download audio to the temp file
	tmpFile, err := ioutil.TempFile(os.TempDir(), "feed-master-*.mp3")
	if err != nil {
		return nil, err
	}
	defer os.Remove(tmpFile.Name())
	if _, err := io.Copy(tmpFile, httpBody); err != nil {
		return nil, err
	}
	if err := tmpFile.Close(); err != nil {
		return nil, err
	}

	audio := tb.Audio{
		File:      tb.FromDisk(tmpFile.Name()),
		FileName:  item.GetFilename(),
		MIME:      "audio/mpeg",
		Caption:   client.getMessageHTML(item, htmlMessageParams{TrimCaption: true}),
		Title:     item.Title,
		Performer: item.Author,
		Duration:  client.DurationService.File(tmpFile.Name()),
	}

	return audio.Send(
		client.Bot,
		recipient{chatID: channelID},
		&tb.SendOptions{
			ParseMode: tb.ModeHTML,
		},
	)
}

// https://core.telegram.org/bots/api#html-style
func (client TelegramClient) tagLinkOnlySupport(htmlText string) string {
	p := bluemonday.NewPolicy()
	p.AllowAttrs("href").OnElements("a")
	return html.UnescapeString(p.Sanitize(htmlText))
}

type htmlMessageParams struct{ WithMp3Link, TrimCaption bool }

// getMessageHTML generates HTML message from provided feed.Item
func (client TelegramClient) getMessageHTML(item feed.Item, params htmlMessageParams) string {
	var header, footer string
	title := strings.TrimSpace(item.Title)
	if title != "" && item.Link == "" {
		header = fmt.Sprintf("%s\n\n", title)
	} else if title != "" && item.Link != "" {
		header = fmt.Sprintf("%s\n\n", item.Link, title)
	}

	if params.WithMp3Link {
		footer += fmt.Sprintf("\n\n%s", item.Enclosure.URL)
	}

	description := string(item.Description)
	description = strings.TrimPrefix(description, "<![CDATA[")
	description = strings.TrimSuffix(description, "]]>")
	// apparently bluemonday doesn't remove escaped HTML tags
	description = client.tagLinkOnlySupport(html.UnescapeString(description))
	description = strings.TrimSpace(description)

	// https://limits.tginfo.me/en 1024 symbol limit for caption
	if params.TrimCaption && len(header+description+footer) > 1024 {
		description = CropText(description, 1024-len(header+footer))
	}

	return header + description + footer
}

type recipient struct {
	chatID string
}

func (r recipient) Recipient() string {
	if !strings.HasPrefix(r.chatID, "@") {
		return "@" + r.chatID
	}

	return r.chatID
}

// CropText shrinks the provided string, removing HTML tags in case it's exceeding the limit
func CropText(inp string, max int) string {
	if len([]rune(inp)) > max {
		return CleanText(inp, max)
	}
	return inp
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

type gitHandler struct {
	method      string
	regexp      *regexp.Regexp
	handle_func func(string, string, string, http.ResponseWriter, *http.Request)
	rpc         string
}

var http_client = &http.Client{}
var repo_root string
var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server")
var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend")

var git_handlers = [...]gitHandler{
	gitHandler{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
}

func main() {
	flag.Parse()
	repo_root = flag.Arg(0)
	log.Printf("repo_root: %s", repo_root)
	http.HandleFunc("/", git_handler)
	log.Fatal(http.ListenAndServe(*listen_addr, nil))
}

func git_handler(w http.ResponseWriter, r *http.Request) {
	var user string
	log.Print(r.Method, " ", r.URL)
	for _, g := range git_handlers {
		path_match := g.regexp.FindStringSubmatch(r.URL.Path)
		if r.Method == g.method && path_match != nil {
			auth_response, err := do_auth_request(r)
			if err != nil {
				fail_500(w, err)
				return
			}
			if auth_response.StatusCode != 200 {
				for k, v := range auth_response.Header {
					w.Header()[k] = v
				}
				w.WriteHeader(auth_response.StatusCode)
				io.Copy(w, auth_response.Body)
				return
			}
			if _, err := fmt.Fscan(auth_response.Body, &user); err != nil {
				fail_500(w, err)
				return
			}
			g.handle_func(user, g.rpc, path.Join(repo_root, path_match[1]), w, r)
			return
		}
	}
	log.Print("Reached end of dispatch for loop")
	w.WriteHeader(404)
}

func do_auth_request(r *http.Request) (*http.Response, error) {
	var err error
	url := fmt.Sprintf("%s%s", *auth_backend, r.URL.RequestURI())
	auth_req, err := http.NewRequest(r.Method, url, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range r.Header {
		auth_req.Header[k] = v
	}
	result, err := http_client.Do(auth_req)
	if err != nil {
		return nil, err
	}
	return nil, err
}

func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) {
	rpc := r.URL.Query().Get("service")
	switch rpc {
	case "git-upload-pack", "git-receive-pack":
		cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", "--advertise-refs", path)
		cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fail_500(w, err)
			return
		}
		if err := cmd.Start(); err != nil {
			fail_500(w, err)
			return
		}
		w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		no_cache(w)
		fmt.Fprintf(w, "%s0000", pkt_line(fmt.Sprintf("# service=%s\n", rpc)))
		if _, err := io.Copy(w, stdout); err != nil {
			fail_500(w, err)
			return
		}
		if err := cmd.Wait(); err != nil {
			fail_500(w, err)
			return
		}
	case "":
		log.Print("dumb info refs")
	}
}

func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var err error
	if r.Header.Get("Content-Encoding") == "gzip" {
		body, err = gzip.NewReader(r.Body)
		if err != nil {
			fail_500(w, err)
			return
		}
	} else {
		body = r.Body
	}
	cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", path)
	cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Start(); err != nil {
		fail_500(w, err)
		return
	}
	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
	no_cache(w)
	if _, err := io.Copy(stdin, body); err != nil {
		fail_500(w, err)
		return
	}
	stdin.Close()
	if _, err := io.Copy(w, stdout); err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Wait(); err != nil {
		fail_500(w, err)
		return
	}
}

func pkt_line(s string) string {
	return fmt.Sprintf("%04x%s", len(s)+4, s)
}

func fail_500(w http.ResponseWriter, err error) {
	w.WriteHeader(500)
	log.Print(err)
}

func no_cache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache")
}
<commit_msg>Fix refactor<commit_after>package main

import (
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

type gitHandler struct {
	method      string
	regexp      *regexp.Regexp
	handle_func func(string, string, string, http.ResponseWriter, *http.Request)
	rpc         string
}

var http_client = &http.Client{}
var repo_root string
var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server")
var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend")

var git_handlers = [...]gitHandler{
	gitHandler{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
}

func main() {
	flag.Parse()
	repo_root = flag.Arg(0)
	log.Printf("repo_root: %s", repo_root)
	http.HandleFunc("/", git_handler)
	log.Fatal(http.ListenAndServe(*listen_addr, nil))
}

func git_handler(w http.ResponseWriter, r *http.Request) {
	var user string
	log.Print(r.Method, " ", r.URL)
	for _, g := range git_handlers {
		path_match := g.regexp.FindStringSubmatch(r.URL.Path)
		if r.Method == g.method && path_match != nil {
			auth_response, err := do_auth_request(r)
			if err != nil {
				fail_500(w, err)
				return
			}
			if auth_response.StatusCode != 200 {
				for k, v := range auth_response.Header {
					w.Header()[k] = v
				}
				w.WriteHeader(auth_response.StatusCode)
				io.Copy(w, auth_response.Body)
				return
			}
			if _, err := fmt.Fscan(auth_response.Body, &user); err != nil {
				fail_500(w, err)
				return
			}
			g.handle_func(user, g.rpc, path.Join(repo_root, path_match[1]), w, r)
			return
		}
	}
	log.Print("Reached end of dispatch for loop")
	w.WriteHeader(404)
}

func do_auth_request(r *http.Request) (*http.Response, error) {
	var err error
	url := fmt.Sprintf("%s%s", *auth_backend, r.URL.RequestURI())
	auth_req, err := http.NewRequest(r.Method, url, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range r.Header {
		auth_req.Header[k] = v
	}
	result, err := http_client.Do(auth_req)
	if err != nil {
		return nil, err
	}
	return result, nil
}

func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) {
	rpc := r.URL.Query().Get("service")
	switch rpc {
	case "git-upload-pack", "git-receive-pack":
		cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", "--advertise-refs", path)
		cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fail_500(w, err)
			return
		}
		if err := cmd.Start(); err != nil {
			fail_500(w, err)
			return
		}
		w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		no_cache(w)
		fmt.Fprintf(w, "%s0000", pkt_line(fmt.Sprintf("# service=%s\n", rpc)))
		if _, err := io.Copy(w, stdout); err != nil {
			fail_500(w, err)
			return
		}
		if err := cmd.Wait(); err != nil {
			fail_500(w, err)
			return
		}
	case "":
		log.Print("dumb info refs")
	}
}

func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var err error
	if r.Header.Get("Content-Encoding") == "gzip" {
		body, err = gzip.NewReader(r.Body)
		if err != nil {
			fail_500(w, err)
			return
		}
	} else {
		body = r.Body
	}
	cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", path)
	cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Start(); err != nil {
		fail_500(w, err)
		return
	}
	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
	no_cache(w)
	if _, err := io.Copy(stdin, body); err != nil {
		fail_500(w, err)
		return
	}
	stdin.Close()
	if _, err := io.Copy(w, stdout); err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Wait(); err != nil {
		fail_500(w, err)
		return
	}
}

func pkt_line(s string) string {
	return fmt.Sprintf("%04x%s", len(s)+4, s)
}

func fail_500(w http.ResponseWriter, err error) {
	w.WriteHeader(500)
	log.Print(err)
}

func no_cache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache")
}
<|endoftext|>


<commit_before>package consul

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	consul "github.com/hashicorp/consul/api"
	"github.com/micro/go-micro/registry"
	hash "github.com/mitchellh/hashstructure"
)

type consulRegistry struct {
	Address string
	Client  *consul.Client
	opts    registry.Options

	// connect enabled
	connect bool

	queryOptions *consul.QueryOptions

	sync.Mutex
	register map[string]uint64
	// lastChecked tracks when a node was last checked as existing in Consul
	lastChecked map[string]time.Time
}

func getDeregisterTTL(t time.Duration) time.Duration {
	// splay slightly for the watcher?
	splay := time.Second * 5
	deregTTL := t + splay

	// consul has a minimum timeout on deregistration of 1 minute.
	if t < time.Minute {
		deregTTL = time.Minute + splay
	}

	return deregTTL
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func configure(c *consulRegistry, opts ...registry.Option) {
	// set opts
	for _, o := range opts {
		o(&c.opts)
	}

	// use default config
	config := consul.DefaultConfig()

	if c.opts.Context != nil {
		// Use the consul config passed in the options, if available
		if co, ok := c.opts.Context.Value("consul_config").(*consul.Config); ok {
			config = co
		}
		if cn, ok := c.opts.Context.Value("consul_connect").(bool); ok {
			c.connect = cn
		}

		// Use the consul query options passed in the options, if available
		if qo, ok := c.opts.Context.Value("consul_query_options").(*consul.QueryOptions); ok && qo != nil {
			c.queryOptions = qo
		}
		if as, ok := c.opts.Context.Value("consul_allow_stale").(bool); ok {
			c.queryOptions.AllowStale = as
		}
	}

	// check if there are any addrs
	if len(c.opts.Addrs) > 0 {
		addr, port, err := net.SplitHostPort(c.opts.Addrs[0])
		if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
			port = "8500"
			addr = c.opts.Addrs[0]
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		} else if err == nil {
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		}
	}

	if config.HttpClient == nil {
		config.HttpClient = new(http.Client)
	}

	// requires secure connection?
	if c.opts.Secure || c.opts.TLSConfig != nil {

		config.Scheme = "https"
		// We're going to support InsecureSkipVerify
		config.HttpClient.Transport = newTransport(c.opts.TLSConfig)
	}

	// set timeout
	if c.opts.Timeout > 0 {
		config.HttpClient.Timeout = c.opts.Timeout
	}

	// create the client
	client, _ := consul.NewClient(config)

	// set address/client
	c.Address = config.Address
	c.Client = client
}

func (c *consulRegistry) Init(opts ...registry.Option) error {
	configure(c, opts...)
	return nil
}

func (c *consulRegistry) Deregister(s *registry.Service) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	// delete our hash and time check of the service
	c.Lock()
	delete(c.register, s.Name)
	delete(c.lastChecked, s.Name)
	c.Unlock()

	node := s.Nodes[0]
	return c.Client.Agent().ServiceDeregister(node.Id)
}

func (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	var regTCPCheck bool
	var regInterval time.Duration

	var options registry.RegisterOptions
	for _, o := range opts {
		o(&options)
	}

	if c.opts.Context != nil {
		if tcpCheckInterval, ok := c.opts.Context.Value("consul_tcp_check").(time.Duration); ok {
			regTCPCheck = true
			regInterval = tcpCheckInterval
		}
	}

	// create hash of service; uint64
	h, err := hash.Hash(s, nil)
	if err != nil {
		return err
	}

	// use first node
	node := s.Nodes[0]

	// get existing hash and last checked time
	c.Lock()
	v, ok := c.register[s.Name]
	lastChecked := c.lastChecked[s.Name]
	c.Unlock()

	// if it's already registered and matches then just pass the check
	if ok && v == h {
		if options.TTL == time.Duration(0) {
			// ensure that our service hasn't been deregistered by Consul
			if time.Since(lastChecked) <= getDeregisterTTL(regInterval) {
				return nil
			}
			services, _, err := c.Client.Health().Checks(s.Name, c.queryOptions)
			if err == nil {
				for _, v := range services {
					if v.ServiceID == node.Id {
						return nil
					}
				}
			}
		} else {
			// if the err is nil we're all good, bail out
			// if not, we don't know what the state is, so full re-register
			if err := c.Client.Agent().PassTTL("service:"+node.Id, ""); err == nil {
				return nil
			}
		}
	}

	// encode the tags
	tags := encodeMetadata(node.Metadata)
	tags = append(tags, encodeEndpoints(s.Endpoints)...)
	tags = append(tags, encodeVersion(s.Version)...)

	var check *consul.AgentServiceCheck

	if regTCPCheck {
		deregTTL := getDeregisterTTL(regInterval)

		check = &consul.AgentServiceCheck{
			TCP:                            fmt.Sprintf("%s:%d", node.Address, node.Port),
			Interval:                       fmt.Sprintf("%v", regInterval),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}

		// if the TTL is greater than 0 create an associated check
	} else if options.TTL > time.Duration(0) {
		deregTTL := getDeregisterTTL(options.TTL)

		check = &consul.AgentServiceCheck{
			TTL:                            fmt.Sprintf("%v", options.TTL),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}
	}

	// register the service
	asr := &consul.AgentServiceRegistration{
		ID:      node.Id,
		Name:    s.Name,
		Tags:    tags,
		Port:    node.Port,
		Address: node.Address,
		Check:   check,
	}

	// Specify consul connect
	if c.connect {
		asr.Connect = &consul.AgentServiceConnect{
			Native: true,
		}
	}

	if err := c.Client.Agent().ServiceRegister(asr); err != nil {
		return err
	}

	// save our hash and time check of the service
	c.Lock()
	c.register[s.Name] = h
	c.lastChecked[s.Name] = time.Now()
	c.Unlock()

	// if the TTL is 0 we don't mess with the checks
	if options.TTL == time.Duration(0) {
		return nil
	}

	// pass the healthcheck
	return c.Client.Agent().PassTTL("service:"+node.Id, "")
}

func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
	var rsp []*consul.ServiceEntry
	var err error

	// if we're connect enabled only get connect services
	if c.connect {
		rsp, _, err = c.Client.Health().Connect(name, "", false, c.queryOptions)
	} else {
		rsp, _, err = c.Client.Health().Service(name, "", false, c.queryOptions)
	}
	if err != nil {
		return nil, err
	}

	serviceMap := map[string]*registry.Service{}

	for _, s := range rsp {
		if s.Service.Service != name {
			continue
		}

		// version is now a tag
		version, _ := decodeVersion(s.Service.Tags)
		// service ID is now the node id
		id := s.Service.ID
		// key is always the version
		key := version

		// address is service address
		address := s.Service.Address

		// use node address
		if len(address) == 0 {
			address = s.Node.Address
		}

		svc, ok := serviceMap[key]
		if !ok {
			svc = &registry.Service{
				Endpoints: decodeEndpoints(s.Service.Tags),
				Name:      s.Service.Service,
				Version:   version,
			}
			serviceMap[key] = svc
		}

		var del bool

		for _, check := range s.Checks {
			// delete the node if the status is critical
			if check.Status == "critical" {
				del = true
				break
			}
		}

		// if delete then skip the node
		if del {
			continue
		}

		svc.Nodes = append(svc.Nodes, &registry.Node{
			Id:       id,
			Address:  address,
			Port:     s.Service.Port,
			Metadata: decodeMetadata(s.Service.Tags),
		})
	}

	var services []*registry.Service
	for _, service := range serviceMap {
		services = append(services, service)
	}
	return services, nil
}

func (c *consulRegistry) ListServices() ([]*registry.Service, error) {
	rsp, _, err := c.Client.Catalog().Services(c.queryOptions)
	if err != nil {
		return nil, err
	}

	var services []*registry.Service

	for service := range rsp {
		services = append(services, &registry.Service{Name: service})
	}

	return services, nil
}

func (c *consulRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
	return newConsulWatcher(c, opts...)
}

func (c *consulRegistry) String() string {
	return "consul"
}

func (c *consulRegistry) Options() registry.Options {
	return c.opts
}

func NewRegistry(opts ...registry.Option) registry.Registry {
	cr := &consulRegistry{
		opts:        registry.Options{},
		register:    make(map[string]uint64),
		lastChecked: make(map[string]time.Time),
		queryOptions: &consul.QueryOptions{
			AllowStale: true,
		},
	}
	configure(cr, opts...)
	return cr
}
<commit_msg>gofmt<commit_after>package consul

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	consul "github.com/hashicorp/consul/api"
	"github.com/micro/go-micro/registry"
	hash "github.com/mitchellh/hashstructure"
)

type consulRegistry struct {
	Address string
	Client  *consul.Client
	opts    registry.Options

	// connect enabled
	connect bool

	queryOptions *consul.QueryOptions

	sync.Mutex
	register map[string]uint64
	// lastChecked tracks when a node was last checked as existing in Consul
	lastChecked map[string]time.Time
}

func getDeregisterTTL(t time.Duration) time.Duration {
	// splay slightly for the watcher?
	splay := time.Second * 5
	deregTTL := t + splay

	// consul has a minimum timeout on deregistration of 1 minute.
	if t < time.Minute {
		deregTTL = time.Minute + splay
	}

	return deregTTL
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func configure(c *consulRegistry, opts ...registry.Option) {
	// set opts
	for _, o := range opts {
		o(&c.opts)
	}

	// use default config
	config := consul.DefaultConfig()

	if c.opts.Context != nil {
		// Use the consul config passed in the options, if available
		if co, ok := c.opts.Context.Value("consul_config").(*consul.Config); ok {
			config = co
		}
		if cn, ok := c.opts.Context.Value("consul_connect").(bool); ok {
			c.connect = cn
		}

		// Use the consul query options passed in the options, if available
		if qo, ok := c.opts.Context.Value("consul_query_options").(*consul.QueryOptions); ok && qo != nil {
			c.queryOptions = qo
		}
		if as, ok := c.opts.Context.Value("consul_allow_stale").(bool); ok {
			c.queryOptions.AllowStale = as
		}
	}

	// check if there are any addrs
	if len(c.opts.Addrs) > 0 {
		addr, port, err := net.SplitHostPort(c.opts.Addrs[0])
		if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
			port = "8500"
			addr = c.opts.Addrs[0]
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		} else if err == nil {
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		}
	}

	if config.HttpClient == nil {
		config.HttpClient = new(http.Client)
	}

	// requires secure connection?
	if c.opts.Secure || c.opts.TLSConfig != nil {

		config.Scheme = "https"
		// We're going to support InsecureSkipVerify
		config.HttpClient.Transport = newTransport(c.opts.TLSConfig)
	}

	// set timeout
	if c.opts.Timeout > 0 {
		config.HttpClient.Timeout = c.opts.Timeout
	}

	// create the client
	client, _ := consul.NewClient(config)

	// set address/client
	c.Address = config.Address
	c.Client = client
}

func (c *consulRegistry) Init(opts ...registry.Option) error {
	configure(c, opts...)
	return nil
}

func (c *consulRegistry) Deregister(s *registry.Service) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	// delete our hash and time check of the service
	c.Lock()
	delete(c.register, s.Name)
	delete(c.lastChecked, s.Name)
	c.Unlock()

	node := s.Nodes[0]
	return c.Client.Agent().ServiceDeregister(node.Id)
}

func (c *consulRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	var regTCPCheck bool
	var regInterval time.Duration

	var options registry.RegisterOptions
	for _, o := range opts {
		o(&options)
	}

	if c.opts.Context != nil {
		if tcpCheckInterval, ok := c.opts.Context.Value("consul_tcp_check").(time.Duration); ok {
			regTCPCheck = true
			regInterval = tcpCheckInterval
		}
	}

	// create hash of service; uint64
	h, err := hash.Hash(s, nil)
	if err != nil {
		return err
	}

	// use first node
	node := s.Nodes[0]

	// get existing hash and last checked time
	c.Lock()
	v, ok := c.register[s.Name]
	lastChecked := c.lastChecked[s.Name]
	c.Unlock()

	// if it's already registered and matches then just pass the check
	if ok && v == h {
		if options.TTL == time.Duration(0) {
			// ensure that our service hasn't been deregistered by Consul
			if time.Since(lastChecked) <= getDeregisterTTL(regInterval) {
				return nil
			}
			services, _, err := c.Client.Health().Checks(s.Name, c.queryOptions)
			if err == nil {
				for _, v := range services {
					if v.ServiceID == node.Id {
						return nil
					}
				}
			}
		} else {
			// if the err is nil we're all good, bail out
			// if not, we don't know what the state is, so full re-register
			if err := c.Client.Agent().PassTTL("service:"+node.Id, ""); err == nil {
				return nil
			}
		}
	}

	// encode the tags
	tags := encodeMetadata(node.Metadata)
	tags = append(tags, encodeEndpoints(s.Endpoints)...)
	tags = append(tags, encodeVersion(s.Version)...)

	var check *consul.AgentServiceCheck

	if regTCPCheck {
		deregTTL := getDeregisterTTL(regInterval)

		check = &consul.AgentServiceCheck{
			TCP:                            fmt.Sprintf("%s:%d", node.Address, node.Port),
			Interval:                       fmt.Sprintf("%v", regInterval),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}

		// if the TTL is greater than 0 create an associated check
	} else if options.TTL > time.Duration(0) {
		deregTTL := getDeregisterTTL(options.TTL)

		check = &consul.AgentServiceCheck{
			TTL:                            fmt.Sprintf("%v", options.TTL),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}
	}

	// register the service
	asr := &consul.AgentServiceRegistration{
		ID:      node.Id,
		Name:    s.Name,
		Tags:    tags,
		Port:    node.Port,
		Address: node.Address,
		Check:   check,
	}

	// Specify consul connect
	if c.connect {
		asr.Connect = &consul.AgentServiceConnect{
			Native: true,
		}
	}

	if err := c.Client.Agent().ServiceRegister(asr); err != nil {
		return err
	}

	// save our hash and time check of the service
	c.Lock()
	c.register[s.Name] = h
	c.lastChecked[s.Name] = time.Now()
	c.Unlock()

	// if the TTL is 0 we don't mess with the checks
	if options.TTL == time.Duration(0) {
		return nil
	}

	// pass the healthcheck
	return c.Client.Agent().PassTTL("service:"+node.Id, "")
}

func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
	var rsp []*consul.ServiceEntry
	var err error

	// if we're connect enabled only get connect services
	if c.connect {
		rsp, _, err = c.Client.Health().Connect(name, "", false, c.queryOptions)
	} else {
		rsp, _, err = c.Client.Health().Service(name, "", false, c.queryOptions)
	}
	if err != nil {
		return nil, err
	}

	serviceMap := map[string]*registry.Service{}

	for _, s := range rsp {
		if s.Service.Service != name {
			continue
		}

		// version is now a tag
		version, _ := decodeVersion(s.Service.Tags)
		// service ID is now the node id
		id := s.Service.ID
		// key is always the version
		key := version

		// address is service address
		address := s.Service.Address

		// use node address
		if len(address) == 0 {
			address = s.Node.Address
		}

		svc, ok := serviceMap[key]
		if !ok {
			svc = &registry.Service{
				Endpoints: decodeEndpoints(s.Service.Tags),
				Name:      s.Service.Service,
				Version:   version,
			}
			serviceMap[key] = svc
		}

		var del bool

		for _, check := range s.Checks {
			// delete the node if the status is critical
			if check.Status == "critical" {
				del = true
				break
			}
		}

		// if delete then skip the node
		if del {
			continue
		}

		svc.Nodes = append(svc.Nodes, &registry.Node{
			Id:       id,
			Address:  address,
			Port:     s.Service.Port,
			Metadata: decodeMetadata(s.Service.Tags),
		})
	}

	var services []*registry.Service
	for _, service := range serviceMap {
		services = append(services, service)
	}
	return services, nil
}

func (c *consulRegistry) ListServices() ([]*registry.Service, error) {
	rsp, _, err := c.Client.Catalog().Services(c.queryOptions)
	if err != nil {
		return nil, err
	}

	var services []*registry.Service

	for service := range rsp {
		services = append(services, &registry.Service{Name: service})
	}

	return services, nil
}

func (c *consulRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
	return newConsulWatcher(c, opts...)
}

func (c *consulRegistry) String() string {
	return "consul"
}

func (c *consulRegistry) Options() registry.Options {
	return c.opts
}

func NewRegistry(opts ...registry.Option) registry.Registry {
	cr := &consulRegistry{
		opts:        registry.Options{},
		register:    make(map[string]uint64),
		lastChecked: make(map[string]time.Time),
		queryOptions: &consul.QueryOptions{
			AllowStale: true,
		},
	}
	configure(cr, opts...)
	return cr
}
<|endoftext|>


<commit_before>package verbose

import (
	"github.com/dotcloud/docker/api/client"
	"bufio"
  "fmt"
  "io"
  "time"
  "testing"
	"strings"

)

const (
        unitTestImageName        = "docker-test-image"
        unitTestImageID          = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0
        unitTestImageIDShort     = "83599e29c455"
        unitTestNetworkBridge    = "testdockbr0"
        unitTestStoreBase        = "/var/lib/docker/unit-tests"
        testDaemonAddr           = "172.17.8.100:4243"
        testDaemonProto          = "tcp"
        testDaemonHttpsProto     = "tcp"
        testDaemonHttpsAddr      = "localhost:4271"
        testDaemonRogueHttpsAddr = "localhost:4272"
)

func closeWrap(args ...io.Closer) error {
	e := false
	ret := fmt.Errorf("Error closing elements")
	for _, c := range args {
		if err := c.Close(); err != nil {
			e = true
			ret = fmt.Errorf("%s\n%s", ret, err)
		}
	}
	if e {
		return ret
	}
	return nil
}

func getIPAdressTest(t *testing.T) string{
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)
	var IPAdress string
	go func(){
		err:= cli.CmdInspect("--format","{{ .NetworkSettings.IPAddress }}","deis-etcd")
		if err != nil {
			t.Fatalf("getIPAdressTest %s",err)
		}
		if err = closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("getIPAdressTest %s",err)
		}
	}()
	time.Sleep(2000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
			IPAdress=cmdBytes
			fmt.Println(cmdBytes)
		}else{
			break
		}
		fmt.Println("get IPAddress")
	}
	return IPAdress
}

func pullEtcdTest(t *testing.T,done chan bool){
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	fmt.Println("1")
	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)
	fmt.Println("2")
	go func (){
		err:= cli.CmdPull("phife.atribecalledchris.com:5000/deis/etcd:0.3.0")
		if err != nil {
			t.Fatalf("pullEtcdTest %s",err)
		}
		if err = closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("pullEtcdTest %s",err)
		}
	}( )
	time.Sleep(3000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
		fmt.Println(cmdBytes)
		}else{
			break
		}
		fmt.Println("pulling etcd")
	}
	done<-true

}

func runEtcdTest(t *testing.T,done chan bool){
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)
	go func (){
		fmt.Println("12")
		err:= cli.CmdRun("--name","deis-etcd","phife.atribecalledchris.com:5000/deis/etcd:0.3.0")
		if err != nil {
			t.Fatalf("runEtcdTest %s",err)
		}

	}( )
	go func() {
		fmt.Println("here")
		time.Sleep(5000 * time.Millisecond)
		if err := closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("runEtcdTest %s",err)
		}
	}()
	time.Sleep(1000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
		fmt.Println(cmdBytes)
		}else{
			break
		}
		fmt.Println("Running Etcd ")
	}
	done<-true
}

func buildRegistryTest(t *testing.T){
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)
	go func ( ){
		err:= cli.CmdBuild("../")
		if err != nil {
			t.Fatalf("buildRegistryTest %s",err)
		}
		if err = closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("buildRegistryTest %s",err)
		}
	}( )
	time.Sleep(3000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
		fmt.Println(cmdBytes)
		}else{
			break
		}
		fmt.Println("building Deis registy Dockerfile")
	}
}

func runDeisRegistryDataTest(t *testing.T){
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()

	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)

	go func ( ){
		err:= cli.CmdInspect("--format","'{{ .Config.Hostname }}'","deis-registry-data")
		/*if err != nil {
			t.Fatalf("runDeisRegistryDataTest %s",err)
		}*/
		if err = closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("runDeisRegistryDataTest %s",err)
		}
	}( )
	go func() {
		//fmt.Println("here1")
		time.Sleep(2000 * time.Millisecond)
		if err := closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("runEtcdTest %s",err)
		}
	}()
	var hostname string
	time.Sleep(1000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
		fmt.Println(cmdBytes)
		hostname=cmdBytes
		}else{
			break
		}
		fmt.Println("inspecting deis registry data")
	}

	if strings.Contains(hostname, "Error") ==true {
		go func ( ){
			err:= cli.CmdRun("--name","deis-registry-data","-v","/data","deis/base","/bin/true")
			if err != nil {
				t.Fatalf("%s",err)
			}
			if err := closeWrap(stdout, stdoutPipe,stdin); err != nil {
				t.Fatalf("%s",err)
			}
		}( )
		for{
			if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
			fmt.Println(cmdBytes)
			}else{
				break
			}
			fmt.Println("pulling Deis registry data")
		}
	}
}

func runDeisRegistryTest(t *testing.T,IPAddress string){
	stdin, _   := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, testDaemonProto, testDaemonAddr, nil)
	go func (){
		//docker run --name deis-registry -p 5000:5000 -e PUBLISH=5000 -e HOST=10.0.0.37 --volumes-from deis-registry-data deis/registry

		err:= cli.CmdRun("--name","deis-registry","-p","5000:5000","-e","PUBLISH=5000","-e","HOST="+IPAddress,"--volumes-from","deis-registry-data","deis/registry")
		if err != nil {
			t.Fatalf("runDeisRegistryTest %s",err)
		}
		if err := closeWrap(stdout, stdoutPipe,stdin); err != nil {
			t.Fatalf("runDeisRegistryTest %s",err)
		}
	}( )
	time.Sleep(3000 * time.Millisecond)
	for{
		if cmdBytes,err:= bufio.NewReader(stdout).ReadString('\n'); err==nil{
			if strings.Contains(cmdBytes, "Booting") ==true {
				if err := closeWrap(stdout, stdoutPipe,stdin); err != nil {
					t.Fatalf("runDeisRegistryTest %s",err)
				}
			}
		fmt.Println(cmdBytes)
		}else{
			break
		}
		fmt.Println("pulling Deis Registry")
	}

}

func TestBuild(t *testing.T) {
	done := make(chan bool, 1)
	done2 := make(chan bool,1)
	pullEtcdTest(t,done)
	fmt.Println("1st")
	go func() {
		fmt.Println("2nd")
		<-done
		fmt.Println("3rd")
		runEtcdTest(t,done2)
		fmt.Println("4th")
	}()
	<-done2
	fmt.Println("5th")
	buildRegistryTest(t)
	fmt.Println("6th")
	runDeisRegistryDataTest(t)
	fmt.Println("7th")
	IPAddress:=strings.TrimSuffix(getIPAdressTest(t), "\n")
	fmt.Println(IPAddress)
	fmt.Println("8th")
	runDeisRegistryTest(t,IPAddress)
}
<commit_msg>fix(registry/test): use env vars for docker daemon connection info<commit_after>package verbose

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/dotcloud/docker/api/client"
)

const (
	unitTestImageName        = "docker-test-image"
	unitTestImageID          = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0
	unitTestImageIDShort     = "83599e29c455"
	unitTestNetworkBridge    = "testdockbr0"
	unitTestStoreBase        = "/var/lib/docker/unit-tests"
	testDaemonHttpsProto     = "tcp"
	testDaemonHttpsAddr      = "localhost:4271"
	testDaemonRogueHttpsAddr = "localhost:4272"
)

func daemonAddr() string {
	addr := os.Getenv("TEST_DAEMON_ADDR")
	if addr == "" {
		addr = "/var/run/docker.sock"
	}
	return addr
}

func daemonProto() string {
	proto := os.Getenv("TEST_DAEMON_PROTO")
	if proto == "" {
		proto = "unix"
	}
	return proto
}

func closeWrap(args ...io.Closer) error {
	e := false
	ret := fmt.Errorf("error closing elements")
	for _, c := range args {
		if err := c.Close(); err != nil {
			e = true
			ret = fmt.Errorf("%s\n%s", ret, err)
		}
	}
	if e {
		return ret
	}
	return nil
}

func getIPAddressTest(t *testing.T) string {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)
	var IPAdress string
	go func() {
		err := cli.CmdInspect("--format", "{{ .NetworkSettings.IPAddress }}", "deis-etcd")
		if err != nil {
			t.Fatalf("getIPAddressTest %s", err)
		}
		if err = closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("getIPAddressTest %s", err)
		}
	}()
	time.Sleep(2000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			IPAdress = cmdBytes
			fmt.Println(cmdBytes)
		} else {
			break
		}
		fmt.Println("get IPAddress")
	}
	return IPAdress
}

func pullEtcdTest(t *testing.T, done chan bool) {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	fmt.Println("1")
	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)
	fmt.Println("2")
	go func() {
		err := cli.CmdPull("phife.atribecalledchris.com:5000/deis/etcd:0.3.0")
		if err != nil {
			t.Fatalf("pullEtcdTest %s", err)
		}
		if err = closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("pullEtcdTest %s", err)
		}
	}()
	time.Sleep(3000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			fmt.Println(cmdBytes)
		} else {
			break
		}
		fmt.Println("pulling etcd")
	}
	done <- true

}

func runEtcdTest(t *testing.T, done chan bool) {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)
	go func() {
		fmt.Println("12")
		err := cli.CmdRun("--name", "deis-etcd", "phife.atribecalledchris.com:5000/deis/etcd:0.3.0")
		if err != nil {
			t.Fatalf("runEtcdTest %s", err)
		}

	}()
	go func() {
		fmt.Println("here")
		time.Sleep(5000 * time.Millisecond)
		if err := closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("runEtcdTest %s", err)
		}
	}()
	time.Sleep(1000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			fmt.Println(cmdBytes)
		} else {
			break
		}
		fmt.Println("Running Etcd ")
	}
	done <- true
}

func buildRegistryTest(t *testing.T) {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)
	go func() {
		err := cli.CmdBuild("../")
		if err != nil {
			t.Fatalf("buildRegistryTest %s", err)
		}
		if err = closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("buildRegistryTest %s", err)
		}
	}()
	time.Sleep(3000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			fmt.Println(cmdBytes)
		} else {
			break
		}
		fmt.Println("building Deis registy Dockerfile")
	}
}

func runDeisRegistryDataTest(t *testing.T) {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()

	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)

	go func() {
		err := cli.CmdInspect("--format", "'{{ .Config.Hostname }}'", "deis-registry-data")
		/*if err != nil {
			t.Fatalf("runDeisRegistryDataTest %s",err)
		}*/
		if err = closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("runDeisRegistryDataTest %s", err)
		}
	}()
	go func() {
		//fmt.Println("here1")
		time.Sleep(2000 * time.Millisecond)
		if err := closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("runEtcdTest %s", err)
		}
	}()
	var hostname string
	time.Sleep(1000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			fmt.Println(cmdBytes)
			hostname = cmdBytes
		} else {
			break
		}
		fmt.Println("inspecting deis registry data")
	}

	if strings.Contains(hostname, "Error") == true {
		go func() {
			err := cli.CmdRun("--name", "deis-registry-data", "-v", "/data", "deis/base", "/bin/true")
			if err != nil {
				t.Fatalf("%s", err)
			}
			if err := closeWrap(stdout, stdoutPipe, stdin); err != nil {
				t.Fatalf("%s", err)
			}
		}()
		for {
			if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
				fmt.Println(cmdBytes)
			} else {
				break
			}
			fmt.Println("pulling Deis registry data")
		}
	}
}

func runDeisRegistryTest(t *testing.T, IPAddress string) {
	stdin, _ := io.Pipe()
	stdout, stdoutPipe := io.Pipe()
	cli := client.NewDockerCli(nil, stdoutPipe, nil, daemonProto(), daemonAddr(), nil)
	go func() {
		//docker run --name deis-registry -p 5000:5000 -e PUBLISH=5000 -e HOST=10.0.0.37 --volumes-from deis-registry-data deis/registry

		err := cli.CmdRun("--name", "deis-registry", "-p", "5000:5000", "-e", "PUBLISH=5000", "-e", "HOST="+IPAddress, "--volumes-from", "deis-registry-data", "deis/registry")
		if err != nil {
			t.Fatalf("runDeisRegistryTest %s", err)
		}
		if err := closeWrap(stdout, stdoutPipe, stdin); err != nil {
			t.Fatalf("runDeisRegistryTest %s", err)
		}
	}()
	time.Sleep(3000 * time.Millisecond)
	for {
		if cmdBytes, err := bufio.NewReader(stdout).ReadString('\n'); err == nil {
			if strings.Contains(cmdBytes, "Booting") == true {
				if err := closeWrap(stdout, stdoutPipe, stdin); err != nil {
					t.Fatalf("runDeisRegistryTest %s", err)
				}
			}
			fmt.Println(cmdBytes)
		} else {
			break
		}
		fmt.Println("pulling Deis Registry")
	}

}

func TestBuild(t *testing.T) {
	done := make(chan bool, 1)
	done2 := make(chan bool, 1)
	pullEtcdTest(t, done)
	fmt.Println("1st")
	go func() {
		fmt.Println("2nd")
		<-done
		fmt.Println("3rd")
		runEtcdTest(t, done2)
		fmt.Println("4th")
	}()
	<-done2
	fmt.Println("5th")
	buildRegistryTest(t)
	fmt.Println("6th")
	runDeisRegistryDataTest(t)
	fmt.Println("7th")
	IPAddress := strings.TrimSuffix(getIPAddressTest(t), "\n")
	fmt.Println(IPAddress)
	fmt.Println("8th")
	runDeisRegistryTest(t, IPAddress)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"

	"gitgud.io/softashell/comfy-translator/config"
	"gitgud.io/softashell/comfy-translator/translator"
	"gitgud.io/softashell/comfy-translator/translator/bing"
	"gitgud.io/softashell/comfy-translator/translator/google"
	"gitgud.io/softashell/comfy-translator/translator/yandex"
)

var (
	cache       *Cache
	conf        *config.Config
	translators []translator.Translator
)

func main() {
	log.Info("Starting comfy translator")

	debug := os.Getenv("DEBUG")
	if len(debug) > 0 {
		log.SetLevel(log.DebugLevel)
	}

	m := http.NewServeMux()
	m.HandleFunc("/api/translate", translateHandler)

	n := negroni.New()
	n.UseHandler(m)

	var err error

	conf = config.NewConfig()
	err = conf.Load("comfy-translator.toml")
	if err != nil {
		log.Fatal(err)
	}

	cache, err = NewCache(conf.Database.Path)
	if err != nil {
		log.Fatalf("Failed to initialize translation cache: %v", err)
	}
	defer cache.Close()

	port := os.Getenv("PORT")
	if port == "" {
		port = conf.Port
	}

	startTranslators()

	listenAddr := fmt.Sprintf("%s:%s", conf.Host, port)

	log.WithFields(log.Fields{
		"addr": listenAddr,
	}).Info("Ready to accept connections")

	err = graceful.RunWithErr(listenAddr, 60*time.Second, n)
	if err != nil {
		log.Fatal(err)
	}
}

func startTranslators() {
	t := []translator.Translator{
		google.New(),
		bing.New(),   // FIXME: Bing starts refusing connection pretty randomly and I can't tell what it doesn't like
		yandex.New(), // Pretty bad quality
	}

	log.Info("Starting translation engines")

	for i := range t {
		name := t[i].Name()

		c, found := conf.Translator[name]
		if !found {
			log.Errorf("Couldn't find config for %q", name)
		}

		if c.Enabled {
			log.Infof("%s: Starting", name)
			err := t[i].Start(c)
			if err != nil {
				log.Warn(err)
			}

			if t[i].Enabled() {
				log.Infof("%s: Should be started", name)
			} else {
				log.Infof("%s: Did not start", name)
			}
		} else {
			log.Infof("%s: Disabled in config", name)
		}

		err := cache.createBucket(name)
		if err != nil {
			log.Errorf("Failed to create missing bucket %q", name)
		}
	}
	/*

		sort.Slice(t, func(i, j int) bool {
			return conf.Translator[t[i].Name()].Priority < conf.Translator[t[j].Name()].Priority
		})
	*/

	var order string
	for i := range t {
		order += t[i].Name()

		if !t[i].Enabled() {
			order += " (only cache)"
		}

		if i+i < len(t) {
			order += ", "
		}
	}

	log.Infof("Translation order: %s", order)

	translators = t
}
<commit_msg>Uncomment translator sorting<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"sort"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"

	"gitgud.io/softashell/comfy-translator/config"
	"gitgud.io/softashell/comfy-translator/translator"
	"gitgud.io/softashell/comfy-translator/translator/bing"
	"gitgud.io/softashell/comfy-translator/translator/google"
	"gitgud.io/softashell/comfy-translator/translator/yandex"
)

var (
	cache       *Cache
	conf        *config.Config
	translators []translator.Translator
)

func main() {
	log.Info("Starting comfy translator")

	debug := os.Getenv("DEBUG")
	if len(debug) > 0 {
		log.SetLevel(log.DebugLevel)
	}

	m := http.NewServeMux()
	m.HandleFunc("/api/translate", translateHandler)

	n := negroni.New()
	n.UseHandler(m)

	var err error

	conf = config.NewConfig()
	err = conf.Load("comfy-translator.toml")
	if err != nil {
		log.Fatal(err)
	}

	cache, err = NewCache(conf.Database.Path)
	if err != nil {
		log.Fatalf("Failed to initialize translation cache: %v", err)
	}
	defer cache.Close()

	port := os.Getenv("PORT")
	if port == "" {
		port = conf.Port
	}

	startTranslators()

	listenAddr := fmt.Sprintf("%s:%s", conf.Host, port)

	log.WithFields(log.Fields{
		"addr": listenAddr,
	}).Info("Ready to accept connections")

	err = graceful.RunWithErr(listenAddr, 60*time.Second, n)
	if err != nil {
		log.Fatal(err)
	}
}

func startTranslators() {
	t := []translator.Translator{
		google.New(),
		bing.New(),   // FIXME: Bing starts refusing connection pretty randomly and I can't tell what it doesn't like
		yandex.New(), // Pretty bad quality
	}

	log.Info("Starting translation engines")

	for i := range t {
		name := t[i].Name()

		c, found := conf.Translator[name]
		if !found {
			log.Errorf("Couldn't find config for %q", name)
		}

		if c.Enabled {
			log.Infof("%s: Starting", name)
			err := t[i].Start(c)
			if err != nil {
				log.Warn(err)
			}

			if t[i].Enabled() {
				log.Infof("%s: Should be started", name)
			} else {
				log.Infof("%s: Did not start", name)
			}
		} else {
			log.Infof("%s: Disabled in config", name)
		}

		err := cache.createBucket(name)
		if err != nil {
			log.Errorf("Failed to create missing bucket %q", name)
		}
	}

	sort.Slice(t, func(i, j int) bool {
		return conf.Translator[t[i].Name()].Priority < conf.Translator[t[j].Name()].Priority
	})

	var order string
	for i := range t {
		order += t[i].Name()

		if !t[i].Enabled() {
			order += " (only cache)"
		}

		if i+i < len(t) {
			order += ", "
		}
	}

	log.Infof("Translation order: %s", order)

	translators = t
}
<|endoftext|>


<commit_before>package main

import "math/rand"
import "time"
import "fmt"
import "strings"
import "flag"

var players []*Player
var debug bool
var singleRound bool

func main() {
	rand.Seed(time.Now().Unix())

	flag.BoolVar(&debug, "v", false, "Provide more verbose output for debugging purposes")
	flag.BoolVar(&singleRound, "s", false, "Stop after running only a single round of the game")
	flag.Parse()

	players = make([]*Player, flag.NArg())
	var startPlayer = 0

	var deck = new(Deck)

	for i, _ := range players {
			players[i] = new(Player)
	}

	for won, _ := PlayerWon(); !won; won, _ = PlayerWon() {
		deck.Init()
		for i, _ := range players {
			players[i].Init(flag.Arg(i), i, len(players), startPlayer)
			cardDrawn := deck.Draw()
			players[i].Send("draw", cardDrawn)
			players[i].AddToHand(cardDrawn)
			Debug("*** Player", i, "drew", cardDrawn, "***")
			Debug("*** Player", i, "hand:", players[i].HandString(), "***")
		}

		_ = deck.Draw() //throw away a card to prevent perfect information

		var currentPlayer = startPlayer
		for !deck.IsEmpty() && (SurvivingPlayers() > 1) {
			var fullMove, action, cardStr, targetCardStr string
			var target, n  int
			var card, cardDrawn Card

			if players[currentPlayer].lost {
				goto endTurn
			}

			SendAll("player", currentPlayer)

			cardDrawn = deck.Draw()
			players[currentPlayer].Send("draw", cardDrawn)
			players[currentPlayer].AddToHand(cardDrawn)

			Debug("*** Player", currentPlayer, "drew", cardDrawn, "***")
			Debug("*** Player", currentPlayer, "hand:", players[currentPlayer].HandString(), "***")

			fullMove = players[currentPlayer].Recieve()
			Debug("*** Player", currentPlayer, "move:", fullMove, "***")

			n, _ = fmt.Sscan(fullMove, &action, &cardStr, &target, &targetCardStr)

			if !strings.EqualFold(action, "play") {
				//must be "forfeit" or some invalid action
				Debug("*** ERROR: Player", currentPlayer, "made an illegal play or forfeited ***")
				Out(currentPlayer)
				goto endTurn
			}

			if n < 2 {
				//malformed move
				Debug("*** ERROR: Player", currentPlayer, "passed too few arguments ***")
				Out(currentPlayer)
				goto endTurn
			}

			card = ParseCard(cardStr)
			if ok := players[currentPlayer].RemoveFromHand(card); !ok {
				//trying to play a card you don't have is illegal
				Debug("*** ERROR: Player", currentPlayer, "played a card they dont have:", card, "***")
				Out(currentPlayer)
				goto endTurn
			}
			Debug("*** Player", currentPlayer, "played", card, "***")
			Debug("*** Player", currentPlayer, "hand:", players[currentPlayer].HandString(), "***")

			switch card {
			case Princess:
				SendAll("played", currentPlayer, card)
				Out(currentPlayer)
			}

		endTurn:
			currentPlayer++
			if currentPlayer >= len(players) {
				currentPlayer = 0
			}
		}
		startPlayer++
		if startPlayer >= len(players) {
			startPlayer = 0
		}

		winningHandValue := 0
		for i, _ := range players {
			if players[i].HandValue() > winningHandValue {
				winningHandValue = players[i].HandValue()
			}
		}
		for i, _ := range players {
			if players[i].HandValue() == winningHandValue {
				players[i].roundsWon++
			}
			players[i].Forfeit()
		}
		fmt.Print("Round ended.\t")
		for i, p := range players {
			fmt.Print("Player ", i, " score: ", p.roundsWon, "\t")
		}
		fmt.Println()

		if singleRound {
			//only running one round so stop now
			return
		}
	}
	_, winner := PlayerWon()
	fmt.Println("Player won:", winner, players[winner].name)
}

func SendAll(args ...interface{}) {
	for _, player := range players {
		if !player.lost {
			player.Send(args...)
		}
	}
}

func SurvivingPlayers() int {
	result := 0
	for _, player := range players {
		if !player.lost {
			result++
		}
	}
	return result
}

func PlayerWon() (bool, int) {
	for i, p := range players {
		if p.roundsWon >= 4 {
			return true, i
		}
	}
	return false, 0
}

func Out(playerNumber int) {
	players[playerNumber].Forfeit()
	SendAll("out", playerNumber, players[playerNumber].HandString())
	Debug("*** Player", playerNumber, "out:", players[playerNumber].HandString(), "***")
}

func Debug(args ...interface{}) {
	if debug {
		fmt.Println(args...)
	}
}<commit_msg>Implemented Minister behaviour<commit_after>package main

import "math/rand"
import "time"
import "fmt"
import "strings"
import "flag"

var players []*Player
var debug bool
var singleRound bool

func main() {
	rand.Seed(time.Now().Unix())

	flag.BoolVar(&debug, "v", false, "Provide more verbose output for debugging purposes")
	flag.BoolVar(&singleRound, "s", false, "Stop after running only a single round of the game")
	flag.Parse()

	players = make([]*Player, flag.NArg())
	var startPlayer = 0

	var deck = new(Deck)

	for i, _ := range players {
			players[i] = new(Player)
	}

	for won, _ := PlayerWon(); !won; won, _ = PlayerWon() {
		deck.Init()
		for i, _ := range players {
			players[i].Init(flag.Arg(i), i, len(players), startPlayer)
			cardDrawn := deck.Draw()
			players[i].Send("draw", cardDrawn)
			players[i].AddToHand(cardDrawn)
			Debug("*** Player", i, "drew", cardDrawn, "***")
			Debug("*** Player", i, "hand:", players[i].HandString(), "***")
		}

		_ = deck.Draw() //throw away a card to prevent perfect information

		var currentPlayer = startPlayer
		for !deck.IsEmpty() && (SurvivingPlayers() > 1) {
			var fullMove, action, cardStr, targetCardStr string
			var target, n  int
			var card, cardDrawn Card

			if players[currentPlayer].lost {
				goto endTurn
			}

			SendAll("player", currentPlayer)

			cardDrawn = deck.Draw()
			players[currentPlayer].Send("draw", cardDrawn)
			players[currentPlayer].AddToHand(cardDrawn)
			if players[currentPlayer].ministered && (players[currentPlayer].HandValue() >= 12) {
				Out(currentPlayer)
				goto endTurn
			}

			Debug("*** Player", currentPlayer, "drew", cardDrawn, "***")
			Debug("*** Player", currentPlayer, "hand:", players[currentPlayer].HandString(), "***")

			fullMove = players[currentPlayer].Recieve()
			Debug("*** Player", currentPlayer, "move:", fullMove, "***")

			n, _ = fmt.Sscan(fullMove, &action, &cardStr, &target, &targetCardStr)

			if !strings.EqualFold(action, "play") {
				//must be "forfeit" or some invalid action
				Debug("*** ERROR: Player", currentPlayer, "made an illegal play or forfeited ***")
				Out(currentPlayer)
				goto endTurn
			}

			if n < 2 {
				//malformed move
				Debug("*** ERROR: Player", currentPlayer, "passed too few arguments ***")
				Out(currentPlayer)
				goto endTurn
			}

			card = ParseCard(cardStr)
			if ok := players[currentPlayer].RemoveFromHand(card); !ok {
				//trying to play a card you don't have is illegal
				Debug("*** ERROR: Player", currentPlayer, "played a card they dont have:", card, "***")
				Out(currentPlayer)
				goto endTurn
			}
			Debug("*** Player", currentPlayer, "played", card, "***")
			Debug("*** Player", currentPlayer, "hand:", players[currentPlayer].HandString(), "***")

			switch card {
			case Princess:
				SendAll("played", currentPlayer, card)
				Out(currentPlayer)
			case Minister:
				SendAll("played", currentPlayer, card)
				players[currentPlayer].ministered = true
			}

		endTurn:
			currentPlayer++
			if currentPlayer >= len(players) {
				currentPlayer = 0
			}
		}
		startPlayer++
		if startPlayer >= len(players) {
			startPlayer = 0
		}

		winningHandValue := 0
		for i, _ := range players {
			if players[i].HandValue() > winningHandValue {
				winningHandValue = players[i].HandValue()
			}
		}
		for i, _ := range players {
			if players[i].HandValue() == winningHandValue {
				players[i].roundsWon++
			}
			players[i].Forfeit()
		}
		fmt.Print("Round ended.\t")
		for i, p := range players {
			fmt.Print("Player ", i, " score: ", p.roundsWon, "\t")
		}
		fmt.Println()

		if singleRound {
			//only running one round so stop now
			return
		}
	}
	_, winner := PlayerWon()
	fmt.Println("Player won:", winner, players[winner].name)
}

func SendAll(args ...interface{}) {
	for _, player := range players {
		if !player.lost {
			player.Send(args...)
		}
	}
}

func SurvivingPlayers() int {
	result := 0
	for _, player := range players {
		if !player.lost {
			result++
		}
	}
	return result
}

func PlayerWon() (bool, int) {
	for i, p := range players {
		if p.roundsWon >= 4 {
			return true, i
		}
	}
	return false, 0
}

func Out(playerNumber int) {
	players[playerNumber].Forfeit()
	SendAll("out", playerNumber, players[playerNumber].HandString())
	Debug("*** Player", playerNumber, "out:", players[playerNumber].HandString(), "***")
}

func Debug(args ...interface{}) {
	if debug {
		fmt.Println(args...)
	}
}<|endoftext|>