Skip to content

Commit 55214d7

Browse files
committed
docs: releases extension allows CI authentication and retries
1 parent 5e0f628 commit 55214d7

File tree

2 files changed

+81
-8
lines changed

2 files changed

+81
-8
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ jobs:
533533
# This playbook renders the documentation
534534
# content for the website. It includes
535535
# master, develop, and tags.
536+
GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
537+
export GH_TOKEN
536538
npm ci
537539
npx antora antora-playbook.yml
538540
mkdir -p ../build/website/docs
@@ -553,6 +555,8 @@ jobs:
553555
# before a workflow that pushes to the
554556
# website is triggered.
555557
set -x
558+
GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
559+
export GH_TOKEN
556560
npx antora antora-playbook.yml --attribute branchesarray=HEAD --stacktrace
557561
mkdir -p ../build/docs-local
558562
cp -r build/site/* ../build/docs-local

docs/extensions/mrdocs-releases.js

+77-8
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,96 @@ function humanizeBytes(bytes) {
1919
}
2020

2121
function humanizeDate(date) {
22-
const options = { year: 'numeric', month: 'long', day: 'numeric' };
22+
const options = {year: 'numeric', month: 'long', day: 'numeric'};
2323
return new Date(date).toLocaleDateString('en-US', options);
2424
}
2525

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+
2692
module.exports = function (registry) {
2793
registry.blockMacro('mrdocs-releases', function () {
2894
const self = this
2995
self.process(function (parent, target, attrs) {
3096
// Collect all release URLs
3197
let cacheFilenamePath = 'releasesResponse.json';
3298
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+
}
34107
const readFromCacheFile = fs.existsSync(cachePath) && fs.statSync(cachePath).mtime > new Date(Date.now() - 1000 * 60 * 60 * 24);
35108
const releasesResponse =
36109
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)
43112
if (!readFromCacheFile) {
44113
fs.writeFileSync(cachePath, releasesResponse);
45114
}

0 commit comments

Comments
 (0)