github.com/matthewdale/lab@v0.14.0/cmd/snippet_create.go (about)

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