22
33const fs = require ( "fs" ) ;
44const path = require ( "path" ) ;
5+ const fetch = require ( "node-fetch" ) ;
56
67/**
78 * Verifies that all expected binary assets are present in the GitHub release
@@ -11,6 +12,10 @@ async function verifyReleaseAssets() {
1112 try {
1213 // Read version from package.json
1314 const packagePath = path . join ( __dirname , ".." , "package.json" ) ;
15+ if ( ! fs . existsSync ( packagePath ) ) {
16+ console . error ( "❌ package.json not found at" , packagePath ) ;
17+ process . exit ( 1 ) ;
18+ }
1419 const packageJson = JSON . parse ( fs . readFileSync ( packagePath , "utf8" ) ) ;
1520 const version = packageJson . version ;
1621
@@ -28,10 +33,9 @@ async function verifyReleaseAssets() {
2833
2934 const inGitHubActions = process . env . GITHUB_ACTIONS === "true" ;
3035
31- // ✅ Skip token validation in GitHub Actions (since built-in token is limited)
3236 if ( ! inGitHubActions ) {
3337 console . log ( "🔐 Checking GITHUB_TOKEN permissions..." ) ;
34-
38+ // Validate token by calling /user
3539 const userCheck = await fetch ( "https://api.github.com/user" , {
3640 headers : {
3741 Authorization : `token ${ token } ` ,
@@ -52,48 +56,40 @@ async function verifyReleaseAssets() {
5256 console . log ( `✅ Authenticated as: ${ userData . login } ` ) ;
5357 } else {
5458 console . log ( "🏃 Running inside GitHub Actions — no user authentication check needed" ) ;
59+ // quick repo check to ensure token can access repo
60+ const appCheck = await fetch ( `https://api.github.com/repos/${ owner } /${ repo } ` , {
61+ headers : {
62+ Authorization : `token ${ token } ` ,
63+ Accept : "application/vnd.github.v3+json" ,
64+ "User-Agent" : "alifullstack-release-verifier" ,
65+ } ,
66+ } ) ;
5567
56- // Test API access by fetching org/user info
57- try {
58- const appCheck = await fetch ( `https://api.github.com/repos/${ owner } /${ repo } ` , {
59- headers : {
60- Authorization : `token ${ token } ` ,
61- Accept : "application/vnd.github.v3+json" ,
62- "User-Agent" : "alifullstack-release-verifier" ,
63- } ,
64- } ) ;
65-
66- if ( ! appCheck . ok ) {
67- const body = await appCheck . text ( ) ;
68- console . error ( "❌ Token authentication failed!" ) ;
69- console . error ( `Status: ${ appCheck . status } ${ appCheck . statusText } ` ) ;
70- console . error ( `Response body: ${ body } ` ) ;
71- process . exit ( 1 ) ;
72- }
73-
74- const repoData = await appCheck . json ( ) ;
75- console . log ( `✅ Token authenticated for repository: ${ repoData . full_name } ` ) ;
76- } catch ( error ) {
77- console . error ( "❌ Error testing token authentication:" , error . message ) ;
68+ if ( ! appCheck . ok ) {
69+ const body = await appCheck . text ( ) ;
70+ console . error ( "❌ Token authentication failed on repo check!" ) ;
71+ console . error ( `Status: ${ appCheck . status } ${ appCheck . statusText } ` ) ;
72+ console . error ( `Response body: ${ body } ` ) ;
7873 process . exit ( 1 ) ;
7974 }
75+
76+ const repoData = await appCheck . json ( ) ;
77+ console . log ( `✅ Token authenticated for repository: ${ repoData . full_name } ` ) ;
8078 }
8179
82- // --- Fetch releases with retry logic ---
8380 const tagName = `v${ version } ` ;
84- const maxRetries = 5 ;
81+ const maxRetries = 8 ;
8582 const baseDelay = 10000 ; // 10 seconds
8683 let release = null ;
8784 let lastError = null ;
8885
86+ // Try to fetch the release by tag name. This avoids scanning the entire releases list.
8987 for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
9088 try {
91- console . log (
92- `📡 Attempt ${ attempt } /${ maxRetries } : Fetching releases to find: ${ tagName } ` ,
93- ) ;
89+ console . log ( `📡 Attempt ${ attempt } /${ maxRetries } : Fetching release by tag: ${ tagName } ` ) ;
9490
95- const allReleasesUrl = `https://api.github.com/repos/${ owner } /${ repo } /releases` ;
96- const response = await fetch ( allReleasesUrl , {
91+ const releaseUrl = `https://api.github.com/repos/${ owner } /${ repo } /releases/tags/ ${ tagName } ` ;
92+ const response = await fetch ( releaseUrl , {
9793 headers : {
9894 Authorization : `token ${ token } ` ,
9995 Accept : "application/vnd.github.v3+json" ,
@@ -102,29 +98,36 @@ async function verifyReleaseAssets() {
10298 } ) ;
10399
104100 if ( ! response . ok ) {
105- console . error ( `❌ GitHub API error: ${ response . status } ${ response . statusText } ` ) ;
106- const errorBody = await response . text ( ) ;
107- console . error ( `Response Body: ${ errorBody } ` ) ;
108- throw new Error ( `GitHub API returned ${ response . status } ` ) ;
109- }
110-
111- const allReleases = await response . json ( ) ;
112-
113- const releaseExists = allReleases . some ( ( r ) => r . tag_name === tagName ) ;
114- if ( ! releaseExists ) {
115- console . warn ( `⚠️ Release ${ tagName } not found. Retrying...` ) ;
101+ const body = await response . text ( ) ;
102+ console . warn ( `⚠️ GitHub API returned ${ response . status } ${ response . statusText } ` ) ;
103+ console . warn ( "Response body:" , body ) ;
104+ if ( response . status === 404 ) {
105+ console . warn ( `⚠️ Release ${ tagName } not found (404). Will retry.` ) ;
106+ } else {
107+ console . warn ( "⚠️ Non-404 response; will retry after delay." ) ;
108+ }
116109 if ( attempt < maxRetries ) {
117110 const delay = baseDelay * attempt ;
118111 console . log ( `⏳ Waiting ${ delay / 1000 } s before retry...` ) ;
119112 await new Promise ( ( r ) => setTimeout ( r , delay ) ) ;
113+ continue ;
114+ } else {
115+ throw new Error ( `Failed to fetch release: ${ response . status } ` ) ;
120116 }
121- continue ;
122117 }
123118
124- release = allReleases . find ( ( r ) => r . tag_name === tagName ) ;
125- console . log (
126- `✅ Found release: ${ release . tag_name } (${ release . draft ? "DRAFT" : "PUBLISHED" } )` ,
127- ) ;
119+ release = await response . json ( ) ;
120+
121+ console . log ( `✅ Found release: ${ release . tag_name } (${ release . draft ? "DRAFT" : "PUBLISHED" } )` ) ;
122+ // If release exists but has zero assets, wait and retry (registrations can be delayed)
123+ const assets = release . assets || [ ] ;
124+ console . log ( `📦 Found ${ assets . length } assets in release ${ tagName } ` ) ;
125+ if ( assets . length === 0 && attempt < maxRetries ) {
126+ const delay = baseDelay * attempt ;
127+ console . log ( `⚠️ No assets present yet. Waiting ${ delay / 1000 } s before retry...` ) ;
128+ await new Promise ( ( r ) => setTimeout ( r , delay ) ) ;
129+ continue ;
130+ }
128131 break ;
129132 } catch ( err ) {
130133 lastError = err ;
@@ -147,6 +150,7 @@ async function verifyReleaseAssets() {
147150
148151 console . log ( `📦 Found ${ assets . length } assets in release ${ tagName } ` ) ;
149152 console . log ( `📄 Release status: ${ release . draft ? "DRAFT" : "PUBLISHED" } ` ) ;
153+ console . log ( "" ) ;
150154
151155 // --- Define expected assets ---
152156 const normalizeVersionForPlatform = ( version , platform ) => {
@@ -179,14 +183,27 @@ async function verifyReleaseAssets() {
179183
180184 const actualAssets = assets . map ( ( a ) => a . name ) ;
181185 console . log ( "📋 Actual assets:" ) ;
182- actualAssets . forEach ( ( a ) => console . log ( ` - ${ a } ` ) ) ;
186+ if ( actualAssets . length === 0 ) {
187+ console . log ( "(none)" ) ;
188+ } else {
189+ actualAssets . forEach ( ( a ) => console . log ( ` - ${ a } ` ) ) ;
190+ }
183191 console . log ( "" ) ;
184192
185193 // --- Compare assets ---
186194 const missingAssets = expectedAssets . filter ( ( a ) => ! actualAssets . includes ( a ) ) ;
187195 if ( missingAssets . length > 0 ) {
188196 console . error ( "❌ VERIFICATION FAILED! Missing assets:" ) ;
189197 missingAssets . forEach ( ( a ) => console . error ( ` - ${ a } ` ) ) ;
198+ console . error ( "" ) ;
199+ // For debugging, emit the full release JSON to help identify naming differences
200+ console . error ( "🔎 Full release JSON preview (first 2000 chars):" ) ;
201+ try {
202+ const releaseJson = JSON . stringify ( release , null , 2 ) ;
203+ console . error ( releaseJson . substring ( 0 , 2000 ) ) ;
204+ } catch ( _ ) {
205+ // ignore
206+ }
190207 process . exit ( 1 ) ;
191208 }
192209
@@ -206,10 +223,10 @@ async function verifyReleaseAssets() {
206223 console . log ( ` Published: ${ release . published_at } ` ) ;
207224 console . log ( ` URL: ${ release . html_url } ` ) ;
208225 } catch ( error ) {
209- console . error ( "❌ Error verifying release assets:" , error . message ) ;
226+ console . error ( "❌ Error verifying release assets:" , error && error . message ? error . message : error ) ;
210227 process . exit ( 1 ) ;
211228 }
212229}
213230
214231// Run the verification
215- verifyReleaseAssets ( ) ;
232+ verifyReleaseAssets ( ) ;
0 commit comments