cli/commands/snippet/create/create.go

147 lines
4.3 KiB
Go

package create
import (
"errors"
"fmt"
"io"
"os"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
"github.com/xanzy/go-gitlab"
"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/iostreams"
)
type CreateOpts struct {
Title string
Description string
DisplayFilename string
Visibility string
ForUser bool
FilePath string
IO *iostreams.IOStreams
Lab func() (*gitlab.Client, error)
BaseRepo func() (glrepo.Interface, error)
}
func (opts CreateOpts) isSnippetFromFile() bool {
return opts.FilePath != ""
}
func NewCmdCreate(f *cmdutils.Factory) *cobra.Command {
opts := &CreateOpts{}
var snippetCreateCmd = &cobra.Command{
Use: "create [path]",
Short: `Create 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 main.go -t Title -f "different.go" -d Description
`),
PreRunE: func(cmd *cobra.Command, args []string) error {
opts.IO = f.IO
opts.BaseRepo = f.BaseRepo
opts.Lab = f.HttpClient
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 is required")}
}
} else {
if opts.DisplayFilename == "" {
opts.DisplayFilename = args[0]
}
opts.FilePath = args[0]
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := opts.Lab()
if err != nil {
return err
}
repo, err := opts.BaseRepo()
if err != nil {
return err
}
return runCreate(client, repo, opts)
},
}
snippetCreateCmd.Flags().StringVarP(&opts.Title, "title", "t", "", "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}")
return snippetCreateCmd
}
func runCreate(client *gitlab.Client, repo glrepo.Interface, opts *CreateOpts) error {
content, err := readSnippetsContent(opts)
if err != nil {
return err
}
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,
Content: gitlab.String(string(content)),
FileName: &opts.DisplayFilename,
Visibility: gitlab.Visibility(gitlab.VisibilityValue(opts.Visibility)),
})
if err != nil {
return fmt.Errorf("failed to create snippet. %w", err)
}
snippetID := opts.IO.Color().Green(fmt.Sprintf("$%d", snippet.ID))
if opts.IO.IsaTTY {
fmt.Fprintf(opts.IO.StdOut, "%s %s (%s)\n %s\n", snippetID, snippet.Title, snippet.FileName, snippet.WebURL)
} else {
fmt.Fprintln(opts.IO.StdOut, snippet.WebURL)
}
return nil
}
// FIXME: Adding more then one file can't be done right now because the GitLab API library
//
// Doesn't support it yet.
//
// See for the API reference: https://docs.gitlab.com/ee/api/snippets.html#create-new-snippet
// See for the library docs : https://pkg.go.dev/github.com/xanzy/go-gitlab#CreateSnippetOptions
// See for GitHub issue : https://github.com/xanzy/go-gitlab/issues/1372
func readSnippetsContent(opts *CreateOpts) (string, error) {
if opts.isSnippetFromFile() {
return readFromFile(opts.FilePath)
}
return readFromSTDIN(opts.IO)
}
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
}