@@ -191,13 +191,90 @@ function writeJson(file, value) {
191191 writeFileSync ( file , `${ JSON . stringify ( value , null , 2 ) } \n` ) ;
192192}
193193
194- async function download ( url , destination ) {
194+ function githubHeaders ( accept ) {
195195 const headers = {
196196 'user-agent' : 'sealtun-npm-packager'
197197 } ;
198+ if ( accept ) {
199+ headers . accept = accept ;
200+ }
198201 if ( process . env . GITHUB_TOKEN ) {
199202 headers . authorization = `Bearer ${ process . env . GITHUB_TOKEN } ` ;
200203 }
204+ if ( urlUsesGitHubApi ( accept ) ) {
205+ headers [ 'x-github-api-version' ] = '2022-11-28' ;
206+ }
207+ return headers ;
208+ }
209+
210+ function urlUsesGitHubApi ( accept ) {
211+ return accept === 'application/vnd.github+json' || accept === 'application/octet-stream' ;
212+ }
213+
214+ async function githubJson ( url ) {
215+ const response = await fetch ( url , {
216+ headers : githubHeaders ( 'application/vnd.github+json' )
217+ } ) ;
218+ if ( ! response . ok ) {
219+ throw new Error ( `Failed to query ${ url } : ${ response . status } ${ response . statusText } ` ) ;
220+ }
221+ return response . json ( ) ;
222+ }
223+
224+ async function resolveReleaseAssetUrls ( args ) {
225+ if ( ! process . env . GITHUB_TOKEN ) {
226+ return new Map ( ) ;
227+ }
228+
229+ const releaseApiUrl = `https://api.github.com/repos/${ args . repo } /releases/tags/${ encodeURIComponent ( args . tag ) } ` ;
230+ try {
231+ const release = await githubJson ( releaseApiUrl ) ;
232+ return assetUrlsFromRelease ( release ) ;
233+ } catch ( error ) {
234+ console . warn ( `${ error . message } . Trying draft release lookup.` ) ;
235+ }
236+
237+ const releasesApiUrl = `https://api.github.com/repos/${ args . repo } /releases?per_page=100` ;
238+ try {
239+ const releases = await githubJson ( releasesApiUrl ) ;
240+ const release = releases . find ( ( candidate ) => candidate . tag_name === args . tag ) ;
241+ if ( ! release ) {
242+ throw new Error ( `Release ${ args . tag } was not found in ${ releasesApiUrl } ` ) ;
243+ }
244+ return assetUrlsFromRelease ( release ) ;
245+ } catch ( error ) {
246+ console . warn ( `${ error . message } . Falling back to public release asset URLs.` ) ;
247+ return new Map ( ) ;
248+ }
249+ }
250+
251+ function assetUrlsFromRelease ( release ) {
252+ const assets = new Map ( ) ;
253+ for ( const asset of release . assets || [ ] ) {
254+ if ( asset . name && asset . url ) {
255+ assets . set ( asset . name , asset . url ) ;
256+ }
257+ }
258+ return assets ;
259+ }
260+
261+ function releaseAssetSource ( args , releaseAssetUrls , assetName ) {
262+ const apiUrl = releaseAssetUrls . get ( assetName ) ;
263+ if ( apiUrl ) {
264+ return {
265+ url : apiUrl ,
266+ accept : 'application/octet-stream'
267+ } ;
268+ }
269+
270+ return {
271+ url : `https://github.com/${ args . repo } /releases/download/${ args . tag } /${ assetName } ` ,
272+ accept : ''
273+ } ;
274+ }
275+
276+ async function download ( url , destination , options = { } ) {
277+ const headers = githubHeaders ( options . accept ) ;
201278
202279 const attempts = Number ( process . env . NPM_DOWNLOAD_RETRIES || 3 ) + 1 ;
203280 let lastError = null ;
@@ -417,10 +494,11 @@ This package installs the Sealtun CLI by selecting one of the optional platform-
417494
418495 const downloadDir = mkdtempSync ( path . join ( tmpdir ( ) , 'sealtun-npm-assets-' ) ) ;
419496 try {
497+ const releaseAssetUrls = await resolveReleaseAssetUrls ( args ) ;
420498 const checksumsPath = path . join ( downloadDir , 'checksums.txt' ) ;
421- const checksumsUrl = `https://github.com/ ${ args . repo } /releases/download/ ${ args . tag } / checksums.txt` ;
499+ const checksumsSource = releaseAssetSource ( args , releaseAssetUrls , ' checksums.txt' ) ;
422500 console . log ( 'Downloading checksums.txt' ) ;
423- await download ( checksumsUrl , checksumsPath ) ;
501+ await download ( checksumsSource . url , checksumsPath , { accept : checksumsSource . accept } ) ;
424502 const checksums = parseChecksums ( readFileSync ( checksumsPath , 'utf8' ) ) ;
425503 if ( checksums . size === 0 ) {
426504 throw new Error ( 'checksums.txt did not contain any SHA-256 entries' ) ;
@@ -431,11 +509,11 @@ This package installs the Sealtun CLI by selecting one of the optional platform-
431509 const packageDir = path . join ( args . outDir , target . id ) ;
432510 const packageBinDir = path . join ( packageDir , 'bin' ) ;
433511 const assetName = assetNameFor ( target ) ;
434- const assetUrl = `https://github.com/ ${ args . repo } /releases/download/ ${ args . tag } / ${ assetName } ` ;
512+ const assetSource = releaseAssetSource ( args , releaseAssetUrls , assetName ) ;
435513 const archivePath = path . join ( downloadDir , assetName ) ;
436514
437515 console . log ( `Downloading ${ assetName } ` ) ;
438- await download ( assetUrl , archivePath ) ;
516+ await download ( assetSource . url , archivePath , { accept : assetSource . accept } ) ;
439517 verifyChecksum ( archivePath , assetName , checksums ) ;
440518
441519 mkdirSync ( packageBinDir , { recursive : true } ) ;
0 commit comments