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 }