1- package list
1+ package create
22
33import (
4- "bytes"
5- "encoding/json"
64 "fmt"
75 "io/ioutil"
86 "net/http"
7+ "strings"
98
10- "github.com/cli/cli/api"
11- "github.com/cli/cli/internal/ghinstance"
129 "github.com/cli/cli/internal/ghrepo"
10+ "github.com/cli/cli/pkg/cmd/release/shared"
1311 "github.com/cli/cli/pkg/cmdutil"
1412 "github.com/cli/cli/pkg/iostreams"
1513 "github.com/spf13/cobra"
@@ -20,7 +18,18 @@ type CreateOptions struct {
2018 IO * iostreams.IOStreams
2119 BaseRepo func () (ghrepo.Interface , error )
2220
23- TagName string
21+ TagName string
22+ Target string
23+ Name string
24+ Body string
25+ BodyProvided bool
26+ Draft bool
27+ Prerelease bool
28+
29+ Assets []* shared.AssetForUpload
30+
31+ // maximum number of simultaneous uploads
32+ Concurrency int
2433}
2534
2635func NewCmdCreate (f * cmdutil.Factory , runF func (* CreateOptions ) error ) * cobra.Command {
@@ -29,23 +38,55 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
2938 HttpClient : f .HttpClient ,
3039 }
3140
41+ var notesFile string
42+
3243 cmd := & cobra.Command {
33- Use : "create <tag>" ,
44+ Use : "create <tag> [<files>...] " ,
3445 Short : "Create a new release" ,
35- Args : cobra .ExactArgs (1 ),
46+ Args : cobra .MinimumNArgs (1 ),
3647 RunE : func (cmd * cobra.Command , args []string ) error {
3748 // support `-R, --repo` override
3849 opts .BaseRepo = f .BaseRepo
3950
4051 opts .TagName = args [0 ]
4152
53+ var err error
54+ opts .Assets , err = shared .AssetsFromArgs (args [1 :])
55+ if err != nil {
56+ return err
57+ }
58+
59+ opts .Concurrency = 5
60+
61+ opts .BodyProvided = cmd .Flags ().Changed ("notes" )
62+ if notesFile != "" {
63+ var b []byte
64+ if notesFile == "-" {
65+ b , err = ioutil .ReadAll (opts .IO .In )
66+ } else {
67+ b , err = ioutil .ReadFile (notesFile )
68+ }
69+ if err != nil {
70+ return err
71+ }
72+ opts .Body = string (b )
73+ opts .BodyProvided = true
74+ }
75+
4276 if runF != nil {
4377 return runF (opts )
4478 }
4579 return createRun (opts )
4680 },
4781 }
4882
83+ cmd .Flags ().BoolVarP (& opts .Draft , "draft" , "d" , false , "Save the release as a draft instead of publishing it" )
84+ cmd .Flags ().BoolVarP (& opts .Prerelease , "prerelease" , "p" , false , "Mark the release as a prerelease" )
85+ cmd .Flags ().StringVar (& opts .Target , "target" , "" , "Target `branch` or commit SHA (default: main branch)" )
86+ cmd .Flags ().StringVarP (& opts .Name , "title" , "t" , "" , "Release title" )
87+ cmd .Flags ().StringVarP (& opts .Body , "notes" , "n" , "" , "Release notes" )
88+ cmd .Flags ().StringVarP (& notesFile , "notes-file" , "F" , "" , "Read release notes from `file`" )
89+
4990 return cmd
5091}
5192
@@ -61,47 +102,45 @@ func createRun(opts *CreateOptions) error {
61102 }
62103
63104 params := map [string ]interface {}{
64- "tag_name" : opts .TagName ,
105+ "tag_name" : opts .TagName ,
106+ "draft" : opts .Draft ,
107+ "prerelease" : opts .Prerelease ,
108+ "name" : opts .Name ,
109+ "body" : opts .Body ,
65110 }
66-
67- bodyBytes , err := json .Marshal (params )
68- if err != nil {
69- return err
111+ if opts .Target != "" {
112+ params ["target_commitish" ] = opts .Target
70113 }
71114
72- path := fmt .Sprintf ("repos/%s/%s/releases" , baseRepo .RepoOwner (), baseRepo .RepoName ())
73- url := ghinstance .RESTPrefix (baseRepo .RepoHost ()) + path
74- req , err := http .NewRequest ("POST" , url , bytes .NewBuffer (bodyBytes ))
75- if err != nil {
76- return err
115+ hasAssets := len (opts .Assets ) > 0
116+ if hasAssets {
117+ params ["draft" ] = true
77118 }
78119
79- req .Header .Set ("Content-Type" , "application/json; charset=utf-8" )
80-
81- resp , err := httpClient .Do (req )
82- if err != nil {
83- return err
84- }
85- defer resp .Body .Close ()
86-
87- success := resp .StatusCode >= 200 && resp .StatusCode < 300
88- if ! success {
89- return api .HandleHTTPError (resp )
90- }
91-
92- b , err := ioutil .ReadAll (resp .Body )
120+ newRelease , err := createRelease (httpClient , baseRepo , params )
93121 if err != nil {
94122 return err
95123 }
96124
97- var newRelease struct {
98- HTMLURL string `json:"html_url"`
99- AssetsURL string `json:"assets_url"`
100- }
101-
102- err = json .Unmarshal (b , & newRelease )
103- if err != nil {
104- return err
125+ if hasAssets {
126+ uploadURL := newRelease .UploadURL
127+ if idx := strings .IndexRune (uploadURL , '{' ); idx > 0 {
128+ uploadURL = uploadURL [:idx ]
129+ }
130+
131+ opts .IO .StartProgressIndicator ()
132+ err = shared .ConcurrentUpload (httpClient , uploadURL , opts .Concurrency , opts .Assets )
133+ opts .IO .StopProgressIndicator ()
134+ if err != nil {
135+ return err
136+ }
137+
138+ if ! opts .Draft {
139+ err := publishRelease (httpClient , newRelease .URL )
140+ if err != nil {
141+ return err
142+ }
143+ }
105144 }
106145
107146 fmt .Fprintf (opts .IO .Out , "%s\n " , newRelease .HTMLURL )
0 commit comments