github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/release/upload/upload.go (about) 1 package upload 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "github.com/MakeNowJust/heredoc" 9 "github.com/cli/cli/internal/ghrepo" 10 "github.com/cli/cli/pkg/cmd/release/shared" 11 "github.com/cli/cli/pkg/cmdutil" 12 "github.com/cli/cli/pkg/iostreams" 13 "github.com/cli/cli/utils" 14 "github.com/spf13/cobra" 15 ) 16 17 type UploadOptions struct { 18 HttpClient func() (*http.Client, error) 19 IO *iostreams.IOStreams 20 BaseRepo func() (ghrepo.Interface, error) 21 22 TagName string 23 Assets []*shared.AssetForUpload 24 25 // maximum number of simultaneous uploads 26 Concurrency int 27 OverwriteExisting bool 28 } 29 30 func NewCmdUpload(f *cmdutil.Factory, runF func(*UploadOptions) error) *cobra.Command { 31 opts := &UploadOptions{ 32 IO: f.IOStreams, 33 HttpClient: f.HttpClient, 34 } 35 36 cmd := &cobra.Command{ 37 Use: "upload <tag> <files>...", 38 Short: "Upload assets to a release", 39 Long: heredoc.Doc(` 40 Upload asset files to a GitHub Release. 41 42 To define a display label for an asset, append text starting with '#' after the 43 file name. 44 `), 45 Args: cobra.MinimumNArgs(2), 46 RunE: func(cmd *cobra.Command, args []string) error { 47 // support `-R, --repo` override 48 opts.BaseRepo = f.BaseRepo 49 50 opts.TagName = args[0] 51 52 var err error 53 opts.Assets, err = shared.AssetsFromArgs(args[1:]) 54 if err != nil { 55 return err 56 } 57 58 opts.Concurrency = 5 59 60 if runF != nil { 61 return runF(opts) 62 } 63 return uploadRun(opts) 64 }, 65 } 66 67 cmd.Flags().BoolVar(&opts.OverwriteExisting, "clobber", false, "Overwrite existing assets of the same name") 68 69 return cmd 70 } 71 72 func uploadRun(opts *UploadOptions) error { 73 httpClient, err := opts.HttpClient() 74 if err != nil { 75 return err 76 } 77 78 baseRepo, err := opts.BaseRepo() 79 if err != nil { 80 return err 81 } 82 83 release, err := shared.FetchRelease(httpClient, baseRepo, opts.TagName) 84 if err != nil { 85 return err 86 } 87 88 uploadURL := release.UploadURL 89 if idx := strings.IndexRune(uploadURL, '{'); idx > 0 { 90 uploadURL = uploadURL[:idx] 91 } 92 93 var existingNames []string 94 for _, a := range opts.Assets { 95 for _, ea := range release.Assets { 96 if ea.Name == a.Name { 97 a.ExistingURL = ea.APIURL 98 existingNames = append(existingNames, ea.Name) 99 break 100 } 101 } 102 } 103 104 if len(existingNames) > 0 && !opts.OverwriteExisting { 105 return fmt.Errorf("asset under the same name already exists: %v", existingNames) 106 } 107 108 opts.IO.StartProgressIndicator() 109 err = shared.ConcurrentUpload(httpClient, uploadURL, opts.Concurrency, opts.Assets) 110 opts.IO.StopProgressIndicator() 111 if err != nil { 112 return err 113 } 114 115 if opts.IO.IsStdoutTTY() { 116 iofmt := opts.IO.ColorScheme() 117 fmt.Fprintf(opts.IO.Out, "Successfully uploaded %s to %s\n", 118 utils.Pluralize(len(opts.Assets), "asset"), 119 iofmt.Bold(release.TagName)) 120 } 121 122 return nil 123 }