commands/snippet/create/create.go (166 lines of code) (raw):

package create import ( "errors" "fmt" "io" "os" "strings" "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/cli/api" "gitlab.com/gitlab-org/cli/commands/cmdutils" "gitlab.com/gitlab-org/cli/internal/glrepo" "gitlab.com/gitlab-org/cli/pkg/dbg" "gitlab.com/gitlab-org/cli/pkg/iostreams" ) type CreateOpts struct { Title string Description string DisplayFilename string Visibility string Personal bool Files []*gitlab.CreateSnippetFileOptions IO *iostreams.IOStreams BaseRepo func() (glrepo.Interface, error) } func (opts *CreateOpts) addFile(path, content *string) { opts.Files = append(opts.Files, &gitlab.CreateSnippetFileOptions{ FilePath: path, Content: content, }) } func hasStdIn() bool { fi, err := os.Stdin.Stat() if err != nil { return false } return fi.Size() > 0 } func NewCmdCreate(f *cmdutils.Factory) *cobra.Command { opts := &CreateOpts{} snippetCreateCmd := &cobra.Command{ Use: `create [flags] -t <title> <file1> [<file2>...] glab snippet create [flags] -t <title> -f <filename> # reads from stdin`, Short: `Create a new snippet.`, Long: ``, Aliases: []string{"new"}, Example: heredoc.Doc(` $ glab snippet create script.py --title "Title of the snippet" $ echo "package main" | glab snippet new --title "Title of the snippet" --filename "main.go" $ glab snippet create -t Title -f "different.go" -d Description main.go $ glab snippet create -t Title -f "different.go" -d Description --filename different.go main.go $ glab snippet create --personal --title "Personal snippet" script.py `), PreRunE: func(cmd *cobra.Command, args []string) error { opts.IO = f.IO opts.BaseRepo = f.BaseRepo if opts.Title == "" { return &cmdutils.FlagError{ Err: errors.New("--title required for snippets"), } } if len(args) == 0 { if opts.DisplayFilename == "" { return &cmdutils.FlagError{Err: errors.New("if 'path' is not provided, 'filename' and stdin are required")} } else { if !f.IO.IsInTTY && !hasStdIn() { return errors.New("stdin required if no 'path' is provided") } } fmt.Fprintln(f.IO.StdOut, "reading from stdin (Ctrl+D to finish, Ctrl+C to abort):") content, err := readFromSTDIN(f.IO) if err != nil { return err } opts.addFile(&opts.DisplayFilename, &content) } else { for _, path := range args { filename := path if len(args) == 1 && opts.DisplayFilename != "" { filename = opts.DisplayFilename } content, err := readFromFile(path) if err != nil { return err } dbg.Debug("Adding:", filename) opts.addFile(&filename, &content) } } return nil }, RunE: func(cmd *cobra.Command, args []string) error { client, err := f.HttpClient() if err != nil { return err } if opts.Personal { return runCreate(client, nil, opts) } else { repo, err := opts.BaseRepo() if err != nil { redCheck := opts.IO.Color().FailedIcon() return fmt.Errorf("%s Project snippet needs a repository. Do you want --personal?", redCheck) } return runCreate(client, repo, opts) } }, } snippetCreateCmd.Flags().StringVarP(&opts.Title, "title", "t", "", "(required) Title of the snippet.") snippetCreateCmd.Flags().StringVarP(&opts.DisplayFilename, "filename", "f", "", "Filename of the snippet in GitLab.") snippetCreateCmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Description of the snippet.") snippetCreateCmd.Flags().StringVarP(&opts.Visibility, "visibility", "v", "private", "Limit by visibility: 'public', 'internal', or 'private'") snippetCreateCmd.Flags().BoolVarP(&opts.Personal, "personal", "p", false, "Create a personal snippet.") return snippetCreateCmd } func runCreate(client *gitlab.Client, repo glrepo.Interface, opts *CreateOpts) error { var snippet *gitlab.Snippet var err error if opts.Personal { fmt.Fprintln(opts.IO.StdErr, "- Creating snippet in personal space") snippet, err = api.CreateSnippet(client, &gitlab.CreateSnippetOptions{ Title: &opts.Title, Description: &opts.Description, Visibility: gitlab.Ptr(gitlab.VisibilityValue(opts.Visibility)), Files: &opts.Files, }) } else { fmt.Fprintln(opts.IO.StdErr, "- Creating snippet in", repo.FullName()) snippet, err = api.CreateProjectSnippet(client, repo.FullName(), &gitlab.CreateProjectSnippetOptions{ Title: &opts.Title, Description: &opts.Description, Visibility: gitlab.Ptr(gitlab.VisibilityValue(opts.Visibility)), Files: &opts.Files, }) } if err != nil { return fmt.Errorf("failed to create snippet: %w", err) } snippetID := opts.IO.Color().Green(fmt.Sprintf("$%d", snippet.ID)) var files []string for _, file := range opts.Files { files = append(files, *file.FilePath) } names := strings.Join(files, " ") if opts.IO.IsaTTY { fmt.Fprintf(opts.IO.StdOut, "%s %s (%s)\n %s\n", snippetID, snippet.Title, names, snippet.WebURL) } else { fmt.Fprintln(opts.IO.StdOut, snippet.WebURL) } return nil } func readFromSTDIN(ioStream *iostreams.IOStreams) (string, error) { content, err := io.ReadAll(ioStream.In) if err != nil { return "", fmt.Errorf("Failed to read snippet from STDIN. %w", err) } return string(content), nil } func readFromFile(filename string) (string, error) { content, err := os.ReadFile(filename) if err != nil { return "", fmt.Errorf("Failed to read snippet from file '%s'. %w", filename, err) } return string(content), nil }