Skip to content

Commit

Permalink
Add support for ARM builds (#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
favrik authored Jun 16, 2024
1 parent c54ac46 commit 6ab5719
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 6 deletions.
36 changes: 33 additions & 3 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand Down Expand Up @@ -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}`,
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -10133,6 +10161,8 @@ module.exports = {
getGleamVersion,
getRebar3Version,
getVersionFromSpec,
githubAMDRunnerArchs,
githubARMRunnerArchs,
install,
parseVersionFile,
}
Expand Down
36 changes: 33 additions & 3 deletions src/setup-beam.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand Down Expand Up @@ -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}`,
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -1033,6 +1061,8 @@ module.exports = {
getGleamVersion,
getRebar3Version,
getVersionFromSpec,
githubAMDRunnerArchs,
githubARMRunnerArchs,
install,
parseVersionFile,
}
224 changes: 224 additions & 0 deletions test/setup-beam.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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') {
Expand All @@ -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
Expand Down

0 comments on commit 6ab5719

Please sign in to comment.