1- const https = require ( "https" ) ;
1+ const https = require ( 'https' ) ;
2+ const util = require ( 'util' ) ;
3+ const exec = util . promisify ( require ( 'child_process' ) . exec ) ;
24
3- const npmTag = "npm-tag" ;
4- const baseUrl =
5- "https://registry.hub.docker.com/v2/repositories/grafana/grafana-dev/tags" ;
5+ const baseUrl = 'https://registry.hub.docker.com/v2/repositories/grafana/grafana-dev/tags' ;
66
77module . exports = async ( { core } ) => {
8- const tag = core . getInput ( npmTag ) ;
9- const exists = await checkIfTagExists ( tag ) ;
8+ const { stdout } = await exec ( 'npm view @grafana/ui dist-tags.canary' ) ;
9+ const tag = stdout . trim ( ) ;
1010
11+ const exists = await checkIfTagExists ( tag ) ;
1112 if ( exists ) {
1213 core . info ( `Found grafana/grafana-dev:${ tag } ` ) ;
1314 return tag ;
@@ -75,8 +76,8 @@ async function findNextTag(tag) {
7576}
7677
7778function parseBuildInfo ( tag ) {
78- const hypenIndex = tag . lastIndexOf ( "-" ) ;
79- const preIndex = tag . lastIndexOf ( " pre" ) ;
79+ const hypenIndex = tag . lastIndexOf ( '-' ) ;
80+ const preIndex = tag . lastIndexOf ( ' pre' ) ;
8081 const version = tag . slice ( 0 , hypenIndex ) ;
8182 const build = parseInt ( tag . slice ( hypenIndex + 1 , preIndex ) , 10 ) ;
8283
@@ -93,23 +94,110 @@ function convertToNameSearchParam(build) {
9394 return target . slice ( 0 , target . length - 1 ) ;
9495}
9596
96- function httpGet ( url ) {
97+ function httpGet ( url , maxRetries = 3 , retryDelay = 1000 ) {
9798 return new Promise ( ( resolve , reject ) => {
98- https
99- . get ( url , ( res ) => {
99+ let attempts = 0 ;
100+ let timeoutId = null ;
101+
102+ const makeRequest = ( ) => {
103+ attempts ++ ;
104+
105+ const req = https . get ( url , { timeout : 10000 } , ( res ) => {
100106 let data = [ ] ;
101107
102- res . on ( " data" , ( chunk ) => {
108+ res . on ( ' data' , ( chunk ) => {
103109 data . push ( chunk ) ;
104110 } ) ;
105111
106- res . on ( "end" , ( ) => {
112+ res . on ( 'end' , ( ) => {
113+ if ( timeoutId ) {
114+ clearTimeout ( timeoutId ) ;
115+ timeoutId = null ;
116+ }
117+
107118 const buffer = Buffer . concat ( data ) . toString ( ) ;
108- resolve ( JSON . parse ( buffer ) ) ;
119+
120+ // Check if response is successful
121+ if ( res . statusCode >= 200 && res . statusCode < 300 ) {
122+ try {
123+ const jsonData = JSON . parse ( buffer ) ;
124+ resolve ( jsonData ) ;
125+ } catch ( parseError ) {
126+ const error = new Error ( `Failed to parse JSON response from ${ url } : ${ parseError . message } ` ) ;
127+ error . responseBody = buffer . substring ( 0 , 500 ) ; // Include first 500 chars for debugging
128+
129+ if ( attempts < maxRetries ) {
130+ console . warn (
131+ `JSON parse error on attempt ${ attempts } /${ maxRetries } for ${ url } . Retrying in ${
132+ retryDelay * attempts
133+ } ms...`
134+ ) ;
135+ timeoutId = setTimeout ( makeRequest , retryDelay * attempts ) ; // Exponential backoff
136+ } else {
137+ reject ( error ) ;
138+ }
139+ }
140+ } else if ( res . statusCode >= 500 && attempts < maxRetries ) {
141+ // Retry on server errors (5xx)
142+ console . warn (
143+ `Server error ${ res . statusCode } on attempt ${ attempts } /${ maxRetries } for ${ url } . Retrying in ${
144+ retryDelay * attempts
145+ } ms...`
146+ ) ;
147+ timeoutId = setTimeout ( makeRequest , retryDelay * attempts ) ; // Exponential backoff
148+ } else {
149+ // Non-retryable error or max retries exceeded
150+ const error = new Error ( `HTTP ${ res . statusCode } error from ${ url } ` ) ;
151+ error . statusCode = res . statusCode ;
152+ error . responseBody = buffer . substring ( 0 , 500 ) ; // Include first 500 chars for debugging
153+ reject ( error ) ;
154+ }
155+ } ) ;
156+
157+ res . on ( 'error' , ( err ) => {
158+ if ( timeoutId ) {
159+ clearTimeout ( timeoutId ) ;
160+ timeoutId = null ;
161+ }
162+ handleRequestError ( err ) ;
109163 } ) ;
110- } )
111- . on ( "error" , ( err ) => {
112- reject ( err ) ;
113164 } ) ;
165+
166+ req . on ( 'timeout' , ( ) => {
167+ req . destroy ( ) ;
168+ const err = new Error ( `Request timeout for ${ url } ` ) ;
169+ err . code = 'ETIMEDOUT' ;
170+ handleRequestError ( err ) ;
171+ } ) ;
172+
173+ req . on ( 'error' , ( err ) => {
174+ if ( timeoutId ) {
175+ clearTimeout ( timeoutId ) ;
176+ timeoutId = null ;
177+ }
178+ handleRequestError ( err ) ;
179+ } ) ;
180+
181+ const handleRequestError = ( err ) => {
182+ if ( attempts < maxRetries && isRetryableNetworkError ( err ) ) {
183+ console . warn (
184+ `Network error on attempt ${ attempts } /${ maxRetries } for ${ url } : ${ err . message } . Retrying in ${
185+ retryDelay * attempts
186+ } ms...`
187+ ) ;
188+ timeoutId = setTimeout ( makeRequest , retryDelay * attempts ) ; // Exponential backoff
189+ } else {
190+ reject ( err ) ;
191+ }
192+ } ;
193+ } ;
194+
195+ makeRequest ( ) ;
114196 } ) ;
115197}
198+
199+ function isRetryableNetworkError ( error ) {
200+ // Retry on common transient network errors
201+ const retryableCodes = [ 'ECONNRESET' , 'ENOTFOUND' , 'ECONNREFUSED' , 'ETIMEDOUT' ] ;
202+ return retryableCodes . includes ( error . code ) ;
203+ }
0 commit comments