mirror of
https://github.com/erlef/setup-beam.git
synced 2026-07-23 07:06:07 +00:00
feat: setup custom retry with builtin node fetch (#304)
This commit is contained in:
@@ -7,7 +7,7 @@ on:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
- '*'
|
||||
workflow_dispatch: {}
|
||||
|
||||
env:
|
||||
@@ -62,6 +62,7 @@ jobs:
|
||||
id: setup-beam
|
||||
uses: ./
|
||||
with:
|
||||
install-rebar: false
|
||||
version-file: test/.tool-versions
|
||||
version-type: strict
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
- '*'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
@@ -53,10 +53,6 @@ jobs:
|
||||
elixir-version: '1.16'
|
||||
rebar3-version: '3'
|
||||
os: 'ubuntu-22.04'
|
||||
- otp-version: '24'
|
||||
elixir-version: '1.16'
|
||||
rebar3-version: '3'
|
||||
os: 'ubuntu-22.04'
|
||||
- otp-version: '25.0'
|
||||
elixir-version: 'v1.13.4-otp-25'
|
||||
rebar3-version: '3.18.0'
|
||||
|
||||
Vendored
+48
-45
@@ -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)
|
||||
})
|
||||
@@ -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') {
|
||||
@@ -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$/))
|
||||
@@ -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 = {}
|
||||
@@ -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?([^ ]+)/)
|
||||
@@ -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
|
||||
@@ -9660,8 +9659,38 @@ function getRunnerOSVersion() {
|
||||
return containerFromEnvImageOS
|
||||
}
|
||||
|
||||
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 (!response.ok) {
|
||||
throw new Error(response.statusText)
|
||||
}
|
||||
|
||||
if (contentType.indexOf('application/json') !== -1) {
|
||||
return response.json()
|
||||
} else {
|
||||
return response.text()
|
||||
}
|
||||
} catch (err) {
|
||||
core.debug(`Error fetching from ${url}: ${err}`)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function get(url0, pageIdxs) {
|
||||
async function getPage(pageIdx) {
|
||||
const url = new URL(url0)
|
||||
const headers = {}
|
||||
const GithubToken = getInput('github-token', false)
|
||||
@@ -9669,29 +9698,17 @@ async function get(url0, pageIdxs) {
|
||||
headers.authorization = `Bearer ${GithubToken}`
|
||||
}
|
||||
|
||||
if (pageIdx !== null) {
|
||||
url.searchParams.append('page', pageIdx)
|
||||
}
|
||||
|
||||
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 (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)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return response.readBody()
|
||||
}
|
||||
|
||||
if (pageIdxs[0] === null) {
|
||||
return getPage(null)
|
||||
}
|
||||
|
||||
return Promise.all(pageIdxs.map(getPage))
|
||||
}
|
||||
|
||||
function maybePrependWithV(v) {
|
||||
@@ -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(
|
||||
@@ -10158,6 +10160,7 @@ function debugLoggingEnabled() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get,
|
||||
getOTPVersion,
|
||||
getElixirVersion,
|
||||
getGleamVersion,
|
||||
|
||||
Generated
-1
@@ -9,7 +9,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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
+48
-45
@@ -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)
|
||||
})
|
||||
@@ -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') {
|
||||
@@ -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$/))
|
||||
@@ -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 = {}
|
||||
@@ -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?([^ ]+)/)
|
||||
@@ -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
|
||||
@@ -560,8 +559,38 @@ function getRunnerOSVersion() {
|
||||
return containerFromEnvImageOS
|
||||
}
|
||||
|
||||
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 (!response.ok) {
|
||||
throw new Error(response.statusText)
|
||||
}
|
||||
|
||||
if (contentType.indexOf('application/json') !== -1) {
|
||||
return response.json()
|
||||
} else {
|
||||
return response.text()
|
||||
}
|
||||
} catch (err) {
|
||||
core.debug(`Error fetching from ${url}: ${err}`)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function get(url0, pageIdxs) {
|
||||
async function getPage(pageIdx) {
|
||||
const url = new URL(url0)
|
||||
const headers = {}
|
||||
const GithubToken = getInput('github-token', false)
|
||||
@@ -569,29 +598,17 @@ async function get(url0, pageIdxs) {
|
||||
headers.authorization = `Bearer ${GithubToken}`
|
||||
}
|
||||
|
||||
if (pageIdx !== null) {
|
||||
url.searchParams.append('page', pageIdx)
|
||||
}
|
||||
|
||||
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 (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)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return response.readBody()
|
||||
}
|
||||
|
||||
if (pageIdxs[0] === null) {
|
||||
return getPage(null)
|
||||
}
|
||||
|
||||
return Promise.all(pageIdxs.map(getPage))
|
||||
}
|
||||
|
||||
function maybePrependWithV(v) {
|
||||
@@ -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(
|
||||
@@ -1058,6 +1060,7 @@ function debugLoggingEnabled() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get,
|
||||
getOTPVersion,
|
||||
getElixirVersion,
|
||||
getGleamVersion,
|
||||
|
||||
+27
-4
@@ -7,6 +7,7 @@ simulateInput('github-token', process.env.GITHUB_TOKEN)
|
||||
simulateInput('hexpm-mirrors', 'https://builds.hex.pm', { multiline: true })
|
||||
|
||||
const assert = require('assert')
|
||||
const http = require('http')
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
@@ -71,8 +72,8 @@ async function all() {
|
||||
await testRebar3Versions()
|
||||
|
||||
await testGetVersionFromSpec()
|
||||
|
||||
await testParseVersionFile()
|
||||
await testGetRetry()
|
||||
|
||||
await testElixirMixCompileError()
|
||||
await testElixirMixCompileWarning()
|
||||
@@ -870,10 +871,10 @@ async function testParseVersionFile() {
|
||||
const gleamVersion = unsimulateInput('gleam-version')
|
||||
const rebar3Version = unsimulateInput('rebar3-version')
|
||||
|
||||
const erlang = '25.1.1'
|
||||
const elixir = '1.14.1'
|
||||
const erlang = '27'
|
||||
const elixir = '1.17.0'
|
||||
const gleam = '0.23.0'
|
||||
const rebar3 = '3.16.0'
|
||||
const rebar3 = '3.24.0'
|
||||
const toolVersions = `# a comment
|
||||
erlang ref:v${erlang}# comment, no space, and ref:v
|
||||
elixir ref:${elixir} # comment, with space and ref:
|
||||
@@ -913,6 +914,28 @@ gleam ${gleam} \nrebar ${rebar3}`
|
||||
simulateInput('rebar3-version', rebar3Version)
|
||||
}
|
||||
|
||||
async function testGetRetry() {
|
||||
let attempt = 0
|
||||
const server = http.createServer((req, res) => {
|
||||
attempt++
|
||||
if (attempt == 2) {
|
||||
res.write('correct!')
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
server.listen(0)
|
||||
const port = server.address().port
|
||||
|
||||
const response = await setupBeam.get(`http://localhost:${port}`, [])
|
||||
assert.equal(response, 'correct!')
|
||||
assert.equal(attempt, 2)
|
||||
} finally {
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
||||
async function testElixirMixCompileError() {
|
||||
const [matcher] = problemMatcher.find(
|
||||
({ owner }) => owner === 'elixir-mixCompileError',
|
||||
|
||||
Reference in New Issue
Block a user