<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package transformers

import (
	"strings"

	"github.com/kubernetes-sigs/kustomize/pkg/resmap"
	"github.com/kubernetes-sigs/kustomize/pkg/types"
)

// imageTagTransformer replace image tags
type imageTagTransformer struct {
	imageTags []types.ImageTag
}

var _ Transformer = &imageTagTransformer{}

// NewImageTagTransformer constructs a imageTagTransformer.
func NewImageTagTransformer(slice []types.ImageTag) (Transformer, error) {
	return &imageTagTransformer{slice}, nil
}

// Transform finds the matching images and replace the tag
func (pt *imageTagTransformer) Transform(resources resmap.ResMap) error {
	if len(pt.imageTags) == 0 {
		return nil
	}
	for _, res := range resources {
		err := pt.findAndReplaceTag(res.UnstructuredContent())
		if err != nil {
			return err
		}
	}
	return nil
}

/*
 findAndReplaceTag replaces the image tags inside one object
 It searches the object for container session
 then loops though all images inside containers session, finds matched ones and update the tag name
*/
func (pt *imageTagTransformer) findAndReplaceTag(obj map[string]interface{}) error {
	_, found := obj["containers"]
	if found {
		return pt.updateContainers(obj)
	}
	return pt.findContainers(obj)
}

func (pt *imageTagTransformer) updateContainers(obj map[string]interface{}) error {
	containers := obj["containers"].([]interface{})
	for i := range containers {
		container := containers[i].(map[string]interface{})
		image, found := container["image"]
		if !found {
			continue
		}
		for _, imagetag := range pt.imageTags {
			if isImageMatched(image.(string), imagetag.Name) {
				container["image"] = strings.Join([]string{imagetag.Name, imagetag.NewTag}, ":")
				break
			}
		}
		containers[i] = container
	}
	obj["containers"] = containers
	return nil
}

func (pt *imageTagTransformer) findContainers(obj map[string]interface{}) error {
	for key := range obj {
		switch typedV := obj[key].(type) {
		case map[string]interface{}:
			err := pt.findAndReplaceTag(typedV)
			if err != nil {
				return err
			}
		case []interface{}:
			for i := range typedV {
				item := typedV[i]
				typedItem, ok := item.(map[string]interface{})
				if ok {
					err := pt.findAndReplaceTag(typedItem)
					if err != nil {
						return err
					}
				}
				typedV[i] = typedItem
			}
		}
	}
	return nil
}

func isImageMatched(s, t string) bool {
	imagetag := strings.Split(s, ":")
	if len(imagetag) >= 1 {
		return imagetag[0] == t
	}
	return false
}
<commit_msg>address comments<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package transformers

import (
	"strings"

	"github.com/kubernetes-sigs/kustomize/pkg/resmap"
	"github.com/kubernetes-sigs/kustomize/pkg/types"
)

// imageTagTransformer replace image tags
type imageTagTransformer struct {
	imageTags []types.ImageTag
}

var _ Transformer = &imageTagTransformer{}

// NewImageTagTransformer constructs a imageTagTransformer.
func NewImageTagTransformer(slice []types.ImageTag) (Transformer, error) {
	return &imageTagTransformer{slice}, nil
}

// Transform finds the matching images and replace the tag
func (pt *imageTagTransformer) Transform(resources resmap.ResMap) error {
	if len(pt.imageTags) == 0 {
		return nil
	}
	for _, res := range resources {
		err := pt.findAndReplaceTag(res.UnstructuredContent())
		if err != nil {
			return err
		}
	}
	return nil
}

/*
 findAndReplaceTag replaces the image tags inside one object
 It searches the object for container session
 then loops though all images inside containers session, finds matched ones and update the tag name
*/
func (pt *imageTagTransformer) findAndReplaceTag(obj map[string]interface{}) error {
	_, found := obj["containers"]
	if found {
		return pt.updateContainers(obj)
	}
	return pt.findContainers(obj)
}

func (pt *imageTagTransformer) updateContainers(obj map[string]interface{}) error {
	containers := obj["containers"].([]interface{})
	for i := range containers {
		container := containers[i].(map[string]interface{})
		image, found := container["image"]
		if !found {
			continue
		}
		for _, imagetag := range pt.imageTags {
			if isImageMatched(image.(string), imagetag.Name) {
				container["image"] = strings.Join([]string{imagetag.Name, imagetag.NewTag}, ":")
				break
			}
		}
	}
	return nil
}

func (pt *imageTagTransformer) findContainers(obj map[string]interface{}) error {
	for key := range obj {
		switch typedV := obj[key].(type) {
		case map[string]interface{}:
			err := pt.findAndReplaceTag(typedV)
			if err != nil {
				return err
			}
		case []interface{}:
			for i := range typedV {
				item := typedV[i]
				typedItem, ok := item.(map[string]interface{})
				if ok {
					err := pt.findAndReplaceTag(typedItem)
					if err != nil {
						return err
					}
				}
			}
		}
	}
	return nil
}

func isImageMatched(s, t string) bool {
	imagetag := strings.Split(s, ":")
	return len(imagetag) >= 1 && imagetag[0] == t
}
<|endoftext|>


<commit_before>package truncindex

import (
	"errors"
	"fmt"
	"strings"
	"sync"

	"github.com/tchap/go-patricia/patricia"
)

var (
	ErrNoID = errors.New("prefix can't be empty")
)

// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
type TruncIndex struct {
	sync.RWMutex
	trie *patricia.Trie
	ids  map[string]struct{}
}

func NewTruncIndex(ids []string) (idx *TruncIndex) {
	idx = &TruncIndex{
		ids:  make(map[string]struct{}),
		trie: patricia.NewTrie(),
	}
	for _, id := range ids {
		idx.addId(id)
	}
	return
}

func (idx *TruncIndex) addId(id string) error {
	if strings.Contains(id, " ") {
		return fmt.Errorf("Illegal character: ' '")
	}
	if id == "" {
		return ErrNoID
	}
	if _, exists := idx.ids[id]; exists {
		return fmt.Errorf("Id already exists: '%s'", id)
	}
	idx.ids[id] = struct{}{}
	if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted {
		return fmt.Errorf("Failed to insert id: %s", id)
	}
	return nil
}

func (idx *TruncIndex) Add(id string) error {
	idx.Lock()
	defer idx.Unlock()
	if err := idx.addId(id); err != nil {
		return err
	}
	return nil
}

func (idx *TruncIndex) Delete(id string) error {
	idx.Lock()
	defer idx.Unlock()
	if _, exists := idx.ids[id]; !exists || id == "" {
		return fmt.Errorf("No such id: '%s'", id)
	}
	delete(idx.ids, id)
	if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted {
		return fmt.Errorf("No such id: '%s'", id)
	}
	return nil
}

func (idx *TruncIndex) Get(s string) (string, error) {
	idx.RLock()
	defer idx.RUnlock()
	var (
		id string
	)
	if s == "" {
		return "", ErrNoID
	}
	subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
		if id != "" {
			// we haven't found the ID if there are two or more IDs
			id = ""
			return fmt.Errorf("we've found two entries")
		}
		id = string(prefix)
		return nil
	}

	if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
		return "", fmt.Errorf("No such id: %s", s)
	}
	if id != "" {
		return id, nil
	}
	return "", fmt.Errorf("No such id: %s", s)
}
<commit_msg>Increase patricia.MaxPrefixPerNode<commit_after>package truncindex

import (
	"errors"
	"fmt"
	"strings"
	"sync"

	"github.com/tchap/go-patricia/patricia"
)

var (
	ErrNoID = errors.New("prefix can't be empty")
)

func init() {
	// Change patricia max prefix per node length,
	// because our len(ID) always 64
	patricia.MaxPrefixPerNode = 64
}

// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
type TruncIndex struct {
	sync.RWMutex
	trie *patricia.Trie
	ids  map[string]struct{}
}

func NewTruncIndex(ids []string) (idx *TruncIndex) {
	idx = &TruncIndex{
		ids:  make(map[string]struct{}),
		trie: patricia.NewTrie(),
	}
	for _, id := range ids {
		idx.addId(id)
	}
	return
}

func (idx *TruncIndex) addId(id string) error {
	if strings.Contains(id, " ") {
		return fmt.Errorf("Illegal character: ' '")
	}
	if id == "" {
		return ErrNoID
	}
	if _, exists := idx.ids[id]; exists {
		return fmt.Errorf("Id already exists: '%s'", id)
	}
	idx.ids[id] = struct{}{}
	if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted {
		return fmt.Errorf("Failed to insert id: %s", id)
	}
	return nil
}

func (idx *TruncIndex) Add(id string) error {
	idx.Lock()
	defer idx.Unlock()
	if err := idx.addId(id); err != nil {
		return err
	}
	return nil
}

func (idx *TruncIndex) Delete(id string) error {
	idx.Lock()
	defer idx.Unlock()
	if _, exists := idx.ids[id]; !exists || id == "" {
		return fmt.Errorf("No such id: '%s'", id)
	}
	delete(idx.ids, id)
	if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted {
		return fmt.Errorf("No such id: '%s'", id)
	}
	return nil
}

func (idx *TruncIndex) Get(s string) (string, error) {
	idx.RLock()
	defer idx.RUnlock()
	var (
		id string
	)
	if s == "" {
		return "", ErrNoID
	}
	subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
		if id != "" {
			// we haven't found the ID if there are two or more IDs
			id = ""
			return fmt.Errorf("we've found two entries")
		}
		id = string(prefix)
		return nil
	}

	if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
		return "", fmt.Errorf("No such id: %s", s)
	}
	if id != "" {
		return id, nil
	}
	return "", fmt.Errorf("No such id: %s", s)
}
<|endoftext|>


<commit_before>package migrations

import "github.com/BurntSushi/migration"

func AddRunningWorkerMustHaveAddrConstraint(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
		ALTER TABLE workers
		ALTER COLUMN baggageclaim_url DROP NOT NULL
	`)
	if err != nil {
		return err
	}

	_, err = tx.Exec(`
		ALTER TABLE workers
    ADD CONSTRAINT addr_when_running CHECK (
			(
				state != 'stalled' AND addr IS NOT NULL AND baggageclaim_url IS NOT NULL
			) OR (
				state = 'stalled' AND addr IS NULL AND baggageclaim_url IS NULL
			)
		)
	`)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>fix baggageclaim_url default to account for null<commit_after>package migrations

import "github.com/BurntSushi/migration"

func AddRunningWorkerMustHaveAddrConstraint(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
		ALTER TABLE workers
		ALTER COLUMN baggageclaim_url DROP NOT NULL,
		ALTER COLUMN baggageclaim_url SET DEFAULT NULL
	`)
	if err != nil {
		return err
	}

	_, err = tx.Exec(`
		ALTER TABLE workers
    ADD CONSTRAINT addr_when_running CHECK (
			(
				state != 'stalled' AND addr IS NOT NULL AND baggageclaim_url IS NOT NULL
			) OR (
				state = 'stalled' AND addr IS NULL AND baggageclaim_url IS NULL
			)
		)
	`)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

/*
Minimal IRC bot in Go

TODO:
* google app engine integration to evaluate python code
* add more plugins (!title, !hn, !reddit, ...)
* store connection info in json file
* separate out plugins from main program
*/

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/textproto"
	"net/url"
	"strings"
	"time"
)

const (
	VERSION = "0.1.1"
	USER = "USER"
	NICK = "NICK"
	JOIN = "JOIN"
	PING = "PING"
	PONG = "PONG"
	PRIVMSG = "PRIVMSG"
	SUFFIX = "\r\n"
	BEERTIME_WD = "Friday"
	BEERTIME_HR = 16
	BEERTIME_MIN = 30
	JIRA = "https://webdrive.atlassian.net"
)

/* structs */
type Privmsg struct {
	Source string
	Target string
	Message []string
}

type DuckDuckGo struct {
	AbstractText string
	AbstractURL string
}

type GIF struct {
	ID string
}

type Giphy struct {
	Data []GIF
}

/* simple message builders */
func msgUser(nick string) string {
	return USER + " " + nick + " 8 * :" + nick + SUFFIX
}

func msgNick(nick string) string {
	return NICK + " " + nick + SUFFIX
}

func msgJoin(channel string) string {
	return JOIN + " " + channel + SUFFIX
}

func msgPong(host string) string {
	return PONG + " :" + host + SUFFIX
}

func msgPrivmsg(receiver string, msg string) string {
	return PRIVMSG + " " + receiver + " :" + msg + SUFFIX
}

/* plugin helpers */
func searchGiphy(term string) *Giphy{
	var giphy *Giphy = &Giphy{}

	if term == "" {
		term = "cat"
	}
	encoded := url.QueryEscape(term)
	resource := fmt.Sprintf("http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=%s", encoded)

	resp, err := http.Get(resource)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if err = json.Unmarshal(body, giphy); err != nil {
		log.Fatal(err)
	}
	return giphy
}

func queryDuckDuckGo(term string) *DuckDuckGo {
	var ddg *DuckDuckGo = &DuckDuckGo{}

	encoded := url.QueryEscape(term)
	resource := fmt.Sprintf("http://api.duckduckgo.com?format=json&q=%s", encoded)

	resp, err := http.Get(resource)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if err = json.Unmarshal(body, ddg); err != nil {
		log.Fatal(err)
	}

	return ddg
}

func timeDelta(weekday string, hour int, minute int) string {
	now := time.Now()
	wd := now.Weekday().String()
	if wd == weekday {
		y, m, d := now.Date()
		location := now.Location()

		beertime := time.Date(y, m, d, hour, minute, 0, 0, location)
		diff := beertime.Sub(now)

		if diff.Seconds() > 0 {
			return fmt.Sprintf("less than %d minute(s) to go...", int(math.Ceil(diff.Minutes())))
		}
		return "it's beertime!"
	}
	return fmt.Sprintf("it's only %s...", strings.ToLower(wd))
}

/* plugins */
func replyVer(pm Privmsg) string {
	return msgPrivmsg(pm.Target, fmt.Sprintf("gerri version: %s", VERSION))
}

func replyPing(pm Privmsg) string {
	return msgPrivmsg(pm.Target, "meow")
}

func replyGIF(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	giphy := searchGiphy(msg)
	if len(giphy.Data) > 0 {
		m := fmt.Sprintf("http://media.giphy.com/media/%s/giphy.gif", giphy.Data[rand.Intn(len(giphy.Data))].ID)
		return msgPrivmsg(pm.Target, m)
	}
	return msgPrivmsg(pm.Target, "(zzzzz...)")
}

func replyDay(pm Privmsg) string {
	return msgPrivmsg(pm.Target, strings.ToLower(time.Now().Weekday().String()))
}

func replyWik(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		ddg := queryDuckDuckGo(msg)
		if ddg.AbstractText != "" && ddg.AbstractURL != "" {
			size := 30
			words := strings.Split(ddg.AbstractText, " ")
			var m string
			if len(words) > size {
				m = fmt.Sprintf("%s... (source: %s)", strings.Join(words[:size], " "), ddg.AbstractURL)
			} else {
				m = fmt.Sprintf("%s (source: %s)", ddg.AbstractText, ddg.AbstractURL)
			}
			return msgPrivmsg(pm.Target, m)
		}
		return msgPrivmsg(pm.Target, "(zzzzz...)")
	}
	return ""
}

func replyBeertime(pm Privmsg) string {
	return msgPrivmsg(pm.Target, timeDelta(BEERTIME_WD, BEERTIME_HR, BEERTIME_MIN))
}

func replyJira(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		return msgPrivmsg(pm.Target, JIRA + "/browse/" + strings.ToUpper(msg))
	}
	return msgPrivmsg(pm.Target, JIRA)
}

func replyAsk(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		rand.Seed(time.Now().UnixNano())
		return msgPrivmsg(pm.Target, [2]string{"yes!", "no..."}[rand.Intn(2)])
	}
	return ""
}

var repliers = map[string]func(Privmsg) string {
	":!ver": replyVer,
	":!version": replyVer,
	":!ping": replyPing,
	":!day": replyDay,
	":!gif": replyGIF,
	":!wik": replyWik,
	":!beertime": replyBeertime,
	":!jira": replyJira,
	":!ask": replyAsk,
}

func buildReply(pm Privmsg) string {
	/* replies PRIVMSG message */
	fn, found := repliers[pm.Message[0]]
	if found {
		return fn(pm)
	}
	return ""
}

func connect(server string, port string) (net.Conn, error) {
	/* establishes irc connection  */
	log.Printf("connecting to %s:%s...", server, port)
	conn, err := net.Dial("tcp", server + ":" + port)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("connected")
	return conn, err
}

func send(ch chan<- string, conn net.Conn) {
	/* defines goroutine sending messages to channel */
	reader := textproto.NewReader(bufio.NewReader(conn))
	for {
		line, err := reader.ReadLine()
		if err != nil {
			log.Fatal(err)
			break
		}
		ch <- line
	}
}

func receive(ch <-chan string, conn net.Conn) {
	/* defines goroutine receiving messages from channel */
	for {
		line, ok := <-ch
		if !ok {
			log.Fatal("aborted: failed to receive from channel")
			break
		}
		log.Printf(line)

		if strings.HasPrefix(line, PING) {
			// reply PING with PONG
			msg := msgPong(strings.Split(line, ":")[1])
			conn.Write([]byte(msg))
			log.Printf(msg)
		} else {
			// reply PRIVMSG
			tokens := strings.Split(line, " ")
			if len(tokens) >= 4 && tokens[1] == PRIVMSG {
				pm := Privmsg{Source: tokens[0], Target: tokens[2], Message: tokens[3:]}
				reply := buildReply(pm)
				if reply != "" {
					log.Printf("reply: %s", reply)
					conn.Write([]byte(reply))
				}
			}
		}
	}
}

func main() {
	server, port := "chat.freenode.net", "8002"
	nick, channel := "gerri", "#microamp"

	// connect to irc
	conn, err := connect(server, port)
	if err != nil {
		log.Fatal(err)
	}

	// send messages: USER/NICK/JOIN
	conn.Write([]byte(msgUser(nick)))
	conn.Write([]byte(msgNick(nick)))
	conn.Write([]byte(msgJoin(channel)))

	defer conn.Close()

	// define goroutines communicating via channel
	ch := make(chan string)
	go send(ch, conn)
	go receive(ch, conn)

	var input string
	fmt.Scanln(&input)
}
<commit_msg>Added slap plugin.<commit_after>package main

/*
Minimal IRC bot in Go

TODO:
* google app engine integration to evaluate python code
* add more plugins (!title, !hn, !reddit, ...)
* store connection info in json file
* separate out plugins from main program
*/

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/textproto"
	"net/url"
	"strings"
	"time"
)

const (
	VERSION = "0.1.1"
	USER = "USER"
	NICK = "NICK"
	JOIN = "JOIN"
	PING = "PING"
	PONG = "PONG"
	PRIVMSG = "PRIVMSG"
	ACTION = "\x01ACTION"
	SUFFIX = "\r\n"
	BEERTIME_WD = "Friday"
	BEERTIME_HR = 16
	BEERTIME_MIN = 30
	JIRA = "https://webdrive.atlassian.net"
)

/* structs */
type Privmsg struct {
	Source string
	Target string
	Message []string
}

type DuckDuckGo struct {
	AbstractText string
	AbstractURL string
}

type GIF struct {
	ID string
}

type Giphy struct {
	Data []GIF
}

/* simple message builders */
func msgUser(nick string) string {
	return USER + " " + nick + " 8 * :" + nick + SUFFIX
}

func msgNick(nick string) string {
	return NICK + " " + nick + SUFFIX
}

func msgJoin(channel string) string {
	return JOIN + " " + channel + SUFFIX
}

func msgPong(host string) string {
	return PONG + " :" + host + SUFFIX
}

func msgPrivmsg(receiver string, msg string) string {
	return PRIVMSG + " " + receiver + " :" + msg + SUFFIX
}

/* plugin helpers */
func searchGiphy(term string) *Giphy{
	var giphy *Giphy = &Giphy{}

	if term == "" {
		term = "cat"
	}
	encoded := url.QueryEscape(term)
	resource := fmt.Sprintf("http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=%s", encoded)

	resp, err := http.Get(resource)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if err = json.Unmarshal(body, giphy); err != nil {
		log.Fatal(err)
	}
	return giphy
}

func queryDuckDuckGo(term string) *DuckDuckGo {
	var ddg *DuckDuckGo = &DuckDuckGo{}

	encoded := url.QueryEscape(term)
	resource := fmt.Sprintf("http://api.duckduckgo.com?format=json&q=%s", encoded)

	resp, err := http.Get(resource)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if err = json.Unmarshal(body, ddg); err != nil {
		log.Fatal(err)
	}

	return ddg
}

func timeDelta(weekday string, hour int, minute int) string {
	now := time.Now()
	wd := now.Weekday().String()
	if wd == weekday {
		y, m, d := now.Date()
		location := now.Location()

		beertime := time.Date(y, m, d, hour, minute, 0, 0, location)
		diff := beertime.Sub(now)

		if diff.Seconds() > 0 {
			return fmt.Sprintf("less than %d minute(s) to go...", int(math.Ceil(diff.Minutes())))
		}
		return "it's beertime!"
	}
	return fmt.Sprintf("it's only %s...", strings.ToLower(wd))
}

func slapAction(target string) string {
	actions := []string {
		"slaps", "kicks", "destroys", "annihilates", "punches",
		"roundhouse kicks", "rusty hooks", "pwns", "owns"}
	if strings.TrimSpace(target) != "" {
		selected_action := actions[rand.Intn(len(actions))]
		return fmt.Sprintf(ACTION + " " + selected_action + " " + target)
	} else {
		return fmt.Sprintf(ACTION + " zzzzz...")
	}
}

/* plugins */
func replyVer(pm Privmsg) string {
	return msgPrivmsg(pm.Target, fmt.Sprintf("gerri version: %s", VERSION))
}

func replyPing(pm Privmsg) string {
	return msgPrivmsg(pm.Target, "meow")
}

func replyGIF(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	giphy := searchGiphy(msg)
	if len(giphy.Data) > 0 {
		m := fmt.Sprintf("http://media.giphy.com/media/%s/giphy.gif", giphy.Data[rand.Intn(len(giphy.Data))].ID)
		return msgPrivmsg(pm.Target, m)
	}
	return msgPrivmsg(pm.Target, "(zzzzz...)")
}

func replyDay(pm Privmsg) string {
	return msgPrivmsg(pm.Target, strings.ToLower(time.Now().Weekday().String()))
}

func replyWik(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		ddg := queryDuckDuckGo(msg)
		if ddg.AbstractText != "" && ddg.AbstractURL != "" {
			size := 30
			words := strings.Split(ddg.AbstractText, " ")
			var m string
			if len(words) > size {
				m = fmt.Sprintf("%s... (source: %s)", strings.Join(words[:size], " "), ddg.AbstractURL)
			} else {
				m = fmt.Sprintf("%s (source: %s)", ddg.AbstractText, ddg.AbstractURL)
			}
			return msgPrivmsg(pm.Target, m)
		}
		return msgPrivmsg(pm.Target, "(zzzzz...)")
	}
	return ""
}

func replyBeertime(pm Privmsg) string {
	return msgPrivmsg(pm.Target, timeDelta(BEERTIME_WD, BEERTIME_HR, BEERTIME_MIN))
}

func replyJira(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		return msgPrivmsg(pm.Target, JIRA + "/browse/" + strings.ToUpper(msg))
	}
	return msgPrivmsg(pm.Target, JIRA)
}

func replyAsk(pm Privmsg) string {
	msg := strings.Join(pm.Message[1:], " ")
	if strings.TrimSpace(msg) != "" {
		rand.Seed(time.Now().UnixNano())
		return msgPrivmsg(pm.Target, [2]string{"yes!", "no..."}[rand.Intn(2)])
	}
	return ""
}

func replySlap(pm Privmsg) string {
	slap := slapAction(strings.Join(pm.Message[1:], " "))
	return msgPrivmsg(pm.Target, slap)
}

var repliers = map[string]func(Privmsg) string {
	":!ver": replyVer,
	":!version": replyVer,
	":!ping": replyPing,
	":!day": replyDay,
	":!gif": replyGIF,
	":!wik": replyWik,
	":!beertime": replyBeertime,
	":!jira": replyJira,
	":!ask": replyAsk,
	":!slap": replySlap,
}

func buildReply(pm Privmsg) string {
	/* replies PRIVMSG message */
	fn, found := repliers[pm.Message[0]]
	if found {
		return fn(pm)
	}
	return ""
}

func connect(server string, port string) (net.Conn, error) {
	/* establishes irc connection  */
	log.Printf("connecting to %s:%s...", server, port)
	conn, err := net.Dial("tcp", server + ":" + port)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("connected")
	return conn, err
}

func send(ch chan<- string, conn net.Conn) {
	/* defines goroutine sending messages to channel */
	reader := textproto.NewReader(bufio.NewReader(conn))
	for {
		line, err := reader.ReadLine()
		if err != nil {
			log.Fatal(err)
			break
		}
		ch <- line
	}
}

func receive(ch <-chan string, conn net.Conn) {
	/* defines goroutine receiving messages from channel */
	for {
		line, ok := <-ch
		if !ok {
			log.Fatal("aborted: failed to receive from channel")
			break
		}
		log.Printf(line)

		if strings.HasPrefix(line, PING) {
			// reply PING with PONG
			msg := msgPong(strings.Split(line, ":")[1])
			conn.Write([]byte(msg))
			log.Printf(msg)
		} else {
			// reply PRIVMSG
			tokens := strings.Split(line, " ")
			if len(tokens) >= 4 && tokens[1] == PRIVMSG {
				pm := Privmsg{Source: tokens[0], Target: tokens[2], Message: tokens[3:]}
				reply := buildReply(pm)
				if reply != "" {
					log.Printf("reply: %s", reply)
					conn.Write([]byte(reply))
				}
			}
		}
	}
}

func main() {
	server, port := "chat.freenode.net", "8002"
	nick, channel := "gerri", "#microamp"

	// connect to irc
	conn, err := connect(server, port)
	if err != nil {
		log.Fatal(err)
	}

	// send messages: USER/NICK/JOIN
	conn.Write([]byte(msgUser(nick)))
	conn.Write([]byte(msgNick(nick)))
	conn.Write([]byte(msgJoin(channel)))

	defer conn.Close()

	// define goroutines communicating via channel
	ch := make(chan string)
	go send(ch, conn)
	go receive(ch, conn)

	var input string
	fmt.Scanln(&input)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"runtime"
	"fmt"
	"log"
	"strings"
	"sync"
	"github.com/codegangsta/cli"
)

type Option struct {
	From string
	To string
	PhotoDir string
	VideoDir string
	Recursive bool
	DryRun bool
	Excludes []string
	Concurrency int
	Verbose bool
}

func main() {
	app := cli.NewApp()
	app.Name = "ghoto"
	app.Usage = "Transfer photo(video)"
	app.Flags = []cli.Flag {
		cli.StringFlag {
			Name: "from",
			Value: "/path/to/src",
			Usage: "Source directory",
		},
		cli.StringFlag {
			Name: "to",
			Value: "/path/to/dst",
			Usage: "Destination directory",
		},
		cli.StringFlag {
			Name: "photo-dir, P",
			Value: "photo",
			Usage: "Destination photo directory",
		},
		cli.StringFlag {
			Name: "video-dir, V",
			Value: "video",
			Usage: "Destination video directory",
		},
		cli.StringFlag {
			Name: "exclude, x",
			Value: "",
			Usage: "Exclude dir/file separate comma.",
		},
		cli.IntFlag {
			Name: "concurrency, c",
			Value: runtime.NumCPU(),
			Usage: "Concurrency num.",
		},
		cli.BoolFlag {
			Name: "recursive, r",
			Usage: "Resursive",
		},
		cli.BoolFlag {
			Name: "dry-run",
			Usage: "Dry Run",
		},
		cli.BoolFlag {
			Name: "verbose, vvv",
			Usage: "Verbose",
		},
	}
	app.Action = func(c *cli.Context) {
		// options
		option := &Option{
			c.String("from"),
			c.String("to"),
			c.String("photo-dir"),
			c.String("video-dir"),
			c.Bool("recursive"),
			c.Bool("dry-run"),
			strings.Split(c.String("exclude"), ","),
			c.Int("concurrency"),
			c.Bool("verbose"),
		}

		// check path
		isDir, err := IsDirectory(option.From)
		if err != nil {
			log.Fatal(err)
		}

		if isDir != false {
			fmt.Errorf("%s is not found.", option.From)
		}

		isDir, err = IsDirectory(option.To)
		if err != nil {
			log.Fatal(err)
		}

		if isDir != false {
			fmt.Errorf("%s is not found.", option.To)
		}

		// move
		var wg sync.WaitGroup
		ch := make(chan int, option.Concurrency)
		Transfer(&wg, ch, option.From, option)
		wg.Wait()
	}
	app.Run(os.Args)
}
<commit_msg>add version and author<commit_after>package main

import (
	"os"
	"runtime"
	"fmt"
	"log"
	"strings"
	"sync"
	"github.com/codegangsta/cli"
)

type Option struct {
	From string
	To string
	PhotoDir string
	VideoDir string
	Recursive bool
	DryRun bool
	Excludes []string
	Concurrency int
	Verbose bool
}

func main() {
	app := cli.NewApp()
	app.Version = "0.0.1"
	app.Authors = []cli.Author{ cli.Author{"fukata", "tatsuya.fukata@gmail.com"} }
	app.Name = "ghoto"
	app.Usage = "Transfer photo(video)"
	app.Flags = []cli.Flag {
		cli.StringFlag {
			Name: "from",
			Value: "/path/to/src",
			Usage: "Source directory",
		},
		cli.StringFlag {
			Name: "to",
			Value: "/path/to/dst",
			Usage: "Destination directory",
		},
		cli.StringFlag {
			Name: "photo-dir, P",
			Value: "photo",
			Usage: "Destination photo directory",
		},
		cli.StringFlag {
			Name: "video-dir, V",
			Value: "video",
			Usage: "Destination video directory",
		},
		cli.StringFlag {
			Name: "exclude, x",
			Value: "",
			Usage: "Exclude dir/file separate comma.",
		},
		cli.IntFlag {
			Name: "concurrency, c",
			Value: runtime.NumCPU(),
			Usage: "Concurrency num.",
		},
		cli.BoolFlag {
			Name: "recursive, r",
			Usage: "Resursive",
		},
		cli.BoolFlag {
			Name: "dry-run",
			Usage: "Dry Run",
		},
		cli.BoolFlag {
			Name: "verbose, vvv",
			Usage: "Verbose",
		},
	}
	app.Action = func(c *cli.Context) {
		// options
		option := &Option{
			c.String("from"),
			c.String("to"),
			c.String("photo-dir"),
			c.String("video-dir"),
			c.Bool("recursive"),
			c.Bool("dry-run"),
			strings.Split(c.String("exclude"), ","),
			c.Int("concurrency"),
			c.Bool("verbose"),
		}

		// check path
		isDir, err := IsDirectory(option.From)
		if err != nil {
			log.Fatal(err)
		}

		if isDir != false {
			fmt.Errorf("%s is not found.", option.From)
		}

		isDir, err = IsDirectory(option.To)
		if err != nil {
			log.Fatal(err)
		}

		if isDir != false {
			fmt.Errorf("%s is not found.", option.To)
		}

		// move
		var wg sync.WaitGroup
		ch := make(chan int, option.Concurrency)
		Transfer(&wg, ch, option.From, option)
		wg.Wait()
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>//一个简单的生成错误的帮助类
package goerr

import (
	"bytes"
	"fmt"
	"strconv"
)

// new errorContext with error and string
//
// return error
func Errorf(err error, format string, p ...interface{}) error {
	return &errorContext{text: fmt.Sprintf(format, p...), err: err, code: -1}
}

// new errorContext with error
//
//  return  error
func Error(err error) *errorContext {
	return &errorContext{err: err, code: -1}
}

// new errorContext with string
func String(format string, p ...interface{}) *errorContext {
	return &errorContext{text: fmt.Sprintf(format, p...), code: -1}
}

//new error
type errorContext struct {
	code int
	text string
	file string
	line int
	err  error
}

func (e *errorContext) Format(format string, p ...interface{}) *errorContext {
	e.text = fmt.Sprintf(format, p...)
	return e
}
func (e *errorContext) Code(code int) *errorContext {
	e.code = code
	return e
}
func (e *errorContext) Line(line int) *errorContext {
	e.line = line
	return e
}
func (e *errorContext) File(file string) *errorContext {
	e.file = file
	return e
}

//实现error接口
func (e *errorContext) Error() string {
	var buffer bytes.Buffer
	if e.file != "" {
		buffer.WriteString("File: ")
		buffer.WriteString(e.file)
		buffer.WriteString("\t")
	}
	if e.line != -1 {
		bs := strconv.AppendInt(nil, int64(e.line), 10)
		buffer.WriteString("Line: ")
		buffer.Write(bs)
		buffer.WriteString("\t")
	}
	if e.code != 0 {
		bs := strconv.AppendInt(nil, int64(e.code), 10)
		buffer.WriteString("Error Code: ")
		buffer.Write(bs)
		buffer.WriteString("\t")
	}
	if e.text != "" {
		buffer.WriteString("Text: ")
		buffer.WriteString(e.text)
		buffer.WriteString("\t")
	}
	buffer.WriteString("\nTrace: ")
	buffer.WriteString(e.err.Error())
	return buffer.String()
}
<commit_msg>new error<commit_after>//一个简单的生成错误的帮助类
package goerr

import (
	"bytes"
	"fmt"
	"strconv"
)

// new errorContext with error and string
//
// return error
func Errorf(err error, format string, p ...interface{}) error {
	return &errorContext{text: fmt.Sprintf(format, p...), err: err, code: -1}
}

// new errorContext with error
//
//  return  error
func Error(err error) *errorContext {
	return &errorContext{err: err, code: -1}
}

// new errorContext with string
func String(format string, p ...interface{}) *errorContext {
	return &errorContext{text: fmt.Sprintf(format, p...), code: -1}
}

//new error
type errorContext struct {
	code int
	text string
	file string
	line int
	err  error
}

func (e *errorContext) Format(format string, p ...interface{}) *errorContext {
	e.text = fmt.Sprintf(format, p...)
	return e
}
func (e *errorContext) Code(code int) *errorContext {
	e.code = code
	return e
}
func (e *errorContext) Line(line int) *errorContext {
	e.line = line
	return e
}
func (e *errorContext) File(file string) *errorContext {
	e.file = file
	return e
}

//实现error接口
func (e *errorContext) Error() string {
	var buffer bytes.Buffer
	if e.file != "" {
		buffer.WriteString("File: ")
		buffer.WriteString(e.file)
		buffer.WriteString("\t")
	}
	if e.line != -1 {
		bs := strconv.AppendInt(nil, int64(e.line), 10)
		buffer.WriteString("Line: ")
		buffer.Write(bs)
		buffer.WriteString("\t")
	}
	if e.code != 0 {
		bs := strconv.AppendInt(nil, int64(e.code), 10)
		buffer.WriteString("Error Code: ")
		buffer.Write(bs)
		buffer.WriteString("\t")
	}
	if e.text != "" {
		buffer.WriteString("Text: ")
		buffer.WriteString(e.text)
		buffer.WriteString("\t")
	}
	if e.err != nil {
		buffer.WriteString("\nTrace: ")
		buffer.WriteString(e.err.Error())
	}
	return buffer.String()
}
<|endoftext|>


<commit_before>package golet

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"

	"github.com/Code-Hex/golet/internal/port"
	colorable "github.com/mattn/go-colorable"
	"github.com/robfig/cron"
)

type color int

const (
	red color = iota // + 31
	green
	yellow
	blue
	magenta
	cyan

	colornum int = 5
)

// config is main struct.
// struct comments from http://search.cpan.org/dist/Proclet/lib/Proclet.pm
// Proclet is a great module!!
type config struct {
	interval   time.Duration // interval in seconds between spawning services unless a service exits abnormally.
	color      bool          // colored log.
	logger     io.Writer     // sets the output destination file. use stderr by default.
	logWorker  bool          // enable worker for format logs. If disabled this option, cannot use logger opt too.
	execNotice bool          // enable start and exec notice message like: `16:38:12 worker.1 | Start callback: worker``.

	services   []Service
	wg         sync.WaitGroup
	once       sync.Once
	cancel     func()
	ctx        *signalCtx
	serviceNum int
	tags       map[string]bool
	cron       *cron.Cron
}

var shell []string

func init() {
	if runtime.GOOS == "windows" {
		path, err := exec.LookPath("cmd")
		if err != nil {
			panic("Could not find `cmd` command")
		}
		shell = []string{path, "/c"}
	} else {
		path, err := exec.LookPath("bash")
		if err != nil {
			panic("Could not find `bash` command")
		}
		shell = []string{path, "-c"}
	}
}

// Runner interface have methods for configuration and to run services.
type Runner interface {
	SetInterval(time.Duration)
	EnableColor()
	SetLogger(io.Writer)
	DisableLogger()
	DisableExecNotice()
	Env(map[string]string) error
	Add(...Service) error
	Run() error
}

// for settings
// SetInterval can specify the interval at which the command is executed.
func (c *config) SetInterval(t time.Duration) { c.interval = t }

// EnableColor can output colored log.
func (c *config) EnableColor() { c.color = true }

// SetLogger can specify the io.Writer
// for example in https://github.com/lestrrat/go-file-rotatelogs
/*
      logf, _ := rotatelogs.New(
  	    "/path/to/access_log.%Y%m%d%H%M",
  	    rotatelogs.WithLinkName("/path/to/access_log"),
  	    rotatelogs.WithMaxAge(24 * time.Hour),
  	    rotatelogs.WithRotationTime(time.Hour),
      )

	  golet.New(context.Background()).SetLogger(logf)
*/
func (c *config) SetLogger(f io.Writer) { c.logger = f }

// DisableLogger is prevent to output log
func (c *config) DisableLogger() { c.logWorker = false }

// DisableExecNotice is disable execute notifications
func (c *config) DisableExecNotice() { c.execNotice = false }

// New to create struct of golet.
func New(c context.Context) Runner {
	ctx, cancel := context.WithCancel(c)
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
	return &config{
		interval:   0,
		color:      false,
		logger:     colorable.NewColorableStderr(),
		logWorker:  true,
		execNotice: true,

		ctx: &signalCtx{
			parent:  ctx,
			sigchan: signals,
		},
		cancel: cancel,
		tags:   map[string]bool{},
		cron:   cron.New(),
	}
}

// Env can add temporary environment variables.
func (c *config) Env(envs map[string]string) error {
	for k := range envs {
		if e := os.Setenv(k, envs[k]); e != nil {
			return e
		}
	}
	return nil
}

// Add can add runnable services
func (c *config) Add(services ...Service) error {
	for _, service := range services {
		c.serviceNum++
		if service.Tag == "" {
			service.Tag = fmt.Sprintf("%d", c.serviceNum)
		}
		if service.Worker <= 0 {
			service.Worker = 1
		}
		if _, ok := c.tags[service.Tag]; ok {
			return errors.New("tag: " + service.Tag + " is already exists")
		}
		c.tags[service.Tag] = true

		n, err := port.GetPort()
		if err != nil {
			return err
		}

		service.tmpPort = n
		service.color = color(c.serviceNum%colornum + 32)

		c.services = append(c.services, service)
	}
	return nil
}

// Run just like the name.
func (c *config) Run() error {
	services := make(map[string]Service)

	order := make([]string, 0, c.calcCapacitySize())

	// Assign services.
	if err := c.assign(&order, services); err != nil {
		return err
	}
	chps := make(chan *os.Process, 1)
	go c.waitSignals(chps, len(order))

	// Invoke workers.
	for _, sid := range order {
		service := services[sid]
		if service.isExecute() {
			// Execute the command with cron or goroutine
			if service.isCron() {
				c.addCmd(service, chps)
			} else {
				c.wg.Add(1)
				go func() {
					defer func() {
						service.ctx.Close()
						c.wg.Done()
					}()
				PROCESS:
					for {
						// Notify you have executed the command
						if c.execNotice {
							service.Printf("Exec command: %s\n", service.Exec)
						}
						select {
						case <-c.ctx.Done():
							return
						default:
							// If golet is recieved signal or exit code is 0, golet do not restart process.
							if err := run(service.prepare(), chps); err != nil {
								if exiterr, ok := err.(*exec.ExitError); ok {
									// See https://stackoverflow.com/a/10385867
									if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
										if !status.Signaled() {
											continue PROCESS
										}
										return
									}
								}
							}
							return
						}
					}
				}()
			}
		}

		if service.isCode() {
			// Run callback with cron or goroutine
			if service.isCron() {
				c.addTask(service)
			} else {
				c.wg.Add(1)
				go func() {
					defer func() {
						service.ctx.Close()
						c.wg.Done()
					}()
					// If this callback is dead, we should restart it. (like a supervisor)
					// So, this loop for that.
				CALLBACK:
					for {
						// Notify you have run the callback
						if c.execNotice {
							service.Printf("Callback: %s\n", service.Tag)
						}
						select {
						case <-c.ctx.Done():
							return
						default:
							if err := service.Code(service.ctx); err != nil {
								service.Printf("Callback Error: %s\n", err.Error())
								continue CALLBACK
							}
							return
						}
					}
				}()
			}
		}
		// Enable log worker if logWorker is true.
		if c.logWorker && (service.Code != nil || service.Exec != "") {
			rd := service.reader
			go c.logging(bufio.NewScanner(rd), sid, service.color)
		}
		// When the task is cron, it does not cause wait time.
		if service.Every == "" {
			time.Sleep(c.interval)
		}
	}

	c.wait(chps)

	return nil
}

// Calculate of the number of workers.
func (c *config) calcCapacitySize() (cap int) {
	for _, service := range c.services {
		cap += service.Worker
	}
	return
}

// Assign the service ID.
// It also make `order` slice to keep key order of `map[string]Service`.
func (c *config) assign(order *[]string, services map[string]Service) error {
	for _, service := range c.services {
		worker := service.Worker
		for i := 1; i <= worker; i++ {
			s := service
			if err := s.createContext(c.ctx, i); err != nil {
				return err
			}
			sid := fmt.Sprintf("%s.%d", s.Tag, i)
			services[sid] = s
			*order = append(*order, sid)
		}
	}
	return nil
}

// Receive process ID to be executed. or
// It traps the signal relate to parent process. sends a signal to the received process ID.
func (c *config) waitSignals(chps <-chan *os.Process, cap int) {
	procs := make([]*os.Process, 0, cap)
Loop:
	for {
		select {
		case proc := <-chps:
			// Replace used process(nil) with the newly generated process.
			// This run to reduce the memory allocation frequency.
			for i, p := range procs {
				if p == nil {
					procs[i] = proc
					continue Loop
				}
			}
			// If using all processes, allocate newly.
			procs = append(procs, proc)
		case c.ctx.signal = <-c.ctx.sigchan:
			switch c.ctx.signal {
			case syscall.SIGTERM, syscall.SIGHUP:
				c.ctx.signal = syscall.SIGTERM
				sendSignal2Procs(syscall.SIGTERM, procs)
				c.ctx.notifySignal()
			case syscall.SIGINT:
				sendSignal2Procs(syscall.SIGINT, procs)
				c.ctx.notifySignal()
			}
		case <-c.ctx.Done():
			c.cron.Stop()
			return
		}
	}
}

// sendSignal2Procs can send signal and replace os.Process struct of the terminated process with nil
func sendSignal2Procs(sig syscall.Signal, procs []*os.Process) {
	for i, p := range procs {
		if p != nil {
			p.Signal(sig)
			// In case of error, the process has already finished.
			if _, err := p.Wait(); err != nil {
				procs[i] = nil
			}
		}
	}
}

// Execute the command and send its process ID.
func run(c *exec.Cmd, chps chan<- *os.Process) error {
	if err := c.Start(); err != nil {
		return err
	}
	chps <- c.Process
	return c.Wait()
}

// Add a task to execute the command to cron.
func (c *config) addCmd(s Service, chps chan<- *os.Process) {
	// Notify you have executed the command
	if c.execNotice {
		s.Printf("Exec command: %s\n", s.Exec)
	}
	c.cron.AddFunc(s.Every, func() {
		run(s.prepare(), chps)
	})
}

// Add a task to execute the code block to cron.
func (c *config) addTask(s Service) {
	// Notify you have run the callback
	if c.execNotice {
		s.Printf("Callback: %s\n", s.Tag)
	}
	c.cron.AddFunc(s.Every, func() {
		if err := s.Code(s.ctx); err != nil {
			s.Printf("Callback Error: %s\n", err.Error())
		}
	})
}

// Wait services
func (c *config) wait(chps chan<- *os.Process) {
	c.cron.Start()
	c.wg.Wait()
	signal.Stop(c.ctx.sigchan)
}

// Logging
func (c *config) logging(sc *bufio.Scanner, sid string, clr color) {
	for sc.Scan() {
		hour, min, sec := time.Now().Clock()
		if c.color {
			fmt.Fprintf(c.logger, "\x1b[%dm%02d:%02d:%02d %-10s |\x1b[0m %s\n",
				clr,
				hour, min, sec, sid,
				sc.Text(),
			)
		} else {
			fmt.Fprintf(c.logger, "%02d:%02d:%02d %-10s | %s\n", hour, min, sec, sid, sc.Text())
		}
	}
}
<commit_msg>micronization on main process<commit_after>package golet

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"

	"github.com/Code-Hex/golet/internal/port"
	colorable "github.com/mattn/go-colorable"
	"github.com/robfig/cron"
)

type color int

const (
	red color = iota // + 31
	green
	yellow
	blue
	magenta
	cyan

	colornum int = 5
)

// config is main struct.
// struct comments from http://search.cpan.org/dist/Proclet/lib/Proclet.pm
// Proclet is a great module!!
type config struct {
	interval   time.Duration // interval in seconds between spawning services unless a service exits abnormally.
	color      bool          // colored log.
	logger     io.Writer     // sets the output destination file. use stderr by default.
	logWorker  bool          // enable worker for format logs. If disabled this option, cannot use logger opt too.
	execNotice bool          // enable start and exec notice message like: `16:38:12 worker.1 | Start callback: worker``.

	services   []Service
	wg         sync.WaitGroup
	once       sync.Once
	cancel     func()
	ctx        *signalCtx
	serviceNum int
	tags       map[string]bool
	cron       *cron.Cron
}

var shell []string

func init() {
	if runtime.GOOS == "windows" {
		path, err := exec.LookPath("cmd")
		if err != nil {
			panic("Could not find `cmd` command")
		}
		shell = []string{path, "/c"}
	} else {
		path, err := exec.LookPath("bash")
		if err != nil {
			panic("Could not find `bash` command")
		}
		shell = []string{path, "-c"}
	}
}

// Runner interface have methods for configuration and to run services.
type Runner interface {
	SetInterval(time.Duration)
	EnableColor()
	SetLogger(io.Writer)
	DisableLogger()
	DisableExecNotice()
	Env(map[string]string) error
	Add(...Service) error
	Run() error
}

// for settings
// SetInterval can specify the interval at which the command is executed.
func (c *config) SetInterval(t time.Duration) { c.interval = t }

// EnableColor can output colored log.
func (c *config) EnableColor() { c.color = true }

// SetLogger can specify the io.Writer
// for example in https://github.com/lestrrat/go-file-rotatelogs
/*
      logf, _ := rotatelogs.New(
  	    "/path/to/access_log.%Y%m%d%H%M",
  	    rotatelogs.WithLinkName("/path/to/access_log"),
  	    rotatelogs.WithMaxAge(24 * time.Hour),
  	    rotatelogs.WithRotationTime(time.Hour),
      )

	  golet.New(context.Background()).SetLogger(logf)
*/
func (c *config) SetLogger(f io.Writer) { c.logger = f }

// DisableLogger is prevent to output log
func (c *config) DisableLogger() { c.logWorker = false }

// DisableExecNotice is disable execute notifications
func (c *config) DisableExecNotice() { c.execNotice = false }

// New to create struct of golet.
func New(c context.Context) Runner {
	ctx, cancel := context.WithCancel(c)
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
	return &config{
		interval:   0,
		color:      false,
		logger:     colorable.NewColorableStderr(),
		logWorker:  true,
		execNotice: true,

		ctx: &signalCtx{
			parent:  ctx,
			sigchan: signals,
		},
		cancel: cancel,
		tags:   map[string]bool{},
		cron:   cron.New(),
	}
}

// Env can add temporary environment variables.
func (c *config) Env(envs map[string]string) error {
	for k := range envs {
		if e := os.Setenv(k, envs[k]); e != nil {
			return e
		}
	}
	return nil
}

// Add can add runnable services
func (c *config) Add(services ...Service) error {
	for _, service := range services {
		c.serviceNum++
		if service.Tag == "" {
			service.Tag = fmt.Sprintf("%d", c.serviceNum)
		}
		if service.Worker <= 0 {
			service.Worker = 1
		}
		if _, ok := c.tags[service.Tag]; ok {
			return errors.New("tag: " + service.Tag + " is already exists")
		}
		c.tags[service.Tag] = true

		n, err := port.GetPort()
		if err != nil {
			return err
		}

		service.tmpPort = n
		service.color = color(c.serviceNum%colornum + 32)

		c.services = append(c.services, service)
	}
	return nil
}

// Run just like the name.
func (c *config) Run() error {
	services := make(map[string]Service)

	order := make([]string, 0, c.calcCapacitySize())

	// Assign services.
	if err := c.assign(&order, services); err != nil {
		return err
	}
	chps := make(chan *os.Process, 1)
	go c.waitSignals(chps, len(order))

	// Invoke workers.
	for _, sid := range order {
		service := services[sid]
		// Run one for each service.
		if service.isExecute() {
			c.executeRun(service, chps)
		} else if service.isCode() {
			c.executeCallback(service)
		}
		// Enable log worker if logWorker is true.
		if c.logWorker && (service.Code != nil || service.Exec != "") {
			rd := service.reader
			go c.logging(bufio.NewScanner(rd), sid, service.color)
		}
		// When the task is cron, it does not cause wait time.
		if service.Every == "" {
			time.Sleep(c.interval)
		}
	}

	c.wait(chps)

	return nil
}

// executeRun run command as a process
func (c *config) executeRun(service Service, chps chan<- *os.Process) {
	// Execute the command with cron or goroutine
	if service.isCron() {
		c.addCmd(service, chps)
	} else {
		c.wg.Add(1)
		go func() {
			defer func() {
				service.ctx.Close()
				c.wg.Done()
			}()
		PROCESS:
			for {
				// Notify you have executed the command
				if c.execNotice {
					service.Printf("Exec command: %s\n", service.Exec)
				}
				select {
				case <-c.ctx.Done():
					return
				default:
					// If golet is received signal or exit code is 0, golet do not restart process.
					if err := run(service.prepare(), chps); err != nil {
						if exiterr, ok := err.(*exec.ExitError); ok {
							// See https://stackoverflow.com/a/10385867
							if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
								if !status.Signaled() {
									continue PROCESS
								}
								return
							}
						}
					}
					return
				}
			}
		}()
	}
}

// executeCallback run callback as goroutine or cron
func (c *config) executeCallback(service Service) {
	// Run callback with cron or goroutine
	if service.isCron() {
		c.addTask(service)
	} else {
		c.wg.Add(1)
		go func() {
			defer func() {
				service.ctx.Close()
				c.wg.Done()
			}()
			// If this callback is dead, we should restart it. (like a supervisor)
			// So, this loop for that.
		CALLBACK:
			for {
				// Notify you have run the callback
				if c.execNotice {
					service.Printf("Callback: %s\n", service.Tag)
				}
				select {
				case <-c.ctx.Done():
					return
				default:
					if err := service.Code(service.ctx); err != nil {
						service.Printf("Callback Error: %s\n", err.Error())
						continue CALLBACK
					}
					return
				}
			}
		}()
	}
}

// Calculate of the number of workers.
func (c *config) calcCapacitySize() (cap int) {
	for _, service := range c.services {
		cap += service.Worker
	}
	return
}

// Assign the service ID.
// It also make `order` slice to keep key order of `map[string]Service`.
func (c *config) assign(order *[]string, services map[string]Service) error {
	for _, service := range c.services {
		worker := service.Worker
		for i := 1; i <= worker; i++ {
			s := service
			if err := s.createContext(c.ctx, i); err != nil {
				return err
			}
			sid := fmt.Sprintf("%s.%d", s.Tag, i)
			services[sid] = s
			*order = append(*order, sid)
		}
	}
	return nil
}

// Receive process ID to be executed. or
// It traps the signal relate to parent process. sends a signal to the received process ID.
func (c *config) waitSignals(chps <-chan *os.Process, cap int) {
	procs := make([]*os.Process, 0, cap)
Loop:
	for {
		select {
		case proc := <-chps:
			// Replace used process(nil) with the newly generated process.
			// This run to reduce the memory allocation frequency.
			for i, p := range procs {
				if p == nil {
					procs[i] = proc
					continue Loop
				}
			}
			// If using all processes, allocate newly.
			procs = append(procs, proc)
		case c.ctx.signal = <-c.ctx.sigchan:
			switch c.ctx.signal {
			case syscall.SIGTERM, syscall.SIGHUP:
				c.ctx.signal = syscall.SIGTERM
				sendSignal2Procs(syscall.SIGTERM, procs)
				c.ctx.notifySignal()
			case syscall.SIGINT:
				sendSignal2Procs(syscall.SIGINT, procs)
				c.ctx.notifySignal()
			}
		case <-c.ctx.Done():
			c.cron.Stop()
			return
		}
	}
}

// sendSignal2Procs can send signal and replace os.Process struct of the terminated process with nil
func sendSignal2Procs(sig syscall.Signal, procs []*os.Process) {
	for i, p := range procs {
		if p != nil {
			p.Signal(sig)
			// In case of error, the process has already finished.
			if _, err := p.Wait(); err != nil {
				procs[i] = nil
			}
		}
	}
}

// Execute the command and send its process ID.
func run(c *exec.Cmd, chps chan<- *os.Process) error {
	if err := c.Start(); err != nil {
		return err
	}
	chps <- c.Process
	return c.Wait()
}

// Add a task to execute the command to cron.
func (c *config) addCmd(s Service, chps chan<- *os.Process) {
	// Notify you have executed the command
	if c.execNotice {
		s.Printf("Exec command: %s\n", s.Exec)
	}
	c.cron.AddFunc(s.Every, func() {
		run(s.prepare(), chps)
	})
}

// Add a task to execute the code block to cron.
func (c *config) addTask(s Service) {
	// Notify you have run the callback
	if c.execNotice {
		s.Printf("Callback: %s\n", s.Tag)
	}
	c.cron.AddFunc(s.Every, func() {
		if err := s.Code(s.ctx); err != nil {
			s.Printf("Callback Error: %s\n", err.Error())
		}
	})
}

// Wait services
func (c *config) wait(chps chan<- *os.Process) {
	c.cron.Start()
	c.wg.Wait()
	signal.Stop(c.ctx.sigchan)
}

// Logging
func (c *config) logging(sc *bufio.Scanner, sid string, clr color) {
	for sc.Scan() {
		hour, min, sec := time.Now().Clock()
		if c.color {
			fmt.Fprintf(c.logger, "\x1b[%dm%02d:%02d:%02d %-10s |\x1b[0m %s\n",
				clr,
				hour, min, sec, sid,
				sc.Text(),
			)
		} else {
			fmt.Fprintf(c.logger, "%02d:%02d:%02d %-10s | %s\n", hour, min, sec, sid, sc.Text())
		}
	}
}
<|endoftext|>


<commit_before>package golog

import (
	"fmt"
	"os"
	"sync"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	INFO
	WARN
	ERROR
	FATAL
)

var logLevel = INFO

var level_string = [...]string{
	"DEBUG",
	"INFO",
	"WARN",
	"ERROR",
	"FATAL",
}

type FileLog struct {
	writer *os.File
	path   string
}

type Message struct {
	message string
	level   Level
}

func SetLogLevel(level Level) {
	logLevel = level
}

func NewFd(w *os.File) (fl *FileLog) {
	return &FileLog{
		writer: w,
		path:   "",
	}
}

func NewFile(f string) (fl *FileLog, err error) {
	w, err := os.OpenFile(f, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return nil, err
	}
	fl = NewFd(w)
	fl.path = f
	return
}

var queue = make(chan *Message, 32)
var quit_signal = make(chan byte, 1)

var logger = NewFd(os.Stderr)
var prefix = ""
var lock sync.Mutex

func daemon() {
	for {
		select {
		case msg := <-queue:
			lock.Lock()
			if msg.level >= logLevel {
				fmt.Fprintf(logger.writer, "[%5s @ %s] %s%s\n", level_string[msg.level], time.Now().Format("Jan 2 15:04:05.000"), prefix, msg.message)
			}
			if msg.level == FATAL {
				quit_signal <- '\x00'
			}
			lock.Unlock()
		}
	}
}

func SetPrefix(pre string) {
	prefix = pre
}

func Open(f string) (err error) {
	lock.Lock()
	defer lock.Unlock()
	fl, err := NewFile(f)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open file %s: %s", f, err)
		return err
	} else {
		logger = fl
		Infof("Log ready.")
	}
	return nil
}

func OpenFd(fd *os.File) {
	logger = NewFd(fd)
}

func init() {
	go daemon()
}

func Fatal(msg string) {
	queue <- &Message{
		message: msg,
		level:   FATAL,
	}
	/* Wait for flushing logs. */
	<-quit_signal
	os.Exit(1)
}

func Fatalf(format string, a ...interface{}) {
	Fatal(fmt.Sprintf(format, a...))
}

func Error(msg string) {
	if logLevel > ERROR {
		return
	}
	queue <- &Message{
		message: msg,
		level:   ERROR,
	}
}

func Errorf(format string, a ...interface{}) {
	if logLevel > ERROR {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   ERROR,
	}
}

func Warn(msg string) {
	if logLevel > WARN {
		return
	}
	queue <- &Message{
		message: msg,
		level:   WARN,
	}
}

func Warnf(format string, a ...interface{}) {
	if logLevel > WARN {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   WARN,
	}
}

func Info(msg string) {
	if logLevel > INFO {
		return
	}
	queue <- &Message{
		message: msg,
		level:   INFO,
	}
}

func Infof(format string, a ...interface{}) {
	if logLevel > INFO {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   INFO,
	}
}

func Debug(msg string) {
	if logLevel > DEBUG {
		return
	}
	queue <- &Message{
		message: msg,
		level:   DEBUG,
	}
}

func Debugf(format string, a ...interface{}) {
	if logLevel > DEBUG {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   DEBUG,
	}
}

func Rotate() (err error) {
	lock.Lock()
	defer lock.Unlock()
	logger.writer.Sync() // Ignore error here.
	if logger.path != "" {
		newfd, err := os.OpenFile(logger.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
		if err != nil {
			Errorf("Reopen log file %s: %s", logger.path, err)
			return err
		} else {
			Infof("Reopened log file %s", logger.path)
			newlog := NewFd(newfd)
			newlog.path = logger.path
			logger = newlog
		}
	}
	return nil
}
<commit_msg>ToLevel function added. Invalid level added.<commit_after>package golog

import (
	"fmt"
	"os"
	"strings"
	"sync"
	"time"
)

type Level int

const (
	DEBUG Level = iota
	INFO
	WARN
	ERROR
	FATAL
	INVALID Level = -1
)

var logLevel = INFO

var level_string = [...]string{
	"DEBUG",
	"INFO",
	"WARN",
	"ERROR",
	"FATAL",
}

type FileLog struct {
	writer *os.File
	path   string
}

type Message struct {
	message string
	level   Level
}

func SetLogLevel(level Level) {
	logLevel = level
}

func NewFd(w *os.File) (fl *FileLog) {
	return &FileLog{
		writer: w,
		path:   "",
	}
}

func NewFile(f string) (fl *FileLog, err error) {
	w, err := os.OpenFile(f, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return nil, err
	}
	fl = NewFd(w)
	fl.path = f
	return
}

var queue = make(chan *Message, 32)
var quit_signal = make(chan byte, 1)

var logger = NewFd(os.Stderr)
var prefix = ""
var lock sync.Mutex

func daemon() {
	for {
		select {
		case msg := <-queue:
			lock.Lock()
			if msg.level >= logLevel {
				fmt.Fprintf(logger.writer, "[%5s @ %s] %s%s\n", level_string[msg.level], time.Now().Format("Jan 2 15:04:05.000"), prefix, msg.message)
			}
			if msg.level == FATAL {
				quit_signal <- '\x00'
			}
			lock.Unlock()
		}
	}
}

func SetPrefix(pre string) {
	prefix = pre
}

func Open(f string) (err error) {
	lock.Lock()
	defer lock.Unlock()
	fl, err := NewFile(f)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open file %s: %s", f, err)
		return err
	} else {
		logger = fl
		Infof("Log ready.")
	}
	return nil
}

func OpenFd(fd *os.File) {
	logger = NewFd(fd)
}

func init() {
	go daemon()
}

func Fatal(msg string) {
	queue <- &Message{
		message: msg,
		level:   FATAL,
	}
	/* Wait for flushing logs. */
	<-quit_signal
	os.Exit(1)
}

func Fatalf(format string, a ...interface{}) {
	Fatal(fmt.Sprintf(format, a...))
}

func Error(msg string) {
	if logLevel > ERROR {
		return
	}
	queue <- &Message{
		message: msg,
		level:   ERROR,
	}
}

func Errorf(format string, a ...interface{}) {
	if logLevel > ERROR {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   ERROR,
	}
}

func Warn(msg string) {
	if logLevel > WARN {
		return
	}
	queue <- &Message{
		message: msg,
		level:   WARN,
	}
}

func Warnf(format string, a ...interface{}) {
	if logLevel > WARN {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   WARN,
	}
}

func Info(msg string) {
	if logLevel > INFO {
		return
	}
	queue <- &Message{
		message: msg,
		level:   INFO,
	}
}

func Infof(format string, a ...interface{}) {
	if logLevel > INFO {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   INFO,
	}
}

func Debug(msg string) {
	if logLevel > DEBUG {
		return
	}
	queue <- &Message{
		message: msg,
		level:   DEBUG,
	}
}

func Debugf(format string, a ...interface{}) {
	if logLevel > DEBUG {
		return
	}
	queue <- &Message{
		message: fmt.Sprintf(format, a...),
		level:   DEBUG,
	}
}

func Rotate() (err error) {
	lock.Lock()
	defer lock.Unlock()
	logger.writer.Sync() // Ignore error here.
	if logger.path != "" {
		newfd, err := os.OpenFile(logger.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
		if err != nil {
			Errorf("Reopen log file %s: %s", logger.path, err)
			return err
		} else {
			Infof("Reopened log file %s", logger.path)
			newlog := NewFd(newfd)
			newlog.path = logger.path
			logger = newlog
		}
	}
	return nil
}

func ToLevel(str string) (level Level) {
	str = strings.ToUpper(str)
	for l, s := range level_string {
		if str == s {
			return Level(l)
		}
	}
	return Level(-1)
}
<|endoftext|>


<commit_before>package goref

import (
	"sync"
	"sync/atomic"
	"time"
)

// TODO tracking execution time here might cause performance issues (e.g. in virtualized environments gettimeofday() might be slow)
//   if that turns out to be the case, deactivate Data.TotalNsec

// singleton GoRef instance
var instance = NewGoRef()

// Data -- RefCounter data
type Data struct {
	RefCount   int32
	TotalCount int64
	TotalNsec  int64
}

// GoRef -- A simple, thread safe key-based reference counter that can be used for profiling your application
type GoRef struct {
	data map[string]*Data
	lock *sync.Mutex

	// linked list to old snapshots
	lastSnapshot *GoRef
}

// Instance - Trackable instance
type Instance struct {
	parent    *GoRef
	key       string
	startTime time.Time
}

// Deref -- Dereference an instance of 'key'
func (i Instance) Deref() {
	now := time.Now()
	data := i.parent.get(i.key)
	atomic.AddInt32(&data.RefCount, -1)
	nsec := now.Sub(i.startTime).Nanoseconds()
	atomic.AddInt64(&data.TotalNsec, nsec)
}

// get -- Get the Data object for the specified key (or create it)
func (g *GoRef) get(key string) *Data {
	g.lock.Lock()
	defer g.lock.Unlock()

	rc, ok := g.data[key]
	if !ok {
		rc = &Data{}
		g.data[key] = rc
	}

	return rc
}

// Clone -- Returns a copy of the GoRef  (synchronously)
func (g *GoRef) Clone() GoRef {
	g.lock.Lock()
	defer g.lock.Unlock()

	data := map[string]*Data{}

	for key, d := range g.data {
		data[key] = &Data{
			RefCount:   d.RefCount,
			TotalCount: d.TotalCount,
			TotalNsec:  d.TotalNsec,
		}
	}

	// return a cloned GoRef instance
	return GoRef{
		data:         data,
		lock:         nil, // clones are (meant to be) read-only -> no need for locks
		lastSnapshot: nil, //
	}
}

// Get -- returns the refcounter Data for the specified key (or nil if not found)
func (g *GoRef) Get(key string) *Data {
	if g.lock != nil {
		// make sure this instance is readonly
		panic("GoRef: Called Get() on an active instance! call Clone() or TakeSnapshot() first!")
	}

	return g.data[key]
}

// Keys -- List all keys of this read-only instance
func (g *GoRef) Keys() []string {
	if g.lock != nil {
		panic("GoRef: Called Keys() on an active instance! call Clone() or TakeSnapshot() first!")
	}
	rc := make([]string, 0, len(g.data))

	for k := range g.data {
		rc = append(rc, k)
	}

	return rc
}

// Ref -- References an instance of 'key'
func (g *GoRef) Ref(key string) Instance {
	data := g.get(key)
	atomic.AddInt32(&data.RefCount, 1)
	atomic.AddInt64(&data.TotalCount, 1)

	return Instance{
		parent:    g,
		key:       key,
		startTime: time.Now(),
	}
}

// TakeSnapshot -- Clone the current GoRef instance and return
func (g *GoRef) TakeSnapshot() GoRef {
	old := g.lastSnapshot
	rc := g.Clone()
	rc.lastSnapshot = old
	g.lastSnapshot = &rc
	return rc
}

// NewGoRef -- GoRef constructor
func NewGoRef() *GoRef {
	return &GoRef{
		lock:         &sync.Mutex{},
		data:         map[string]*Data{},
		lastSnapshot: nil,
	}
}
<commit_msg>added GoRef.GetData() (returns the internal .data map - which is JSON-serializable)<commit_after>package goref

import (
	"sync"
	"sync/atomic"
	"time"
)

// TODO tracking execution time here might cause performance issues (e.g. in virtualized environments gettimeofday() might be slow)
//   if that turns out to be the case, deactivate Data.TotalNsec

// singleton GoRef instance
var instance = NewGoRef()

// Data -- RefCounter data
type Data struct {
	RefCount   int32
	TotalCount int64
	TotalNsec  int64
}

// GoRef -- A simple, thread safe key-based reference counter that can be used for profiling your application
type GoRef struct {
	data map[string]*Data
	lock *sync.Mutex

	// linked list to old snapshots
	lastSnapshot *GoRef
}

// Instance - Trackable instance
type Instance struct {
	parent    *GoRef
	key       string
	startTime time.Time
}

// Deref -- Dereference an instance of 'key'
func (i Instance) Deref() {
	now := time.Now()
	data := i.parent.get(i.key)
	atomic.AddInt32(&data.RefCount, -1)
	nsec := now.Sub(i.startTime).Nanoseconds()
	atomic.AddInt64(&data.TotalNsec, nsec)
}

// get -- Get the Data object for the specified key (or create it)
func (g *GoRef) get(key string) *Data {
	g.lock.Lock()
	defer g.lock.Unlock()

	rc, ok := g.data[key]
	if !ok {
		rc = &Data{}
		g.data[key] = rc
	}

	return rc
}

// Clone -- Returns a copy of the GoRef  (synchronously)
func (g *GoRef) Clone() GoRef {
	g.lock.Lock()
	defer g.lock.Unlock()

	data := map[string]*Data{}

	for key, d := range g.data {
		data[key] = &Data{
			RefCount:   d.RefCount,
			TotalCount: d.TotalCount,
			TotalNsec:  d.TotalNsec,
		}
	}

	// return a cloned GoRef instance
	return GoRef{
		data:         data,
		lock:         nil, // clones are (meant to be) read-only -> no need for locks
		lastSnapshot: nil, //
	}
}

// Get -- returns the refcounter Data for the specified key (or nil if not found)
func (g *GoRef) Get(key string) *Data {
	if g.lock != nil {
		// make sure this instance is readonly
		panic("GoRef: Called Get() on an active instance! call Clone() or TakeSnapshot() first!")
	}

	return g.data[key]
}

// GetData -- Returns a map with this read-only instance's data (useful for JSON output)
func (g *GoRef) GetData() map[string]*Data {
	if g.lock != nil {
		panic("GoRef: Called Get() on an active instance! call Clone() or TakeSnapshot() first!")
	}
	return g.data
}

// Keys -- List all keys of this read-only instance
func (g *GoRef) Keys() []string {
	if g.lock != nil {
		panic("GoRef: Called Keys() on an active instance! call Clone() or TakeSnapshot() first!")
	}
	rc := make([]string, 0, len(g.data))

	for k := range g.data {
		rc = append(rc, k)
	}

	return rc
}

// Ref -- References an instance of 'key'
func (g *GoRef) Ref(key string) Instance {
	data := g.get(key)
	atomic.AddInt32(&data.RefCount, 1)
	atomic.AddInt64(&data.TotalCount, 1)

	return Instance{
		parent:    g,
		key:       key,
		startTime: time.Now(),
	}
}

// TakeSnapshot -- Clone the current GoRef instance and return
func (g *GoRef) TakeSnapshot() GoRef {
	old := g.lastSnapshot
	rc := g.Clone()
	rc.lastSnapshot = old
	g.lastSnapshot = &rc
	return rc
}

// NewGoRef -- GoRef constructor
func NewGoRef() *GoRef {
	return &GoRef{
		lock:         &sync.Mutex{},
		data:         map[string]*Data{},
		lastSnapshot: nil,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"math"
	"math/rand"
	"runtime"
	"time"

	"github.com/brunoga/workerpool"
)

var (
	flagNumWorkers = flag.Int("num_workers", runtime.NumCPU(),
		"number of workers to use")
	flagMaxNumber = flag.Uint64("max_number", 100000,
		"max number to compute the prime of")
)

// generateNumbers starts a goroutine that will return random uint64 numbers
// between 0 and maxNumber (using the given Rand instance) and send them
// through the returned channel. It will generate maxNumber numbers and close
// the channel.
func generateNumbers(maxNumber uint64, r *rand.Rand) <-chan interface{} {
	// Create output channel.
	output := make(chan interface{})

	i := uint64(0)
	go func() {
		for {
			output <- (r.Uint64() % maxNumber)

			i++
			if i >= maxNumber {
				break
			}
		}

		close(output)
	}()

	return output
}

// isPrime is a workerpool.WorkerFunc implementation that expects an uint64
// number as input and determines if it is a prime number or not. It returns the// original uint64 number and a nil error on success and nil and a non-nil error// on failure (including when the number is not prime).
func isPrime(i interface{}) (interface{}, error) {
	n, ok := i.(uint64)
	if !ok {
		return nil, fmt.Errorf("expected uint64. Got %t", i)
	}

	// Optimization. We can only check the square root of the given number.
	n2 := uint64(math.Ceil(math.Sqrt(float64(n))))

	if n2 < 2 {
		return nil, fmt.Errorf("not prime")
	}

	for divisor := uint64(2); divisor < n2; divisor++ {
		if (n2 % divisor) == 0 {
			return nil, fmt.Errorf("not prime")
		}
	}

	return n, nil
}

func main() {
	flag.Parse()

	// Start a WorkerPool for the isPrime function.
	workerPool, err := workerpool.NewWorkerPool(isPrime, *flagNumWorkers)
	if err != nil {
		panic(err)
	}

	// Initialize random generator for the generateNumbers() function.
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	// WorkerPool input is the channel where numbers will be sent to.
	err = workerPool.SetInputChannel(generateNumbers(*flagMaxNumber, r))
	if err != nil {
		panic(err)
	}

	// Get WorkerPool output channel.
	outputChannel := workerPool.GetOutputChannel()

	// Start work.
	err = workerPool.Start(context.Background())
	if err != nil {
		panic(err)
	}

	// Read re3sults and handle them.
	for result := range outputChannel {
		switch result.(type) {
		case uint64:
			// Prime number.
			n := result.(uint64)
			fmt.Println(n, "is prime.")
		case workerpool.WorkerError:
			// Error (including non-prime numbers).
			workerError := result.(workerpool.WorkerError)
			if workerError.Error.Error() != "not prime" {
				// Only print anything if it is an actual error.
				fmt.Println(workerError)
			}
		default:
			panic("should never happend")
		}
	}
}
<commit_msg>Update comments.<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"math"
	"math/rand"
	"runtime"
	"time"

	"github.com/brunoga/workerpool"
)

var (
	flagNumWorkers = flag.Int("num_workers", runtime.NumCPU(),
		"number of workers to use")
	flagMaxNumber = flag.Uint64("max_number", 100000,
		"max number to compute the prime of")
)

// generateNumbers starts a goroutine that will return random uint64 numbers
// between 0 and maxNumber (using the given Rand instance) and send them
// through the returned channel. It will generate maxNumber numbers and close
// the channel.
func generateNumbers(maxNumber uint64, r *rand.Rand) <-chan interface{} {
	// Create output channel.
	output := make(chan interface{})

	i := uint64(0)
	go func() {
		for {
			output <- (r.Uint64() % maxNumber)

			i++
			if i >= maxNumber {
				break
			}
		}

		close(output)
	}()

	return output
}

// isPrime is a workerpool.WorkerFunc implementation that expects an uint64
// number as input and determines if it is a prime number or not. It returns the
// original uint64 number and a nil error on success and nil and a non-nil error
// on failure (including when the number is not prime).
func isPrime(i interface{}) (interface{}, error) {
	n, ok := i.(uint64)
	if !ok {
		return nil, fmt.Errorf("expected uint64. Got %t", i)
	}

	// Optimization. We can only check the square root of the given number.
	n2 := uint64(math.Ceil(math.Sqrt(float64(n))))

	if n2 < 2 {
		return nil, fmt.Errorf("not prime")
	}

	for divisor := uint64(2); divisor < n2; divisor++ {
		if (n2 % divisor) == 0 {
			return nil, fmt.Errorf("not prime")
		}
	}

	return n, nil
}

func main() {
	flag.Parse()

	// Start a WorkerPool for the isPrime function.
	workerPool, err := workerpool.NewWorkerPool(isPrime, *flagNumWorkers)
	if err != nil {
		panic(err)
	}

	// Initialize random generator for the generateNumbers() function.
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	// WorkerPool input is the channel where numbers will be sent to.
	err = workerPool.SetInputChannel(generateNumbers(*flagMaxNumber, r))
	if err != nil {
		panic(err)
	}

	// Get WorkerPool output channel.
	outputChannel := workerPool.GetOutputChannel()

	// Start work.
	err = workerPool.Start(context.Background())
	if err != nil {
		panic(err)
	}

	// Read re3sults and handle them.
	for result := range outputChannel {
		switch result.(type) {
		case uint64:
			// Prime number.
			n := result.(uint64)
			fmt.Println(n, "is prime.")
		case workerpool.WorkerError:
			// Error (including non-prime numbers).
			workerError := result.(workerpool.WorkerError)
			if workerError.Error.Error() != "not prime" {
				// Only print anything if it is an actual error.
				fmt.Println(workerError)
			}
		default:
			panic("should never happend")
		}
	}
}
<|endoftext|>


<commit_before>package gorma

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
	"gopkg.in/alecthomas/kingpin.v2"
)

// Generator is the application code generator.
type Generator struct {
	genfiles []string
}

// Generate is the generator entry point called by the meta generator.
func Generate(api *design.APIDefinition) ([]string, error) {
	g, err := NewGenerator()
	if err != nil {
		return nil, err
	}
	return g.Generate(api)
}

// NewGenerator returns the application code generator.
func NewGenerator() (*Generator, error) {
	return new(Generator), nil
}

// Generate produces the skeleton main.
func (g *Generator) Generate(api *design.APIDefinition) ([]string, error) {

	os.RemoveAll(ModelDir())
	os.MkdirAll(ModelDir(), 0755)
	app := kingpin.New("Model generator", "model generator")
	codegen.RegisterFlags(app)
	_, err := app.Parse(os.Args[1:])
	if err != nil {
		panic(err)
	}
	var outPkg string
	// going to hell for this == HELP Wanted (windows)
	outPkg = codegen.DesignPackagePath[0:strings.LastIndex(codegen.DesignPackagePath, "/")]
	if err != nil {
		panic(err)
	}
	outPkg = strings.TrimPrefix(outPkg, "src/")
	appPkg := filepath.Join(outPkg, "app")
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport(appPkg),
		codegen.SimpleImport("github.com/jinzhu/gorm"),
		codegen.SimpleImport("github.com/jinzhu/copier"),
		codegen.SimpleImport("time"),
	}

	rbacimports := []*codegen.ImportSpec{
		codegen.SimpleImport(appPkg),
		codegen.SimpleImport("github.com/mikespook/gorbac"),
	}

	rbactitle := fmt.Sprintf("%s: RBAC", api.Name)
	_, dorbac := api.Metadata["github.com/bketelsen/gorma#rbac"]

	err = api.IterateUserTypes(func(res *design.UserTypeDefinition) error {
		if res.Type.IsObject() {
			title := fmt.Sprintf("%s: Models", api.Name)
			modelname := strings.ToLower(DeModel(res.TypeName))
			filename := filepath.Join(ModelDir(), modelname+"_model.go")
			mtw, err := NewModelWriter(filename)
			if err != nil {
				panic(err)
			}
			mtw.WriteHeader(title, "models", imports)
			if md, ok := res.Metadata["github.com/bketelsen/gorma"]; ok && md == "Model" {
				fmt.Println("Found Gorma Metadata:", md)
				err = mtw.Execute(res)
				if err != nil {
					g.Cleanup()
					return err
				}
			}
			if err := mtw.FormatCode(); err != nil {
				g.Cleanup()

			}
			if err != nil {
				g.genfiles = append(g.genfiles, filename)
			}
			return nil
		}

		return nil

	})
	if dorbac {
		rbacfilename := filepath.Join(ModelDir(), "rbac.go")
		rbacw, err := NewRbacWriter(rbacfilename)
		if err != nil {
			panic(err)
		}
		rbacw.WriteHeader(rbactitle, "models", rbacimports)
		err = rbacw.Execute(api)
		if err != nil {
			g.Cleanup()
			return g.genfiles, err
		}
		if err := rbacw.FormatCode(); err != nil {
			g.Cleanup()
			return nil, err
		}
		if err != nil {
			g.genfiles = append(g.genfiles, rbacfilename)
		}

	}

	return g.genfiles, err
}

// Cleanup removes all the files generated by this generator during the last invokation of Generate.
func (g *Generator) Cleanup() {
	for _, f := range g.genfiles {
		os.Remove(f)
	}
	g.genfiles = nil
}
<commit_msg>decouple contexts<commit_after>package gorma

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
	"gopkg.in/alecthomas/kingpin.v2"
)

// Generator is the application code generator.
type Generator struct {
	genfiles []string
}

// Generate is the generator entry point called by the meta generator.
func Generate(api *design.APIDefinition) ([]string, error) {
	g, err := NewGenerator()
	if err != nil {
		return nil, err
	}
	return g.Generate(api)
}

// NewGenerator returns the application code generator.
func NewGenerator() (*Generator, error) {
	return new(Generator), nil
}

// Generate produces the skeleton main.
func (g *Generator) Generate(api *design.APIDefinition) ([]string, error) {

	os.RemoveAll(ModelDir() + "*genmodel.go")
	os.MkdirAll(ModelDir(), 0755)
	app := kingpin.New("Model generator", "model generator")
	codegen.RegisterFlags(app)
	_, err := app.Parse(os.Args[1:])
	if err != nil {
		panic(err)
	}
	var outPkg string
	// going to hell for this == HELP Wanted (windows)
	outPkg = codegen.DesignPackagePath[0:strings.LastIndex(codegen.DesignPackagePath, "/")]
	if err != nil {
		panic(err)
	}
	outPkg = strings.TrimPrefix(outPkg, "src/")
	appPkg := filepath.Join(outPkg, "app")
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport(appPkg),
		codegen.SimpleImport("github.com/jinzhu/gorm"),
		codegen.SimpleImport("github.com/jinzhu/copier"),
		codegen.SimpleImport("time"),
	}

	rbacimports := []*codegen.ImportSpec{
		codegen.SimpleImport(appPkg),
		codegen.SimpleImport("github.com/mikespook/gorbac"),
	}

	rbactitle := fmt.Sprintf("%s: RBAC", api.Name)
	_, dorbac := api.Metadata["github.com/bketelsen/gorma#rbac"]

	err = api.IterateUserTypes(func(res *design.UserTypeDefinition) error {
		if res.Type.IsObject() {
			title := fmt.Sprintf("%s: Models", api.Name)
			modelname := strings.ToLower(DeModel(res.TypeName))
			filename := filepath.Join(ModelDir(), modelname+"_genmodel.go")
			mtw, err := NewModelWriter(filename)
			if err != nil {
				panic(err)
			}
			mtw.WriteHeader(title, "models", imports)
			if md, ok := res.Metadata["github.com/bketelsen/gorma"]; ok && md == "Model" {
				fmt.Println("Found Gorma Metadata:", md)
				err = mtw.Execute(res)
				if err != nil {
					g.Cleanup()
					return err
				}
			}
			if err := mtw.FormatCode(); err != nil {
				g.Cleanup()

			}
			if err != nil {
				g.genfiles = append(g.genfiles, filename)
			}
			return nil
		}

		return nil

	})
	if dorbac {
		rbacfilename := filepath.Join(ModelDir(), "rbac.go")
		rbacw, err := NewRbacWriter(rbacfilename)
		if err != nil {
			panic(err)
		}
		rbacw.WriteHeader(rbactitle, "models", rbacimports)
		err = rbacw.Execute(api)
		if err != nil {
			g.Cleanup()
			return g.genfiles, err
		}
		if err := rbacw.FormatCode(); err != nil {
			g.Cleanup()
			return nil, err
		}
		if err != nil {
			g.genfiles = append(g.genfiles, rbacfilename)
		}

	}

	return g.genfiles, err
}

// Cleanup removes all the files generated by this generator during the last invokation of Generate.
func (g *Generator) Cleanup() {
	for _, f := range g.genfiles {
		os.Remove(f)
	}
	g.genfiles = nil
}
<|endoftext|>


<commit_before>package marshaller

import (
    "encoding/json"
    "strings"
    "errors"
    "strconv"
)

/**
 * Created by tuxer on 9/6/17.
 */

type JsonObject struct {
    parsed      map[string]interface{}
}

func (j *JsonObject) Parse(data []byte)    {
    json.Unmarshal(data, &j.parsed)
}

func (j *JsonObject) GetJsonArray(path string) []JsonObject    {
    obj := j.get(path)

    values, ok := obj.([]interface{})

    if !ok  {
        return nil
    }
    var arrJson []JsonObject
    for _, value := range values   {
        mapValue, ok := value.(map[string]interface{})
        if ok   {
            jo := JsonObject{parsed: mapValue}
            arrJson = append(arrJson, jo)
        }
    }
    return arrJson
}
func (j *JsonObject) GetJsonObject(path string) *JsonObject    {
    obj := j.get(path)

    v, ok := obj.(map[string]interface{})
    if ok   {
        jo := JsonObject{ parsed: v }
        return &jo
    }
    return nil
}

func (j *JsonObject) GetInt(path string) (int, error) {
    obj := j.get(path)

    switch obj.(type) {
    case float64:
        float, _ := obj.(float64)
        return int(float), nil
    case string:
        str, _ := obj.(string)
        i, e := strconv.Atoi(str)
        if e != nil {
            return 0, e
        }
        return i, nil
    default:
        return 0, errors.New(`unable to get ` + path + `, is not int`)
    }
}
func (j *JsonObject) GetString(path string) (string, error) {
    obj := j.get(path)

    switch obj.(type) {
    case string:
        str, _ := obj.(string)
        return str, nil
    case float64:
        float, _ := obj.(float64)
        str := strconv.FormatFloat(float, 'f', -1, 64)
        return str, nil
    default:
        return ``, errors.New(`unable to get ` + path + `, is not string`)
    }

}

func (j *JsonObject) get(path string) interface{} {
    splittedPath := strings.Split(path, `.`)

    var jsonMap interface{}
    jsonMap = j.parsed
    var val interface{}
    for _, pathItem := range splittedPath   {
        if jsonMap == nil   {
            return nil
        }
        val = jsonMap.(map[string]interface{})[pathItem]

        switch val.(type) {
        case map[string]interface{}:
            jsonMap = val
        default:
            jsonMap = nil
        }
    }
    return val
}
<commit_msg>rename package<commit_after>package goson

import (
    "encoding/json"
    "strings"
    "errors"
    "strconv"
)

/**
 * Created by tuxer on 9/6/17.
 */

type JsonObject struct {
    parsed      map[string]interface{}
}

func (j *JsonObject) Parse(data []byte)    {
    json.Unmarshal(data, &j.parsed)
}

func (j *JsonObject) GetJsonArray(path string) []JsonObject    {
    obj := j.get(path)

    values, ok := obj.([]interface{})

    if !ok  {
        return nil
    }
    var arrJson []JsonObject
    for _, value := range values   {
        mapValue, ok := value.(map[string]interface{})
        if ok   {
            jo := JsonObject{parsed: mapValue}
            arrJson = append(arrJson, jo)
        }
    }
    return arrJson
}
func (j *JsonObject) GetJsonObject(path string) *JsonObject    {
    obj := j.get(path)

    v, ok := obj.(map[string]interface{})
    if ok   {
        jo := JsonObject{ parsed: v }
        return &jo
    }
    return nil
}

func (j *JsonObject) GetInt(path string) (int, error) {
    obj := j.get(path)

    switch obj.(type) {
    case float64:
        float, _ := obj.(float64)
        return int(float), nil
    case string:
        str, _ := obj.(string)
        i, e := strconv.Atoi(str)
        if e != nil {
            return 0, e
        }
        return i, nil
    default:
        return 0, errors.New(`unable to get ` + path + `, is not int`)
    }
}
func (j *JsonObject) GetString(path string) (string, error) {
    obj := j.get(path)

    switch obj.(type) {
    case string:
        str, _ := obj.(string)
        return str, nil
    case float64:
        float, _ := obj.(float64)
        str := strconv.FormatFloat(float, 'f', -1, 64)
        return str, nil
    default:
        return ``, errors.New(`unable to get ` + path + `, is not string`)
    }

}

func (j *JsonObject) get(path string) interface{} {
    splittedPath := strings.Split(path, `.`)

    var jsonMap interface{}
    jsonMap = j.parsed
    var val interface{}
    for _, pathItem := range splittedPath   {
        if jsonMap == nil   {
            return nil
        }
        val = jsonMap.(map[string]interface{})[pathItem]

        switch val.(type) {
        case map[string]interface{}:
            jsonMap = val
        default:
            jsonMap = nil
        }
    }
    return val
}
<|endoftext|>


<commit_before>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

/*  Filename:    gotag.go
 *  Author:      Bryan Matsuo <bryan.matsuo@gmail.com>
 *  Created:     Sat Nov  5 19:46:28 PDT 2011
 *  Description: Main source file in gotag
 */

import (
	"github.com/bmatsuo/go-script/script"
	"template"
	"runtime"
	"strconv"
	"strings"
	"bytes"
	"log"
	"fmt"
	"os"
)

var tfuncs = template.FuncMap{
	"quote": func(x interface{}) (string, error) {
		switch x.(type) {
		case string:
			return script.ShellQuote(x.(string)), nil
		}
		return "", fmt.Errorf("argument %#v is not a string", x)
	},
}

var cmdtemplates = `
{{/* Outputs a shell command given a list of strings (executable + args) */}}
	{{define "cmd"}}{{if ""}}
		{{end}}{{with $cmd := .}}{{range $i, $arg := $cmd}}{{if ""}}
			{{end}}{{if $i}} {{end}}{{quote $arg}}{{end}}{{end}}{{end}}

{{/* Outputs a list of comands .cmds. If .dir is set, the working directory is set with cd*/}}
	{{define "script"}}{{if ""}}
				{{end}}{{if .dir}}cd {{quote .dir}}
{{end}}{{if ""}}
				{{end}}{{range $i, $cmd := .cmds}}{{if $i}}
{{end}}{{if ""}}
				{{end}}{{template "cmd" $cmd}}{{end}}{{end}}
`

var templates = template.SetMust(new(template.Set).Funcs(tfuncs).Parse(cmdtemplates))

type ShellCmd []string

func CmdTemplateScript(sh script.Scriptor, dir string, cmds ...ShellCmd) script.Script {
	if sh == nil {
		panic("nil scriptor")
	}
	var d string
	if dir != "." {
		d = dir
	}
	buff := new(bytes.Buffer)
	err := templates.Template("script").Execute(buff, map[string]interface{}{"dir": d, "cmds": cmds})
	if err != nil {
		log.Println(err)
	}
	return sh.NewScript(string(buff.Bytes()))
}

var archlinker = map[string]string{
	"amd64": "6l",
	"386":   "8l",
	"arm":   "5l",
}

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

var GoLinker = archlinker[runtime.GOARCH]

func GetGoVersion() (version string, revision int, err error) {
	if GoLinker == "" {
		panic(fmt.Errorf("unknown architechture %s", runtime.GOARCH))
	}
	var p []byte
	p, _, err = script.Output(script.Bash.NewScript(fmt.Sprintf("%s -V", GoLinker)))
	if err != nil {
		return
	}
	pieces := strings.Fields(string(p))
	if len(pieces) < 2 {
		err = fmt.Errorf("Didn't understand Go version %s", string(p))
	}
	version = pieces[len(pieces)-2]
	revision, err = strconv.Atoi(pieces[len(pieces)-1])
	return
}

func GoRepositoryTag(version string) string { return "go." + version }

var opt options

func main() {
	opt = parseFlags()

	root := opt.Root
	force := opt.Force
	verbose := opt.Verbose

	gover, gorev, err := GetGoVersion()
	gotag := GoRepositoryTag(gover)
	if verbose {
		log.Printf("  Linker: %s", GoLinker)
		log.Printf(" Version: %s", gover)
		log.Printf("Revision: %d", gorev)
		log.Printf("     Tag: %s", gotag)
	}

	var project GoProject
	project, err = NewProject(root)
	Must(err)
	Must(BuildAndClean(project))

	var git Repository
	git, err = NewGitRepo(root)
	Must(err)

	clean, err := git.IsClean()
	Must(err)
	if !clean {
		fmt.Fprint(os.Stderr, "The repository has uncommited changes.\n")
		fmt.Fprint(os.Stderr, "Commit the changes and run Gotag again.\n")
		os.Exit(1)
	}

	if opt.Fetch {
		fmt.Print("Fetching remote tags\n")
		Must(git.TagsFetch())
	}

	var tags []string
	tags, err = git.Tags()
	Must(err)

	// Look for a tag named for the current version.
	hasCurrentTag := false
	for i := range tags {
		hasCurrentTag = gotag == tags[i]
		if hasCurrentTag {
			break
		}
	}

	// If found a try to delete it.
	if hasCurrentTag {
		fmt.Printf("Found tag %s\n", gotag)
		if force {
			Must(git.TagDelete(gotag))
		} else {
			fmt.Fprintf(os.Stderr, "use -f flag to update %s\n", gotag)
			os.Exit(1)
		}
	}

	// Create the new tag.
	annotation := fmt.Sprintf("Latest build for Go version %s %d", gover, gorev)
	if opt.Commit != "" {
		fmt.Fprintf(os.Stderr, "Creating tag %s %#v (%s)\n", gotag, annotation, opt.Commit)
		Must(git.TagNew(gotag, annotation, opt.Commit))
	} else {
		fmt.Fprintf(os.Stderr, "Creating tag %s %#v\n", gotag, annotation)
		Must(git.TagNew(gotag, annotation))
	}

	if opt.Push {
		fmt.Fprintf(os.Stderr, "Pushing tags to remote repository\n")
		Must(git.TagsPush())
	}
}
<commit_msg>Allow tagging with a dirty HEAD when a commit hash is specified.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

/*  Filename:    gotag.go
 *  Author:      Bryan Matsuo <bryan.matsuo@gmail.com>
 *  Created:     Sat Nov  5 19:46:28 PDT 2011
 *  Description: Main source file in gotag
 */

import (
	"github.com/bmatsuo/go-script/script"
	"template"
	"runtime"
	"strconv"
	"strings"
	"bytes"
	"log"
	"fmt"
	"os"
)

var tfuncs = template.FuncMap{
	"quote": func(x interface{}) (string, error) {
		switch x.(type) {
		case string:
			return script.ShellQuote(x.(string)), nil
		}
		return "", fmt.Errorf("argument %#v is not a string", x)
	},
}

var cmdtemplates = `
{{/* Outputs a shell command given a list of strings (executable + args) */}}
	{{define "cmd"}}{{if ""}}
		{{end}}{{with $cmd := .}}{{range $i, $arg := $cmd}}{{if ""}}
			{{end}}{{if $i}} {{end}}{{quote $arg}}{{end}}{{end}}{{end}}

{{/* Outputs a list of comands .cmds. If .dir is set, the working directory is set with cd*/}}
	{{define "script"}}{{if ""}}
				{{end}}{{if .dir}}cd {{quote .dir}}
{{end}}{{if ""}}
				{{end}}{{range $i, $cmd := .cmds}}{{if $i}}
{{end}}{{if ""}}
				{{end}}{{template "cmd" $cmd}}{{end}}{{end}}
`

var templates = template.SetMust(new(template.Set).Funcs(tfuncs).Parse(cmdtemplates))

type ShellCmd []string

func CmdTemplateScript(sh script.Scriptor, dir string, cmds ...ShellCmd) script.Script {
	if sh == nil {
		panic("nil scriptor")
	}
	var d string
	if dir != "." {
		d = dir
	}
	buff := new(bytes.Buffer)
	err := templates.Template("script").Execute(buff, map[string]interface{}{"dir": d, "cmds": cmds})
	if err != nil {
		log.Println(err)
	}
	return sh.NewScript(string(buff.Bytes()))
}

var archlinker = map[string]string{
	"amd64": "6l",
	"386":   "8l",
	"arm":   "5l",
}

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

var GoLinker = archlinker[runtime.GOARCH]

func GetGoVersion() (version string, revision int, err error) {
	if GoLinker == "" {
		panic(fmt.Errorf("unknown architechture %s", runtime.GOARCH))
	}
	var p []byte
	p, _, err = script.Output(script.Bash.NewScript(fmt.Sprintf("%s -V", GoLinker)))
	if err != nil {
		return
	}
	pieces := strings.Fields(string(p))
	if len(pieces) < 2 {
		err = fmt.Errorf("Didn't understand Go version %s", string(p))
	}
	version = pieces[len(pieces)-2]
	revision, err = strconv.Atoi(pieces[len(pieces)-1])
	return
}

func GoRepositoryTag(version string) string { return "go." + version }

var opt options

func main() {
	opt = parseFlags()

	root := opt.Root
	force := opt.Force
	verbose := opt.Verbose

	gover, gorev, err := GetGoVersion()
	gotag := GoRepositoryTag(gover)
	if verbose {
		log.Printf("  Linker: %s", GoLinker)
		log.Printf(" Version: %s", gover)
		log.Printf("Revision: %d", gorev)
		log.Printf("     Tag: %s", gotag)
	}

	var project GoProject
	project, err = NewProject(root)
	Must(err)
	Must(BuildAndClean(project))

	var git Repository
	git, err = NewGitRepo(root)
	Must(err)

	if opt.Commit != "" { // Its OK to tag past commits if the HEAD is dirty.
		clean, err := git.IsClean()
		Must(err)
		if !clean {
			fmt.Fprint(os.Stderr, "The repository has uncommited changes.\n")
			fmt.Fprint(os.Stderr, "Commit the changes and run Gotag again.\n")
			os.Exit(1)
		}
	}

	if opt.Fetch {
		fmt.Print("Fetching remote tags\n")
		Must(git.TagsFetch())
	}

	var tags []string
	tags, err = git.Tags()
	Must(err)

	// Look for a tag named for the current version.
	hasCurrentTag := false
	for i := range tags {
		hasCurrentTag = gotag == tags[i]
		if hasCurrentTag {
			break
		}
	}

	// If found a try to delete it.
	if hasCurrentTag {
		fmt.Printf("Found tag %s\n", gotag)
		if force {
			Must(git.TagDelete(gotag))
		} else {
			fmt.Fprintf(os.Stderr, "use -f flag to update %s\n", gotag)
			os.Exit(1)
		}
	}

	// Create the new tag.
	annotation := fmt.Sprintf("Latest build for Go version %s %d", gover, gorev)
	if opt.Commit != "" {
		fmt.Fprintf(os.Stderr, "Creating tag %s %#v (%s)\n", gotag, annotation, opt.Commit)
		Must(git.TagNew(gotag, annotation, opt.Commit))
	} else {
		fmt.Fprintf(os.Stderr, "Creating tag %s %#v\n", gotag, annotation)
		Must(git.TagNew(gotag, annotation))
	}

	if opt.Push {
		fmt.Fprintf(os.Stderr, "Pushing tags to remote repository\n")
		Must(git.TagsPush())
	}
}
<|endoftext|>