<commit_before>/*
   Multihost
   Multiple ports
*/

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/skynetservices/skydns/client"
	"github.com/skynetservices/skydns/msg"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

var (
	pathToSocket string
	domain       string
	environment  string
	skydnsUrl    string
	secret       string
	ttl          int
	beat         int

	skydns       *client.Client
	dockerClient *docker
	running      = make(map[string]struct{})
	errNotTagged = errors.New("image not tagged")
)

func init() {
	flag.StringVar(&pathToSocket, "s", "/var/run/docker.sock", "path to the docker unix socket")
	flag.StringVar(&skydnsUrl, "skydns", "", "url to the skydns url")
	flag.StringVar(&secret, "secret", "", "skydns secret")
	flag.StringVar(&domain, "domain", "", "same domain passed to skydns")
	flag.StringVar(&environment, "environment", "dev", "environment name where service is running")
	flag.IntVar(&ttl, "ttl", 60, "default ttl to use when registering a service")
	flag.IntVar(&beat, "beat", 0, "heartbeat interval")

	flag.Parse()

	if beat < 1 {
		beat = ttl - (ttl / 4)
	}

	if skydnsUrl == "" {
		skydnsUrl = "http://" + os.Getenv("SKYDNS_PORT_8080_TCP_ADDR") + ":8080"
	}

	if domain == "" {
		log.Fatal("Must specify your skydns domain")
	}
}

func heartbeat(uuid string) {
	if _, exists := running[uuid]; exists {
		return
	}
	// TODO: not safe for concurrent access
	running[uuid] = struct{}{}
	defer delete(running, uuid)

	var (
		errorCount int
		err        error
		container  *Container
	)

	for _ = range time.Tick(time.Duration(beat) * time.Second) {
		if errorCount > 10 {
			// if we encountered more than 10 errors just quit
			log.Printf("Aborting heartbeat for %s after 10 errors\n", uuid)
			return
		}

		if container, err = dockerClient.fetchContainer(uuid, ""); err != nil {
			errorCount++
			log.Println(err)
			break
		}

		if !container.State.Running {
			if err := removeService(uuid); err != nil {
				log.Println(err)
			}
			return
		}

		// don't fill logs if we have a low beat
		// may need to do something better here
		if beat >= 30 {
			log.Printf("Updating ttl for %s\n", container.Name)
		}

		if err := skydns.Update(uuid, uint32(ttl)); err != nil {
			errorCount++
			log.Println(err)
			break
		}
	}
}

// restoreContainers loads all running containers and inserts
// them into skydns when skydock starts
func restoreContainers() error {
	req, err := http.NewRequest("GET", fmt.Sprintf("/containers/json"), nil)
	if err != nil {
		return err
	}

	c, err := dockerClient.newConn()
	if err != nil {
		return err
	}
	defer c.Close()

	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		var (
			containers []*Container
			container  *Container
		)

		if err = json.NewDecoder(resp.Body).Decode(&containers); err != nil {
			return err
		}

		for _, cnt := range containers {
			uuid := truncate(cnt.Id)
			if container, err = dockerClient.fetchContainer(uuid, cnt.Image); err != nil {
				if err != errNotTagged {
					log.Printf("Failed to fetch %s for restore - %s\n", cnt.Id, err)
				}
				continue
			}

			if err := sendService(uuid, createService(container)); err != nil {
				log.Printf("Failed to send %s to skydns for restore - %s\n", uuid, err)
			}
		}
	}
	return nil
}

// <uuid>.<host>.<region>.<version>.<service>.<environment>.skydns.local
func createService(container *Container) *msg.Service {
	return &msg.Service{
		Name:        cleanImageImage(container.Image), // Service name
		Version:     removeSlash(container.Name),      // Instance of the service
		Host:        container.NetworkSettings.IpAddress,
		Environment: environment, // testing, prod, dev
		TTL:         uint32(ttl), // 60 seconds
		Port:        80,          // TODO: How to handle multiple ports
	}
}

// sendService sends the uuid and service data to skydns
func sendService(uuid string, service *msg.Service) error {
	log.Printf("Adding %s (%s)\n", uuid, service.Name)

	if err := skydns.Add(uuid, service); err != nil {
		return err
	}
	go heartbeat(uuid)

	return nil
}

func removeService(uuid string) error {
	log.Printf("Removing %s from skydns\n", uuid)
	return skydns.Delete(uuid)
}

func addService(uuid, image string) error {
	log.Printf("Adding %s for %s\n", uuid, image)
	container, err := dockerClient.fetchContainer(uuid, image)
	if err != nil {
		if err != errNotTagged {
			return err
		}
		return nil
	}

	if err := sendService(uuid, createService(container)); err != nil {
		return err
	}
	return nil
}

func main() {
	var err error
	if dockerClient, err = newClient(pathToSocket); err != nil {
		log.Fatal(err)
	}

	if skydns, err = client.NewClient(skydnsUrl, secret, domain, 53); err != nil {
		log.Fatal(err)
	}

	log.Println("Starting restore of running containers...")
	if err := restoreContainers(); err != nil {
		log.Fatal(err)
	}

	c, err := dockerClient.newConn()
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	req, err := http.NewRequest("GET", "/events", nil)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := c.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	log.Println("Starting run loop...")
	d := json.NewDecoder(resp.Body)
	for {
		var event *Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				log.Println("Stopping cleanly via EOF")
				break
			}
			log.Printf("Error decoding json %s\n", err)
			continue
		}
		uuid := truncate(event.ContainerId)

		switch event.Status {
		case "die", "stop", "kill":
			if err := removeService(uuid); err != nil {
				log.Printf("Error deleting %s - %s\n", uuid, err)
			}
		case "start", "restart":
			if err := addService(uuid, event.Image); err != nil {
				log.Printf("Error adding %s - %s\n", uuid, err)
			}
		}
	}
}
<commit_msg>Add handlers in goroutines to handle concurrent events<commit_after>/*
   Multihost
   Multiple ports
*/

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/skynetservices/skydns/client"
	"github.com/skynetservices/skydns/msg"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

var (
	pathToSocket     string
	domain           string
	environment      string
	skydnsUrl        string
	secret           string
	ttl              int
	beat             int
	numberOfHandlers int

	skydns       *client.Client
	dockerClient *docker
	running      = make(map[string]struct{})
	errNotTagged = errors.New("image not tagged")
)

func init() {
	flag.StringVar(&pathToSocket, "s", "/var/run/docker.sock", "path to the docker unix socket")
	flag.StringVar(&skydnsUrl, "skydns", "", "url to the skydns url")
	flag.StringVar(&secret, "secret", "", "skydns secret")
	flag.StringVar(&domain, "domain", "", "same domain passed to skydns")
	flag.StringVar(&environment, "environment", "dev", "environment name where service is running")
	flag.IntVar(&ttl, "ttl", 60, "default ttl to use when registering a service")
	flag.IntVar(&beat, "beat", 0, "heartbeat interval")
	flag.IntVar(&numberOfHandlers, "workers", 10, "number of concurrent workers")

	flag.Parse()

	if beat < 1 {
		beat = ttl - (ttl / 4)
	}

	if skydnsUrl == "" {
		skydnsUrl = "http://" + os.Getenv("SKYDNS_PORT_8080_TCP_ADDR") + ":8080"
	}

	if domain == "" {
		log.Fatal("Must specify your skydns domain")
	}
}

func heartbeat(uuid string) {
	if _, exists := running[uuid]; exists {
		return
	}
	// TODO: not safe for concurrent access
	running[uuid] = struct{}{}
	defer delete(running, uuid)

	var (
		errorCount int
		err        error
		container  *Container
	)

	for _ = range time.Tick(time.Duration(beat) * time.Second) {
		if errorCount > 10 {
			// if we encountered more than 10 errors just quit
			log.Printf("Aborting heartbeat for %s after 10 errors\n", uuid)
			return
		}

		if container, err = dockerClient.fetchContainer(uuid, ""); err != nil {
			errorCount++
			log.Println(err)
			break
		}

		if !container.State.Running {
			if err := removeService(uuid); err != nil {
				log.Println(err)
			}
			return
		}

		// don't fill logs if we have a low beat
		// may need to do something better here
		if beat >= 30 {
			log.Printf("Updating ttl for %s\n", container.Name)
		}

		if err := skydns.Update(uuid, uint32(ttl)); err != nil {
			errorCount++
			log.Println(err)
			break
		}
	}
}

// restoreContainers loads all running containers and inserts
// them into skydns when skydock starts
func restoreContainers() error {
	req, err := http.NewRequest("GET", fmt.Sprintf("/containers/json"), nil)
	if err != nil {
		return err
	}

	c, err := dockerClient.newConn()
	if err != nil {
		return err
	}
	defer c.Close()

	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		var (
			containers []*Container
			container  *Container
		)

		if err = json.NewDecoder(resp.Body).Decode(&containers); err != nil {
			return err
		}

		for _, cnt := range containers {
			uuid := truncate(cnt.Id)
			if container, err = dockerClient.fetchContainer(uuid, cnt.Image); err != nil {
				if err != errNotTagged {
					log.Printf("Failed to fetch %s for restore - %s\n", cnt.Id, err)
				}
				continue
			}

			if err := sendService(uuid, createService(container)); err != nil {
				log.Printf("Failed to send %s to skydns for restore - %s\n", uuid, err)
			}
		}
	}
	return nil
}

// <uuid>.<host>.<region>.<version>.<service>.<environment>.skydns.local
func createService(container *Container) *msg.Service {
	return &msg.Service{
		Name:        cleanImageImage(container.Image), // Service name
		Version:     removeSlash(container.Name),      // Instance of the service
		Host:        container.NetworkSettings.IpAddress,
		Environment: environment, // testing, prod, dev
		TTL:         uint32(ttl), // 60 seconds
		Port:        80,          // TODO: How to handle multiple ports
	}
}

// sendService sends the uuid and service data to skydns
func sendService(uuid string, service *msg.Service) error {
	log.Printf("Adding %s (%s)\n", uuid, service.Name)

	if err := skydns.Add(uuid, service); err != nil {
		return err
	}
	go heartbeat(uuid)

	return nil
}

func removeService(uuid string) error {
	log.Printf("Removing %s from skydns\n", uuid)
	return skydns.Delete(uuid)
}

func addService(uuid, image string) error {
	log.Printf("Adding %s for %s\n", uuid, image)
	container, err := dockerClient.fetchContainer(uuid, image)
	if err != nil {
		if err != errNotTagged {
			return err
		}
		return nil
	}

	if err := sendService(uuid, createService(container)); err != nil {
		return err
	}
	return nil
}

func eventHandler(c chan *Event, group *sync.WaitGroup) {
	group.Add(1)
	defer group.Done()

	for event := range c {
		uuid := truncate(event.ContainerId)
		switch event.Status {
		case "die", "stop", "kill":
			if err := removeService(uuid); err != nil {
				log.Printf("Error deleting %s - %s\n", uuid, err)
			}
		case "start", "restart":
			if err := addService(uuid, event.Image); err != nil {
				log.Printf("Error adding %s - %s\n", uuid, err)
			}
		}
	}
}

func main() {
	var (
		err       error
		eventChan = make(chan *Event, 100) // 100 event buffer
		group     = &sync.WaitGroup{}
	)
	if dockerClient, err = newClient(pathToSocket); err != nil {
		log.Fatal(err)
	}

	if skydns, err = client.NewClient(skydnsUrl, secret, domain, 53); err != nil {
		log.Fatal(err)
	}

	log.Println("Starting restore of running containers...")
	if err := restoreContainers(); err != nil {
		log.Fatal(err)
	}

	c, err := dockerClient.newConn()
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	// Start event handlers
	for i := 0; i < numberOfHandlers; i++ {
		go eventHandler(eventChan, group)
	}

	req, err := http.NewRequest("GET", "/events", nil)
	if err != nil {
		log.Fatal(err)
	}

	resp, err := c.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	log.Println("Starting run loop...")
	d := json.NewDecoder(resp.Body)
	for {
		var event *Event
		if err := d.Decode(&event); err != nil {
			if err == io.EOF {
				break
			}
			log.Printf("Error decoding json %s\n", err)
			continue
		}
		eventChan <- event
	}

	// Close the event chan then wait for handlers to finish
	close(eventChan)
	group.Wait()

	log.Println("Stopping cleanly via EOF")
}
<|endoftext|>


<commit_before>package main

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

	"github.com/liamzdenek/go-irc/irc"
	"github.com/liamzdenek/go-irc/irc/irce"
)

func main() {
	i := irc.NewIRC(Conf.Server)
	ch := irce.NewChannelHandler(i)

	name := Conf.Name

	for channel, channel_data := range Conf.Channels {
		ch.Join(channel)
		for _, feed_str := range channel_data.Feeds {
			this := feed_str
			go func() {
				feed := NewRSSFeed(this, NewRamCache())
				for item := range feed.Rx {
					time.Sleep(time.Second * time.Duration(len(Conf.Channels)))
					i.Tx <- &irc.Line{
						Command:   "PRIVMSG",
						Arguments: []string{channel},
						Suffix:    fmt.Sprintf("%s - %s", item.Title, item.Link),
					}
				}
			}()
		}
	}

	log.Printf("Entering main loop\n")
	//for e := range i.Rx {
	for {
		select {
		case e := <-i.Rx:
			// prints nice messages to stdout... great for debugging/logging
			// this line can be put after the PingHandler (given that you're
			// continuing when it returns true) and it won't print Pings
			irce.LogHandler(e)

			// handles PING/PONG automatically. You generally want this
			if i.PingHandler(e) {
				// prevents other handlers from wasting their time
				// when this ping has already been handled. we can just abort
				// early
				continue
			}

			ch.Handle(e)

			// Custom stuffs
			switch l := e.(type) {
			case *irc.EConnect:
				i.Tx <- &irc.Line{
					Command:   "NICK",
					Arguments: []string{name},
				}
				i.Tx <- &irc.Line{
					Command:   "USER",
					Arguments: []string{name, "8", "*"},
					Suffix:    "Liam Test",
				}
			case *irc.Line:
				switch l.Command {
				case "JOIN":
					c := l.Suffix;
					if Conf.Channels[c] != nil {
						log.Printf("GOT A JOIN IN: %s\n", c);
						p := strings.Split(l.Prefix, "@");
						if len(p) > 1 {
							ident := p[len(p)-1];
							name_parts := strings.Split(p[0], "!");
							name := name_parts[0];
							for _, person := range Conf.Channels[c].Ops {
								log.Printf("NAME: %s, IDENT: %s PERSON: %s\n", name, ident, person);
								if(person == ident) {
									i.Tx <- &irc.Line{
										Command:   "MODE",
										Arguments: []string{c, "+o", name},
									}
								}
							}
						}
					}
				case "001":
					/*i.Tx <-&irc.Line{
						Command: "PRIVMSG",
						Arguments: []string{"#rust-nuts"},
						Suffix: "LOL THING",
					};*/
				}
			}
		}
	}
}
<commit_msg>Fix messages getting sent to wrong channels<commit_after>package main

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

	"github.com/liamzdenek/go-irc/irc"
	"github.com/liamzdenek/go-irc/irc/irce"
)

func main() {
	i := irc.NewIRC(Conf.Server)
	ch := irce.NewChannelHandler(i)

	name := Conf.Name

	for channel, channel_data := range Conf.Channels {
		ch.Join(channel)
		for _, feed_str := range channel_data.Feeds {
			go func(f, c string) {
				feed := NewRSSFeed(f, NewRamCache())
				for item := range feed.Rx {
					time.Sleep(time.Second * time.Duration(len(Conf.Channels)))
					i.Tx <- &irc.Line{
						Command:   "PRIVMSG",
						Arguments: []string{c},
						Suffix:    fmt.Sprintf("%s - %s", item.Title, item.Link),
					}
				}
			}(feed_str, channel)
		}
	}

	log.Printf("Entering main loop\n")
	//for e := range i.Rx {
	for {
		select {
		case e := <-i.Rx:
			// prints nice messages to stdout... great for debugging/logging
			// this line can be put after the PingHandler (given that you're
			// continuing when it returns true) and it won't print Pings
			irce.LogHandler(e)

			// handles PING/PONG automatically. You generally want this
			if i.PingHandler(e) {
				// prevents other handlers from wasting their time
				// when this ping has already been handled. we can just abort
				// early
				continue
			}

			ch.Handle(e)

			// Custom stuffs
			switch l := e.(type) {
			case *irc.EConnect:
				i.Tx <- &irc.Line{
					Command:   "NICK",
					Arguments: []string{name},
				}
				i.Tx <- &irc.Line{
					Command:   "USER",
					Arguments: []string{name, "8", "*"},
					Suffix:    "Liam Test",
				}
			case *irc.Line:
				switch l.Command {
				case "JOIN":
					c := l.Suffix;
					if Conf.Channels[c] != nil {
						log.Printf("GOT A JOIN IN: %s\n", c);
						p := strings.Split(l.Prefix, "@");
						if len(p) > 1 {
							ident := p[len(p)-1];
							name_parts := strings.Split(p[0], "!");
							name := name_parts[0];
							for _, person := range Conf.Channels[c].Ops {
								log.Printf("NAME: %s, IDENT: %s PERSON: %s\n", name, ident, person);
								if(person == ident) {
									i.Tx <- &irc.Line{
										Command:   "MODE",
										Arguments: []string{c, "+o", name},
									}
								}
							}
						}
					}
				case "001":
					/*i.Tx <-&irc.Line{
						Command: "PRIVMSG",
						Arguments: []string{"#rust-nuts"},
						Suffix: "LOL THING",
					};*/
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/kballard/go-shellquote"
	"os/exec"
	"errors"
	. "github.com/bootstraponline/bitrise-step-firebase-test-lab/utils"
)

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

type FirebaseConfig struct {
	ResultsBucket string
	Options       string
	User          string
	Project       string
	KeyPath       string
	AppApk        string
	TestApk       string
	Debug         bool
}

func NewFirebaseConfig() (*FirebaseConfig, error) {
	empty := &FirebaseConfig{}

	gcloud_user := GetOptionalEnv(GCLOUD_USER)
	gcloud_project := GetOptionalEnv(GCLOUD_PROJECT)

	app_apk, err := GetRequiredEnv(APP_APK)
	if err != nil {
		return empty, err
	}

	err = FileExists(app_apk)
	if err != nil {
		return empty, err
	}

	test_apk := GetOptionalEnv(TEST_APK)
	if !IsEmpty(test_apk) {
		err = FileExists(test_apk)
		if err != nil {
			return empty, err
		}
	}

	gcloud_key_base64, err := GetRequiredEnv(GCLOUD_KEY)
	if err != nil {
		return empty, err
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(gcloud_key_base64)
	if err != nil {
		return empty, err
	}

	empty_gcloud_user := IsEmpty(gcloud_user)
	empty_gcloud_project := IsEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if IsEmpty(gcloud_user) {
				return empty, errors.New("GCLOUD_USER not defined in env or gcloud key")

			}
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if IsEmpty(gcloud_project) {
				return empty, errors.New("GCLOUD_PROJECT not defined in env or gcloud key")
			}
		}
	}

	home_dir, err := GetRequiredEnv(HOME)
	if err != nil {
		return empty, err
	}

	key_file_path := path.Join(home_dir, "gcloudkey.json")
	err = ioutil.WriteFile(key_file_path, gcloud_key, 0644)
	if err != nil {
		return empty, err
	}

	gcloud_bucket_value, err := GetRequiredEnv(GCLOUD_BUCKET)
	if err != nil {
		return empty, err
	}

	gcloud_options_value := GetOptionalEnv(GCLOUD_OPTIONS)

	return &FirebaseConfig{
		ResultsBucket: gcloud_bucket_value,
		User:          gcloud_user,
		Project:       gcloud_project,
		KeyPath:       key_file_path,
		AppApk:        app_apk,
		TestApk:       test_apk,
		Options:       gcloud_options_value,
		Debug:         false,
	}, nil
}

func exportGcsDir(bucket string, object string) error {
	gcs_results_dir := "gs://" + bucket + "/" + object
	fmt.Println("Exporting ", GCS_RESULTS_DIR, " ", gcs_results_dir)
	cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", GCS_RESULTS_DIR, "--value", gcs_results_dir).CombinedOutput()
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to export "+GCS_RESULTS_DIR+", error: %#v | output: %s", err.Error(), cmdLog))
	}

	return nil
}

func executeGcloud(config *FirebaseConfig, gcs_object string) ([]string, error) {
	if !config.Debug {
		RunCommand("gcloud config set project " + config.Project)
		RunCommand("gcloud auth activate-service-account --key-file " + config.KeyPath + " " + config.User)
	}

	// https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
	userOptionsSlice, err := shellquote.Split(config.Options)
	if err != nil {
		return make([]string, 0), err
	}

	userOptionsSet := GcloudOptionsToSet(userOptionsSlice)

	// Set --app, --test, --results-bucket, --results-dir and test type
	// Use user values for flags if supplied.
	args := make([]string, 0)
	args = append(args, "gcloud", "firebase", "test", "android", "run")

	const TEST_FLAG = "--test"
	const APP_FLAG = "--app"
	const RESULTS_BUCKET_FLAG = "--results-bucket="
	const RESULTS_DIR_FLAG = "--results-dir="

	if IsEmpty(config.TestApk) {
		args = append(args, "robo")
	} else {
		args = append(args, "instrumentation")
		if !userOptionsSet[TEST_FLAG] {
			args = append(args, "--test", config.TestApk)
		}
	}

	if !userOptionsSet[APP_FLAG] {
		args = append(args, APP_FLAG, config.AppApk)
	}
	if !userOptionsSet[RESULTS_BUCKET_FLAG] {
		args = append(args, RESULTS_BUCKET_FLAG+config.ResultsBucket)
	}
	if !userOptionsSet[RESULTS_DIR_FLAG] {
		args = append(args, RESULTS_DIR_FLAG+gcs_object)
	}

	// Don't export results bucket when it's user defined.
	if !userOptionsSet[RESULTS_BUCKET_FLAG] || !userOptionsSet[RESULTS_DIR_FLAG] {
		exportGcsDir(config.ResultsBucket, gcs_object)
	}

	if config.Debug {
		fmt.Println("auto args: ", args)
		fmt.Println("user args: ", userOptionsSlice)
	}

	return append(args, userOptionsSlice...), nil
}

func main() {
	config, err := NewFirebaseConfig()
	FatalError(err)

	command, err := executeGcloud(config, NewGcsObjectName())
	FatalError(err)

	err = RunCommandSlice(command)
	FatalError(err)

	os.Exit(0)
}
<commit_msg>Print command before running<commit_after>package main

import (
	"fmt"
	"os"
	"encoding/base64"
	"io/ioutil"
	"encoding/json"
	"path"
	"github.com/kballard/go-shellquote"
	"os/exec"
	"errors"
	. "github.com/bootstraponline/bitrise-step-firebase-test-lab/utils"
)

type GcloudKeyFile struct {
	ProjectID   string `json:"project_id"`
	ClientEmail string `json:"client_email"`
}

type FirebaseConfig struct {
	ResultsBucket string
	Options       string
	User          string
	Project       string
	KeyPath       string
	AppApk        string
	TestApk       string
	Debug         bool
}

func NewFirebaseConfig() (*FirebaseConfig, error) {
	empty := &FirebaseConfig{}

	gcloud_user := GetOptionalEnv(GCLOUD_USER)
	gcloud_project := GetOptionalEnv(GCLOUD_PROJECT)

	app_apk, err := GetRequiredEnv(APP_APK)
	if err != nil {
		return empty, err
	}

	err = FileExists(app_apk)
	if err != nil {
		return empty, err
	}

	test_apk := GetOptionalEnv(TEST_APK)
	if !IsEmpty(test_apk) {
		err = FileExists(test_apk)
		if err != nil {
			return empty, err
		}
	}

	gcloud_key_base64, err := GetRequiredEnv(GCLOUD_KEY)
	if err != nil {
		return empty, err
	}

	gcloud_key, err := base64.StdEncoding.DecodeString(gcloud_key_base64)
	if err != nil {
		return empty, err
	}

	empty_gcloud_user := IsEmpty(gcloud_user)
	empty_gcloud_project := IsEmpty(gcloud_project)

	if empty_gcloud_user || empty_gcloud_project {
		parsedKeyFile := GcloudKeyFile{}
		json.Unmarshal([]byte(gcloud_key), &parsedKeyFile)

		if empty_gcloud_user {
			gcloud_user = parsedKeyFile.ClientEmail
			if IsEmpty(gcloud_user) {
				return empty, errors.New("GCLOUD_USER not defined in env or gcloud key")

			}
		}

		if empty_gcloud_project {
			gcloud_project = parsedKeyFile.ProjectID
			if IsEmpty(gcloud_project) {
				return empty, errors.New("GCLOUD_PROJECT not defined in env or gcloud key")
			}
		}
	}

	home_dir, err := GetRequiredEnv(HOME)
	if err != nil {
		return empty, err
	}

	key_file_path := path.Join(home_dir, "gcloudkey.json")
	err = ioutil.WriteFile(key_file_path, gcloud_key, 0644)
	if err != nil {
		return empty, err
	}

	gcloud_bucket_value, err := GetRequiredEnv(GCLOUD_BUCKET)
	if err != nil {
		return empty, err
	}

	gcloud_options_value := GetOptionalEnv(GCLOUD_OPTIONS)

	return &FirebaseConfig{
		ResultsBucket: gcloud_bucket_value,
		User:          gcloud_user,
		Project:       gcloud_project,
		KeyPath:       key_file_path,
		AppApk:        app_apk,
		TestApk:       test_apk,
		Options:       gcloud_options_value,
		Debug:         false,
	}, nil
}

func exportGcsDir(bucket string, object string) error {
	gcs_results_dir := "gs://" + bucket + "/" + object
	fmt.Println("Exporting ", GCS_RESULTS_DIR, " ", gcs_results_dir)
	cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", GCS_RESULTS_DIR, "--value", gcs_results_dir).CombinedOutput()
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to export "+GCS_RESULTS_DIR+", error: %#v | output: %s", err.Error(), cmdLog))
	}

	return nil
}

func executeGcloud(config *FirebaseConfig, gcs_object string) ([]string, error) {
	if !config.Debug {
		RunCommand("gcloud config set project " + config.Project)
		RunCommand("gcloud auth activate-service-account --key-file " + config.KeyPath + " " + config.User)
	}

	// https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
	userOptionsSlice, err := shellquote.Split(config.Options)
	if err != nil {
		return make([]string, 0), err
	}

	userOptionsSet := GcloudOptionsToSet(userOptionsSlice)

	// Set --app, --test, --results-bucket, --results-dir and test type
	// Use user values for flags if supplied.
	args := make([]string, 0)
	args = append(args, "gcloud", "firebase", "test", "android", "run")

	const TEST_FLAG = "--test"
	const APP_FLAG = "--app"
	const RESULTS_BUCKET_FLAG = "--results-bucket="
	const RESULTS_DIR_FLAG = "--results-dir="

	if IsEmpty(config.TestApk) {
		args = append(args, "robo")
	} else {
		args = append(args, "instrumentation")
		if !userOptionsSet[TEST_FLAG] {
			args = append(args, "--test", config.TestApk)
		}
	}

	if !userOptionsSet[APP_FLAG] {
		args = append(args, APP_FLAG, config.AppApk)
	}
	if !userOptionsSet[RESULTS_BUCKET_FLAG] {
		args = append(args, RESULTS_BUCKET_FLAG+config.ResultsBucket)
	}
	if !userOptionsSet[RESULTS_DIR_FLAG] {
		args = append(args, RESULTS_DIR_FLAG+gcs_object)
	}

	// Don't export results bucket when it's user defined.
	if !userOptionsSet[RESULTS_BUCKET_FLAG] || !userOptionsSet[RESULTS_DIR_FLAG] {
		exportGcsDir(config.ResultsBucket, gcs_object)
	}

	if config.Debug {
		fmt.Println("auto args: ", args)
		fmt.Println("user args: ", userOptionsSlice)
	}

	return append(args, userOptionsSlice...), nil
}

func main() {
	config, err := NewFirebaseConfig()
	FatalError(err)

	command, err := executeGcloud(config, NewGcsObjectName())
	FatalError(err)

	fmt.Println(command)
	err = RunCommandSlice(command)
	FatalError(err)

	os.Exit(0)
}
<|endoftext|>


<commit_before>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

// Package main implements mediumctl.
package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
	RefreshToken      string
}

var (
	version       = "v0.2.1"
	revision      = "dev"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Printf("Your article was successfully posted.\n\n")
	fmt.Printf("title: %s\n", p.Title)
	if p.PublishStatus == "" {
		fmt.Println("publishStatus: public")
	} else {
		fmt.Printf("publishStatus: %s\n", p.PublishStatus)
	}
	if len(p.Tags) > 0 {
		fmt.Printf("tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("canonicalURL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, publicationNumber int, err error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("%s is empty", filename)
		return
	}

	var (
		content         string
		contentFormat   string
		canonicalURL    string
		license         string
		notifyFollowers bool
		publishedAt     string
		publishStatus   string
		tags            []string
		title           string
	)
	contentFormat = "markdown"
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		contentFormat = "html"
	}
	lines := strings.Split(string(b), "\n")

	for i, line := range lines[1:] {
		if strings.HasPrefix(line, "---") {
			content = strings.Join(lines[i+2:], "\n")
			break
		}
		if strings.HasPrefix(line, "canonicalURL: ") {
			canonicalURL = line[len("canonicalURL: "):]
		}
		if strings.HasPrefix(line, "license: ") {
			license = line[len("license: "):]
		}
		if strings.HasPrefix(line, "number: ") {
			publicationNumber, err = strconv.Atoi(line[len("number: "):])
			if err != nil {
				return
			}
		}
		if strings.HasPrefix(line, "notifyFollowers: true") {
			notifyFollowers = true
		}
		if strings.HasPrefix(line, "publishedAt: ") {
			publishedAt = line[len("publishedAt: "):]
		}
		if strings.HasPrefix(line, "publishStatus: ") {
			publishStatus = line[len("publishStatus: "):]
		}
		if strings.HasPrefix(line, "tags: ") {
			tags = strings.Split(line[len("tags: "):], " ")
		}
		if strings.HasPrefix(line, "title: ") {
			title = line[len("title: "):]
		}
	}
	if content == "" {
		content = strings.Join(lines, "\n")
	}
	article = medium.Article{
		Title:           title,
		ContentFormat:   contentFormat,
		Content:         content,
		CanonicalURL:    canonicalURL,
		Tags:            tags,
		PublishStatus:   publishStatus,
		PublishedAt:     publishedAt,
		License:         license,
		NotifyFollowers: notifyFollowers,
	}
	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, t *medium.Token) (err error) {
	b, err := json.Marshal(token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       t.AccessToken,
		ExpiresAt:         t.ExpiresAt,
		RefreshToken:      t.RefreshToken,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first")
	}
	var t token
	err = json.Unmarshal(b, &t)
	return &t, err
}

func main() {
	err := run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func run(args []string) (err error) {
	if len(args) < 2 {
		return helpCommand(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "o":
		err = authCommand(args)
	case "oauth":
		err = authCommand(args)
	case "r":
		err = refreshCommand(args)
	case "refresh":
		err = refreshCommand(args)
	case "i":
		err = infoCommand(args)
	case "info":
		err = infoCommand(args)
	case "p":
		err = postCommand(args, false)
	case "publication":
		err = postCommand(args, false)
	case "u":
		err = postCommand(args, true)
	case "user":
		err = postCommand(args, true)
	case "v":
		err = versionCommand(args)
	case "version":
		err = versionCommand(args)
	case "h":
		err = helpCommand(args)
	case "help":
		err = helpCommand(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = helpCommand(args)
	}
	return
}

func authCommand(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func refreshCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	refreshedToken, err := c.RefreshToken(t.RefreshToken)
	if err != nil {
		return
	}
	if err = saveToken(t.ApplicationID, t.ApplicationSecret, refreshedToken); err != nil {
		return
	}
	fmt.Println("Your API token was successfully refreshed.")
	return
}
func infoCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("You are logged in as:\n\n")
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Printf("\nYou have publication(s) below:\n\n")
	for i, p := range ps {
		fmt.Printf("Number: %d\n", i)
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n\n", p.URL)
	}
	return
}

func postCommand(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, publicationNumber, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	if publicationNumber < 0 || publicationNumber > len(ps)-1 {
		err = fmt.Errorf("publication number '%d' is invalid", publicationNumber)
		return
	}
	p, err := ps[publicationNumber].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func versionCommand(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func helpCommand(args []string) (err error) {
	fmt.Println(`usage: mediumctl <command> [options]

Commands:
  oauth, o
    Setting up API token with OAuth.
  refresh, r
    Refresh existing API token.
  info, i
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version, v
  Show version and revision information.
  help, h
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<commit_msg>Use frontmatter package<commit_after>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

// Package main implements mediumctl.
package main

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

	"github.com/ericaro/frontmatter"
	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
	RefreshToken      string
}

var (
	version       = "v0.3.0"
	revision      = "dev"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Printf("Your article was successfully posted.\n\n")
	fmt.Printf("title: %s\n", p.Title)
	if p.PublishStatus == "" {
		fmt.Println("publishStatus: public")
	} else {
		fmt.Printf("publishStatus: %s\n", p.PublishStatus)
	}
	if len(p.Tags) > 0 {
		fmt.Printf("tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("canonicalURL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, publicationNumber int, err error) {
	type FrontmatterOption struct {
		Title             string   `fm:"title"`
		ContentFormat     string   `fm:"contentFormat"`
		Content           string   `fm:"content"`
		CanonicalURL      string   `fm:"canonicalUrl"`
		Tags              []string `fm:"tags"`
		PublishStatus     string   `fm:"publishStatus"`
		PublishedAt       string   `fm:"publishedAt"`
		PublicationNumber int      `fm:"publicationNumber"`
		License           string   `fm:"license"`
		NotifyFollowers   bool     `fm:"notifyFollowers"`
	}

	var file []byte
	var fo *FrontmatterOption = &FrontmatterOption{}

	if file, err = ioutil.ReadFile(filename); err != nil {
		return
	}
	if err = frontmatter.Unmarshal(file, fo); err != nil {
		return
	}
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		fo.ContentFormat = "html"
	} else {
		fo.ContentFormat = "markdown"
	}

	article = medium.Article{
		Title:           fo.Title,
		ContentFormat:   fo.ContentFormat,
		Content:         fo.Content,
		CanonicalURL:    fo.CanonicalURL,
		Tags:            fo.Tags,
		PublishStatus:   fo.PublishStatus,
		PublishedAt:     fo.PublishedAt,
		License:         fo.License,
		NotifyFollowers: fo.NotifyFollowers,
	}
	publicationNumber = fo.PublicationNumber

	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, t *medium.Token) (err error) {
	b, err := json.Marshal(token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       t.AccessToken,
		ExpiresAt:         t.ExpiresAt,
		RefreshToken:      t.RefreshToken,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first")
	}
	var t token
	err = json.Unmarshal(b, &t)
	return &t, err
}

func main() {
	err := run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func run(args []string) (err error) {
	if len(args) < 2 {
		return helpCommand(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "o":
		err = authCommand(args)
	case "oauth":
		err = authCommand(args)
	case "r":
		err = refreshCommand(args)
	case "refresh":
		err = refreshCommand(args)
	case "i":
		err = infoCommand(args)
	case "info":
		err = infoCommand(args)
	case "p":
		err = postCommand(args, false)
	case "publication":
		err = postCommand(args, false)
	case "u":
		err = postCommand(args, true)
	case "user":
		err = postCommand(args, true)
	case "v":
		err = versionCommand(args)
	case "version":
		err = versionCommand(args)
	case "h":
		err = helpCommand(args)
	case "help":
		err = helpCommand(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = helpCommand(args)
	}
	return
}

func authCommand(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func refreshCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	refreshedToken, err := c.RefreshToken(t.RefreshToken)
	if err != nil {
		return
	}
	if err = saveToken(t.ApplicationID, t.ApplicationSecret, refreshedToken); err != nil {
		return
	}
	fmt.Println("Your API token was successfully refreshed.")
	return
}
func infoCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("You are logged in as:\n\n")
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Printf("\nYou have publication(s) below:\n\n")
	for i, p := range ps {
		fmt.Printf("Number: %d\n", i)
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n\n", p.URL)
	}
	return
}

func postCommand(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, publicationNumber, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	if publicationNumber < 0 || publicationNumber > len(ps)-1 {
		err = fmt.Errorf("publication number '%d' is invalid", publicationNumber)
		return
	}
	p, err := ps[publicationNumber].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func versionCommand(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func helpCommand(args []string) (err error) {
	fmt.Println(`usage: mediumctl <command> [options]

Commands:
  oauth, o
    Setting up API token with OAuth.
  refresh, r
    Refresh existing API token.
  info, i
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version, v
  Show version and revision information.
  help, h
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<|endoftext|>


<commit_before>/*
HubGraph grabs the latest events from the GitHub's API and builds an entertaining graph upon them.

A frontend web page is exposed with a D3-powered (https://d3js.org/) force graph in it.
Both unauthenticated and authenticated (OAUTH2 token) requests are supported, enabling the use of the 60 req/hr and 5000 req/hr rate limits.

Consult the help by running `./hubgraph -h` to learn more about the configuration options.
*/
package main

import (
	"flag"
	"fmt"
	"reflect"
	"time"
)

var (
	port  string
	pages int
	token string
	delay int
)

type node struct {
	ID    string `json:"id"`
	Group int    `json:"group"`
	Title string `json:"title"`
}

type link struct {
	Source string `json:"source"`
	Target string `json:"target"`
	Value  int    `json:"value"`
}

// D3 is the structure used to construct the data for the frontend D3 graph.
type D3 struct {
	Nodes []node `json:"nodes"`
	Links []link `json:"links"`
}

// Dashboard is the structure containing the necessary details for the dashboard.
type Dashboard struct {
	RequestsUsed    int    `json:"requestsUsed"`
	MaxRequests     int    `json:"maxRequests"`
	LastUpdate      string `json:"lastUpdate"`
	RefreshInterval int64  `json:"refreshInterval"`
}

// stringInSlice determines whenever a string is already present in a slice.
func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

// extractReposAsNodes parses the obtained data and creates the main nodes for every present repo.
func extractReposAsNodes(events GithubEvents, d3Data *D3) {
	var repos []string
	for _, evt := range events {
		if !stringInSlice(evt.Repo.Name, repos) {
			repos = append(repos, evt.Repo.Name)
		}
	}
	for _, repoName := range repos {
		d3Data.Nodes = append(d3Data.Nodes, node{repoName, 0, ""})
	}
}

// extractEventsAsLinks parses the obtained data and creates the links between the nodes.
func extractEventsAsLinks(events GithubEvents, d3Data *D3) {
	for _, evt := range events {
		// Generic event
		group, title := GetSpecsFromEventType(evt.Type)
		d3Data.Nodes = append(d3Data.Nodes, node{evt.ID, group, title})
		d3Data.Links = append(d3Data.Links, link{evt.Repo.Name, evt.ID, 1})
		// Fork event
		if evt.Payload.Forkee.ID != 0 {
			d3Data.Nodes = append(d3Data.Nodes, node{evt.Payload.Forkee.FullName, 0, ""})
			d3Data.Links = append(d3Data.Links, link{evt.Repo.Name, evt.Payload.Forkee.FullName, 10})
		}
	}
}

// buildGraph wraps around the other graph-building functions to generate new graph data.
// It iterates on as many API event pages as specified via the CLI flag or the default value.
func buildGraph() {
	// Prepare graph
	var d3Data D3
	for page := 1; page < pages+1; page++ {
		// Get latest events from GitHub
		events, err := GetHubData(pages, page, token)
		if _err, ok := err.(*APIError); ok {
			if _err.status == 403 {
				secondsToWait := RateLimitSpecs.ResetTimestamp - time.Now().UTC().Unix() + 3
				for {
					if secondsToWait <= 0 {
						clearLine()
						break
					}
					fmt.Printf("Rate limit reached. Will reset in %d seconds.    \r", secondsToWait)
					time.Sleep(time.Second * 1)
					secondsToWait--
				}
				buildGraph()
				return
			} else if _err.status == 304 {
				clearLine()
				fmt.Println("No new data available.")
				time.Sleep(time.Second * 1)
				return
			}
		}
		// Create graph nodes
		extractReposAsNodes(events, &d3Data)
		// Create graph links
		extractEventsAsLinks(events, &d3Data)
		clearLine()
		fmt.Printf("Page %d analyzed...\r", page)
	}
	// Output to memory
	MarshalD3ToMemory(d3Data)
}

// clearLine makes sure the terminal line is (theoretically...) empty before writing on it.
func clearLine() {
	fmt.Printf("                                                                                \r")
}

func main() {
	flag.StringVar(&port, "port", "3000", "The port to listen on")
	flag.IntVar(&pages, "pages", 3, "How many pages to read (will impact rate limiting dramatically!)")
	flag.IntVar(&delay, "delay", (60 * pages), "Delay in seconds between data refreshes. Defaults to (60 * pages), a safe timing for unauthenticated requests")
	flag.StringVar(&token, "token", "", "The token to authenticate requests with (will bring rate limiting to 5000/hr instead of 60/hr - https://github.com/settings/tokens/new)")
	flag.Parse()

	Listen(port)
	fmt.Println("Listening on port " + port + " - http://localhost:" + port + "/\n")
	buildGraph()
	var duration time.Duration
	if delay != (60 * pages) {
		duration = time.Second * time.Duration(delay)
	} else {
		if reflect.TypeOf(RateLimitSpecs.PollInterval).String() != "int" || RateLimitSpecs.PollInterval <= 0 {
			RateLimitSpecs.PollInterval = 60 * pages
		}
		duration = time.Second * time.Duration(RateLimitSpecs.PollInterval)
	}
	for {
		secondsToWait := duration.Seconds()
		for {
			if secondsToWait <= 0 {
				clearLine()
				break
			}
			fmt.Printf("Content updated at %s - Next refresh in: %fs (RL: %d/%d req/hr used)\r",
				time.Now().Format(time.RFC822Z), secondsToWait, (RateLimitSpecs.Limit - RateLimitSpecs.Remaining), RateLimitSpecs.Limit)
			time.Sleep(time.Second * 1)
			secondsToWait--
		}
		buildGraph()
	}
}
<commit_msg>Better payload handling<commit_after>/*
HubGraph grabs the latest events from the GitHub's API and builds an entertaining graph upon them.

A frontend web page is exposed with a D3-powered (https://d3js.org/) force graph in it.
Both unauthenticated and authenticated (OAUTH2 token) requests are supported, enabling the use of the 60 req/hr and 5000 req/hr rate limits.

Consult the help by running `./hubgraph -h` to learn more about the configuration options.
*/
package main

import (
	"flag"
	"fmt"
	"reflect"
	"time"
)

var (
	port  string
	pages int
	token string
	delay int
)

type node struct {
	ID    string `json:"id"`
	Group int    `json:"group"`
	Title string `json:"title"`
}

type link struct {
	Source string `json:"source"`
	Target string `json:"target"`
	Value  int    `json:"value"`
}

// D3 is the structure used to construct the data for the frontend D3 graph.
type D3 struct {
	Nodes []node `json:"nodes"`
	Links []link `json:"links"`
}

// Dashboard is the structure containing the necessary details for the dashboard.
type Dashboard struct {
	RequestsUsed    int    `json:"requestsUsed"`
	MaxRequests     int    `json:"maxRequests"`
	LastUpdate      string `json:"lastUpdate"`
	RefreshInterval int64  `json:"refreshInterval"`
}

// stringInSlice determines whenever a string is already present in a slice.
func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

// extractReposAsNodes parses the obtained data and creates the main nodes for every present repo.
func extractReposAsNodes(events GithubEvents, d3Data *D3) {
	var repos []string
	for _, evt := range events {
		if !stringInSlice(evt.Repo.Name, repos) {
			repos = append(repos, evt.Repo.Name)
		}
	}
	for _, repoName := range repos {
		d3Data.Nodes = append(d3Data.Nodes, node{repoName, 0, ""})
	}
}

// extractEventsAsLinks parses the obtained data and creates the links between the nodes.
func extractEventsAsLinks(events GithubEvents, d3Data *D3) {
	for _, evt := range events {
		// Generic handler
		group, title := GetSpecsFromEventType(evt.Type)
		d3Data.Nodes = append(d3Data.Nodes, node{evt.ID, group, title})
		d3Data.Links = append(d3Data.Links, link{evt.Repo.Name, evt.ID, 1})
		// Fork event
		if evt.Type == "ForkEvent" {
			d3Data.Nodes = append(d3Data.Nodes, node{evt.Payload.Forkee.FullName, 0, ""})
			d3Data.Links = append(d3Data.Links, link{evt.Repo.Name, evt.Payload.Forkee.FullName, 10})
		}
	}
}

// buildGraph wraps around the other graph-building functions to generate new graph data.
// It iterates on as many API event pages as specified via the CLI flag or the default value.
func buildGraph() {
	// Prepare graph
	var d3Data D3
	for page := 1; page < pages+1; page++ {
		// Get latest events from GitHub
		events, err := GetHubData(pages, page, token)
		if _err, ok := err.(*APIError); ok {
			if _err.status == 403 {
				secondsToWait := RateLimitSpecs.ResetTimestamp - time.Now().UTC().Unix() + 3
				for {
					if secondsToWait <= 0 {
						clearLine()
						break
					}
					fmt.Printf("Rate limit reached. Will reset in %d seconds.    \r", secondsToWait)
					time.Sleep(time.Second * 1)
					secondsToWait--
				}
				buildGraph()
				return
			} else if _err.status == 304 {
				clearLine()
				fmt.Println("No new data available.")
				time.Sleep(time.Second * 1)
				return
			}
		}
		// Create graph nodes
		extractReposAsNodes(events, &d3Data)
		// Create graph links
		extractEventsAsLinks(events, &d3Data)
		clearLine()
		fmt.Printf("Page %d analyzed...\r", page)
	}
	// Output to memory
	MarshalD3ToMemory(d3Data)
}

// clearLine makes sure the terminal line is (theoretically...) empty before writing on it.
func clearLine() {
	fmt.Printf("                                                                                \r")
}

func main() {
	flag.StringVar(&port, "port", "3000", "The port to listen on")
	flag.IntVar(&pages, "pages", 3, "How many pages to read (will impact rate limiting dramatically!)")
	flag.IntVar(&delay, "delay", (60 * pages), "Delay in seconds between data refreshes. Defaults to (60 * pages), a safe timing for unauthenticated requests")
	flag.StringVar(&token, "token", "", "The token to authenticate requests with (will bring rate limiting to 5000/hr instead of 60/hr - https://github.com/settings/tokens/new)")
	flag.Parse()

	Listen(port)
	fmt.Println("Listening on port " + port + " - http://localhost:" + port + "/\n")
	buildGraph()
	var duration time.Duration
	if delay != (60 * pages) {
		duration = time.Second * time.Duration(delay)
	} else {
		if reflect.TypeOf(RateLimitSpecs.PollInterval).String() != "int" || RateLimitSpecs.PollInterval <= 0 {
			RateLimitSpecs.PollInterval = 60 * pages
		}
		duration = time.Second * time.Duration(RateLimitSpecs.PollInterval)
	}
	for {
		secondsToWait := duration.Seconds()
		for {
			if secondsToWait <= 0 {
				clearLine()
				break
			}
			fmt.Printf("Content updated at %s - Next refresh in: %fs (RL: %d/%d req/hr used)\r",
				time.Now().Format(time.RFC822Z), secondsToWait, (RateLimitSpecs.Limit - RateLimitSpecs.Remaining), RateLimitSpecs.Limit)
			time.Sleep(time.Second * 1)
			secondsToWait--
		}
		buildGraph()
	}
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "github.com/BurntSushi/toml"
    "github.com/codegangsta/cli"
    "bufio"
    "fmt"
    "strings"
    "bytes"
    "io/ioutil"
    "text/template"
    "path/filepath"
)

var (
    Version = "0.0.2"

    CommentLine = "//"
    CommentBlockStart = "/*"
    CommentBlockEnd = "*/"

    PrefixConfig = CommentLine+"dico"
    PrefixStartGen = CommentLine+"AUTOGENERATE.DICO>>>"
    PrefixEndGen = CommentLine+"<<<AUTOGENERATE.DICO"

    PrefixStartConfig = CommentLine+"config."
    PrefixEndConfig = CommentLine+"config."

    HelpMessage = "The text in the section 'AUTOGENERATE.DICO' automatically generated, please do not edit it"

    DEBUG = false
)

var HelpfullTemplateFuncs = template.FuncMap{

}

func NewGenerator(args []string, config *Config) (*Generator, error) {
    if len(args) < 2 {
        return nil, fmt.Errorf("not valid command arguments %v", args);
    }

    // // TODO: check template errors
    var tpl *template.Template
    var err error

    // TODO: check file

    // b, err := ioutil.ReadFile(args[0])

    // if err != nil {
    //     return nil, err
    // }

    // s := string(b)

    // // parse execute file
    tpl = template.New(args[0]).Funcs(HelpfullTemplateFuncs)
    // tpl, err = template.New(args[0]).Funcs(HelpfullTemplateFuncs).Parse(s)

    // if err != nil {
    //     return nil, err
    // }

    // parse tempaltes
    pwd, _ := os.Getwd()
    for _, tplpath := range args[1:]{
        tpl, err = tpl.ParseGlob(pwd + string(os.PathSeparator) + tplpath)
    }

    if err != nil {
        return nil, err
    }

    tpl = template.Must(tpl, err)

    config.TplName = args[0]

    return &Generator{config, tpl}, nil
}

type Generator struct {
    Config *Config
    Tpl *template.Template
}

func (g *Generator) Compile() (string, error) {
    var b = bytes.NewBufferString("")

    g.Config.Config["ENV"] = g.Config.Env

    // if err := g.Tpl.Execute(b, g.Config.Config); err != nil {

    //     return "", err
    // }

    if err := g.Tpl.ExecuteTemplate(b, g.Config.TplName, g.Config.Config); err != nil {

        return "", err
    }

    return b.String(), nil
}

type ConfigParser func (raw string, v interface{}) (error)

var registredConfigTypes = map[string]ConfigParser{
    "toml": func (raw string, config interface{}) (error) {

        _ , err := toml.Decode(raw, config)

        return err
    },
}

func NewConfig(typeName string, configRaw string) *Config {
    // TODO: Check allowable types

    parser, supported := registredConfigTypes[typeName]

    return &Config{
        Type: typeName,
        ConfigRaw: configRaw,
        Config: make(map[string]interface{}),
        parser: parser,
        supportedType: supported,
        Env: make(map[interface{}]interface{}), // TODO: values for the application
    }
}

type Config struct {
    TplName string // output template name

    Type string // type config toml or ...
    ConfigRaw string

    Config map[string]interface{}
    Env map[interface{}]interface{}

    parser ConfigParser
    supportedType bool
}

func (g *Config) IsSupportedType() bool {

    return g.supportedType
}

func (g *Config) BuildConfig() error {
    if g.IsSupportedType() {

        return g.parser(g.ConfigRaw, &g.Config)
    }

    return fmt.Errorf("not supported type")
}

func (g *Config) String() string {

    return "generated text";
}

func removeCommentSymbols(str string) string {
    return strings.Replace(str, CommentLine, "", -1);
}

func getTypeConfig(str string) string {
    return strings.Replace(str, PrefixStartConfig, "", 1);
}

func isStartDicoConfig(str string) bool {
    return strings.HasPrefix(str, PrefixStartConfig)
}

func isEndDicoConfig(str string) bool {
    return strings.HasPrefix(str, PrefixEndConfig)
}

func isDicoCommand(str string) bool {
    return strings.HasPrefix(str, PrefixConfig)
}

func isStartCodeGenerated(str string) bool {
    return strings.HasPrefix(str, PrefixStartGen)
}

func isEndCodeGenerated(str string) bool {
    return strings.HasPrefix(str, PrefixEndGen)
}

func analyzeAndGenerate(path string) ([]byte, error) {
    buff := bytes.NewBuffer([]byte{});

    inFile, err := os.Open(path)

    if err != nil {
        return buff.Bytes(), err
    }

    defer inFile.Close()

    scanner := bufio.NewScanner(inFile)
    scanner.Split(bufio.ScanLines)

    lineCounter := 0

    for scanner.Scan() {
        lineCounter++;

NEXT:
        if isDicoCommand(scanner.Text()) {
            var command = strings.Replace(scanner.Text(), PrefixConfig, "", 1)
            var args = strings.Fields(command)
            var config = bytes.NewBuffer([]byte{});

            fmt.Printf("%s:%d\t%s\n", path, lineCounter, command)

            fmt.Fprintln(buff, scanner.Text()) // for reuse

            var EmbeddedConfig *Config

            // next row
            if scanner.Scan() {

                // detected embedded config
                if isStartDicoConfig(scanner.Text()) {
                    // first row config
                    fmt.Fprintln(buff, scanner.Text())

                    // type config
                    var typeConfig = getTypeConfig(scanner.Text())

                    for scanner.Scan() {
                        fmt.Fprintln(buff, scanner.Text())

                        if isEndDicoConfig(scanner.Text()) {
                            break
                        }

                        fmt.Fprintln(config, removeCommentSymbols(scanner.Text()))
                    }

                    EmbeddedConfig = NewConfig(typeConfig, config.String());

                    scanner.Scan()
                    // if !scanner.Scan() {

                    //     return buff.Bytes(), fmt.Errorf("unexpected ending")
                    // }
                }

                if isStartCodeGenerated(scanner.Text()) {
                    for scanner.Scan() {
                        if isEndCodeGenerated(scanner.Text()) {
                            // clear the previous generated code
                            break
                        }
                    }
                }
            }

            fmt.Fprintln(buff, PrefixStartGen)
            fmt.Fprintln(buff, CommentLine + "\t" + HelpMessage)
            fmt.Fprintln(buff, CommentLine + "[DICO.VERSION]:\t", Version)
            fmt.Fprintln(buff, CommentLine + "[DICO.COMMAND]:\t", command)

            if EmbeddedConfig != nil {

                err := EmbeddedConfig.BuildConfig()

                // for debug
                if DEBUG {
                   fmt.Fprintln(buff, CommentLine + "[DICO.CONFIG]:\t" + fmt.Sprintf("%+v", EmbeddedConfig.Config))
                }

                if err != nil && DEBUG {
                    fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_CONFIG]:\t" + err.Error())
                }

                var g *Generator
                g, err = NewGenerator(args, EmbeddedConfig)

                if g != nil && err == nil{
                    gentext, err := g.Compile()

                    if err == nil {
                        fmt.Fprintln(buff, gentext)

                    } else {
                        fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_TEXT]:\t" + err.Error())
                    }

                } else if (err != nil) {
                    fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_TEXT]:\t" + err.Error())
                }
            }

            fmt.Fprintln(buff, PrefixEndGen)

            if !isEndCodeGenerated(scanner.Text()) {

                // if the beginning of the next line Config
                if isDicoCommand(scanner.Text()) {
                    goto NEXT
                }

                fmt.Fprintln(buff, scanner.Text())
            }

            continue;
        }

        fmt.Fprintln(buff, scanner.Text())
    }

    return buff.Bytes(), nil
}

func analyzeFile(pattern string) func (fp string, fi os.FileInfo, err error) error {
    return func (fp string, fi os.FileInfo, err error) error {
        // https://rosettacode.org/wiki/Walk_a_directory/Recursively#Go

        if err != nil {
            fmt.Println(err) // can't walk here,
            return nil       // but continue walking elsewhere
        }
        if fi.IsDir() {
            return nil // not a file.  ignore.
        }

        matched, err := filepath.Match(pattern, fi.Name())

        if err != nil {
            fmt.Println(err) // malformed pattern
            return err       // this is fatal.
        }
        if !matched {
            return nil
        }

        if out, err := analyzeAndGenerate(fp); err == nil {
            err := ioutil.WriteFile(fp, out, 0644)
            if err != nil {
                fmt.Println(err)
            }
        } else {
            fmt.Println(err)
        }

        return nil
    }
}

func main() {
    app := cli.NewApp()
    app.Action = func(c *cli.Context) {
        path := c.Args().Get(0)

        filepath.Walk(path, analyzeFile(c.Args().Get(1)))
    }

    fmt.Printf("%v\n", os.Args)

    app.Run(os.Args)
}

/*
//dico main templates/golang/*
//AUTOGENERATE.DICO>>>
//	The text in the section 'AUTOGENERATE.DICO' automatically generated, please do not edit it
//[DICO.VERSION]:	 0.0.1
//[DICO.COMMAND]:	  main templates/golang/*
//<<<AUTOGENERATE.DICO
*/
<commit_msg>добавлены вспомогательные функции<commit_after>package main

import (
    "os"
    "github.com/BurntSushi/toml"
    "github.com/codegangsta/cli"
    "bufio"
    "fmt"
    "strings"
    "bytes"
    "io/ioutil"
    "text/template"
    "path/filepath"
    "unicode"
)

var (
    Version = "0.0.2"

    CommentLine = "//"
    CommentBlockStart = "/*"
    CommentBlockEnd = "*/"

    PrefixConfig = CommentLine+"dico"
    PrefixStartGen = CommentLine+"AUTOGENERATE.DICO>>>"
    PrefixEndGen = CommentLine+"<<<AUTOGENERATE.DICO"

    PrefixStartConfig = CommentLine+"config."
    PrefixEndConfig = CommentLine+"config."

    HelpMessage = "The text in the section 'AUTOGENERATE.DICO' automatically generated, please do not edit it"

    DEBUG = false
)

var HelpfullTemplateFuncs = template.FuncMap{
    "intersection": func (v interface{}, vv ...interface{}) bool {
        for _, _v := range vv {
            if _v == v {
                return true
            }
        }

        return false
    },
    "map": func(vv ...interface{}) (res map[interface{}]interface{}) {
        res = make(map[interface{}]interface{})

        if len(vv) == 0 || len(vv) % 2 != 0 {

            return
        }

        for i := 0; i < len(vv); i+=2 {
            res[vv[i]] = vv[i+1]
        }

        return
    },

    // toUpper transform "str str" to "Str<separator>Str"
    "toUpper": func (ss ...string) string {
        if len(ss) == 0 {
            return ""
        }

        res := bytes.NewBufferString("")
        hasSep := true // first symbol up
        replace := ""

        if len(ss) == 2 {
            replace = ss[1]
        }

        for index, runeValue := range ss[0] {
            if !unicode.IsLetter(runeValue) {
                hasSep = true
                continue
            }

            if hasSep {
                if index > 0 {
                    res.WriteString(replace)
                }

                runeValue = unicode.ToUpper(runeValue)
            }

            res.WriteRune(runeValue)

            hasSep = false
        }

        return res.String()
    },
    // toLower transform "str str" to "str<separator>str"
    "toLower": func (ss ...string) string {
        if len(ss) == 0 {
            return ""
        }

        res := bytes.NewBufferString("")
        hasSep := false
        replace := ""

        if len(ss) == 2 {
            replace = ss[1]
        }

        for index, runeValue := range ss[0] {
            if !unicode.IsLetter(runeValue) {
                hasSep = true
                continue
            }

            if unicode.IsUpper(runeValue) {

                runeValue = unicode.ToLower(runeValue)
                hasSep = true
            }

            if index > 0 && hasSep {
                res.WriteString(replace)
            }

            // IsLower
            res.WriteRune(runeValue)

            hasSep = false
        }

        return res.String()
    },
    "firstLower": func (s string) string {
        if len(s) == 0 {
            return ""
        }

        return string(unicode.ToLower([]rune(s)[0]))
    },
}

func NewGenerator(args []string, config *Config) (*Generator, error) {
    if len(args) < 2 {
        return nil, fmt.Errorf("not valid command arguments %v", args);
    }

    // // TODO: check template errors
    var tpl *template.Template
    var err error

    // TODO: check file

    // b, err := ioutil.ReadFile(args[0])

    // if err != nil {
    //     return nil, err
    // }

    // s := string(b)

    // // parse execute file
    tpl = template.New(args[0]).Funcs(HelpfullTemplateFuncs)
    // tpl, err = template.New(args[0]).Funcs(HelpfullTemplateFuncs).Parse(s)

    // if err != nil {
    //     return nil, err
    // }

    // parse tempaltes
    pwd, _ := os.Getwd()
    for _, tplpath := range args[1:]{
        tpl, err = tpl.ParseGlob(pwd + string(os.PathSeparator) + tplpath)
    }

    if err != nil {
        return nil, err
    }

    tpl = template.Must(tpl, err)

    config.TplName = args[0]

    if DEBUG {
        fmt.Printf("defined tempaltes: %v\n", tpl.DefinedTemplates())
    }

    return &Generator{config, tpl}, nil
}

type Generator struct {
    Config *Config
    Tpl *template.Template
}

func (g *Generator) Compile() (string, error) {
    var b = bytes.NewBufferString("")

    g.Config.Config["ENV"] = g.Config.Env

    // if err := g.Tpl.Execute(b, g.Config.Config); err != nil {

    //     return "", err
    // }

    if err := g.Tpl.ExecuteTemplate(b, g.Config.TplName, g.Config.Config); err != nil {

        return "", err
    }

    return b.String(), nil
}

type ConfigParser func (raw string, v interface{}) (error)

var registredConfigTypes = map[string]ConfigParser{
    "toml": func (raw string, config interface{}) (error) {

        _ , err := toml.Decode(raw, config)

        return err
    },
}

func NewConfig(typeName string, configRaw string) *Config {
    // TODO: Check allowable types

    parser, supported := registredConfigTypes[typeName]

    return &Config{
        Type: typeName,
        ConfigRaw: configRaw,
        Config: make(map[string]interface{}),
        parser: parser,
        supportedType: supported,
        Env: make(map[interface{}]interface{}), // TODO: values for the application
    }
}

type Config struct {
    TplName string // output template name

    Type string // type config toml or ...
    ConfigRaw string

    Config map[string]interface{}
    Env map[interface{}]interface{}

    parser ConfigParser
    supportedType bool
}

func (g *Config) IsSupportedType() bool {

    return g.supportedType
}

func (g *Config) BuildConfig() error {
    if g.IsSupportedType() {

        return g.parser(g.ConfigRaw, &g.Config)
    }

    return fmt.Errorf("not supported type")
}

func (g *Config) String() string {

    return "generated text";
}

func removeCommentSymbols(str string) string {
    return strings.Replace(str, CommentLine, "", -1);
}

func getTypeConfig(str string) string {
    return strings.Replace(str, PrefixStartConfig, "", 1);
}

func isStartDicoConfig(str string) bool {
    return strings.HasPrefix(str, PrefixStartConfig)
}

func isEndDicoConfig(str string) bool {
    return strings.HasPrefix(str, PrefixEndConfig)
}

func isDicoCommand(str string) bool {
    return strings.HasPrefix(str, PrefixConfig)
}

func isStartCodeGenerated(str string) bool {
    return strings.HasPrefix(str, PrefixStartGen)
}

func isEndCodeGenerated(str string) bool {
    return strings.HasPrefix(str, PrefixEndGen)
}

func analyzeAndGenerate(path string) ([]byte, error) {
    buff := bytes.NewBuffer([]byte{});

    inFile, err := os.Open(path)

    if err != nil {
        return buff.Bytes(), err
    }

    defer inFile.Close()

    scanner := bufio.NewScanner(inFile)
    scanner.Split(bufio.ScanLines)

    lineCounter := 0

    for scanner.Scan() {
        lineCounter++;

NEXT:
        if isDicoCommand(scanner.Text()) {
            var command = strings.Replace(scanner.Text(), PrefixConfig, "", 1)
            var args = strings.Fields(command)
            var config = bytes.NewBuffer([]byte{});

            fmt.Printf("%s:%d\t%s\n", path, lineCounter, command)

            fmt.Fprintln(buff, scanner.Text()) // for reuse

            var EmbeddedConfig *Config

            // next row
            if scanner.Scan() {

                // detected embedded config
                if isStartDicoConfig(scanner.Text()) {
                    // first row config
                    fmt.Fprintln(buff, scanner.Text())

                    // type config
                    var typeConfig = getTypeConfig(scanner.Text())

                    for scanner.Scan() {
                        fmt.Fprintln(buff, scanner.Text())

                        if isEndDicoConfig(scanner.Text()) {
                            break
                        }

                        fmt.Fprintln(config, removeCommentSymbols(scanner.Text()))
                    }

                    EmbeddedConfig = NewConfig(typeConfig, config.String());

                    scanner.Scan()
                    // if !scanner.Scan() {

                    //     return buff.Bytes(), fmt.Errorf("unexpected ending")
                    // }
                }

                if isStartCodeGenerated(scanner.Text()) {
                    for scanner.Scan() {
                        if isEndCodeGenerated(scanner.Text()) {
                            // clear the previous generated code
                            break
                        }
                    }
                }
            }

            fmt.Fprintln(buff, PrefixStartGen)
            fmt.Fprintln(buff, CommentLine + "\t" + HelpMessage)
            fmt.Fprintln(buff, CommentLine + "[DICO.VERSION]:\t", Version)
            fmt.Fprintln(buff, CommentLine + "[DICO.COMMAND]:\t", command)

            if EmbeddedConfig != nil {

                err := EmbeddedConfig.BuildConfig()

                // for debug
                if DEBUG {
                   fmt.Fprintln(buff, CommentLine + "[DICO.CONFIG]:\t" + fmt.Sprintf("%+v", EmbeddedConfig.Config))
                }

                if err != nil && DEBUG {
                    fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_CONFIG]:\t" + err.Error())
                }

                var g *Generator
                g, err = NewGenerator(args, EmbeddedConfig)

                if g != nil && err == nil{
                    gentext, err := g.Compile()

                    if err == nil {
                        fmt.Fprintln(buff, gentext)

                    } else {
                        fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_TEXT]:\t" + err.Error())
                    }

                } else if (err != nil) {
                    fmt.Fprintln(buff, CommentLine + "[DICO.ERRORS.COMPLIE_TEXT]:\t" + err.Error())
                }
            }

            fmt.Fprintln(buff, PrefixEndGen)

            if !isEndCodeGenerated(scanner.Text()) {

                // if the beginning of the next line Config
                if isDicoCommand(scanner.Text()) {
                    goto NEXT
                }

                fmt.Fprintln(buff, scanner.Text())
            }

            continue;
        }

        fmt.Fprintln(buff, scanner.Text())
    }

    return buff.Bytes(), nil
}

func analyzeFile(pattern string) func (fp string, fi os.FileInfo, err error) error {
    return func (fp string, fi os.FileInfo, err error) error {
        // https://rosettacode.org/wiki/Walk_a_directory/Recursively#Go

        if err != nil {
            fmt.Println(err) // can't walk here,
            return nil       // but continue walking elsewhere
        }
        if fi.IsDir() {
            return nil // not a file.  ignore.
        }

        matched, err := filepath.Match(pattern, fi.Name())

        if err != nil {
            fmt.Println(err) // malformed pattern
            return err       // this is fatal.
        }
        if !matched {
            return nil
        }

        if out, err := analyzeAndGenerate(fp); err == nil {
            err := ioutil.WriteFile(fp, out, 0644)
            if err != nil {
                fmt.Println(err)
            }
        } else {
            fmt.Println(err)
        }

        return nil
    }
}

func main() {
    app := cli.NewApp()
    app.Action = func(c *cli.Context) {
        path := c.Args().Get(0)

        filepath.Walk(path, analyzeFile(c.Args().Get(1)))
    }

    if DEBUG {
        fmt.Printf("%v\n", os.Args)
    }

    app.Run(os.Args)
}

/*
//dico main templates/golang/*
//AUTOGENERATE.DICO>>>
//	The text in the section 'AUTOGENERATE.DICO' automatically generated, please do not edit it
//[DICO.VERSION]:	 0.0.1
//[DICO.COMMAND]:	  main templates/golang/*
//<<<AUTOGENERATE.DICO
*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/albrow/prtty"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

const (
	version = "temple version X.X.X (develop)"
)

var (
	// NOTE: GOPATH might consist of multiple paths. If that is the case, we look in the first path.
	gopath        = strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))[0]
	templePath    = filepath.Join(gopath, "src", "github.com", "albrow", "temple")
	generatedTmpl = template.Must(template.ParseFiles(filepath.Join(templePath, "templates.go.tmpl")))
)

func main() {
	cmdBuild := &cobra.Command{
		Use:   "build <src> <dest>",
		Short: "Compile the templates in the src directory into a package in the dest directory.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) != 2 {
				prtty.Error.Fatal("temple build requires exactly 2 arguments: the src directory and the dest directory.")
			}
			includes := cmd.Flag("includes").Value.String()
			if err := build(args[0], args[1], includes); err != nil {
				prtty.Error.Fatal(err)
			}
		},
	}
	cmdBuild.Flags().String("includes", "", "A directory which includes *.tmpl files to be shared between all templates.")

	cmdVersion := &cobra.Command{
		Use:   "version",
		Short: "Print the current version number.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println(version)
		},
	}

	rootCmd := &cobra.Command{
		Use:   "temple",
		Short: "A command line tool for sharing go templates between client and server.",
	}
	rootCmd.AddCommand(cmdBuild, cmdVersion)
	if err := rootCmd.Execute(); err != nil {
		prtty.Error.Fatal(err)
	}
}

type TemplateData struct {
	PackageName string
	Templates   []*TemplateFile
	Includes    []*TemplateFile
}

type TemplateFile struct {
	VarName string
	Name    string
	Source  string
}

func NewTemplateFile(filename string) (*TemplateFile, error) {
	// name is everything after the last slash, not including the file extension
	name := strings.TrimSuffix(filename[strings.LastIndex(filename, string(os.PathSeparator))+1:], ".tmpl")
	// varName is just the name titlized so it is an exported variable
	varName := strings.Title(name)
	fileContents, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &TemplateFile{
		VarName: varName,
		Name:    name,
		Source:  string(fileContents),
	}, nil
}

func ParseTemplateFiles(dir string) ([]*TemplateFile, error) {
	templateFiles := []*TemplateFile{}
	files, err := filepath.Glob(filepath.Join(dir, "*.tmpl"))
	if err != nil {
		return nil, err
	}
	for _, filename := range files {
		prtty.Default.Printf("    %s", filename)
		tf, err := NewTemplateFile(filename)
		if err != nil {
			return nil, err
		}
		templateFiles = append(templateFiles, tf)
	}
	return templateFiles, nil
}

func build(src, dest, includes string) error {
	prtty.Info.Println("--> building...")
	prtty.Default.Printf("    src: %s", src)
	prtty.Default.Printf("    dest: %s", dest)
	if includes != "" {
		prtty.Default.Printf("    includes: %s", includes)
	}

	// packageName is everything after the last path separator
	packageName := dest[strings.LastIndex(dest, string(os.PathSeparator))+1:]
	templateData := TemplateData{
		PackageName: packageName,
	}

	if includes != "" {
		prtty.Info.Println("--> parsing includes...")
		includes, err := ParseTemplateFiles(includes)
		if err != nil {
			return err
		}
		templateData.Includes = includes
	}
	prtty.Info.Println("--> parsing template files...")
	templates, err := ParseTemplateFiles(src)
	if err != nil {
		return err
	}
	templateData.Templates = templates

	prtty.Info.Println("--> generating go code...")
	if err := os.MkdirAll(dest, os.ModePerm); err != nil {
		return err
	}
	destFilename := filepath.Join(dest, packageName+".go")
	destFile, err := os.Create(destFilename)
	if err != nil {
		return err
	}
	prtty.Success.Printf("    CREATE %s", destFilename)
	if err := generatedTmpl.Execute(destFile, templateData); err != nil {
		return err
	}

	prtty.Info.Println("--> done")
	return nil
}
<commit_msg>Use filepath.Base instead of string.Split when appropriate<commit_after>package main

import (
	"fmt"
	"github.com/albrow/prtty"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

const (
	version = "temple version X.X.X (develop)"
)

var (
	// NOTE: GOPATH might consist of multiple paths. If that is the case, we look in the first path.
	gopath        = strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))[0]
	templePath    = filepath.Join(gopath, "src", "github.com", "albrow", "temple")
	generatedTmpl = template.Must(template.ParseFiles(filepath.Join(templePath, "templates.go.tmpl")))
)

func main() {
	cmdBuild := &cobra.Command{
		Use:   "build <src> <dest>",
		Short: "Compile the templates in the src directory into a package in the dest directory.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) != 2 {
				prtty.Error.Fatal("temple build requires exactly 2 arguments: the src directory and the dest directory.")
			}
			includes := cmd.Flag("includes").Value.String()
			if err := build(args[0], args[1], includes); err != nil {
				prtty.Error.Fatal(err)
			}
		},
	}
	cmdBuild.Flags().String("includes", "", "A directory which includes *.tmpl files to be shared between all templates.")

	cmdVersion := &cobra.Command{
		Use:   "version",
		Short: "Print the current version number.",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println(version)
		},
	}

	rootCmd := &cobra.Command{
		Use:   "temple",
		Short: "A command line tool for sharing go templates between client and server.",
	}
	rootCmd.AddCommand(cmdBuild, cmdVersion)
	if err := rootCmd.Execute(); err != nil {
		prtty.Error.Fatal(err)
	}
}

type TemplateData struct {
	PackageName string
	Templates   []*TemplateFile
	Includes    []*TemplateFile
}

type TemplateFile struct {
	VarName string
	Name    string
	Source  string
}

func NewTemplateFile(filename string) (*TemplateFile, error) {
	// name is everything after the last slash, not including the file extension
	name := strings.TrimSuffix(filepath.Base(filename), ".tmpl")
	// varName is just the name titlized so it is an exported variable
	varName := strings.Title(name)
	fileContents, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &TemplateFile{
		VarName: varName,
		Name:    name,
		Source:  string(fileContents),
	}, nil
}

func ParseTemplateFiles(dir string) ([]*TemplateFile, error) {
	templateFiles := []*TemplateFile{}
	files, err := filepath.Glob(filepath.Join(dir, "*.tmpl"))
	if err != nil {
		return nil, err
	}
	for _, filename := range files {
		prtty.Default.Printf("    %s", filename)
		tf, err := NewTemplateFile(filename)
		if err != nil {
			return nil, err
		}
		templateFiles = append(templateFiles, tf)
	}
	return templateFiles, nil
}

func build(src, dest, includes string) error {
	prtty.Info.Println("--> building...")
	prtty.Default.Printf("    src: %s", src)
	prtty.Default.Printf("    dest: %s", dest)
	if includes != "" {
		prtty.Default.Printf("    includes: %s", includes)
	}

	// packageName is everything after the last path separator
	packageName := filepath.Base(dest)
	templateData := TemplateData{
		PackageName: packageName,
	}

	if includes != "" {
		prtty.Info.Println("--> parsing includes...")
		includes, err := ParseTemplateFiles(includes)
		if err != nil {
			return err
		}
		templateData.Includes = includes
	}
	prtty.Info.Println("--> parsing template files...")
	templates, err := ParseTemplateFiles(src)
	if err != nil {
		return err
	}
	templateData.Templates = templates

	prtty.Info.Println("--> generating go code...")
	if err := os.MkdirAll(dest, os.ModePerm); err != nil {
		return err
	}
	destFilename := filepath.Join(dest, packageName+".go")
	destFile, err := os.Create(destFilename)
	if err != nil {
		return err
	}
	prtty.Success.Printf("    CREATE %s", destFilename)
	if err := generatedTmpl.Execute(destFile, templateData); err != nil {
		return err
	}

	prtty.Info.Println("--> done")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"log"
	"net/http"
	"time"
)

func handler(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	err := r.ParseForm()

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

	q := &Question{}
	decoder := schema.NewDecoder()
	err = decoder.Decode(q, r.Form)

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

	a := &AnswerWrapper{
		Answer: &Answer{
			Answered:  "yes",
			Pid:       "demo-id-01",
			Qid:       q.Qid,
			Time:      int64(time.Since(start) / time.Millisecond),
			Content:   "[YOUR ANSWER HERE]",
			Resources: "resource1,resource2",
		},
	}

	fmt.Fprintf(w, "%s%s", xml.Header, a)
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", handler)
	http.Handle("/", r)

	log.Fatal(http.ListenAndServe(":8080", nil))
	fmt.Println("", "Yeah!")
}
<commit_msg>Added a couple of log statements<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"log"
	"net/http"
	"time"
)

func handler(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	err := r.ParseForm()

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

	q := &Question{}
	decoder := schema.NewDecoder()
	err = decoder.Decode(q, r.Form)
	if err != nil {
		log.Println(err)
		return
	}

	log.Println("Query", q.Qid)

	// Process query here

	a := &AnswerWrapper{
		Answer: &Answer{
			Answered:  "yes",
			Pid:       "demo-id-01",
			Qid:       q.Qid,
			Time:      int64(time.Since(start) / time.Millisecond),
			Content:   "[YOUR ANSWER HERE]",
			Resources: "resource1,resource2",
		},
	}

	log.Println("Query", q.Qid, "got answer `", a.Answer.Content, "` in time", a.Answer.Time)

	fmt.Fprintf(w, "%s%s", xml.Header, a)
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", handler)
	http.Handle("/", r)

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/yosssi/goat/consts"
	"github.com/yosssi/goat/context"
)

// main executes main processes.
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	version := flag.Bool("v", false, "Show Goat version")
	interval := flag.Int("i", consts.DefaultInterval, "An interval(ms) of a watchers' file check loop")
	flag.Parse()

	if *version {
		fmt.Printf("Goat %s\n", consts.Version)
		os.Exit(0)
	}

	ctx, err := context.NewContext(*interval)
	if err != nil {
		log.Fatal(err)
	}

	initTasks := ctx.Config.InitTasks
	if initTasks != nil && len(initTasks) > 0 {
		executeTasks(initTasks, nil)
	}

	jobsC := make(chan context.Job, consts.JobsChannelBuffer)

	launchWatchers(ctx, jobsC)

	handleJobs(jobsC)
}

// launchWatchers launches watchers.
func launchWatchers(ctx *context.Context, jobsC chan<- context.Job) {
	for _, watcher := range ctx.Config.Watchers {
		go watcher.Launch(ctx, jobsC)
	}
}

// handleJobs handle jobs.
func handleJobs(jobsC <-chan context.Job) {
	for job := range jobsC {
		watcher := job.Watcher
		watcher.Printf("%s", job.Message)
		executeTasks(watcher.Tasks, watcher)
	}
}

// executeTasks executes tasks.
func executeTasks(tasks []*context.Task, watcher *context.Watcher) {
	for _, task := range tasks {
		command := task.Command
		tokens := strings.Split(command, " ")
		name := tokens[0]
		var cmdArg []string
		if len(tokens) > 1 {
			cmdArg = tokens[1:]
		}
		cmd := exec.Command(name, cmdArg...)
		if task.Nowait {
			printf(watcher, "execute(nowait): %s", command)
			if err := cmd.Start(); err != nil {
				printf(watcher, "An error occurred: %s", err.Error())
			} else {
				printf(watcher, "end(nowait): %s", command)
			}
		} else {
			printf(watcher, "execute: %s", command)
			bytes, err := cmd.CombinedOutput()
			if err != nil {
				printf(watcher, "An error occurred: %s", cmd.Stderr)
			} else {
				fmt.Print(string(bytes))
				printf(watcher, "end: %s", command)
			}
		}
	}
}

func printf(watcher *context.Watcher, format string, v ...interface{}) {
	if watcher != nil {
		watcher.Printf(format, v)
	} else {
		log.Printf(format, v)
	}
}
<commit_msg>Redirecting the STDOut and STDErr to the CMD's output<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/yosssi/goat/consts"
	"github.com/yosssi/goat/context"
)

// main executes main processes.
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	version := flag.Bool("v", false, "Show Goat version")
	interval := flag.Int("i", consts.DefaultInterval, "An interval(ms) of a watchers' file check loop")
	flag.Parse()

	if *version {
		fmt.Printf("Goat %s\n", consts.Version)
		os.Exit(0)
	}

	ctx, err := context.NewContext(*interval)
	if err != nil {
		log.Fatal(err)
	}

	initTasks := ctx.Config.InitTasks
	if initTasks != nil && len(initTasks) > 0 {
		executeTasks(initTasks, nil)
	}

	jobsC := make(chan context.Job, consts.JobsChannelBuffer)

	launchWatchers(ctx, jobsC)

	handleJobs(jobsC)
}

// launchWatchers launches watchers.
func launchWatchers(ctx *context.Context, jobsC chan<- context.Job) {
	for _, watcher := range ctx.Config.Watchers {
		go watcher.Launch(ctx, jobsC)
	}
}

// handleJobs handle jobs.
func handleJobs(jobsC <-chan context.Job) {
	for job := range jobsC {
		watcher := job.Watcher
		watcher.Printf("%s", job.Message)
		executeTasks(watcher.Tasks, watcher)
	}
}

// executeTasks executes tasks.
func executeTasks(tasks []*context.Task, watcher *context.Watcher) {
	for _, task := range tasks {
		command := task.Command
		tokens := strings.Split(command, " ")
		name := tokens[0]
		var cmdArg []string
		if len(tokens) > 1 {
			cmdArg = tokens[1:]
		}
		cmd := exec.Command(name, cmdArg...)
		if task.Nowait {
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			printf(watcher, "execute(nowait): %s", command)
			if err := cmd.Start(); err != nil {
				printf(watcher, "An error occurred: %s", err.Error())
			} else {
				printf(watcher, "end(nowait): %s", command)
			}
		} else {
			printf(watcher, "execute: %s", command)
			bytes, err := cmd.CombinedOutput()
			if err != nil {
				printf(watcher, "An error occurred: %s", cmd.Stderr)
			} else {
				fmt.Print(string(bytes))
				printf(watcher, "end: %s", command)
			}
		}
	}
}

func printf(watcher *context.Watcher, format string, v ...interface{}) {
	if watcher != nil {
		watcher.Printf(format, v)
	} else {
		log.Printf(format, v)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

func init() {
	flag.Usage = func() {
		fmt.Printf("Usage: %s -out=out.path<optional> in.path<required>\n\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()
}

func main() {
	checkRequirements()

	fmt.Printf("%v", flag.Args())
}

func checkRequirements() {
	args := flag.Args()

	if len(args) == 0 {
		flag.Usage()

		fmt.Printf("Warning: The input file is required\n")

		os.Exit(1)
	} else if len(args) > 1 {
		fmt.Printf("Notice: to many positional arguments, ignoring %v\n", args[1:])
	}
}
<commit_msg>Add -out flag<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
)

var output string

func init() {
	flag.Usage = func() {
		fmt.Printf("Usage: %s -out=out.path<optional> in.path<required>\n\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&output, "out", "out.go", "Specify a path to the output file (optional)")

	flag.Parse()
}

func main() {
	checkRequirements()

	fmt.Printf("%v, %s", flag.Args(), output)
}

func checkRequirements() {
	args := flag.Args()

	if len(args) == 0 {
		flag.Usage()

		fmt.Printf("Error: The input file is required\n")

		os.Exit(1)
	} else if len(args) > 1 {
		fmt.Printf("Notice: to many positional arguments, ignoring %v\n", args[1:])
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"log"
	"github.com/julienschmidt/httprouter"
)

func main() {
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/static/*filename", Static)
	router.POST("/zaam/new/", NewVideo)

	log.Fatal(http.ListenAndServe(":8080", router))
}
<commit_msg>Added couple url patterns<commit_after>package main

import (
	"net/http"
	"log"
	"github.com/julienschmidt/httprouter"
)

func main() {
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/static/*filename", Static)
	router.GET("/new/:id", New)
	router.GET("/args/:id", GetArgs)

	log.Fatal(http.ListenAndServe(":8080", router))
}
<|endoftext|>