github.com/nkprince007/lab@v0.6.2-0.20171218071646-19d68b56f403/cmd/snippetCreate.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  	"text/template"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/spf13/cobra"
    17  	"github.com/xanzy/go-gitlab"
    18  	"github.com/zaquestion/lab/internal/git"
    19  	lab "github.com/zaquestion/lab/internal/gitlab"
    20  )
    21  
    22  var (
    23  	msgs    []string
    24  	name    string
    25  	file    string
    26  	private bool
    27  	public  bool
    28  )
    29  
    30  // mrCmd represents the mr command
    31  var snippetCreateCmd = &cobra.Command{
    32  	Use:   "create [remote]",
    33  	Short: "Create a personal or project snippet",
    34  	Long: `
    35  Source snippets from stdin, file, or in editor from scratch
    36  Write title & description in editor, or using -m`,
    37  	Run: func(cmd *cobra.Command, args []string) {
    38  		remote := forkedFromRemote
    39  		if len(args) > 0 {
    40  			ok, err := git.IsRemote(args[0])
    41  			if err != nil {
    42  				log.Fatal(err)
    43  			}
    44  			if ok {
    45  				remote = args[0]
    46  			} else if ok && len(args) > 1 {
    47  				file = args[1]
    48  			} else {
    49  				file = args[0]
    50  			}
    51  		}
    52  		code, err := snipCode(file)
    53  		if err != nil {
    54  			log.Fatal(err)
    55  		}
    56  		if strings.TrimSpace(code) == "" {
    57  			log.Fatal("aborting snippet due to empty contents")
    58  		}
    59  		title, body, err := snipMsg(msgs, code)
    60  		if title == "" {
    61  			log.Fatal("aborting snippet due to empty msg")
    62  		}
    63  
    64  		visibility := gitlab.InternalVisibility
    65  		switch {
    66  		case private:
    67  			visibility = gitlab.PrivateVisibility
    68  		case public:
    69  			visibility = gitlab.PublicVisibility
    70  		}
    71  		// See if we're in a git repo or if global is set to determine
    72  		// if this should be a personal snippet
    73  		rn, _ := git.PathWithNameSpace(remote)
    74  		if global || rn == "" {
    75  			opts := gitlab.CreateSnippetOptions{
    76  				Title:       gitlab.String(title),
    77  				Description: gitlab.String(body),
    78  				Content:     gitlab.String(code),
    79  				FileName:    gitlab.String(name),
    80  				Visibility:  &visibility,
    81  			}
    82  			snip, err := lab.SnippetCreate(&opts)
    83  			if err != nil || snip == nil {
    84  				log.Fatal(errors.Wrap(err, "failed to create snippet"))
    85  			}
    86  			fmt.Println(snip.WebURL)
    87  			return
    88  		}
    89  
    90  		project, err := lab.FindProject(rn)
    91  		if err != nil {
    92  			log.Fatal(err)
    93  		}
    94  		opts := gitlab.CreateProjectSnippetOptions{
    95  			Title:       gitlab.String(title),
    96  			Description: gitlab.String(body),
    97  			Code:        gitlab.String(code),
    98  			FileName:    gitlab.String(name),
    99  			Visibility:  &visibility,
   100  		}
   101  		snip, err := lab.ProjectSnippetCreate(project.ID, &opts)
   102  		if err != nil || snip == nil {
   103  			log.Fatal(errors.Wrap(err, "failed to create snippet"))
   104  		}
   105  		fmt.Println(snip.WebURL)
   106  	},
   107  }
   108  
   109  func snipMsg(msgs []string, code string) (string, string, error) {
   110  	if len(msgs) > 0 {
   111  		return msgs[0], strings.Join(msgs[1:], "\n\n"), nil
   112  	}
   113  
   114  	// Read up to the first 30 chars
   115  	buf := bytes.NewBufferString(code)
   116  	reader := io.LimitReader(buf, 30)
   117  	b, err := ioutil.ReadAll(reader)
   118  	if err != nil {
   119  		return "", "", nil
   120  	}
   121  	i := bytes.IndexByte(b, '\n')
   122  	if i != -1 {
   123  		b = b[:i]
   124  	}
   125  
   126  	var tmpl = string(b) + `
   127  {{.CommentChar}} Write a message for this snippet. The first block
   128  {{.CommentChar}} is the title and the rest is the description.`
   129  
   130  	msg, err := snipText(tmpl)
   131  	if err != nil {
   132  		log.Fatal(err)
   133  	}
   134  
   135  	title, body, err := git.Edit("SNIPMSG", msg)
   136  	if err != nil {
   137  		_, f, l, _ := runtime.Caller(0)
   138  		log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
   139  	}
   140  	return title, body, err
   141  }
   142  
   143  func snipCode(path string) (string, error) {
   144  	b, err := ioutil.ReadFile(path)
   145  	if !os.IsNotExist(err) && err != nil {
   146  		return "", err
   147  	}
   148  	if len(b) > 0 {
   149  		return string(b), nil
   150  	}
   151  
   152  	if stat, _ := os.Stdin.Stat(); (stat.Mode() & os.ModeCharDevice) == 0 {
   153  		b, err = ioutil.ReadAll(os.Stdin)
   154  		if err != nil {
   155  			return "", err
   156  		}
   157  		if len(b) > 0 {
   158  			return string(b), nil
   159  		}
   160  	}
   161  
   162  	var tmpl = `
   163  {{.CommentChar}} In this mode you are writing a snippet from scratch
   164  {{.CommentChar}} The first block is the title and the rest is the contents.`
   165  
   166  	text, err := snipText(tmpl)
   167  	if err != nil {
   168  		log.Fatal(err)
   169  	}
   170  	title, body, err := git.Edit("SNIPCODE", text)
   171  	if err != nil {
   172  		_, f, l, _ := runtime.Caller(0)
   173  		log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
   174  	}
   175  	return fmt.Sprintf("%s\n\n%s", title, body), nil
   176  }
   177  
   178  func snipText(tmpl string) (string, error) {
   179  	t, err := template.New("tmpl").Parse(tmpl)
   180  	if err != nil {
   181  		return "", err
   182  	}
   183  
   184  	cc := git.CommentChar()
   185  	msg := &struct{ CommentChar string }{CommentChar: cc}
   186  	var b bytes.Buffer
   187  	err = t.Execute(&b, msg)
   188  	if err != nil {
   189  		return "", err
   190  	}
   191  
   192  	return b.String(), nil
   193  }
   194  
   195  func init() {
   196  	snippetCreateCmd.Flags().BoolVarP(&private, "private", "p", false, "Make snippet private; visible only to project members (default: internal)")
   197  	snippetCreateCmd.Flags().BoolVar(&public, "public", false, "Make snippet public; can be accessed without any authentication (default: internal)")
   198  	snippetCreateCmd.Flags().StringVarP(&name, "name", "n", "", "(optional) Name snippet to add code highlighting, e.g. potato.go for GoLang")
   199  	snippetCreateCmd.Flags().StringSliceVarP(&msgs, "message", "m", []string{}, "Use the given <msg>; multiple -m are concatenated as seperate paragraphs")
   200  	snippetCmd.Flags().AddFlagSet(snippetCreateCmd.Flags())
   201  	snippetCmd.AddCommand(snippetCreateCmd)
   202  }