diff --git a/dist/index.js b/dist/index.js index eaf3664f..afaf85ef 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9160,7 +9160,9 @@ async function main() { async function installOTP(otpSpec, osVersion) { const otpVersion = await getOTPVersion(otpSpec, osVersion) - core.startGroup(`Installing Erlang/OTP ${otpVersion} - built on ${osVersion}`) + core.startGroup( + `Installing Erlang/OTP ${otpVersion} - built on ${getRunnerOSArchitecture()}/${osVersion}`, + ) await doWithMirrors({ hexMirrors: hexMirrorsInput(), actionTitle: `install Erlang/OTP ${otpVersion}`, @@ -9354,7 +9356,7 @@ async function getOTPVersions(osVersion) { let otpVersionsListings let originListing if (process.platform === 'linux') { - originListing = `/builds/otp/${osVersion}/builds.txt` + originListing = `/builds/otp/${getRunnerOSArchitecture()}/${osVersion}/builds.txt` otpVersionsListings = await doWithMirrors({ hexMirrors: hexMirrorsInput(), actionTitle: `fetch ${originListing}`, @@ -9605,6 +9607,32 @@ function isKnownBranch(ver) { return ['main', 'master', 'maint', 'latest'].includes(ver) } +function githubARMRunnerArchs() { + return ['ARM', 'ARM64'] +} + +function githubAMDRunnerArchs() { + return ['X86', 'X64'] +} + +function getRunnerOSArchitecture() { + // These options come from: + // https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables + if (githubARMRunnerArchs().includes(process.env.RUNNER_ARCH)) { + return 'arm64' + } + + if (githubAMDRunnerArchs().includes(process.env.RUNNER_ARCH)) { + return 'amd64' + } + + throw new Error( + 'Invalid Github runner architecture, expected one of ' + + `${githubAMDRunnerArchs().concat(githubARMRunnerArchs()).join(', ')} ` + + `but got process.env.RUNNER_ARCH = ${process.env.RUNNER_ARCH}`, + ) +} + function getRunnerOSVersion() { const ImageOSToContainer = { ubuntu18: 'ubuntu-18.04', @@ -9843,7 +9871,7 @@ async function install(toolName, opts) { tool: 'Erlang/OTP', linux: { downloadToolURL: () => - `${hexMirror}/builds/otp/${versionSpec}.tar.gz`, + `${hexMirror}/builds/otp/${getRunnerOSArchitecture()}/${versionSpec}.tar.gz`, extract: async (file) => { const dest = undefined const flags = ['zx', '--strip-components=1'] @@ -10133,6 +10161,8 @@ module.exports = { getGleamVersion, getRebar3Version, getVersionFromSpec, + githubAMDRunnerArchs, + githubARMRunnerArchs, install, parseVersionFile, } diff --git a/src/setup-beam.js b/src/setup-beam.js index b5c34ba8..1d8ccc08 100644 --- a/src/setup-beam.js +++ b/src/setup-beam.js @@ -55,7 +55,9 @@ async function main() { async function installOTP(otpSpec, osVersion) { const otpVersion = await getOTPVersion(otpSpec, osVersion) - core.startGroup(`Installing Erlang/OTP ${otpVersion} - built on ${osVersion}`) + core.startGroup( + `Installing Erlang/OTP ${otpVersion} - built on ${getRunnerOSArchitecture()}/${osVersion}`, + ) await doWithMirrors({ hexMirrors: hexMirrorsInput(), actionTitle: `install Erlang/OTP ${otpVersion}`, @@ -254,7 +256,7 @@ async function getOTPVersions(osVersion) { let otpVersionsListings let originListing if (process.platform === 'linux') { - originListing = `/builds/otp/${osVersion}/builds.txt` + originListing = `/builds/otp/${getRunnerOSArchitecture()}/${osVersion}/builds.txt` otpVersionsListings = await doWithMirrors({ hexMirrors: hexMirrorsInput(), actionTitle: `fetch ${originListing}`, @@ -505,6 +507,32 @@ function isKnownBranch(ver) { return ['main', 'master', 'maint', 'latest'].includes(ver) } +function githubARMRunnerArchs() { + return ['ARM', 'ARM64'] +} + +function githubAMDRunnerArchs() { + return ['X86', 'X64'] +} + +function getRunnerOSArchitecture() { + // These options come from: + // https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables + if (githubARMRunnerArchs().includes(process.env.RUNNER_ARCH)) { + return 'arm64' + } + + if (githubAMDRunnerArchs().includes(process.env.RUNNER_ARCH)) { + return 'amd64' + } + + throw new Error( + 'Invalid Github runner architecture, expected one of ' + + `${githubAMDRunnerArchs().concat(githubARMRunnerArchs()).join(', ')} ` + + `but got process.env.RUNNER_ARCH = ${process.env.RUNNER_ARCH}`, + ) +} + function getRunnerOSVersion() { const ImageOSToContainer = { ubuntu18: 'ubuntu-18.04', @@ -743,7 +771,7 @@ async function install(toolName, opts) { tool: 'Erlang/OTP', linux: { downloadToolURL: () => - `${hexMirror}/builds/otp/${versionSpec}.tar.gz`, + `${hexMirror}/builds/otp/${getRunnerOSArchitecture()}/${versionSpec}.tar.gz`, extract: async (file) => { const dest = undefined const flags = ['zx', '--strip-components=1'] @@ -1033,6 +1061,8 @@ module.exports = { getGleamVersion, getRebar3Version, getVersionFromSpec, + githubAMDRunnerArchs, + githubARMRunnerArchs, install, parseVersionFile, } diff --git a/test/setup-beam.test.js b/test/setup-beam.test.js index ab4d3d4b..56553a0c 100644 --- a/test/setup-beam.test.js +++ b/test/setup-beam.test.js @@ -61,8 +61,11 @@ async function all() { await testFailInstallElixir() await testFailInstallGleam() await testFailInstallRebar3() + await testFailGetOTPVersion() await testOTPVersions() + await testLinuxARM64OTPVersions() + await testLinuxAMD64OTPVersions() await testElixirVersions() await testGleamVersions() await testRebar3Versions() @@ -171,6 +174,9 @@ async function testOTPVersions() { ) if (process.platform === 'linux') { + const previousRunnerArch = process.env.RUNNER_ARCH + process.env.RUNNER_ARCH = 'X64' + before = simulateInput('version-type', 'strict') spec = '26' osVersion = 'ubuntu-24.04' @@ -252,6 +258,8 @@ async function testOTPVersions() { expected = 'master' got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) assert.deepStrictEqual(got, expected) + + process.env.RUNNER_ARCH = previousRunnerArch } if (process.platform === 'win32') { @@ -277,6 +285,222 @@ async function testOTPVersions() { simulateInput('hexpm-mirrors', hexMirrors, { multiline: true }) } +async function testLinuxARM64OTPVersions() { + let got + let expected + let spec + let osVersion + let before + const hexMirrors = simulateInput( + 'hexpm-mirrors', + 'https://repo.hex.pm, https://cdn.jsdelivr.net/hex', + { multiline: true }, + ) + + const arm64Options = setupBeam.githubARMRunnerArchs() + process.env.RUNNER_ARCH = + arm64Options[Math.floor(Math.random() * arm64Options.length)] + + if (process.platform === 'linux') { + before = simulateInput('version-type', 'strict') + spec = '26' + osVersion = 'ubuntu-24.04' + expected = 'maint-26' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', before) + spec = '27.0' + osVersion = 'ubuntu-24.04' + expected = 'OTP-27.0' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', before) + spec = '25.3.2.1' + osVersion = 'ubuntu-20.04' + expected = 'OTP-25.3.2.1' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', before) + + spec = '20' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.3.8.26' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + spec = '20.x' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.0' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.0.5' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.0.x' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.0.5' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = 'maint' + osVersion = 'ubuntu-22.04' + expected = 'maint' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + spec = 'master' + osVersion = 'ubuntu-22.04' + expected = 'master' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + } + + simulateInput('hexpm-mirrors', hexMirrors, { multiline: true }) +} + +async function testLinuxAMD64OTPVersions() { + let got + let expected + let spec + let osVersion + let before + const hexMirrors = simulateInput( + 'hexpm-mirrors', + 'https://repo.hex.pm, https://cdn.jsdelivr.net/hex', + { multiline: true }, + ) + + const amd64Options = setupBeam.githubAMDRunnerArchs() + process.env.RUNNER_ARCH = + amd64Options[Math.floor(Math.random() * amd64Options.length)] + + if (process.platform === 'linux') { + before = simulateInput('version-type', 'strict') + spec = '26' + osVersion = 'ubuntu-24.04' + expected = 'maint-26' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', before) + spec = '27.0' + osVersion = 'ubuntu-24.04' + expected = 'OTP-27.0' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', before) + spec = '25.3.2.1' + osVersion = 'ubuntu-20.04' + expected = 'OTP-25.3.2.1' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', before) + spec = '19.3.x' + osVersion = 'ubuntu-16.04' + expected = 'OTP-19.3.6.13' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '^19.3.6' + osVersion = 'ubuntu-16.04' + expected = 'OTP-19.3.6.13' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '^19.3' + osVersion = 'ubuntu-18.04' + expected = 'OTP-19.3.6.13' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.3.8.26' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + spec = '20.x' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.3.8.26' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.0' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.0.5' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = '20.0.x' + osVersion = 'ubuntu-20.04' + expected = 'OTP-20.0.5' + got = await setupBeam.getOTPVersion(spec, osVersion) + assert.deepStrictEqual(got, expected) + + spec = 'maint' + osVersion = 'ubuntu-22.04' + expected = 'maint' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + + spec = 'master' + osVersion = 'ubuntu-22.04' + expected = 'master' + got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + assert.deepStrictEqual(got, expected) + } + + simulateInput('hexpm-mirrors', hexMirrors, { multiline: true }) +} + +async function testFailGetOTPVersion() { + const hexMirrors = simulateInput( + 'hexpm-mirrors', + 'https://repo.hex.pm, https://cdn.jsdelivr.net/hex', + { multiline: true }, + ) + + const previousRunnerArch = process.env.RUNNER_ARCH + process.env.RUNNER_ARCH = 'invalid' + + if (process.platform === 'linux') { + const spec = '26' + const osVersion = 'ubuntu-24.04' + + assert.rejects( + async () => { + await setupBeam.getOTPVersion(spec, osVersion, hexMirrors) + }, + (err) => { + assert.ok(err instanceof Error) + return true + }, + `Fetching OTP Version with invalid Github runner architecture is supposed to fail`, + ) + } + process.env.RUNNER_ARCH = previousRunnerArch +} + async function testElixirVersions() { let got let expected