<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

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

	"github.com/fatih/color"
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
	"github.com/shibukawa/configdir"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var Version = "0.22.0"
var Banner = `
          /\      |‾‾|  /‾‾/  /‾/
     /\  /  \     |  |_/  /  / /
    /  \/    \    |      |  /  ‾‾\
   /          \   |  |‾\  \ | (_) |
  / __________ \  |__|  \__\ \___/ .io`

var BannerColor = color.New(color.FgCyan)

var (
	outMutex  = &sync.Mutex{}
	stdoutTTY = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
	stderrTTY = isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())
	stdout    = consoleWriter{colorable.NewColorableStdout(), stdoutTTY, outMutex}
	stderr    = consoleWriter{colorable.NewColorableStderr(), stderrTTY, outMutex}
)

var (
	cfgFile string

	verbose bool
	quiet   bool
	noColor bool
	logFmt  string
	address string
)

// RootCmd represents the base command when called without any subcommands.
var RootCmd = &cobra.Command{
	Use:           "k6",
	Short:         "a next-generation load generator",
	Long:          BannerColor.Sprint(Banner),
	SilenceUsage:  true,
	SilenceErrors: true,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		setupLoggers(logFmt)
		if noColor {
			stdout.Writer = colorable.NewNonColorable(os.Stdout)
			stdout.Writer = colorable.NewNonColorable(os.Stderr)
		}
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		log.Error(err.Error())
		if e, ok := err.(ExitCode); ok {
			os.Exit(e.Code)
		}
		os.Exit(-1)
	}
}

func init() {
	defaultConfigPathMsg := ""
	configFolders := configDirs.QueryFolders(configdir.Global)
	if len(configFolders) > 0 {
		defaultConfigPathMsg = fmt.Sprintf(" (default %s)", filepath.Join(configFolders[0].Path, configFilename))
	}

	RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable debug logging")
	RootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "disable progress updates")
	RootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "disable colored output")
	RootCmd.PersistentFlags().StringVar(&logFmt, "logformat", "", "log output format")
	RootCmd.PersistentFlags().StringVarP(&address, "address", "a", "localhost:6565", "address for the api server")
	RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file"+defaultConfigPathMsg)
	must(cobra.MarkFlagFilename(RootCmd.PersistentFlags(), "config"))
}

// fprintf panics when where's an error writing to the supplied io.Writer
func fprintf(w io.Writer, format string, a ...interface{}) (n int) {
	n, err := fmt.Fprintf(w, format, a...)
	if err != nil {
		panic(err.Error())
	}
	return n
}

// RawFormatter it does nothing with the message just prints it
type RawFormater struct{}

// Format renders a single log entry
func (f RawFormater) Format(entry *log.Entry) ([]byte, error) {
	return append([]byte(entry.Message), '\n'), nil
}

func setupLoggers(logFmt string) {
	if verbose {
		log.SetLevel(log.DebugLevel)
	}
	log.SetOutput(stderr)

	switch logFmt {
	case "raw":
		log.SetFormatter(&RawFormater{})
		log.Debug("Logger format: RAW")
	case "json":
		log.SetFormatter(&log.JSONFormatter{})
		log.Debug("Logger format: JSON")
	default:
		log.SetFormatter(&log.TextFormatter{ForceColors: stderrTTY, DisableColors: noColor})
		log.Debug("Logger format: TEXT")
	}

}
<commit_msg>Fix a bug with --no-color and wrap Go's std logger with logrus<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cmd

import (
	"fmt"
	"io"
	golog "log"
	"os"
	"path/filepath"
	"sync"

	"github.com/fatih/color"
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
	"github.com/shibukawa/configdir"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var Version = "0.22.0"
var Banner = `
          /\      |‾‾|  /‾‾/  /‾/
     /\  /  \     |  |_/  /  / /
    /  \/    \    |      |  /  ‾‾\
   /          \   |  |‾\  \ | (_) |
  / __________ \  |__|  \__\ \___/ .io`

var BannerColor = color.New(color.FgCyan)

var (
	outMutex  = &sync.Mutex{}
	stdoutTTY = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
	stderrTTY = isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())
	stdout    = consoleWriter{colorable.NewColorableStdout(), stdoutTTY, outMutex}
	stderr    = consoleWriter{colorable.NewColorableStderr(), stderrTTY, outMutex}
)

var (
	cfgFile string

	verbose bool
	quiet   bool
	noColor bool
	logFmt  string
	address string
)

// RootCmd represents the base command when called without any subcommands.
var RootCmd = &cobra.Command{
	Use:           "k6",
	Short:         "a next-generation load generator",
	Long:          BannerColor.Sprint(Banner),
	SilenceUsage:  true,
	SilenceErrors: true,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		setupLoggers(logFmt)
		if noColor {
			stdout.Writer = colorable.NewNonColorable(os.Stdout)
			stderr.Writer = colorable.NewNonColorable(os.Stderr)
		}
		golog.SetOutput(log.StandardLogger().Writer())
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		log.Error(err.Error())
		if e, ok := err.(ExitCode); ok {
			os.Exit(e.Code)
		}
		os.Exit(-1)
	}
}

func init() {
	defaultConfigPathMsg := ""
	configFolders := configDirs.QueryFolders(configdir.Global)
	if len(configFolders) > 0 {
		defaultConfigPathMsg = fmt.Sprintf(" (default %s)", filepath.Join(configFolders[0].Path, configFilename))
	}

	RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable debug logging")
	RootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "disable progress updates")
	RootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "disable colored output")
	RootCmd.PersistentFlags().StringVar(&logFmt, "logformat", "", "log output format")
	RootCmd.PersistentFlags().StringVarP(&address, "address", "a", "localhost:6565", "address for the api server")
	RootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file"+defaultConfigPathMsg)
	must(cobra.MarkFlagFilename(RootCmd.PersistentFlags(), "config"))
}

// fprintf panics when where's an error writing to the supplied io.Writer
func fprintf(w io.Writer, format string, a ...interface{}) (n int) {
	n, err := fmt.Fprintf(w, format, a...)
	if err != nil {
		panic(err.Error())
	}
	return n
}

// RawFormatter it does nothing with the message just prints it
type RawFormater struct{}

// Format renders a single log entry
func (f RawFormater) Format(entry *log.Entry) ([]byte, error) {
	return append([]byte(entry.Message), '\n'), nil
}

func setupLoggers(logFmt string) {
	if verbose {
		log.SetLevel(log.DebugLevel)
	}
	log.SetOutput(stderr)

	switch logFmt {
	case "raw":
		log.SetFormatter(&RawFormater{})
		log.Debug("Logger format: RAW")
	case "json":
		log.SetFormatter(&log.JSONFormatter{})
		log.Debug("Logger format: JSON")
	default:
		log.SetFormatter(&log.TextFormatter{ForceColors: stderrTTY, DisableColors: noColor})
		log.Debug("Logger format: TEXT")
	}

}
<|endoftext|>


<commit_before>package multi

import (
	"bytes"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	_ "golang.org/x/image/bmp"
	_ "golang.org/x/image/tiff"

	"github.com/daddye/vips"
	"github.com/disintegration/imaging"
	"github.com/gotgo/fw/logging"
	"github.com/gotgo/fw/me"
)

type AcquireImage struct {
	DownloadTimeout time.Duration
	MaxHeight       int
	MaxWidth        int
	Uploader        Uploader
	Log             logging.Logger
}

type AcquiredImage struct {
	SourceSize        int
	SourceUrl         string
	SourceContentType string

	DestUrl    string
	DestSize   int64
	DestHeight int
	DestWidth  int
	PHash      uint64
}

func (a *AcquireImage) timeout() time.Duration {
	if a.DownloadTimeout == time.Second*0 {
		return time.Second * 60
	}
	return a.DownloadTimeout
}

func (a *AcquireImage) Acquire(url, filename string) (*AcquiredImage, error) {
	//TODO: time
	bts, ctype, err := a.download(url, a.timeout())
	if err != nil {
		return nil, me.Err(err, "failed to download url", &me.KV{"url", url})
	}
	sourceSize := len(bts)
	//resized, err := a.resize(bts, a.MaxHeight, a.MaxWidth)
	//if err != nil {
	//	return nil, me.Err(err, "failed to resize image", &me.KV{"url", url})
	//}

	bts = nil //release memory

	//	phash, w, h, err := a.phash(bytes.NewReader(resized))
	//	if err != nil {
	//		me.LogError(a.Log, "failed to generate phash on resized image", err)
	//	}

	resized, w, h, err := a.resize(bytes.NewReader(bts), a.MaxHeight, a.MaxWidth)

	uploaded, err := a.upload(resized, filename)
	if err != nil {
		return nil, me.Err(err, "failed up to upload resized image",
			&me.KV{"url", url},
			&me.KV{"filename", filename})
	}
	return &AcquiredImage{
		DestUrl:  uploaded.Url,
		DestSize: uploaded.FileSize,
		//	PHash:             phash,
		DestHeight:        h,
		DestWidth:         w,
		SourceUrl:         url,
		SourceContentType: ctype,
		SourceSize:        sourceSize,
	}, nil
}

func (a *AcquireImage) download(url string, timeout time.Duration) ([]byte, string, error) {
	client := http.Client{
		Timeout: timeout,
	}

	resp, err := client.Get(url)
	if err != nil {
		return nil, "", me.Err(err, "failed to get URL ")
	}
	defer resp.Body.Close()
	ct := strings.Join(resp.Header["Content-Type"], ",")
	bts, err := ioutil.ReadAll(resp.Body)
	return bts, ct, err
}

// phash - returns phash, w, h, err
//func (a *AcquireImage) phash(r io.Reader) (uint64, int, int error) {
//	img, err := magick.DecodeData(r)
//	if err != nil {
//		return 0, err
//	}
//	w := img.Width()
//	h := img.Height()
//	phash, err := img.Phash()
//	return phash, w, h, err
//}

func (a *AcquireImage) resizeVips(img []byte, maxWidth, maxHeight int) ([]byte, error) {
	options := vips.Options{
		Width:        maxWidth,
		Height:       maxHeight,
		Crop:         false,
		Enlarge:      false,
		Extend:       vips.EXTEND_WHITE,
		Interpolator: vips.BILINEAR,
		Gravity:      vips.CENTRE,
		Quality:      95,
	}

	return vips.Resize(img, options)
}

func (a *AcquireImage) resize(imgR io.Reader, maxWidth, maxHeight int) (io.Reader, int, int, error) {

	img, err := imaging.Decode(imgR)
	if err != nil {
		return nil, 0, 0, err
	}

	thumb := imaging.Fit(img, maxWidth, maxHeight, imaging.CatmullRom)

	height := thumb.Rect.Dy()
	width := thumb.Rect.Dx()

	buf := new(bytes.Buffer)
	err = imaging.Encode(buf, thumb, imaging.PNG)
	if err != nil {
		return nil, 0, 0, me.Err(err, "failed to encode")
	}
	return bytes.NewReader(buf.Bytes()), width, height, nil
}

func (a *AcquireImage) upload(source io.Reader, filename string) (*FileUploadOutput, error) {
	w, err := a.Uploader.Writer(filename)
	if err != nil {
		return nil, me.Err(err, "failed to get writer")
	}

	size, err := io.Copy(w, source)
	if err != nil {
		return nil, me.Err(err, "copy to upload fail")
	}

	if err = w.Close(); err != nil {
		return nil, me.Err(err, "close writer fail")
	}

	return &FileUploadOutput{
		Url:      a.Uploader.DestinationUrl(filename),
		FileSize: size,
	}, nil
}
<commit_msg>added error check<commit_after>package multi

import (
	"bytes"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	_ "golang.org/x/image/bmp"
	_ "golang.org/x/image/tiff"

	"github.com/daddye/vips"
	"github.com/disintegration/imaging"
	"github.com/gotgo/fw/logging"
	"github.com/gotgo/fw/me"
)

type AcquireImage struct {
	DownloadTimeout time.Duration
	MaxHeight       int
	MaxWidth        int
	Uploader        Uploader
	Log             logging.Logger
}

type AcquiredImage struct {
	SourceSize        int
	SourceUrl         string
	SourceContentType string

	DestUrl    string
	DestSize   int64
	DestHeight int
	DestWidth  int
	PHash      uint64
}

func (a *AcquireImage) timeout() time.Duration {
	if a.DownloadTimeout == time.Second*0 {
		return time.Second * 60
	}
	return a.DownloadTimeout
}

func (a *AcquireImage) Acquire(url, filename string) (*AcquiredImage, error) {
	//TODO: time
	bts, ctype, err := a.download(url, a.timeout())
	if err != nil {
		return nil, me.Err(err, "failed to download url", &me.KV{"url", url})
	}
	sourceSize := len(bts)
	//resized, err := a.resize(bts, a.MaxHeight, a.MaxWidth)
	//if err != nil {
	//	return nil, me.Err(err, "failed to resize image", &me.KV{"url", url})
	//}

	bts = nil //release memory

	//	phash, w, h, err := a.phash(bytes.NewReader(resized))
	//	if err != nil {
	//		me.LogError(a.Log, "failed to generate phash on resized image", err)
	//	}

	resized, w, h, err := a.resize(bytes.NewReader(bts), a.MaxHeight, a.MaxWidth)
	if err != nil {
		return nil, me.Err(err, "failed to resize", &me.KV{"url", url})
	}

	uploaded, err := a.upload(resized, filename)
	if err != nil {
		return nil, me.Err(err, "failed up to upload resized image",
			&me.KV{"url", url},
			&me.KV{"filename", filename})
	}
	return &AcquiredImage{
		DestUrl:  uploaded.Url,
		DestSize: uploaded.FileSize,
		//	PHash:             phash,
		DestHeight:        h,
		DestWidth:         w,
		SourceUrl:         url,
		SourceContentType: ctype,
		SourceSize:        sourceSize,
	}, nil
}

func (a *AcquireImage) download(url string, timeout time.Duration) ([]byte, string, error) {
	client := http.Client{
		Timeout: timeout,
	}

	resp, err := client.Get(url)
	if err != nil {
		return nil, "", me.Err(err, "failed to get URL ")
	}
	defer resp.Body.Close()
	ct := strings.Join(resp.Header["Content-Type"], ",")
	bts, err := ioutil.ReadAll(resp.Body)
	return bts, ct, err
}

// phash - returns phash, w, h, err
//func (a *AcquireImage) phash(r io.Reader) (uint64, int, int error) {
//	img, err := magick.DecodeData(r)
//	if err != nil {
//		return 0, err
//	}
//	w := img.Width()
//	h := img.Height()
//	phash, err := img.Phash()
//	return phash, w, h, err
//}

func (a *AcquireImage) resizeVips(img []byte, maxWidth, maxHeight int) ([]byte, error) {
	options := vips.Options{
		Width:        maxWidth,
		Height:       maxHeight,
		Crop:         false,
		Enlarge:      false,
		Extend:       vips.EXTEND_WHITE,
		Interpolator: vips.BILINEAR,
		Gravity:      vips.CENTRE,
		Quality:      95,
	}

	return vips.Resize(img, options)
}

func (a *AcquireImage) resize(imgR io.Reader, maxWidth, maxHeight int) (io.Reader, int, int, error) {

	img, err := imaging.Decode(imgR)
	if err != nil {
		return nil, 0, 0, err
	}

	thumb := imaging.Fit(img, maxWidth, maxHeight, imaging.CatmullRom)

	height := thumb.Rect.Dy()
	width := thumb.Rect.Dx()

	buf := new(bytes.Buffer)
	err = imaging.Encode(buf, thumb, imaging.PNG)
	if err != nil {
		return nil, 0, 0, me.Err(err, "failed to encode")
	}
	return bytes.NewReader(buf.Bytes()), width, height, nil
}

func (a *AcquireImage) upload(source io.Reader, filename string) (*FileUploadOutput, error) {
	w, err := a.Uploader.Writer(filename)
	if err != nil {
		return nil, me.Err(err, "failed to get writer")
	}

	size, err := io.Copy(w, source)
	if err != nil {
		return nil, me.Err(err, "copy to upload fail")
	}

	if err = w.Close(); err != nil {
		return nil, me.Err(err, "close writer fail")
	}

	return &FileUploadOutput{
		Url:      a.Uploader.DestinationUrl(filename),
		FileSize: size,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright c 2017 tadaken3 <k.tanaka6057@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"fmt"
	"os"
	"runtime"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string
var message string
var token string

var RootCmd = &cobra.Command{
	Use:   "notify",
	Short: `This application can notify any chat serviece`,
	Long:  `This application is a simple CLI tool. You can quickly notify to any chat serviece`,

	//Run: func(cmd *cobra.Command, args []string) {
	//	fmt.Printf("Usage: notify <command> [<args>]")
	//	},
}

func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	RootCmd.PersistentFlags().StringVarP(&message, "message", "m", "This message is from notify", "message")
	RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.notify.yaml)")
	RootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "access token")
	//viper.BindPFlag("token", RootCmd.PersistentFlags().Lookup("token"))
	//RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func initConfig() {
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".notify")
	if runntime.GOOS == "windows" {
		viper.AddConfigPath(os.Getenv("HOMEPATH"))
	} else {
		viper.AddConfigPath(os.Getenv("HOME"))
	}

	viper.AutomaticEnv()

	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
<commit_msg>modify for windows<commit_after>// Copyright c 2017 tadaken3 <k.tanaka6057@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"fmt"
	"os"
	"runtime"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cfgFile string
var message string
var token string

var RootCmd = &cobra.Command{
	Use:   "notify",
	Short: `This application can notify any chat serviece`,
	Long:  `This application is a simple CLI tool. You can quickly notify to any chat serviece`,

	//Run: func(cmd *cobra.Command, args []string) {
	//	fmt.Printf("Usage: notify <command> [<args>]")
	//	},
}

func Execute() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	RootCmd.PersistentFlags().StringVarP(&message, "message", "m", "This message is from notify", "message")
	RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.notify.yaml)")
	RootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "access token")
	//viper.BindPFlag("token", RootCmd.PersistentFlags().Lookup("token"))
	//RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func initConfig() {
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".notify")
	if runtime.GOOS == "windows" {
		viper.AddConfigPath(os.Getenv("HOMEPATH"))
	} else {
		viper.AddConfigPath(os.Getenv("HOME"))
	}

	viper.AutomaticEnv()

	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"log"
	"os"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "mocli",
	Short: "",
	Long:  `Mobingi API command line interface.`,
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		log.Println(err)
		os.Exit(-1)
	}
}

func init() {
	rootCmd.PersistentFlags().StringP("token", "t", "", "access token for API access")
}
<commit_msg>Add short description for help.<commit_after>package cmd

import (
	"log"
	"os"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "mocli",
	Short: "Mobingi API command line interface.",
	Long:  `Mobingi API command line interface.`,
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		log.Println(err)
		os.Exit(-1)
	}
}

func init() {
	rootCmd.PersistentFlags().StringP("token", "t", "", "access token for API access")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for i, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &ps[i])
		} else {
			normalProblems = append(normalProblems, &ps[i])
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if v == fileName {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnore(path) {
			checkFile(path)
		}
		return err
	})
}
<commit_msg>add ignore path pattern check<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for i, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &ps[i])
		} else {
			normalProblems = append(normalProblems, &ps[i])
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if ok, _ := filepath.Match(v, fileName); ok {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnore(path) {
			checkFile(path)
		}
		return err
	})
}
<|endoftext|>


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

package cmd

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

	"github.com/Unknwon/com"
	"github.com/urfave/cli"
	log "gopkg.in/clog.v1"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/models/errors"
	"github.com/gogits/gogs/pkg/setting"
	http "github.com/gogits/gogs/routers/repo"
	"syscall"
)

const (
	_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
)

var Serv = cli.Command{
	Name:        "serv",
	Usage:       "This command should only be called by SSH shell",
	Description: `Serv provide access auth for repositories`,
	Action:      runServ,
	Flags: []cli.Flag{
		stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
	},
}

func fail(userMessage, logMessage string, args ...interface{}) {
	fmt.Fprintln(os.Stderr, "Gin:", userMessage)

	if len(logMessage) > 0 {
		if !setting.ProdMode {
			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
		}
		log.Fatal(3, logMessage, args...)
	}

	os.Exit(1)
}

func setup(c *cli.Context, logPath string, connectDB bool) {
	if c.IsSet("config") {
		setting.CustomConf = c.String("config")
	} else if c.GlobalIsSet("config") {
		setting.CustomConf = c.GlobalString("config")
	}

	setting.NewContext()

	level := log.TRACE
	if setting.ProdMode {
		level = log.ERROR
	}
	log.New(log.FILE, log.FileConfig{
		Level:    level,
		Filename: filepath.Join(setting.LogRootPath, logPath),
		FileRotationConfig: log.FileRotationConfig{
			Rotate:  true,
			Daily:   true,
			MaxDays: 3,
		},
	})
	log.Delete(log.CONSOLE) // Remove primary logger

	if !connectDB {
		return
	}

	models.LoadConfigs()

	if setting.UseSQLite3 {
		workDir, _ := setting.WorkDir()
		os.Chdir(workDir)
	}

	if err := models.SetEngine(); err != nil {
		fail("Internal error", "SetEngine: %v", err)
	}
}

func isAnnexShell(cmd string) bool {
	return cmd == "git-annex-shell"
}

func parseSSHCmd(cmd string) (string, string, []string) {
	ss := strings.Split(cmd, " ")
	if len(ss) < 2 {
		return "", "", nil
	}
	if isAnnexShell(ss[0]) {
		return ss[0], strings.Replace(ss[2], "/", "'", 1), ss
	} else {
		return ss[0], strings.Replace(ss[1], "/", "'", 1), ss
	}
}

func checkDeployKey(key *models.PublicKey, repo *models.Repository) {
	// Check if this deploy key belongs to current repository.
	if !models.HasDeployKey(key.ID, repo.ID) {
		fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
	}

	// Update deploy key activity.
	deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
	if err != nil {
		fail("Internal error", "GetDeployKey: %v", err)
	}

	deployKey.Updated = time.Now()
	if err = models.UpdateDeployKey(deployKey); err != nil {
		fail("Internal error", "UpdateDeployKey: %v", err)
	}
}

var (
	allowedCommands = map[string]models.AccessMode{
		"git-upload-pack":    models.ACCESS_MODE_READ,
		"git-upload-archive": models.ACCESS_MODE_READ,
		"git-receive-pack":   models.ACCESS_MODE_WRITE,
		"git-annex-shell":    models.ACCESS_MODE_READ,
	}
)

func runServ(c *cli.Context) error {
	setup(c, "serv.log", true)

	if setting.SSH.Disabled {
		println("Gins: SSH has been disabled")
		return nil
	}

	if len(c.Args()) < 1 {
		fail("Not enough arguments", "Not enough arguments")
	}

	sshCmd := strings.Replace(os.Getenv("SSH_ORIGINAL_COMMAND"), "'", "", -1)
	log.Info("SSH commadn:%s", sshCmd)
	if len(sshCmd) == 0 {
		println("Hi there, You've successfully authenticated, but Gin does not provide shell access.")
		return nil
	}

	verb, path, args := parseSSHCmd(sshCmd)
	repoFullName := strings.ToLower(strings.Trim(path, "'"))
	repoFields := strings.SplitN(repoFullName, "/", 2)
	if len(repoFields) != 2 {
		fail("Invalid repository path", "Invalid repository path: %v", path)
	}
	ownerName := strings.ToLower(repoFields[0])
	repoName := strings.TrimSuffix(strings.ToLower(repoFields[1]), ".git")
	repoName = strings.TrimSuffix(repoName, ".wiki")

	owner, err := models.GetUserByName(ownerName)
	if err != nil {
		if errors.IsUserNotExist(err) {
			fail("Repository owner does not exist", "Unregistered owner: %s", ownerName)
		}
		fail("Internal error", "Fail to get repository owner '%s': %v", ownerName, err)
	}

	repo, err := models.GetRepositoryByName(owner.ID, repoName)
	if err != nil {
		if errors.IsRepoNotExist(err) {
			fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", owner.Name, repoName)
		}
		fail("Internal error", "Fail to get repository: %v", err)
	}
	repo.Owner = owner

	requestMode, ok := allowedCommands[verb]
	if !ok {
		fail("Unknown git command", "Unknown git command '%s'", verb)
	}

	// Prohibit push to mirror repositories.
	if requestMode > models.ACCESS_MODE_READ && repo.IsMirror {
		fail("Mirror repository is read-only", "")
	}

	// Allow anonymous (user is nil) clone for public repositories.
	var user *models.User

	key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
	if err != nil {
		fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
	}

	if requestMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
		// Check deploy key or user key.
		if key.IsDeployKey() {
			if key.Mode < requestMode {
				fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
			}
			checkDeployKey(key, repo)
		} else {
			user, err = models.GetUserByKeyID(key.ID)
			if err != nil {
				fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
			}

			mode, err := models.AccessLevel(user.ID, repo)
			if err != nil {
				fail("Internal error", "Fail to check access: %v", err)
			}

			if mode < requestMode {
				clientMessage := _ACCESS_DENIED_MESSAGE
				if mode >= models.ACCESS_MODE_READ {
					clientMessage = "You do not have sufficient authorization for this action"
				}
				fail(clientMessage,
					"User '%s' does not have level '%v' access to repository '%s'",
					user.Name, requestMode, repoFullName)
			}
		}
	} else {
		setting.NewService()
		// Check if the key can access to the repository in case of it is a deploy key (a deploy keys != user key).
		// A deploy key doesn't represent a signed in user, so in a site with Service.RequireSignInView activated
		// we should give read access only in repositories where this deploy key is in use. In other case, a server
		// or system using an active deploy key can get read access to all the repositories in a Gogs service.
		if key.IsDeployKey() && setting.Service.RequireSignInView {
			checkDeployKey(key, repo)
		}
	}

	// Update user key activity.
	if key.ID > 0 {
		key, err := models.GetPublicKeyByID(key.ID)
		if err != nil {
			fail("Internal error", "GetPublicKeyByID: %v", err)
		}

		key.Updated = time.Now()
		if err = models.UpdatePublicKey(key); err != nil {
			fail("Internal error", "UpdatePublicKey: %v", err)
		}
	}

	// Special handle for Windows.
	// Todo will break with annex
	if setting.IsWindows {
		verb = strings.Replace(verb, "-", " ", 1)
	}
	verbs := strings.Split(verb, " ")
	var cmd []string
	if len(verbs) == 2 {
		cmd = []string{verbs[0], verbs[1], repoFullName}
	} else if isAnnexShell(verb) {
		repoAbsPath := setting.RepoRootPath + "/" + repoFullName
		if err := secureGitAnnex(repoAbsPath, user, repo); err != nil {
			fail("Git annex failed", "Git annex failed: %s", err)
		}
		cmd = args
		// Setting full path to repo as git-annex-shell requires it
		cmd[2] = repoAbsPath
	} else {
		cmd = []string{verb, repoFullName}
	}
	runGit(cmd, requestMode, user, owner, repo)
	return nil

}

func runGit(cmd [] string, requestMode models.AccessMode, user *models.User, owner *models.User,
	repo *models.Repository) error {
	log.Info("will exectute:%s", cmd)
	gitCmd := exec.Command(cmd[0], cmd[1:]...)
	if requestMode == models.ACCESS_MODE_WRITE {
		gitCmd.Env = append(os.Environ(), http.ComposeHookEnvs(http.ComposeHookEnvsOptions{
			AuthUser:  user,
			OwnerName: owner.Name,
			OwnerSalt: owner.Salt,
			RepoID:    repo.ID,
			RepoName:  repo.Name,
			RepoPath:  repo.RepoPath(),
		})...)
	}
	gitCmd.Dir = setting.RepoRootPath
	gitCmd.Stdout = os.Stdout
	gitCmd.Stdin = os.Stdin
	gitCmd.Stderr = os.Stderr
	log.Info("args:%s", gitCmd.Args)
	err := gitCmd.Run()
	log.Info("err:%s", err)
	if t, ok := err.(*exec.ExitError); ok {
		log.Info("t:%s", t)
		os.Exit(t.Sys().(syscall.WaitStatus).ExitStatus())
	}

	return nil
}

// Make sure git-annex-shell does not make "bad" changes (refectored from repo)
func secureGitAnnex(path string, user *models.User, repo *models.Repository) error {
	// "If set, disallows running git-shell to handle unknown commands."
	err := os.Setenv("GIT_ANNEX_SHELL_LIMITED", "True")
	if err != nil {
		return fmt.Errorf("ERROR: Could set annex shell to be limited.")
	}
	// "If set, git-annex-shell will refuse to run commands
	//  that do not operate on the specified directory."
	err = os.Setenv("GIT_ANNEX_SHELL_DIRECTORY", path)
	if err != nil {
		return fmt.Errorf("ERROR: Could set annex shell directory.")
	}
	mode, err := models.AccessLevel(user.ID, repo)
	if err != nil {
		fail("Internal error", "Fail to check access: %v", err)
	}

	if mode < models.ACCESS_MODE_WRITE {
		err = os.Setenv("GIT_ANNEX_SHELL_READONLY", "True")
		if err != nil {
			return fmt.Errorf("ERROR: Could set annex shell to read only.")
		}
	}
	return nil
}
<commit_msg>[annex] fix anon user bug<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package cmd

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

	"github.com/Unknwon/com"
	"github.com/urfave/cli"
	log "gopkg.in/clog.v1"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/models/errors"
	"github.com/gogits/gogs/pkg/setting"
	http "github.com/gogits/gogs/routers/repo"
	"syscall"
)

const (
	_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
)

var Serv = cli.Command{
	Name:        "serv",
	Usage:       "This command should only be called by SSH shell",
	Description: `Serv provide access auth for repositories`,
	Action:      runServ,
	Flags: []cli.Flag{
		stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
	},
}

func fail(userMessage, logMessage string, args ...interface{}) {
	fmt.Fprintln(os.Stderr, "Gin:", userMessage)

	if len(logMessage) > 0 {
		if !setting.ProdMode {
			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
		}
		log.Fatal(3, logMessage, args...)
	}

	os.Exit(1)
}

func setup(c *cli.Context, logPath string, connectDB bool) {
	if c.IsSet("config") {
		setting.CustomConf = c.String("config")
	} else if c.GlobalIsSet("config") {
		setting.CustomConf = c.GlobalString("config")
	}

	setting.NewContext()

	level := log.TRACE
	if setting.ProdMode {
		level = log.ERROR
	}
	log.New(log.FILE, log.FileConfig{
		Level:    level,
		Filename: filepath.Join(setting.LogRootPath, logPath),
		FileRotationConfig: log.FileRotationConfig{
			Rotate:  true,
			Daily:   true,
			MaxDays: 3,
		},
	})
	log.Delete(log.CONSOLE) // Remove primary logger

	if !connectDB {
		return
	}

	models.LoadConfigs()

	if setting.UseSQLite3 {
		workDir, _ := setting.WorkDir()
		os.Chdir(workDir)
	}

	if err := models.SetEngine(); err != nil {
		fail("Internal error", "SetEngine: %v", err)
	}
}

func isAnnexShell(cmd string) bool {
	return cmd == "git-annex-shell"
}

func parseSSHCmd(cmd string) (string, string, []string) {
	ss := strings.Split(cmd, " ")
	if len(ss) < 2 {
		return "", "", nil
	}
	if isAnnexShell(ss[0]) {
		return ss[0], strings.Replace(ss[2], "/", "'", 1), ss
	} else {
		return ss[0], strings.Replace(ss[1], "/", "'", 1), ss
	}
}

func checkDeployKey(key *models.PublicKey, repo *models.Repository) {
	// Check if this deploy key belongs to current repository.
	if !models.HasDeployKey(key.ID, repo.ID) {
		fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
	}

	// Update deploy key activity.
	deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
	if err != nil {
		fail("Internal error", "GetDeployKey: %v", err)
	}

	deployKey.Updated = time.Now()
	if err = models.UpdateDeployKey(deployKey); err != nil {
		fail("Internal error", "UpdateDeployKey: %v", err)
	}
}

var (
	allowedCommands = map[string]models.AccessMode{
		"git-upload-pack":    models.ACCESS_MODE_READ,
		"git-upload-archive": models.ACCESS_MODE_READ,
		"git-receive-pack":   models.ACCESS_MODE_WRITE,
		"git-annex-shell":    models.ACCESS_MODE_READ,
	}
)

func runServ(c *cli.Context) error {
	setup(c, "serv.log", true)

	if setting.SSH.Disabled {
		println("Gins: SSH has been disabled")
		return nil
	}

	if len(c.Args()) < 1 {
		fail("Not enough arguments", "Not enough arguments")
	}

	sshCmd := strings.Replace(os.Getenv("SSH_ORIGINAL_COMMAND"), "'", "", -1)
	log.Info("SSH commadn:%s", sshCmd)
	if len(sshCmd) == 0 {
		println("Hi there, You've successfully authenticated, but Gin does not provide shell access.")
		return nil
	}

	verb, path, args := parseSSHCmd(sshCmd)
	repoFullName := strings.ToLower(strings.Trim(path, "'"))
	repoFields := strings.SplitN(repoFullName, "/", 2)
	if len(repoFields) != 2 {
		fail("Invalid repository path", "Invalid repository path: %v", path)
	}
	ownerName := strings.ToLower(repoFields[0])
	repoName := strings.TrimSuffix(strings.ToLower(repoFields[1]), ".git")
	repoName = strings.TrimSuffix(repoName, ".wiki")

	owner, err := models.GetUserByName(ownerName)
	if err != nil {
		if errors.IsUserNotExist(err) {
			fail("Repository owner does not exist", "Unregistered owner: %s", ownerName)
		}
		fail("Internal error", "Fail to get repository owner '%s': %v", ownerName, err)
	}

	repo, err := models.GetRepositoryByName(owner.ID, repoName)
	if err != nil {
		if errors.IsRepoNotExist(err) {
			fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", owner.Name, repoName)
		}
		fail("Internal error", "Fail to get repository: %v", err)
	}
	repo.Owner = owner

	requestMode, ok := allowedCommands[verb]
	if !ok {
		fail("Unknown git command", "Unknown git command '%s'", verb)
	}

	// Prohibit push to mirror repositories.
	if requestMode > models.ACCESS_MODE_READ && repo.IsMirror {
		fail("Mirror repository is read-only", "")
	}

	// Allow anonymous (user is nil) clone for public repositories.
	var user *models.User

	key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
	if err != nil {
		fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
	}

	if us, err := models.GetUserByKeyID(key.ID); err == nil {
		user = us
	} else {
		fail("Key Error", "Cannot find key %v", err)
	}

	if requestMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
		// Check deploy key or user key.
		if key.IsDeployKey() {
			if key.Mode < requestMode {
				fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
			}
			checkDeployKey(key, repo)
		} else {
			user, err = models.GetUserByKeyID(key.ID)
			if err != nil {
				fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
			}

			mode, err := models.AccessLevel(user.ID, repo)
			if err != nil {
				fail("Internal error", "Fail to check access: %v", err)
			}

			if mode < requestMode {
				clientMessage := _ACCESS_DENIED_MESSAGE
				if mode >= models.ACCESS_MODE_READ {
					clientMessage = "You do not have sufficient authorization for this action"
				}
				fail(clientMessage,
					"User '%s' does not have level '%v' access to repository '%s'",
					user.Name, requestMode, repoFullName)
			}
		}
	} else {
		setting.NewService()
		// Check if the key can access to the repository in case of it is a deploy key (a deploy keys != user key).
		// A deploy key doesn't represent a signed in user, so in a site with Service.RequireSignInView activated
		// we should give read access only in repositories where this deploy key is in use. In other case, a server
		// or system using an active deploy key can get read access to all the repositories in a Gogs service.
		if key.IsDeployKey() && setting.Service.RequireSignInView {
			checkDeployKey(key, repo)
		}
	}

	// Update user key activity.
	if key.ID > 0 {
		key, err := models.GetPublicKeyByID(key.ID)
		if err != nil {
			fail("Internal error", "GetPublicKeyByID: %v", err)
		}

		key.Updated = time.Now()
		if err = models.UpdatePublicKey(key); err != nil {
			fail("Internal error", "UpdatePublicKey: %v", err)
		}
	}

	// Special handle for Windows.
	// Todo will break with annex
	if setting.IsWindows {
		verb = strings.Replace(verb, "-", " ", 1)
	}
	verbs := strings.Split(verb, " ")
	var cmd []string
	if len(verbs) == 2 {
		cmd = []string{verbs[0], verbs[1], repoFullName}
	} else if isAnnexShell(verb) {
		repoAbsPath := setting.RepoRootPath + "/" + repoFullName
		if err := secureGitAnnex(repoAbsPath, user, repo); err != nil {
			fail("Git annex failed", "Git annex failed: %s", err)
		}
		cmd = args
		// Setting full path to repo as git-annex-shell requires it
		cmd[2] = repoAbsPath
	} else {
		cmd = []string{verb, repoFullName}
	}
	runGit(cmd, requestMode, user, owner, repo)
	return nil

}

func runGit(cmd []string, requestMode models.AccessMode, user *models.User, owner *models.User,
	repo *models.Repository) error {
	log.Info("will exectute:%s", cmd)
	gitCmd := exec.Command(cmd[0], cmd[1:]...)
	if requestMode == models.ACCESS_MODE_WRITE {
		gitCmd.Env = append(os.Environ(), http.ComposeHookEnvs(http.ComposeHookEnvsOptions{
			AuthUser:  user,
			OwnerName: owner.Name,
			OwnerSalt: owner.Salt,
			RepoID:    repo.ID,
			RepoName:  repo.Name,
			RepoPath:  repo.RepoPath(),
		})...)
	}
	gitCmd.Dir = setting.RepoRootPath
	gitCmd.Stdout = os.Stdout
	gitCmd.Stdin = os.Stdin
	gitCmd.Stderr = os.Stderr
	log.Info("args:%s", gitCmd.Args)
	err := gitCmd.Run()
	log.Info("err:%s", err)
	if t, ok := err.(*exec.ExitError); ok {
		log.Info("t:%s", t)
		os.Exit(t.Sys().(syscall.WaitStatus).ExitStatus())
	}

	return nil
}

// Make sure git-annex-shell does not make "bad" changes (refectored from repo)
func secureGitAnnex(path string, user *models.User, repo *models.Repository) error {
	// "If set, disallows running git-shell to handle unknown commands."
	err := os.Setenv("GIT_ANNEX_SHELL_LIMITED", "True")
	if err != nil {
		return fmt.Errorf("ERROR: Could set annex shell to be limited.")
	}
	// "If set, git-annex-shell will refuse to run commands
	//  that do not operate on the specified directory."
	err = os.Setenv("GIT_ANNEX_SHELL_DIRECTORY", path)
	if err != nil {
		return fmt.Errorf("ERROR: Could set annex shell directory.")
	}
	mode := models.ACCESS_MODE_NONE
	if user != nil {
		mode, err = models.AccessLevel(user.ID, repo)
		if err != nil {
			fail("Internal error", "Fail to check access: %v", err)
		}
	}
	if mode < models.ACCESS_MODE_WRITE {
		err = os.Setenv("GIT_ANNEX_SHELL_READONLY", "True")
		if err != nil {
			return fmt.Errorf("ERROR: Could set annex shell to read only.")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 Toomore Chiang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"database/sql"
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/spf13/cobra"
	"github.com/toomore/mailbox/utils"
)

var (
	userDryRun *bool
	userConn   *sql.DB
)

type user struct {
	email  string
	groups string
	fname  string
	lname  string
}

func readCSV(path string) []user {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	data, err := csv.NewReader(file).ReadAll()
	if err != nil {
		log.Fatal(err)
	}
	result := make([]user, len(data[1:]))
	for i, v := range data[0] {
		switch v {
		case "email":
			for di, dv := range data[1:] {
				result[di].email = dv[i]
			}
		case "groups":
			for di, dv := range data[1:] {
				result[di].groups = dv[i]
			}
		case "f_name":
			for di, dv := range data[1:] {
				result[di].fname = dv[i]
			}
		case "l_name":
			for di, dv := range data[1:] {
				result[di].lname = dv[i]
			}
		}
	}
	return result
}

func insertInto(data []user) {
	stmt, err := userConn.Prepare(`INSERT INTO user(email,groups,f_name,l_name)
	                           VALUES(?,?,?,?) ON DUPLICATE KEY UPDATE f_name=?, l_name=?`)
	if err != nil {
		log.Fatal(err)
	}
	for _, v := range data {
		if result, err := stmt.Exec(v.email, v.groups, v.fname, v.lname, v.fname, v.lname); err == nil {
			insertID, _ := result.LastInsertId()
			rowAff, _ := result.RowsAffected()
			log.Println("LastInsertId", insertID, "RowsAffected", rowAff)
		} else {
			log.Println("[Err]", err)
		}
	}
}

func readUser(group string) {
	rows, err := userConn.Query(`SELECT id,email,f_name,l_name,alive,created FROM user WHERE groups=?`, group)
	defer rows.Close()
	if err != nil {
		log.Fatal(err)
	}
	var (
		id      string
		email   string
		fname   string
		lname   string
		created time.Time
		alive   int
	)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "id", "email", "fname", "lname", "alive", "created")
	for rows.Next() {
		if err := rows.Scan(&id, &email, &fname, &lname, &alive, &created); err != nil {
			log.Println(err)
		} else {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n", id, email, fname, lname, alive, created)
		}
	}
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "id", "email", "fname", "lname", "alive", "created")
	w.Flush()
}

var userCmd = &cobra.Command{
	Use:   "user",
	Short: "User info",
	Long:  `相關 user 的操作`,
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

var importCmd = &cobra.Command{
	Use:   "import [csv path ...]",
	Short: "Import user from csv",
	Long: `匯入使用者資訊，CSV 檔案需要 email, groups, f_name, l_name 欄位， 支援
多檔案匯入。`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		userConn = utils.GetConn()
	},
	Run: func(cmd *cobra.Command, args []string) {
		for n, path := range args {
			log.Printf(">>> Read csv[%d]: `%s`", n, path)
			if *userDryRun {
				log.Println(">>> Dry Run data")
				for i, v := range readCSV(path) {
					fmt.Printf("%d %+v\n", i, v)
				}
			} else {
				insertInto(readCSV(path))
			}
		}
	},
}

var showCmd = &cobra.Command{
	Use:   "show [groups ...]",
	Short: "Show users",
	Long:  "列出群組使用者名單。",
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		userConn = utils.GetConn()
	},
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			cmd.Help()
		} else {
			for _, g := range args {
				fmt.Printf("----- %s -----\n", g)
				readUser(g)
			}
		}
	},
}

func init() {
	userDryRun = importCmd.Flags().BoolP("dryRun", "d", false, "Dry run read csv data")

	RootCmd.AddCommand(userCmd)
	userCmd.AddCommand(importCmd, showCmd)
}
<commit_msg>[cmd] add `alive=1` at user<commit_after>// Copyright © 2017 Toomore Chiang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"database/sql"
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/spf13/cobra"
	"github.com/toomore/mailbox/utils"
)

var (
	userDryRun *bool
	userConn   *sql.DB
)

type user struct {
	email  string
	groups string
	fname  string
	lname  string
}

func readCSV(path string) []user {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	data, err := csv.NewReader(file).ReadAll()
	if err != nil {
		log.Fatal(err)
	}
	result := make([]user, len(data[1:]))
	for i, v := range data[0] {
		switch v {
		case "email":
			for di, dv := range data[1:] {
				result[di].email = dv[i]
			}
		case "groups":
			for di, dv := range data[1:] {
				result[di].groups = dv[i]
			}
		case "f_name":
			for di, dv := range data[1:] {
				result[di].fname = dv[i]
			}
		case "l_name":
			for di, dv := range data[1:] {
				result[di].lname = dv[i]
			}
		}
	}
	return result
}

func insertInto(data []user) {
	stmt, err := userConn.Prepare(`INSERT INTO user(email,groups,f_name,l_name)
	                           VALUES(?,?,?,?) ON DUPLICATE KEY UPDATE f_name=?, l_name=?`)
	if err != nil {
		log.Fatal(err)
	}
	for _, v := range data {
		if result, err := stmt.Exec(v.email, v.groups, v.fname, v.lname, v.fname, v.lname); err == nil {
			insertID, _ := result.LastInsertId()
			rowAff, _ := result.RowsAffected()
			log.Println("LastInsertId", insertID, "RowsAffected", rowAff)
		} else {
			log.Println("[Err]", err)
		}
	}
}

func readUser(group string) {
	rows, err := userConn.Query(`SELECT id,email,f_name,l_name,alive,created FROM user WHERE alive=1 AND groups=?`, group)
	defer rows.Close()
	if err != nil {
		log.Fatal(err)
	}
	var (
		id      string
		email   string
		fname   string
		lname   string
		created time.Time
		alive   int
	)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "id", "email", "fname", "lname", "alive", "created")
	for rows.Next() {
		if err := rows.Scan(&id, &email, &fname, &lname, &alive, &created); err != nil {
			log.Println(err)
		} else {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n", id, email, fname, lname, alive, created)
		}
	}
	fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "id", "email", "fname", "lname", "alive", "created")
	w.Flush()
}

var userCmd = &cobra.Command{
	Use:   "user",
	Short: "User info",
	Long:  `相關 user 的操作`,
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

var importCmd = &cobra.Command{
	Use:   "import [csv path ...]",
	Short: "Import user from csv",
	Long: `匯入使用者資訊，CSV 檔案需要 email, groups, f_name, l_name 欄位， 支援
多檔案匯入。`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		userConn = utils.GetConn()
	},
	Run: func(cmd *cobra.Command, args []string) {
		for n, path := range args {
			log.Printf(">>> Read csv[%d]: `%s`", n, path)
			if *userDryRun {
				log.Println(">>> Dry Run data")
				for i, v := range readCSV(path) {
					fmt.Printf("%d %+v\n", i, v)
				}
			} else {
				insertInto(readCSV(path))
			}
		}
	},
}

var showCmd = &cobra.Command{
	Use:   "show [groups ...]",
	Short: "Show users",
	Long:  "列出群組使用者名單。",
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		userConn = utils.GetConn()
	},
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			cmd.Help()
		} else {
			for _, g := range args {
				fmt.Printf("----- %s -----\n", g)
				readUser(g)
			}
		}
	},
}

func init() {
	userDryRun = importCmd.Flags().BoolP("dryRun", "d", false, "Dry run read csv data")

	RootCmd.AddCommand(userCmd)
	userCmd.AddCommand(importCmd, showCmd)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"
)

func echo(c net.Conn, shout string, delay time.Duration) {
	fmt.Fprintln(c, "\t", strings.ToUpper(shout))
	time.Sleep(delay)
	fmt.Fprintln(c, "\t", shout)
	time.Sleep(delay)
	fmt.Fprintln(c, "\t", strings.ToLower(shout))
}

func handleConn(c net.Conn) {
	wg := sync.WaitGroup{}
	ch := make(chan string)

	input := bufio.NewScanner(c)

	defer func() {
		c.Close()
		wg.Wait()
		close(ch)
	}()

	wg.Add(1)
	go func() {
		for input.Scan() {
			text := input.Text()
			if text != "" {
				ch <- text
			}
		}
		wg.Done()
	}()

	for {
		select {
		case <-time.After(10 * time.Second):
			log.Println("disconnect after 10s of silence")
			return
		case msg := <-ch:
			wg.Add(1)
			go func(message string) {
				echo(c, message, 1*time.Second)
				wg.Done()
			}(msg)
		}
	}
}

func main() {
	l, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Print(err)
			continue
		}
		go handleConn(conn)
	}
}
<commit_msg>[8.8] add flag duration and more clear log msg<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"
)

func echo(c net.Conn, shout string, delay time.Duration) {
	fmt.Fprintln(c, "\t", strings.ToUpper(shout))
	time.Sleep(delay)
	fmt.Fprintln(c, "\t", shout)
	time.Sleep(delay)
	fmt.Fprintln(c, "\t", strings.ToLower(shout))
}

func handleConn(c net.Conn, timeout time.Duration) {
	wg := sync.WaitGroup{}
	ch := make(chan string)

	input := bufio.NewScanner(c)

	defer func() {
		c.Close()
		wg.Wait()
		close(ch)
	}()

	wg.Add(1)
	go func() {
		for input.Scan() {
			text := input.Text()
			if text != "" {
				ch <- text
			}
		}
		wg.Done()
	}()

	for {
		select {
		case <-time.After(timeout):
			log.Printf("disconnecting %s after %s of silence\n",
				c.RemoteAddr().String(), timeout)
			return
		case msg := <-ch:
			wg.Add(1)
			go func(message string) {
				echo(c, message, 1*time.Second)
				wg.Done()
			}(msg)
		}
	}
}

func main() {
	timeout := flag.Duration("timeout",
		time.Duration(10*time.Second),
		"disconnect after seconds of silence, 10s by default")
	flag.Parse()

	l, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Print(err)
			continue
		}
		go handleConn(conn, *timeout)
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/sbinet/go-commander"
	"github.com/sbinet/go-flag"
)

func hwaf_make_cmd_init() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_init,
		UsageLine: "init [options] <workarea>",
		Short:     "initialize a new workarea",
		Long: `
init initializes a new workarea.

ex:
 $ hwaf init
 $ hwaf init .
 $ hwaf init my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-init", flag.ExitOnError),
	}
	cmd.Flag.Bool("q", false, "only print error and warning messages, all other output will be suppressed")
	cmd.Flag.String("name", "", "workarea/project name (default: directory-name)")
	return cmd
}

func hwaf_run_cmd_init(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := ""

	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	proj_name := cmd.Flag.Lookup("name").Value.Get().(string)
	if proj_name == "" {
		proj_name = filepath.Base(dirname)
	}

	if !quiet {
		fmt.Printf("%s: creating workarea [%s]...\n", n, dirname)
	}

	if !path_exists(dirname) {
		err = os.MkdirAll(dirname, 0700)
		handle_err(err)
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	// init a git repository in dirname
	if !quiet {
		fmt.Printf("%s: initialize git workarea repository...\n", n)
	}
	git := exec.Command("git", "init", ".")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// add hep-waf-tools
	if !quiet {
		fmt.Printf("%s: add .hwaf/tools...\n", n)
	}
	hwaf_tools_dir := filepath.Join("${HOME}", ".config", "hwaf", "tools")
	hwaf_tools_dir = os.ExpandEnv(hwaf_tools_dir)
	if !path_exists(hwaf_tools_dir) {
		git = exec.Command(
			"git", "clone",
			"git://github.com/mana-fwk/hep-waftools",
			hwaf_tools_dir,
		)
		if !quiet {
			git.Stdout = os.Stdout
			git.Stderr = os.Stderr
		}
		err = git.Run()
		handle_err(err)
	}
	if !path_exists(".hwaf") {
		err = os.MkdirAll(".hwaf", 0700)
		handle_err(err)
	}
	err = os.Symlink(hwaf_tools_dir, ".hwaf/tools")
	handle_err(err)

	git = exec.Command("git", "add", ".hwaf/tools")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// add template wscript
	if !quiet {
		fmt.Printf("%s: add top-level wscript...\n", n)
	}

	wscript_tmpl, err := os.Open(".hwaf/tools/hwaf-wscript")
	handle_err(err)
	defer wscript_tmpl.Close()

	wscript_b, err := ioutil.ReadAll(wscript_tmpl)
	handle_err(err)

	// replace 'hwaf-workarea' with workarea name
	wscript_s := strings.Replace(
		string(wscript_b),
		"APPNAME = 'hwaf-workarea'",
		fmt.Sprintf("APPNAME = '%s'", proj_name),
		-1)

	wscript, err := os.Create("wscript")
	handle_err(err)
	defer wscript.Close()

	_, err = io.WriteString(wscript, wscript_s)
	handle_err(err)
	handle_err(wscript.Sync())
	handle_err(wscript.Close())

	git = exec.Command("git", "add", "wscript")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// create 'pkg' directory
	if !path_exists("pkg") {
		err = os.MkdirAll("pkg", 0700)
		handle_err(err)
	}

	// commit
	if !quiet {
		fmt.Printf("%s: commit workarea...\n", n)
	}
	git = exec.Command(
		"git", "commit", "-m",
		fmt.Sprintf("init hwaf project [%s]", proj_name),
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: creating workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<commit_msg>init: add a default gitignore<commit_after>package main

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

	"github.com/sbinet/go-commander"
	"github.com/sbinet/go-flag"
)

func hwaf_make_cmd_init() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_init,
		UsageLine: "init [options] <workarea>",
		Short:     "initialize a new workarea",
		Long: `
init initializes a new workarea.

ex:
 $ hwaf init
 $ hwaf init .
 $ hwaf init my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-init", flag.ExitOnError),
	}
	cmd.Flag.Bool("q", false, "only print error and warning messages, all other output will be suppressed")
	cmd.Flag.String("name", "", "workarea/project name (default: directory-name)")
	return cmd
}

func hwaf_run_cmd_init(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := ""

	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	proj_name := cmd.Flag.Lookup("name").Value.Get().(string)
	if proj_name == "" {
		proj_name = filepath.Base(dirname)
	}

	if !quiet {
		fmt.Printf("%s: creating workarea [%s]...\n", n, dirname)
	}

	if !path_exists(dirname) {
		err = os.MkdirAll(dirname, 0700)
		handle_err(err)
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	// init a git repository in dirname
	if !quiet {
		fmt.Printf("%s: initialize git workarea repository...\n", n)
	}
	git := exec.Command("git", "init", ".")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// add hep-waf-tools
	if !quiet {
		fmt.Printf("%s: add .hwaf/tools...\n", n)
	}
	hwaf_tools_dir := filepath.Join("${HOME}", ".config", "hwaf", "tools")
	hwaf_tools_dir = os.ExpandEnv(hwaf_tools_dir)
	if !path_exists(hwaf_tools_dir) {
		git = exec.Command(
			"git", "clone",
			"git://github.com/mana-fwk/hep-waftools",
			hwaf_tools_dir,
		)
		if !quiet {
			git.Stdout = os.Stdout
			git.Stderr = os.Stderr
		}
		err = git.Run()
		handle_err(err)
	}
	if !path_exists(".hwaf") {
		err = os.MkdirAll(".hwaf", 0700)
		handle_err(err)
	}
	err = os.Symlink(hwaf_tools_dir, ".hwaf/tools")
	handle_err(err)

	git = exec.Command("git", "add", ".hwaf/tools")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// add template wscript
	if !quiet {
		fmt.Printf("%s: add top-level wscript...\n", n)
	}

	wscript_tmpl, err := os.Open(".hwaf/tools/hwaf-wscript")
	handle_err(err)
	defer wscript_tmpl.Close()

	wscript_b, err := ioutil.ReadAll(wscript_tmpl)
	handle_err(err)

	// replace 'hwaf-workarea' with workarea name
	wscript_s := strings.Replace(
		string(wscript_b),
		"APPNAME = 'hwaf-workarea'",
		fmt.Sprintf("APPNAME = '%s'", proj_name),
		-1)

	wscript, err := os.Create("wscript")
	handle_err(err)
	defer wscript.Close()

	_, err = io.WriteString(wscript, wscript_s)
	handle_err(err)
	handle_err(wscript.Sync())
	handle_err(wscript.Close())

	git = exec.Command("git", "add", "wscript")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// create 'pkg' directory
	if !path_exists("pkg") {
		err = os.MkdirAll("pkg", 0700)
		handle_err(err)
	}

	// add a default .gitignore
	gitignore_tmpl, err := os.Open(".hwaf/tools/.gitignore")
	handle_err(err)
	defer gitignore_tmpl.Close()

	gitignore, err := os.Create(".gitignore")
	handle_err(err)
	defer gitignore.Close()

	_, err = io.Copy(gitignore, gitignore_tmpl)
	handle_err(err)
	handle_err(gitignore.Sync())
	handle_err(gitignore.Close())

	git = exec.Command("git", "add", ".gitignore")
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	// commit
	if !quiet {
		fmt.Printf("%s: commit workarea...\n", n)
	}
	git = exec.Command(
		"git", "commit", "-m",
		fmt.Sprintf("init hwaf project [%s]", proj_name),
	)
	if !quiet {
		git.Stdout = os.Stdout
		git.Stderr = os.Stderr
	}
	err = git.Run()
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: creating workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<|endoftext|>


<commit_before>// Package errs improves on the standard `error` by encapsulating stack traces, timestamps,
// optional internal information, and optional public user-facing messages.
//
// Usage
//
// Create an empty error with stack trace and timestamp:
//
//  err := errs.New(nil)
//  err.Time() // time.Time at time of creation
//  err.Stack() // output from debug.Stack() at time of creation
//
// Create an error with associated internal info and a user-facing message:
//
//  userEmail := "user@example.com"
//  emailExists := checkIfEmailExists(userEmail)
//  if emailExists {
//    err := errs.New(errs.Info{ "Email":userEmail }, userEmail, "is already taken. Try another!")
//    return err
//  }
//  ...
//  err.Info("Email") // "user@example.com"
//  err.PublicMsg() // "user@example.com is already taken. Try another!"
//
// Wrap a standard error:
//
//  stdErr := errors.New("An error")
//  err := errs.Wrap(stdErr, errs.Info{"Foo": "Bar"}, "User message")
//  ...
//  err.StdError() // stdErr
package errs

import (
	"fmt"
	"runtime/debug"
	"time"
)

type Err interface {
	// Error is an alias for LogString.
	// (errs.Err implements the error interface).
	Error() string

	// Stack returns the result of debug.Stack() from the time when this Err was created.
	Stack() []byte

	// Time returns the time.Time at which this Err was created.
	Time() time.Time

	// If errs.Wrap was used then WrappedError returns the wrapped error.
	WrappedError() error

	// If errs.Wrap or errs.New was called with any publicMsg values
	// then PublicMsg returns a string representation of those values.
	// This is useful for bubbling up user-facing message strings,
	// e.g `errs.New(nil, userEmail, "is already taken. Try another!")`
	PublicMsg() string

	// If errs.Wrap or errs.New was called with an errs.Info object
	// then Info("Foo") return the value of errs.Info{"Foo":...}
	// This is useful for bubbling up internal-facing info,
	// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
	Info(name string) interface{}

	// AllInfo returns all info key-value-pairs passed through errs.New or errs.Wrap
	AllInfo() Info

	// LogString returns a string suitable for logging
	LogString() string
}

// New creates a new Err with the given Info and optional public message
func New(info Info, publicMsg ...interface{}) Err {
	return newErr(nil, info, publicMsg)
}

// Wrap creates a new Err with the given standard error, Info, and optional public message.
// If stdErr is nil, Wrap returns nil.
func Wrap(stdErr error, info Info, publicMsg ...interface{}) Err {
	if stdErr == nil {
		return nil
	}
	if errsErr, isErr := IsErr(stdErr); isErr {
		if errs_err, isErrsErr := errsErr.(*err); isErrsErr {
			errs_err.mergeIn(info, publicMsg)
			return errs_err
		}
		return errsErr
	}
	return newErr(stdErr, info, publicMsg)
}

// Info allows for associating internal info with an error,
// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
type Info map[string]interface{}

// IsErr checks if stdErr is an Err, and return it as an Err if it is.
// This is equivalent to stdErr.(errs.Err)
func IsErr(stdErr error) (err Err, isErr bool) {
	err, isErr = stdErr.(Err)
	return
}

// Internal
///////////

// err implements Err
type err struct {
	stack      []byte
	time       time.Time
	wrappedErr error
	info       Info
	publicMsg  string
}

func newErr(stdErr error, info Info, publicMsgParts []interface{}) Err {
	publicMsg := fmt.Sprint(publicMsgParts...)
	stack := debug.Stack()
	return &err{stack, time.Now(), stdErr, info, publicMsg}
}

// Implements Err
func (e *err) Stack() []byte       { return e.stack }
func (e *err) Time() time.Time     { return e.time }
func (e *err) WrappedError() error { return e.wrappedErr }
func (e *err) PublicMsg() string   { return e.publicMsg }
func (e *err) Error() string       { return e.LogString() }
func (e *err) String() string      { return e.LogString() }
func (e *err) AllInfo() Info       { return e.info }

// Implements Err
func (e *err) Info(key string) interface{} {
	if e.info == nil {
		return nil
	}
	return e.info[key]
}

// Implements Err
func (e *err) LogString() string {
	return fmt.Sprint("Error",
		"| Time:", e.time,
		"| Stack:", string(e.stack),
		"| StdError:", e.wrappedErrStr(),
		"| Info:["+fmt.Sprint(e.info)+"]",
		"| PublicMsg:", e.publicMsg,
	)
}

// Merge in the given info and public message parts into this error
func (e *err) mergeIn(info Info, publicMsgParts []interface{}) {
	for key, val := range info {
		for e.info[key] != nil {
			key = key + "_duplicate"
		}
		e.info[key] = val
	}
	e.publicMsg = fmt.Sprint(publicMsgParts...) + " - " + e.publicMsg
}

// Get the string representation of the stdErr, or an empty string if stdErr is nil
func (e *err) wrappedErrStr() string {
	if e == nil {
		return ""
	}
	if e.wrappedErr == nil {
		return ""
	}
	return e.wrappedErr.Error()
}
<commit_msg>Improve documentation<commit_after>// Package errs improves on the standard `error` by encapsulating stack traces, timestamps,
// optional internal information, and optional public user-facing messages.
//
// Usage
//
// Create an empty error with stack trace and timestamp:
//
//  err := errs.New(nil)
//  err.Time() // time.Time at time of creation
//  err.Stack() // output from debug.Stack() at time of creation
//
// Create an error with associated internal info and a user-facing message:
//
//  userEmail := "user@example.com"
//  emailExists := checkIfEmailExists(userEmail)
//  if emailExists {
//    err := errs.New(errs.Info{ "Email":userEmail }, userEmail, "is already taken. Try another!")
//    return err
//  }
//  ...
//  err.Info("Email") // "user@example.com"
//  err.PublicMsg() // "user@example.com is already taken. Try another!"
//
// Wrap a standard error:
//
//  err := errors.New("An error")
//  err = errs.Wrap(err, errs.Info{"Foo": "Bar"}, "User message")
//  ...
//  err.WrappedErr().Error() == "An error"
package errs

import (
	"fmt"
	"runtime/debug"
	"time"
)

type Err interface {
	// Error is an alias for LogString.
	// (errs.Err implements the error interface).
	Error() string

	// Stack returns the result of debug.Stack() from the time when this Err was created.
	Stack() []byte

	// Time returns the time.Time at which this Err was created.
	Time() time.Time

	// If errs.Wrap was used then WrappedError returns the wrapped error.
	WrappedError() error

	// If errs.Wrap or errs.New was called with any publicMsg values
	// then PublicMsg returns a string representation of those values.
	// This is useful for bubbling up user-facing message strings,
	// e.g `errs.New(nil, userEmail, "is already taken. Try another!")`
	PublicMsg() string

	// If errs.Wrap or errs.New was called with an errs.Info object
	// then Info("Foo") return the value of errs.Info{"Foo":...}
	// This is useful for bubbling up internal-facing info,
	// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
	Info(name string) interface{}

	// AllInfo returns all info key-value-pairs passed through errs.New or errs.Wrap
	AllInfo() Info

	// LogString returns a string suitable for logging
	LogString() string
}

// New creates a new Err with the given Info and optional public message
func New(info Info, publicMsg ...interface{}) Err {
	return newErr(nil, info, publicMsg)
}

// Wrap the given error in an errs.Err. If err is nil, Wrap returns nil.
// Use Err.WrappedError for direct access to the wrapped error.
func Wrap(wrapErr error, info Info, publicMsg ...interface{}) Err {
	if wrapErr == nil {
		return nil
	}
	if errsErr, isErr := IsErr(wrapErr); isErr {
		if errs_err, isErrsErr := errsErr.(*err); isErrsErr {
			errs_err.mergeIn(info, publicMsg)
			return errs_err
		}
		return errsErr
	}
	return newErr(wrapErr, info, publicMsg)
}

// Info allows for associating key-value-pair info with an error for debugging,
// e.g `errs.Wrap(sqlError, { "SqlString":sqlStr, "SqlArgs":sqlArgs })`
type Info map[string]interface{}

// IsErr checks if err is an errs.Err, and return it as an errs.Err if it is.
// This is equivalent to err.(errs.Err)
func IsErr(err error) (Err, bool) {
	errsErr, isErr := err.(Err)
	return errsErr, isErr
}

// Internal
///////////

// err implements Err
type err struct {
	stack      []byte
	time       time.Time
	wrappedErr error
	info       Info
	publicMsg  string
}

func newErr(wrappedErr error, info Info, publicMsgParts []interface{}) Err {
	publicMsg := fmt.Sprint(publicMsgParts...)
	stack := debug.Stack()
	return &err{stack, time.Now(), wrappedErr, info, publicMsg}
}

// Implements Err
func (e *err) Stack() []byte       { return e.stack }
func (e *err) Time() time.Time     { return e.time }
func (e *err) WrappedError() error { return e.wrappedErr }
func (e *err) PublicMsg() string   { return e.publicMsg }
func (e *err) Error() string       { return e.LogString() }
func (e *err) String() string      { return e.LogString() }
func (e *err) AllInfo() Info       { return e.info }

// Implements Err
func (e *err) Info(key string) interface{} {
	if e.info == nil {
		return nil
	}
	return e.info[key]
}

// Implements Err
func (e *err) LogString() string {
	return fmt.Sprint("Error",
		"| Time:", e.time,
		"| Stack:", string(e.stack),
		"| StdError:", e.wrappedErrStr(),
		"| Info:["+fmt.Sprint(e.info)+"]",
		"| PublicMsg:", e.publicMsg,
	)
}

// Merge in the given info and public message parts into this error
func (e *err) mergeIn(info Info, publicMsgParts []interface{}) {
	for key, val := range info {
		for e.info[key] != nil {
			key = key + "_duplicate"
		}
		e.info[key] = val
	}
	e.publicMsg = fmt.Sprint(publicMsgParts...) + " - " + e.publicMsg
}

// Get the string representation of the wrapper error,
// or an empty string if wrappedErr is nil
func (e *err) wrappedErrStr() string {
	if e == nil {
		return ""
	}
	if e.wrappedErr == nil {
		return ""
	}
	return e.wrappedErr.Error()
}
<|endoftext|>


<commit_before>package cofu

import (
	"encoding/json"
	"errors"
	"fmt"
	fatihColor "github.com/fatih/color"
	"github.com/kohkimakimoto/cofu/infra"
	"github.com/kohkimakimoto/cofu/support/color"
	"github.com/kohkimakimoto/loglv"
	"github.com/labstack/gommon/log"
	"github.com/yuin/gopher-lua"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type App struct {
	LState                *lua.LState
	Logger                Logger
	ResourceTypes         []*ResourceType
	ResourceTypesMap      map[string]*ResourceType
	Resources             []*Resource
	DelayedNotifications  []*Notification
	Infra                 *infra.Infra
	DryRun                bool
	Tmpdir                string
	Tmpfiles              []string
	variable              map[string]interface{}
	Parent                *App
	Level                 int
	LogHeaderWitoutIndent string
	BuiltinRecipes        map[string]string
}

const LUA_APP_KEY = "*__COFU_APP__"

func NewApp() *App {
	defaultLogHeader := `${level}${prefix}`
	defaultLogger := log.New("cofu")
	defaultLogger.SetPrefix("")
	defaultLogger.SetHeader(defaultLogHeader)

	return &App{
		LState:               lua.NewState(),
		Logger:               defaultLogger,
		ResourceTypesMap:     map[string]*ResourceType{},
		Resources:            []*Resource{},
		DelayedNotifications: []*Notification{},
		Infra:                infra.New(),
		Tmpdir:               "/tmp/cofu_tmp",
		Tmpfiles:             []string{},
		variable: map[string]interface{}{
			"GOARCH": runtime.GOARCH,
			"GOOS":   runtime.GOOS,
		},
		Parent:                nil,
		Level:                 0,
		LogHeaderWitoutIndent: defaultLogHeader,
		BuiltinRecipes:        map[string]string{},
	}
}

func (app *App) Init() error {
	// load resource types and define lua functions.
	for _, resourceType := range app.ResourceTypes {
		if err := app.loadResourceType(resourceType); err != nil {
			return err
		}
	}

	// load lua libraries.
	openLibs(app)

	// register app into the LState
	ud := app.LState.NewUserData()
	ud.Value = app

	app.LState.SetGlobal(LUA_APP_KEY, ud)
	app.LState.SetGlobal("var", toLValue(app.LState, app.variable))

	return nil
}

func (app *App) loadResourceType(resourceType *ResourceType) error {
	if _, ok := app.ResourceTypesMap[resourceType.Name]; ok {
		return fmt.Errorf("Already defined resource type '%s'", resourceType.Name)
	}
	app.ResourceTypesMap[resourceType.Name] = resourceType

	// set app reference.
	resourceType.app = app

	// append common attributes
	resourceType.Attributes = append(resourceType.Attributes, CommonAttributes...)

	// append common action
	if resourceType.Actions == nil {
		resourceType.Actions = map[string]ResourceAction{}
	}
	resourceType.Actions["nothing"] = func(r *Resource) error {
		return nil
	}

	return nil
}

func (app *App) LoadDefinition(definition *Definition) {
	// set lua api
	L := app.LState
	L.SetGlobal(definition.Name, L.NewFunction(definition.LGFunction()))
}

func (app *App) Close() {
	app.LState.Close()
	for _, f := range app.Tmpfiles {
		os.RemoveAll(f)
	}

	if app.Parent != nil {
		app.Logger.SetHeader(app.Parent.LogHeaderWitoutIndent + GenLogIndent(app.Parent.Level))
	}
}

func (app *App) LoadVariableFromJSON(v string) error {
	variable := app.variable
	err := json.Unmarshal([]byte(v), &variable)
	if err != nil {
		return err
	}

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadVariableFromJSONFile(jsonFile string) error {
	b, err := ioutil.ReadFile(jsonFile)
	if err != nil {
		return err
	}

	variable := app.variable
	err = json.Unmarshal(b, &variable)
	if err != nil {
		return err
	}

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadVariableFromMap(m map[string]interface{}) error {
	variable := app.variable
	for k, v := range m {
		variable[k] = v
	}
	app.variable = variable

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadRecipe(recipeContent string) error {
	if err := app.LState.DoString(recipeContent); err != nil {
		return err
	}

	return nil
}

func (app *App) LoadRecipeFile(recipeFile string) error {
	if err := app.LState.DoFile(recipeFile); err != nil {
		return err
	}

	return nil
}

func (app *App) RemoveDuplicateDelayedNotification() {
	newDelayedNotifications := []*Notification{}

	for _, n := range app.DelayedNotifications {
		dup := false

		for _, nn := range newDelayedNotifications {
			if nn.TargetResourceDesc == n.TargetResourceDesc && nn.Action == n.Action {
				// duplication
				dup = true
			}
		}

		if !dup {
			newDelayedNotifications = append(newDelayedNotifications, n)
		}
	}

	app.DelayedNotifications = newDelayedNotifications
}

func (app *App) EnqueueDelayedNotification(n *Notification) {
	app.DelayedNotifications = append(app.DelayedNotifications, n)
}

func (app *App) DequeueDelayedNotification() *Notification {
	if len(app.DelayedNotifications) == 0 {
		return nil
	}

	// shift slice: https://github.com/golang/go/wiki/SliceTricks
	a := app.DelayedNotifications
	n, a := a[0], a[1:]
	app.DelayedNotifications = a

	return n
}

func (app *App) Run(dryRun bool) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()
	logger := app.Logger

	app.DryRun = dryRun

	// create tmp directory
	if _, err := os.Stat(app.Tmpdir); os.IsNotExist(err) {
		defaultUmask := syscall.Umask(0)
		os.MkdirAll(app.Tmpdir, 0777)
		syscall.Umask(defaultUmask)
	}

	if len(app.Resources) == 0 {
		// not found available resources.
		return nil
	}

	// preprocess for resources
	for _, r := range app.Resources {
		// checks required attributes
		for _, definedAttribute := range r.ResourceType.Attributes {
			if definedAttribute.IsRequired() {
				if _, ok := r.Attributes[definedAttribute.GetName()]; !ok {
					return fmt.Errorf("resource '%s': '%s' attribute is required but it is not set.", r.Desc(), definedAttribute.GetName())
				}
			}
		}

		// parse and validate notifies attribute
		if r.GetRawAttribute("notifies") != nil {
			r.Notifications = r.GetRawAttribute("notifies").([]*Notification)
			for _, n := range r.Notifications {
				n.DefinedInResource = r
				if err := n.Validate(); err != nil {
					return err
				}
			}
		}

		// set default diff function it it does not have specific func.
		if r.ResourceType.ShowDifferences == nil {
			r.ResourceType.ShowDifferences = DefaultShowDifferences
		}
	}

	if app.IsRootApp() {
		logger.Info("Starting " + Name + "...")
	}

	if app.DryRun && app.IsRootApp() {
		logger.Info(color.FgCB("Running on dry-run mode. It does not affect any real resources."))
	}

	if app.IsRootApp() {
		logger.Debugf("Log level '%s'", loglv.LvString())
		logger.Debugf("os_family '%s'", app.Infra.Command().OSFamily())
		logger.Debugf("os_release '%s'", app.Infra.Command().OSRelease())
	}

	logger.Infof("Loaded %d resource(s).", len(app.Resources))

	for _, r := range app.Resources {
		err := r.Run("")
		if err != nil {
			return err
		}
	}

	app.RemoveDuplicateDelayedNotification()
	for {
		n := app.DequeueDelayedNotification()
		if n == nil {
			break
		}

		err := n.Run()
		if err != nil {
			return err
		}
	}

	if app.IsRootApp() {
		logger.Info("Complete!")
	}

	return nil
}

func (app *App) RegisterResource(r *Resource) {
	app.Resources = append(app.Resources, r)
}

func (app *App) FindOneResource(desc string) *Resource {
	for _, r := range app.Resources {
		if r.Desc() == desc {
			return r
		}
	}

	return nil
}

func (app *App) SendContentToTempfile(content []byte) (string, error) {
	tmpFile, err := ioutil.TempFile(app.Tmpdir, "")
	if err != nil {
		return "", err
	}
	defer tmpFile.Close()

	_, err = tmpFile.Write(content)
	if err != nil {
		return "", err
	}

	err = tmpFile.Chmod(0600)
	if err != nil {
		return "", err
	}

	app.Tmpfiles = append(app.Tmpfiles, tmpFile.Name())

	return tmpFile.Name(), nil
}

func (app *App) SendFileToTempfile(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	return app.SendContentToTempfile(content)
}

func (app *App) SendDirectoryToTempDirectory(src string) (string, error) {
	tmpDir, err := ioutil.TempDir(app.Tmpdir, "")
	if err != nil {
		return "", err
	}

	tmpDir2 := filepath.Join(tmpDir, fmt.Sprintf("%d", time.Now().Unix()))

	err = CopyDir(src, tmpDir2)
	if err != nil {
		return "", err
	}

	app.Tmpfiles = append(app.Tmpfiles, tmpDir)
	return tmpDir2, nil
}

func (app *App) IsRootApp() bool {
	return app.Level == 0
}

func GetApp(L *lua.LState) (*App, error) {
	ud, ok := L.GetGlobal(LUA_APP_KEY).(*lua.LUserData)
	if !ok {
		return nil, errors.New("Couldn't get a app object from LState. Your global variable '" + LUA_APP_KEY + "' was broken!")
	}

	app, ok := ud.Value.(*App)
	if !ok {
		return nil, errors.New("Your global variable '" + LUA_APP_KEY + "' was broken!")
	}

	return app, nil
}

func GetLogger(L *lua.LState) (Logger, error) {
	app, err := GetApp(L)
	if err != nil {
		return nil, err
	}

	return app.Logger, nil
}

func toLValue(L *lua.LState, value interface{}) lua.LValue {
	switch converted := value.(type) {
	case bool:
		return lua.LBool(converted)
	case float64:
		return lua.LNumber(converted)
	case string:
		return lua.LString(converted)
	case []interface{}:
		arr := L.CreateTable(len(converted), 0)
		for _, item := range converted {
			arr.Append(toLValue(L, item))
		}
		return arr
	case map[string]interface{}:
		tbl := L.CreateTable(0, len(converted))
		for key, item := range converted {
			tbl.RawSetH(lua.LString(key), toLValue(L, item))
		}
		return tbl
	}
	return lua.LNil
}

func GenLogIndent(level int) string {
	return fmt.Sprintf("%s", strings.Repeat("  ", level))
}

func SetNoColor(b bool) {
	fatihColor.NoColor = false
}
<commit_msg>tiny update<commit_after>package cofu

import (
	"encoding/json"
	"errors"
	"fmt"
	fatihColor "github.com/fatih/color"
	"github.com/kohkimakimoto/cofu/infra"
	"github.com/kohkimakimoto/cofu/support/color"
	"github.com/kohkimakimoto/loglv"
	"github.com/labstack/gommon/log"
	"github.com/yuin/gopher-lua"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type App struct {
	LState                *lua.LState
	Logger                Logger
	ResourceTypes         []*ResourceType
	ResourceTypesMap      map[string]*ResourceType
	Resources             []*Resource
	DelayedNotifications  []*Notification
	Infra                 *infra.Infra
	DryRun                bool
	Tmpdir                string
	Tmpfiles              []string
	variable              map[string]interface{}
	Parent                *App
	Level                 int
	LogHeaderWitoutIndent string
	BuiltinRecipes        map[string]string
}

const LUA_APP_KEY = "*__COFU_APP__"

func NewApp() *App {
	defaultLogHeader := `${prefix} ${level}`
	defaultLogger := log.New("cofu")
	defaultLogger.SetPrefix("")
	defaultLogger.SetHeader(defaultLogHeader)

	return &App{
		LState:               lua.NewState(),
		Logger:               defaultLogger,
		ResourceTypesMap:     map[string]*ResourceType{},
		Resources:            []*Resource{},
		DelayedNotifications: []*Notification{},
		Infra:                infra.New(),
		Tmpdir:               "/tmp/cofu_tmp",
		Tmpfiles:             []string{},
		variable: map[string]interface{}{
			"GOARCH": runtime.GOARCH,
			"GOOS":   runtime.GOOS,
		},
		Parent:                nil,
		Level:                 0,
		LogHeaderWitoutIndent: defaultLogHeader,
		BuiltinRecipes:        map[string]string{},
	}
}

func (app *App) Init() error {
	// load resource types and define lua functions.
	for _, resourceType := range app.ResourceTypes {
		if err := app.loadResourceType(resourceType); err != nil {
			return err
		}
	}

	// load lua libraries.
	openLibs(app)

	// register app into the LState
	ud := app.LState.NewUserData()
	ud.Value = app

	app.LState.SetGlobal(LUA_APP_KEY, ud)
	app.LState.SetGlobal("var", toLValue(app.LState, app.variable))

	return nil
}

func (app *App) loadResourceType(resourceType *ResourceType) error {
	if _, ok := app.ResourceTypesMap[resourceType.Name]; ok {
		return fmt.Errorf("Already defined resource type '%s'", resourceType.Name)
	}
	app.ResourceTypesMap[resourceType.Name] = resourceType

	// set app reference.
	resourceType.app = app

	// append common attributes
	resourceType.Attributes = append(resourceType.Attributes, CommonAttributes...)

	// append common action
	if resourceType.Actions == nil {
		resourceType.Actions = map[string]ResourceAction{}
	}
	resourceType.Actions["nothing"] = func(r *Resource) error {
		return nil
	}

	return nil
}

func (app *App) LoadDefinition(definition *Definition) {
	// set lua api
	L := app.LState
	L.SetGlobal(definition.Name, L.NewFunction(definition.LGFunction()))
}

func (app *App) Close() {
	app.LState.Close()
	for _, f := range app.Tmpfiles {
		os.RemoveAll(f)
	}

	if app.Parent != nil {
		app.Logger.SetHeader(app.Parent.LogHeaderWitoutIndent + GenLogIndent(app.Parent.Level))
	}
}

func (app *App) LoadVariableFromJSON(v string) error {
	variable := app.variable
	err := json.Unmarshal([]byte(v), &variable)
	if err != nil {
		return err
	}

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadVariableFromJSONFile(jsonFile string) error {
	b, err := ioutil.ReadFile(jsonFile)
	if err != nil {
		return err
	}

	variable := app.variable
	err = json.Unmarshal(b, &variable)
	if err != nil {
		return err
	}

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadVariableFromMap(m map[string]interface{}) error {
	variable := app.variable
	for k, v := range m {
		variable[k] = v
	}
	app.variable = variable

	L := app.LState
	L.SetGlobal("var", toLValue(L, app.variable))

	return nil
}

func (app *App) LoadRecipe(recipeContent string) error {
	if err := app.LState.DoString(recipeContent); err != nil {
		return err
	}

	return nil
}

func (app *App) LoadRecipeFile(recipeFile string) error {
	if err := app.LState.DoFile(recipeFile); err != nil {
		return err
	}

	return nil
}

func (app *App) RemoveDuplicateDelayedNotification() {
	newDelayedNotifications := []*Notification{}

	for _, n := range app.DelayedNotifications {
		dup := false

		for _, nn := range newDelayedNotifications {
			if nn.TargetResourceDesc == n.TargetResourceDesc && nn.Action == n.Action {
				// duplication
				dup = true
			}
		}

		if !dup {
			newDelayedNotifications = append(newDelayedNotifications, n)
		}
	}

	app.DelayedNotifications = newDelayedNotifications
}

func (app *App) EnqueueDelayedNotification(n *Notification) {
	app.DelayedNotifications = append(app.DelayedNotifications, n)
}

func (app *App) DequeueDelayedNotification() *Notification {
	if len(app.DelayedNotifications) == 0 {
		return nil
	}

	// shift slice: https://github.com/golang/go/wiki/SliceTricks
	a := app.DelayedNotifications
	n, a := a[0], a[1:]
	app.DelayedNotifications = a

	return n
}

func (app *App) Run(dryRun bool) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()
	logger := app.Logger

	app.DryRun = dryRun

	// create tmp directory
	if _, err := os.Stat(app.Tmpdir); os.IsNotExist(err) {
		defaultUmask := syscall.Umask(0)
		os.MkdirAll(app.Tmpdir, 0777)
		syscall.Umask(defaultUmask)
	}

	if len(app.Resources) == 0 {
		// not found available resources.
		return nil
	}

	// preprocess for resources
	for _, r := range app.Resources {
		// checks required attributes
		for _, definedAttribute := range r.ResourceType.Attributes {
			if definedAttribute.IsRequired() {
				if _, ok := r.Attributes[definedAttribute.GetName()]; !ok {
					return fmt.Errorf("resource '%s': '%s' attribute is required but it is not set.", r.Desc(), definedAttribute.GetName())
				}
			}
		}

		// parse and validate notifies attribute
		if r.GetRawAttribute("notifies") != nil {
			r.Notifications = r.GetRawAttribute("notifies").([]*Notification)
			for _, n := range r.Notifications {
				n.DefinedInResource = r
				if err := n.Validate(); err != nil {
					return err
				}
			}
		}

		// set default diff function it it does not have specific func.
		if r.ResourceType.ShowDifferences == nil {
			r.ResourceType.ShowDifferences = DefaultShowDifferences
		}
	}

	if app.IsRootApp() {
		logger.Info("Starting " + Name + "...")
	}

	if app.DryRun && app.IsRootApp() {
		logger.Info(color.FgCB("Running on dry-run mode. It does not affect any real resources."))
	}

	if app.IsRootApp() {
		logger.Debugf("Log level '%s'", loglv.LvString())
		logger.Debugf("os_family '%s'", app.Infra.Command().OSFamily())
		logger.Debugf("os_release '%s'", app.Infra.Command().OSRelease())
	}

	logger.Infof("Loaded %d resource(s).", len(app.Resources))

	for _, r := range app.Resources {
		err := r.Run("")
		if err != nil {
			return err
		}
	}

	app.RemoveDuplicateDelayedNotification()
	for {
		n := app.DequeueDelayedNotification()
		if n == nil {
			break
		}

		err := n.Run()
		if err != nil {
			return err
		}
	}

	if app.IsRootApp() {
		logger.Info("Complete!")
	}

	return nil
}

func (app *App) RegisterResource(r *Resource) {
	app.Resources = append(app.Resources, r)
}

func (app *App) FindOneResource(desc string) *Resource {
	for _, r := range app.Resources {
		if r.Desc() == desc {
			return r
		}
	}

	return nil
}

func (app *App) SendContentToTempfile(content []byte) (string, error) {
	tmpFile, err := ioutil.TempFile(app.Tmpdir, "")
	if err != nil {
		return "", err
	}
	defer tmpFile.Close()

	_, err = tmpFile.Write(content)
	if err != nil {
		return "", err
	}

	err = tmpFile.Chmod(0600)
	if err != nil {
		return "", err
	}

	app.Tmpfiles = append(app.Tmpfiles, tmpFile.Name())

	return tmpFile.Name(), nil
}

func (app *App) SendFileToTempfile(file string) (string, error) {
	content, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	return app.SendContentToTempfile(content)
}

func (app *App) SendDirectoryToTempDirectory(src string) (string, error) {
	tmpDir, err := ioutil.TempDir(app.Tmpdir, "")
	if err != nil {
		return "", err
	}

	tmpDir2 := filepath.Join(tmpDir, fmt.Sprintf("%d", time.Now().Unix()))

	err = CopyDir(src, tmpDir2)
	if err != nil {
		return "", err
	}

	app.Tmpfiles = append(app.Tmpfiles, tmpDir)
	return tmpDir2, nil
}

func (app *App) IsRootApp() bool {
	return app.Level == 0
}

func GetApp(L *lua.LState) (*App, error) {
	ud, ok := L.GetGlobal(LUA_APP_KEY).(*lua.LUserData)
	if !ok {
		return nil, errors.New("Couldn't get a app object from LState. Your global variable '" + LUA_APP_KEY + "' was broken!")
	}

	app, ok := ud.Value.(*App)
	if !ok {
		return nil, errors.New("Your global variable '" + LUA_APP_KEY + "' was broken!")
	}

	return app, nil
}

func GetLogger(L *lua.LState) (Logger, error) {
	app, err := GetApp(L)
	if err != nil {
		return nil, err
	}

	return app.Logger, nil
}

func toLValue(L *lua.LState, value interface{}) lua.LValue {
	switch converted := value.(type) {
	case bool:
		return lua.LBool(converted)
	case float64:
		return lua.LNumber(converted)
	case string:
		return lua.LString(converted)
	case []interface{}:
		arr := L.CreateTable(len(converted), 0)
		for _, item := range converted {
			arr.Append(toLValue(L, item))
		}
		return arr
	case map[string]interface{}:
		tbl := L.CreateTable(0, len(converted))
		for key, item := range converted {
			tbl.RawSetH(lua.LString(key), toLValue(L, item))
		}
		return tbl
	}
	return lua.LNil
}

func GenLogIndent(level int) string {
	return fmt.Sprintf("%s", strings.Repeat("  ", level))
}

func SetNoColor(b bool) {
	fatihColor.NoColor = false
}
<|endoftext|>