Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potentially fix timeouts/retries when fetching from remote #304

Merged
merged 9 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 49 additions & 46 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9106,12 +9106,13 @@ exports["default"] = _default;
const core = __nccwpck_require__(2186)
const { exec } = __nccwpck_require__(1514)
const tc = __nccwpck_require__(7784)
const http = __nccwpck_require__(6255)
const path = __nccwpck_require__(1017)
const semver = __nccwpck_require__(1383)
const fs = __nccwpck_require__(7147)
const os = __nccwpck_require__(2037)

const MAX_HTTP_RETRIES = 3

main().catch((err) => {
core.setFailed(err.message)
})
Expand Down Expand Up @@ -9361,8 +9362,7 @@ async function getOTPVersions(osVersion) {
hexMirrors: hexMirrorsInput(),
actionTitle: `fetch ${originListing}`,
action: async (hexMirror) => {
const l = await get(`${hexMirror}${originListing}`, [null])
return l
return get(`${hexMirror}${originListing}`, [])
},
})
} else if (process.platform === 'win32') {
Expand All @@ -9389,7 +9389,7 @@ async function getOTPVersions(osVersion) {
})
} else if (process.platform === 'win32') {
otpVersionsListings.forEach((otpVersionsListing) => {
jsonParseAsList(otpVersionsListing)
otpVersionsListing
.map((x) => x.assets)
.flat()
.filter((x) => x.name.match(/^otp_win64_.*.exe$/))
Expand All @@ -9413,8 +9413,7 @@ async function getElixirVersions() {
hexMirrors: hexMirrorsInput(),
actionTitle: `fetch ${originListing}`,
action: async (hexMirror) => {
const l = await get(`${hexMirror}${originListing}`, [null])
return l
return get(`${hexMirror}${originListing}`, [])
},
})
const otpVersionsForElixirMap = {}
Expand Down Expand Up @@ -9447,7 +9446,7 @@ async function getGleamVersions() {
)
const gleamVersionsListing = {}
resultJSONs.forEach((resultJSON) => {
jsonParseAsList(resultJSON)
resultJSON
.map((x) => x.tag_name)
.forEach((ver) => {
const gleamMatch = ver.match(/^v?([^ ]+)/)
Expand All @@ -9466,7 +9465,7 @@ async function getRebar3Versions() {
)
const rebar3VersionsListing = {}
resultJSONs.forEach((resultJSON) => {
jsonParseAsList(resultJSON)
resultJSON
.map((x) => x.tag_name)
.forEach((ver) => {
rebar3VersionsListing[ver] = ver
Expand Down Expand Up @@ -9660,38 +9659,56 @@ function getRunnerOSVersion() {
return containerFromEnvImageOS
}

async function get(url0, pageIdxs) {
async function getPage(pageIdx) {
const url = new URL(url0)
const headers = {}
const GithubToken = getInput('github-token', false)
if (GithubToken && url.host === 'api.github.com') {
headers.authorization = `Bearer ${GithubToken}`
}
async function getUrlResponse(url, headers, attempt = 1) {
try {
const response = await fetch(url, {
headers,
signal: AbortSignal.timeout(10000),
})
const contentType = response.headers.get('content-type') || ''

if (pageIdx !== null) {
url.searchParams.append('page', pageIdx)
if (!response.ok) {
throw new Error(response.statusText)
}

const httpClient = new http.HttpClient('setup-beam', [], {
allowRetries: true,
maxRetries: 3,
})
const response = await httpClient.get(url, headers)
if (response.statusCode >= 400 && response.statusCode <= 599) {
throw new Error(
`Got ${response.statusCode} from ${url}. Exiting with error`,
)
if (contentType.indexOf('application/json') !== -1) {
return response.json()
} else {
return response.text()
}
} catch (err) {
core.debug(`Error fetching from ${url}: ${err}`)

return response.readBody()
if (attempt <= MAX_HTTP_RETRIES) {
const delay = attempt * 2 * 1000
core.debug(`Error during fetch. Retrying in ${delay}ms`)
await new Promise((resolve) => setTimeout(resolve, delay))
return getUrlResponse(url, headers, attempt + 1)
} else {
throw err
}
}
}

if (pageIdxs[0] === null) {
return getPage(null)
async function get(url0, pageIdxs) {
const url = new URL(url0)
const headers = {}
const GithubToken = getInput('github-token', false)
if (GithubToken && url.host === 'api.github.com') {
headers.authorization = `Bearer ${GithubToken}`
}

return Promise.all(pageIdxs.map(getPage))
if (pageIdxs.length === 0) {
return getUrlResponse(url, headers)
} else {
return Promise.all(
pageIdxs.map((page) => {
const urlWithPage = new URL(url)
urlWithPage.searchParams.append('page', page)
return getUrlResponse(urlWithPage, headers)
}),
)
}
}

function maybePrependWithV(v) {
Expand Down Expand Up @@ -9786,21 +9803,6 @@ function parseVersionFile(versionFilePath0) {
return appVersions
}

function jsonParseAsList(maybeJson) {
try {
const obj = JSON.parse(maybeJson)
if (!Array.isArray(obj)) {
throw new Error('expected a list!')
}

return obj
} catch (exc) {
throw new Error(
`Got an exception when trying to parse non-JSON list ${maybeJson}: ${exc}`,
)
}
}

function debugLog(groupName, message) {
const group = `Debugging for ${groupName}`
core.debug(
Expand Down Expand Up @@ -10158,6 +10160,7 @@ function debugLoggingEnabled() {
}

module.exports = {
get,
getOTPVersion,
getElixirVersion,
getGleamVersion,
Expand Down
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"dependencies": {
"@actions/core": "1.10.0",
"@actions/exec": "1.1.1",
"@actions/http-client": "2.1.0",
"@actions/tool-cache": "2.0.1",
"semver": "7.6.2"
},
Expand Down
95 changes: 49 additions & 46 deletions src/setup-beam.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const core = require('@actions/core')
const { exec } = require('@actions/exec')
const tc = require('@actions/tool-cache')
const http = require('@actions/http-client')
const path = require('path')
const semver = require('semver')
const fs = require('fs')
const os = require('os')

const MAX_HTTP_RETRIES = 3

main().catch((err) => {
core.setFailed(err.message)
})
Expand Down Expand Up @@ -261,8 +262,7 @@ async function getOTPVersions(osVersion) {
hexMirrors: hexMirrorsInput(),
actionTitle: `fetch ${originListing}`,
action: async (hexMirror) => {
const l = await get(`${hexMirror}${originListing}`, [null])
return l
return get(`${hexMirror}${originListing}`, [])
},
})
} else if (process.platform === 'win32') {
Expand All @@ -289,7 +289,7 @@ async function getOTPVersions(osVersion) {
})
} else if (process.platform === 'win32') {
otpVersionsListings.forEach((otpVersionsListing) => {
jsonParseAsList(otpVersionsListing)
otpVersionsListing
.map((x) => x.assets)
.flat()
.filter((x) => x.name.match(/^otp_win64_.*.exe$/))
Expand All @@ -313,8 +313,7 @@ async function getElixirVersions() {
hexMirrors: hexMirrorsInput(),
actionTitle: `fetch ${originListing}`,
action: async (hexMirror) => {
const l = await get(`${hexMirror}${originListing}`, [null])
return l
return get(`${hexMirror}${originListing}`, [])
},
})
const otpVersionsForElixirMap = {}
Expand Down Expand Up @@ -347,7 +346,7 @@ async function getGleamVersions() {
)
const gleamVersionsListing = {}
resultJSONs.forEach((resultJSON) => {
jsonParseAsList(resultJSON)
resultJSON
.map((x) => x.tag_name)
.forEach((ver) => {
const gleamMatch = ver.match(/^v?([^ ]+)/)
Expand All @@ -366,7 +365,7 @@ async function getRebar3Versions() {
)
const rebar3VersionsListing = {}
resultJSONs.forEach((resultJSON) => {
jsonParseAsList(resultJSON)
resultJSON
.map((x) => x.tag_name)
.forEach((ver) => {
rebar3VersionsListing[ver] = ver
Expand Down Expand Up @@ -560,38 +559,56 @@ function getRunnerOSVersion() {
return containerFromEnvImageOS
}

async function get(url0, pageIdxs) {
async function getPage(pageIdx) {
const url = new URL(url0)
const headers = {}
const GithubToken = getInput('github-token', false)
if (GithubToken && url.host === 'api.github.com') {
headers.authorization = `Bearer ${GithubToken}`
}
async function getUrlResponse(url, headers, attempt = 1) {
try {
const response = await fetch(url, {
headers,
signal: AbortSignal.timeout(10000),
})
const contentType = response.headers.get('content-type') || ''

if (pageIdx !== null) {
url.searchParams.append('page', pageIdx)
if (!response.ok) {
throw new Error(response.statusText)
}

const httpClient = new http.HttpClient('setup-beam', [], {
allowRetries: true,
maxRetries: 3,
})
const response = await httpClient.get(url, headers)
if (response.statusCode >= 400 && response.statusCode <= 599) {
throw new Error(
`Got ${response.statusCode} from ${url}. Exiting with error`,
)
if (contentType.indexOf('application/json') !== -1) {
return response.json()
} else {
return response.text()
}
} catch (err) {
core.debug(`Error fetching from ${url}: ${err}`)

return response.readBody()
if (attempt <= MAX_HTTP_RETRIES) {
const delay = attempt * 2 * 1000
core.debug(`Error during fetch. Retrying in ${delay}ms`)
await new Promise((resolve) => setTimeout(resolve, delay))
return getUrlResponse(url, headers, attempt + 1)
} else {
throw err
}
}
}

if (pageIdxs[0] === null) {
return getPage(null)
async function get(url0, pageIdxs) {
const url = new URL(url0)
const headers = {}
const GithubToken = getInput('github-token', false)
if (GithubToken && url.host === 'api.github.com') {
headers.authorization = `Bearer ${GithubToken}`
}

return Promise.all(pageIdxs.map(getPage))
if (pageIdxs.length === 0) {
return getUrlResponse(url, headers)
} else {
return Promise.all(
pageIdxs.map((page) => {
const urlWithPage = new URL(url)
urlWithPage.searchParams.append('page', page)
return getUrlResponse(urlWithPage, headers)
}),
)
}
}

function maybePrependWithV(v) {
Expand Down Expand Up @@ -686,21 +703,6 @@ function parseVersionFile(versionFilePath0) {
return appVersions
}

function jsonParseAsList(maybeJson) {
try {
const obj = JSON.parse(maybeJson)
if (!Array.isArray(obj)) {
throw new Error('expected a list!')
}

return obj
} catch (exc) {
throw new Error(
`Got an exception when trying to parse non-JSON list ${maybeJson}: ${exc}`,
)
}
}

function debugLog(groupName, message) {
const group = `Debugging for ${groupName}`
core.debug(
Expand Down Expand Up @@ -1058,6 +1060,7 @@ function debugLoggingEnabled() {
}

module.exports = {
get,
getOTPVersion,
getElixirVersion,
getGleamVersion,
Expand Down
Loading
Loading