@@ -6,6 +6,77 @@ import { KeygenOptions } from "builder-util-runtime/out/publishOptions"
66import  {  configureRequestOptions ,  HttpExecutor ,  parseJson  }  from  "builder-util-runtime" 
77import  {  getCompleteExtname  }  from  "../util/filename" 
88
9+ type  RecursivePartial < T >  =  { 
10+   [ P  in  keyof  T ] ?: RecursivePartial < T [ P ] > 
11+ } 
12+ 
13+ export  interface  KeygenError  { 
14+   title : string 
15+   detail : string 
16+   code : string 
17+ } 
18+ 
19+ export  interface  KeygenRelease  { 
20+   id : string 
21+   type : "releases" 
22+   attributes : { 
23+     name : string  |  null 
24+     description : string  |  null 
25+     channel : "stable"  |  "rc"  |  "beta"  |  "alpha"  |  "dev" 
26+     status : "DRAFT"  |  "PUBLISHED"  |  "YANKED" 
27+     tag : string 
28+     version : string 
29+     semver : { 
30+       major : number 
31+       minor : number 
32+       patch : number 
33+       prerelease : string  |  null 
34+       build : string  |  null 
35+     } 
36+     metadata : {  [ s : string ] : any  } 
37+     created : string 
38+     updated : string 
39+     yanked : string  |  null 
40+   } 
41+   relationships : { 
42+     account : { 
43+       data : {  type : "accounts" ;  id : string  } 
44+     } 
45+     product : { 
46+       data : {  type : "products" ;  id : string  } 
47+     } 
48+   } 
49+ } 
50+ 
51+ export  interface  KeygenArtifact  { 
52+   id : string 
53+   type : "artifacts" 
54+   attributes : { 
55+     filename : string 
56+     filetype : string  |  null 
57+     filesize : number  |  null 
58+     platform : string  |  null 
59+     arch : string  |  null 
60+     signature : string  |  null 
61+     checksum : string  |  null 
62+     status : "WAITING"  |  "UPLOADED"  |  "FAILED"  |  "YANKED" 
63+     metadata : {  [ s : string ] : any  } 
64+     created : string 
65+     updated : string 
66+   } 
67+   relationships : { 
68+     account : { 
69+       data : {  type : "accounts" ;  id : string  } 
70+     } 
71+     release : { 
72+       data : {  type : "releases" ;  id : string  } 
73+     } 
74+   } 
75+   links : { 
76+     redirect : string 
77+   } 
78+ } 
79+ 
980export  class  KeygenPublisher  extends  HttpPublisher  { 
1081  readonly  providerName  =  "keygen" 
1182  readonly  hostname  =  "api.keygen.sh" 
@@ -26,7 +97,7 @@ export class KeygenPublisher extends HttpPublisher {
2697    this . info  =  info 
2798    this . auth  =  `Bearer ${ token . trim ( ) }  
2899    this . version  =  version 
29-     this . basePath  =  `/v1/accounts/${ this . info . account } /releases ` 
100+     this . basePath  =  `/v1/accounts/${ this . info . account }  
30101  } 
31102
32103  protected  doUpload ( 
@@ -36,78 +107,143 @@ export class KeygenPublisher extends HttpPublisher {
36107    requestProcessor : ( request : ClientRequest ,  reject : ( error : Error )  =>  void )  =>  void , 
37108    // eslint-disable-next-line @typescript-eslint/no-unused-vars 
38109    _file : string 
39-   ) : Promise < any >  { 
110+   ) : Promise < string >  { 
40111    return  HttpExecutor . retryOnServerError ( async  ( )  =>  { 
41-       const  {  data,  errors }  =  await  this . upsertRelease ( fileName ,   dataLength ) 
112+       const  {  data,  errors }  =  await  this . getOrCreateRelease ( ) 
42113      if  ( errors )  { 
43-         throw  new  Error ( `Keygen - Upserting  release returned errors: ${ JSON . stringify ( errors ) }  ) 
114+         throw  new  Error ( `Keygen - Creating  release returned errors: ${ JSON . stringify ( errors ) }  ) 
44115      } 
45-       const  releaseId  =  data ?. id 
46-       if  ( ! releaseId )  { 
47-         log . warn ( {  file : fileName ,  reason : "UUID doesn't exist and was not created"  } ,  "upserting release failed" ) 
48-         throw  new  Error ( `Keygen - Upserting release returned no UUID: ${ JSON . stringify ( data ) }  ) 
49-       } 
50-       await  this . uploadArtifact ( releaseId ,  dataLength ,  requestProcessor ) 
51-       return  releaseId 
116+ 
117+       await  this . uploadArtifact ( data ! . id ,  fileName ,  dataLength ,  requestProcessor ) 
118+ 
119+       return  data ! . id 
52120    } ) 
53121  } 
54122
55-   private  async  uploadArtifact ( releaseId : any ,  dataLength : number ,  requestProcessor : ( request : ClientRequest ,  reject : ( error : Error )  =>  void )  =>  void )  { 
123+   private  async  uploadArtifact ( 
124+     releaseId : any , 
125+     fileName : string , 
126+     dataLength : number , 
127+     requestProcessor : ( request : ClientRequest ,  reject : ( error : Error )  =>  void )  =>  void 
128+   ) : Promise < void >  { 
129+     const  {  data,  errors }  =  await  this . createArtifact ( releaseId ,  fileName ,  dataLength ) 
130+     if  ( errors )  { 
131+       throw  new  Error ( `Keygen - Creating artifact returned errors: ${ JSON . stringify ( errors ) }  ) 
132+     } 
133+ 
134+     // Follow the redirect and upload directly to S3-equivalent storage provider 
135+     const  url  =  new  URL ( data ! . links . redirect ) 
136+     const  upload : RequestOptions  =  { 
137+       hostname : url . hostname , 
138+       path : url . pathname  +  url . search , 
139+       headers : { 
140+         "Content-Length" : dataLength , 
141+       } , 
142+     } 
143+ 
144+     await  httpExecutor . doApiRequest ( configureRequestOptions ( upload ,  null ,  "PUT" ) ,  this . context . cancellationToken ,  requestProcessor ) 
145+   } 
146+ 
147+   private  async  createArtifact ( releaseId : any ,  fileName : string ,  dataLength : number ) : Promise < {  data ?: KeygenArtifact ;  errors ?: KeygenError [ ]  } >  { 
56148    const  upload : RequestOptions  =  { 
57149      hostname : this . hostname , 
58-       path : `${ this . basePath } ${ releaseId } /artifact ` , 
150+       path : `${ this . basePath } artifacts ` , 
59151      headers : { 
152+         "Content-Type" : "application/vnd.api+json" , 
60153        Accept : "application/vnd.api+json" , 
61-         "Content-Length" : dataLength , 
62-         "Keygen-Version" : "1.0" , 
154+         "Keygen-Version" : "1.1" , 
155+         Prefer : "no-redirect" , 
156+       } , 
157+     } 
158+ 
159+     const  data : RecursivePartial < KeygenArtifact >  =  { 
160+       type : "artifacts" , 
161+       attributes : { 
162+         filename : fileName , 
163+         filetype : getCompleteExtname ( fileName ) , 
164+         filesize : dataLength , 
165+         platform : this . info . platform , 
166+       } , 
167+       relationships : { 
168+         release : { 
169+           data : { 
170+             type : "releases" , 
171+             id : releaseId , 
172+           } , 
173+         } , 
63174      } , 
64175    } 
65-     await  httpExecutor . doApiRequest ( configureRequestOptions ( upload ,  this . auth ,  "PUT" ) ,  this . context . cancellationToken ,  requestProcessor ) 
176+ 
177+     log . debug ( {  data : JSON . stringify ( data )  } ,  "Keygen create artifact" ) 
178+ 
179+     return  parseJson ( httpExecutor . request ( configureRequestOptions ( upload ,  this . auth ,  "POST" ) ,  this . context . cancellationToken ,  {  data } ) ) 
66180  } 
67181
68-   private  async  upsertRelease ( fileName : string ,  dataLength : number ) : Promise < {  data : any ;  errors : any  } >  { 
182+   private  async  getOrCreateRelease ( ) : Promise < {  data ?: KeygenRelease ;  errors ?: KeygenError [ ]  } >  { 
183+     try  { 
184+       return  await  this . getRelease ( ) 
185+     }  catch  ( e )  { 
186+       if  ( e . statusCode  ===  404 )  { 
187+         return  this . createRelease ( ) 
188+       } 
189+ 
190+       throw  e 
191+     } 
192+   } 
193+ 
194+   private  async  getRelease ( ) : Promise < {  data ?: KeygenRelease ;  errors ?: KeygenError [ ]  } >  { 
69195    const  req : RequestOptions  =  { 
70196      hostname : this . hostname , 
71-       method : "PUT" , 
72-       path : this . basePath , 
197+       path : `${ this . basePath } ${ this . version }  , 
198+       headers : { 
199+         Accept : "application/vnd.api+json" , 
200+         "Keygen-Version" : "1.1" , 
201+       } , 
202+     } 
203+ 
204+     return  parseJson ( httpExecutor . request ( configureRequestOptions ( req ,  this . auth ,  "GET" ) ,  this . context . cancellationToken ,  null ) ) 
205+   } 
206+ 
207+   private  async  createRelease ( ) : Promise < {  data ?: KeygenRelease ;  errors ?: KeygenError [ ]  } >  { 
208+     const  req : RequestOptions  =  { 
209+       hostname : this . hostname , 
210+       path : `${ this . basePath }  , 
73211      headers : { 
74212        "Content-Type" : "application/vnd.api+json" , 
75213        Accept : "application/vnd.api+json" , 
76-         "Keygen-Version" : "1.0 " , 
214+         "Keygen-Version" : "1.1 " , 
77215      } , 
78216    } 
79-     const  data  =  { 
80-       data : { 
81-         type : "release" , 
82-         attributes : { 
83-           filename : fileName , 
84-           filetype : getCompleteExtname ( fileName ) , 
85-           filesize : dataLength , 
86-           version : this . version , 
87-           platform : this . info . platform , 
88-           channel : this . info . channel  ||  "stable" , 
89-         } , 
90-         relationships : { 
91-           product : { 
92-             data : { 
93-               type : "product" , 
94-               id : this . info . product , 
95-             } , 
217+ 
218+     const  data : RecursivePartial < KeygenRelease >  =  { 
219+       type : "releases" , 
220+       attributes : { 
221+         version : this . version , 
222+         channel : this . info . channel  ||  "stable" , 
223+         status : "PUBLISHED" , 
224+       } , 
225+       relationships : { 
226+         product : { 
227+           data : { 
228+             type : "products" , 
229+             id : this . info . product , 
96230          } , 
97231        } , 
98232      } , 
99233    } 
100-     log . debug ( {  data : JSON . stringify ( data )  } ,  "Keygen upsert release" ) 
101-     return  parseJson ( httpExecutor . request ( configureRequestOptions ( req ,  this . auth ,  "PUT" ) ,  this . context . cancellationToken ,  data ) ) 
234+ 
235+     log . debug ( {  data : JSON . stringify ( data )  } ,  "Keygen create release" ) 
236+ 
237+     return  parseJson ( httpExecutor . request ( configureRequestOptions ( req ,  this . auth ,  "POST" ) ,  this . context . cancellationToken ,  {  data } ) ) 
102238  } 
103239
104240  async  deleteRelease ( releaseId : string ) : Promise < void >  { 
105241    const  req : RequestOptions  =  { 
106242      hostname : this . hostname , 
107-       path : `${ this . basePath } ${ releaseId }  , 
243+       path : `${ this . basePath } releases/ ${ releaseId }  , 
108244      headers : { 
109245        Accept : "application/vnd.api+json" , 
110-         "Keygen-Version" : "1.0 " , 
246+         "Keygen-Version" : "1.1 " , 
111247      } , 
112248    } 
113249    await  httpExecutor . request ( configureRequestOptions ( req ,  this . auth ,  "DELETE" ) ,  this . context . cancellationToken ) 
0 commit comments