@@ -123,14 +123,29 @@ func uploadcmd(opt Options) error {
123123 return err
124124 }
125125
126- // If asked to replace, first delete the existing asset, if any.
127- if assetID := findAssetID (rel .Assets , name ); opt .Upload .Replace && assetID != - 1 {
128- URL := nvls (EnvApiEndpoint , github .DefaultBaseURL ) +
129- fmt .Sprintf (ASSET_DOWNLOAD_URI , user , repo , assetID )
130- resp , err := github .DoAuthRequest ("DELETE" , URL , "application/json" , token , nil , nil )
131- if err != nil || resp .StatusCode != http .StatusNoContent {
132- return fmt .Errorf ("could not replace asset %s (ID: %d), deletion failed (error: %v, status: %s)" ,
133- name , assetID , err , resp .Status )
126+ // If the user has attempted to upload this asset before, someone could
127+ // expect it to be present in the release struct (rel.Assets). However,
128+ // we have to separately ask for the specific assets of this release.
129+ // Reason: the assets in the Release struct do not contain incomplete
130+ // uploads (which regrettably happen often using the Github API). See
131+ // issue #26.
132+ var assets []Asset
133+ err = github.Client {Token : token , BaseURL : EnvApiEndpoint }.Get (fmt .Sprintf (ASSET_RELEASE_LIST_URI , user , repo , rel .Id ), & assets )
134+ if err != nil {
135+ return err
136+ }
137+
138+ // Incomplete (failed) uploads will have their state set to new. These
139+ // assets are (AFAIK) useless in all cases. The only thing they will do
140+ // is prevent the upload of another asset of the same name. To work
141+ // around this GH API weirdness, let's just delete assets if:
142+ //
143+ // 1. Their state is new.
144+ // 2. The user explicitly asked to delete/replace the asset with -R.
145+ if asset := findAsset (assets , name ); asset != nil &&
146+ (asset .State == "new" || opt .Upload .Replace ) {
147+ if err := asset .Delete (user , repo , token ); err != nil {
148+ return fmt .Errorf ("could not replace asset: %v" , err )
134149 }
135150 }
136151
@@ -148,21 +163,38 @@ func uploadcmd(opt Options) error {
148163 return fmt .Errorf ("can't create upload request to %v, %v" , url , err )
149164 }
150165 defer resp .Body .Close ()
151-
152166 vprintln ("RESPONSE:" , resp )
153- if resp .StatusCode != http .StatusCreated {
154- if msg , err := ToMessage (resp .Body ); err == nil {
155- return fmt .Errorf ("could not upload, status code (%v), %v" ,
167+
168+ var r io.Reader = resp .Body
169+ if VERBOSITY != 0 {
170+ r = io .TeeReader (r , os .Stderr )
171+ }
172+ var asset * Asset
173+ // For HTTP status 201 and 502, Github will return a JSON encoding of
174+ // the (partially) created asset.
175+ if resp .StatusCode == http .StatusBadGateway || resp .StatusCode == http .StatusCreated {
176+ vprintf ("ASSET: " )
177+ asset = new (Asset )
178+ if err := json .NewDecoder (r ).Decode (& asset ); err != nil {
179+ return fmt .Errorf ("upload failed (%s), could not unmarshal asset (err: %v)" , resp .Status , err )
180+ }
181+ } else {
182+ vprintf ("BODY: " )
183+ if msg , err := ToMessage (r ); err == nil {
184+ return fmt .Errorf ("could not upload, status code (%s), %v" ,
156185 resp .Status , msg )
157186 }
158- return fmt .Errorf ("could not upload, status code (%v )" , resp .Status )
187+ return fmt .Errorf ("could not upload, status code (%s )" , resp .Status )
159188 }
160189
161- if VERBOSITY != 0 {
162- vprintf ("BODY: " )
163- if _ , err := io .Copy (os .Stderr , resp .Body ); err != nil {
164- return fmt .Errorf ("while reading response, %v" , err )
190+ if resp .StatusCode == http .StatusBadGateway {
191+ // 502 means the upload failed, but GitHub still retains metadata
192+ // (an asset in state "new"). Attempt to delete that now since it
193+ // would clutter the list of release assets.
194+ if err := asset .Delete (user , repo , token ); err != nil {
195+ return fmt .Errorf ("upload failed (%s), could not delete partially uploaded asset (ID: %d, err: %v) in order to cleanly reset GH API state, please try again" , resp .Status , asset .Id , err )
165196 }
197+ return fmt .Errorf ("could not upload, status code (%s)" , resp .Status )
166198 }
167199
168200 return nil
@@ -194,17 +226,17 @@ func downloadcmd(opt Options) error {
194226 return err
195227 }
196228
197- assetID := findAssetID (rel .Assets , name )
198- if assetID == - 1 {
229+ asset := findAsset (rel .Assets , name )
230+ if asset == nil {
199231 return fmt .Errorf ("coud not find asset named %s" , name )
200232 }
201233
202234 var resp * http.Response
203235 if token == "" {
204- // Use the regular github.com site it we don't have a token.
236+ // Use the regular github.com site if we don't have a token.
205237 resp , err = http .Get ("https://github.com" + fmt .Sprintf ("/%s/%s/releases/download/%s/%s" , user , repo , tag , name ))
206238 } else {
207- url := nvls (EnvApiEndpoint , github .DefaultBaseURL ) + fmt .Sprintf (ASSET_DOWNLOAD_URI , user , repo , assetID )
239+ url := nvls (EnvApiEndpoint , github .DefaultBaseURL ) + fmt .Sprintf (ASSET_URI , user , repo , asset . Id )
208240 resp , err = github .DoAuthRequest ("GET" , url , "" , token , map [string ]string {
209241 "Accept" : "application/octet-stream" ,
210242 }, nil )
0 commit comments