@@ -19,27 +19,96 @@ function humanizeBytes(bytes) {
19
19
}
20
20
21
21
function humanizeDate ( date ) {
22
- const options = { year : 'numeric' , month : 'long' , day : 'numeric' } ;
22
+ const options = { year : 'numeric' , month : 'long' , day : 'numeric' } ;
23
23
return new Date ( date ) . toLocaleDateString ( 'en-US' , options ) ;
24
24
}
25
25
26
+ /**
27
+ * Formats a given time duration in milliseconds into a human-readable string.
28
+ *
29
+ * @param {number } milliseconds - The time duration in milliseconds.
30
+ * @returns {string } - A formatted string representing the time duration in days, hours, minutes, seconds, and milliseconds.
31
+ */
32
+ function formatTime ( milliseconds ) {
33
+ const seconds = Math . floor ( milliseconds / 1000 ) ;
34
+ const remainingMilliseconds = milliseconds % 1000 ;
35
+
36
+ const minutes = Math . floor ( seconds / 60 ) ;
37
+ const remainingSeconds = seconds % 60 ;
38
+
39
+ const hours = Math . floor ( minutes / 60 ) ;
40
+ const remainingMinutes = minutes % 60 ;
41
+
42
+ const days = Math . floor ( hours / 24 ) ;
43
+ const remainingHours = hours % 24 ;
44
+
45
+ if ( days > 0 ) {
46
+ return `${ days } days, ${ remainingHours } hours, ${ remainingMinutes } minutes, ${ remainingSeconds } seconds, ${ remainingMilliseconds } milliseconds` ;
47
+ }
48
+ if ( hours > 0 ) {
49
+ return `${ hours } hours, ${ remainingMinutes } minutes, ${ remainingSeconds } seconds, ${ remainingMilliseconds } milliseconds` ;
50
+ }
51
+ if ( minutes > 0 ) {
52
+ return `${ minutes } minutes, ${ remainingSeconds } seconds, ${ remainingMilliseconds } milliseconds` ;
53
+ }
54
+ if ( seconds > 0 ) {
55
+ return `${ seconds } seconds, ${ remainingMilliseconds } milliseconds` ;
56
+ }
57
+ return `${ milliseconds } milliseconds` ;
58
+ }
59
+
60
+ /**
61
+ * Fetches data from a URL with exponential backoff retry strategy.
62
+ *
63
+ * @param {string } url - The URL to fetch data from.
64
+ * @param {Object } headers - The headers to include in the request.
65
+ * @returns {string } - The response body.
66
+ * @throws {Error } - If all retries fail.
67
+ */
68
+ function fetchWithRetry ( url , headers ) {
69
+ const maxRetries = 40 ;
70
+ let retryDelay = 1000 ; // Initial delay in milliseconds
71
+
72
+ for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
73
+ try {
74
+ const response = request ( 'GET' , url , { headers} ) ;
75
+ return response . getBody ( 'utf-8' ) ;
76
+ } catch ( error ) {
77
+ if ( attempt === maxRetries ) {
78
+ throw new Error ( `Failed to fetch data after ${ maxRetries } attempts: ${ error . message } ` ) ;
79
+ }
80
+ console . error ( `Failed to fetch data:\n${ error . message } .\nRequest headers: ${ JSON . stringify ( headers ) } .\nRetrying in ${ formatTime ( retryDelay ) } .` ) ;
81
+ // Wait for retryDelay milliseconds before retrying
82
+ Atomics . wait ( new Int32Array ( new SharedArrayBuffer ( 4 ) ) , 0 , 0 , retryDelay ) ;
83
+ retryDelay *= 2 ; // Exponential backoff
84
+ const maxDelay = 180000 ; // Maximum delay of 3 minutes
85
+ if ( retryDelay > maxDelay ) {
86
+ retryDelay = maxDelay ;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
26
92
module . exports = function ( registry ) {
27
93
registry . blockMacro ( 'mrdocs-releases' , function ( ) {
28
94
const self = this
29
95
self . process ( function ( parent , target , attrs ) {
30
96
// Collect all release URLs
31
97
let cacheFilenamePath = 'releasesResponse.json' ;
32
98
let cachePath = `${ __dirname } /../build/requests/${ cacheFilenamePath } ` ;
33
- fs . mkdirSync ( `${ __dirname } /../build/requests/` , { recursive : true } ) ;
99
+ fs . mkdirSync ( `${ __dirname } /../build/requests/` , { recursive : true } ) ;
100
+ const GH_TOKEN = process . env . GITHUB_TOKEN || process . env . GH_TOKEN || ''
101
+ const apiHeaders = GH_TOKEN ? {
102
+ 'User-Agent' : 'request' ,
103
+ 'Authorization' : `Bearer ${ GH_TOKEN } `
104
+ } : {
105
+ 'User-Agent' : 'request'
106
+ }
34
107
const readFromCacheFile = fs . existsSync ( cachePath ) && fs . statSync ( cachePath ) . mtime > new Date ( Date . now ( ) - 1000 * 60 * 60 * 24 ) ;
35
108
const releasesResponse =
36
109
readFromCacheFile ?
37
- fs . readFileSync ( cachePath , 'utf-8' ) :
38
- request ( 'GET' , 'https://api.github.com/repos/cppalliance/mrdocs/releases' , {
39
- headers : {
40
- 'User-Agent' : 'request'
41
- }
42
- } ) . getBody ( 'utf-8' )
110
+ fs . readFileSync ( cachePath , 'utf-8' ) :
111
+ fetchWithRetry ( 'https://api.github.com/repos/cppalliance/mrdocs/releases' , apiHeaders )
43
112
if ( ! readFromCacheFile ) {
44
113
fs . writeFileSync ( cachePath , releasesResponse ) ;
45
114
}
0 commit comments