Compare commits

...

10 Commits

Author SHA1 Message Date
Bryan Paxton ade4f1d567 Don't preprend v when elixir version does not start with digits (#61) 2021-07-20 14:08:24 -05:00
Paulo F. Oliveira 9283f857f1 Improve warning message for missing ImageOS env. variable (self-hosted runners) (#59) 2021-07-12 18:35:08 +01:00
Paulo F. Oliveira 830642517c Improve visibility (+ fix bugs) over version-type and opt-in versions (#57)
(and tweak README regarding action versioning)
2021-07-09 10:11:50 +01:00
Paulo F. Oliveira d8a37f5aac Improve on "Add Windows to the mix" (#52)
* Support more OTP+Windows versions (try to bridge the gap with gleam-lang/setup-erlang that seems to support pre-21 versions)
* Merge ci.yml and test.yml
* Add Elixir to the Windows mix
* Hopefully improve error/warning messages
* Adapt the doc.s to the current reality
* Add option version-type (loose by default, for compatibility)
2021-07-08 15:35:25 +01:00
Paulo F. Oliveira 82b4475ba2 Add Windows to the mix (#49) 2021-06-24 21:58:51 +01:00
Paulo F. Oliveira dd4eb24e91 Adapt test var. naming to current reality (#47) 2021-06-09 16:21:33 +01:00
Paulo F. Oliveira e6db7bde79 Add Erlang 24 to README and some more info on version matching (#45) 2021-06-09 15:59:44 +01:00
Paulo F. Oliveira 31cf3db78c Ease maintenance (#43) 2021-05-28 14:49:33 +01:00
Paulo F. Oliveira 11d59b146c Allow for latest 100 rebar3 versions when fetching from GitHub (#42) 2021-05-27 20:28:05 -05:00
Wojtek Mach e332dc64ad Change how we print installed Elixir version (#36) 2021-05-26 13:29:59 +02:00
25 changed files with 1062 additions and 344 deletions
+13 -10
View File
@@ -6,14 +6,17 @@ extends:
- airbnb - airbnb
parserOptions: parserOptions:
ecmaVersion: 12 ecmaVersion: 12
rules: { rules:
indent: ["warn", 2], indent: ["warn", 2]
max-len: ["warn", 100], max-len: ["warn", 100]
no-console: 0, no-console: 0
no-undef: "error", no-undef: "error"
no-unused-vars: ["error", {"varsIgnorePattern": "^_"}], no-unused-vars: ["error", {"varsIgnorePattern": "^_"}]
no-use-before-define: 0, no-use-before-define: 0
operator-linebreak: 0, operator-linebreak: 0
require-jsdoc: 0, require-jsdoc: 0
semi: 0 semi: 0
}
settings:
react:
version: 999.999.999
@@ -1,5 +1,5 @@
--- ---
name: ci name: action
on: on:
push: push:
@@ -11,15 +11,16 @@ on:
jobs: jobs:
check_integrity: check_integrity:
name: Make sure expected pre-release actions are performed name: Expected local npm actions
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: '12' node-version: 12
- run: npm install -g npm - run: npm install -g npm
- run: npm install - run: npm install
- run: npm run build
- run: npm run format - run: npm run format
- run: npm install -g markdownlint-cli - run: npm install -g markdownlint-cli
- run: npm run markdownlint - run: npm run markdownlint
@@ -27,6 +28,27 @@ jobs:
- run: npm run yamllint - run: npm run yamllint
- run: npm run jslint - run: npm run jslint
- run: npm run licenses - run: npm run licenses
- run: npm run build
- name: Check if build left artifacts - name: Check if build left artifacts
run: git diff --exit-code run: git diff --exit-code
unit_tests_ubuntu:
name: Unit tests (Ubuntu)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm test
unit_tests_windows:
name: Unit tests (Windows)
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm install --production
- run: npm test
@@ -1,30 +1,20 @@
--- ---
name: test name: ubuntu
on: on:
push: {branches: main} push:
pull_request: {branches: main} branches:
repository_dispatch: - main
workflow_dispatch: pull_request:
branches:
- main
jobs: jobs:
unit_test:
name: Pre-release unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with: {node-version: '12'}
- run: npm ci
- run: npm test
integration_test: integration_test:
name: > name: >
Pre-release integration tests OTP ${{matrix.combo.otp-version}},
(Ubuntu ${{matrix.combo.os}}, Elixir ${{matrix.combo.elixir-version}},
Erlang/OTP ${{matrix.combo.otp-version}}, rebar3 ${{matrix.combo.rebar3-version}}
Elixir ${{matrix.combo.elixir-version}},
rebar3 ${{matrix.combo.rebar3-version}})
runs-on: ${{matrix.combo.os}} runs-on: ${{matrix.combo.os}}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -56,7 +46,7 @@ jobs:
os: 'ubuntu-16.04' os: 'ubuntu-16.04'
- elixir-version: 'v1.4' - elixir-version: 'v1.4'
otp-version: '20' otp-version: '20'
rebar3-version: '3.11' rebar3-version: '3.6.0'
os: 'ubuntu-18.04' os: 'ubuntu-18.04'
- elixir-version: 'v1.4' - elixir-version: 'v1.4'
otp-version: '20' otp-version: '20'
@@ -102,6 +92,20 @@ jobs:
otp-version: '24' otp-version: '24'
rebar3-version: '3.15' rebar3-version: '3.15'
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
- elixir-version: 'v1.11.0'
otp-version: '22.3.4.2'
os: 'ubuntu-20.04'
version-type: 'strict'
- elixir-version: '1.10.3'
otp-version: '22.3.4.1'
os: 'ubuntu-20.04'
version-type: 'strict'
- elixir-version: '1.10.3'
otp-version: '22.3.4.1'
os: 'ubuntu-20.04'
- elixir-version: 'master'
otp-version: '23.1'
os: 'ubuntu-20.04'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use erlef/setup-beam - name: Use erlef/setup-beam
@@ -111,6 +115,7 @@ jobs:
otp-version: ${{matrix.combo.otp-version}} otp-version: ${{matrix.combo.otp-version}}
elixir-version: ${{matrix.combo.elixir-version}} elixir-version: ${{matrix.combo.elixir-version}}
rebar3-version: ${{matrix.combo.rebar3-version}} rebar3-version: ${{matrix.combo.rebar3-version}}
version-type: ${{matrix.combo.version-type}}
- name: Erlang/OTP version (action) - name: Erlang/OTP version (action)
run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}" run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}"
- name: Elixir version (action) - name: Elixir version (action)
+87
View File
@@ -0,0 +1,87 @@
---
name: windows
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
integration_test:
name: >
OTP ${{matrix.combo.otp-version}},
Elixir ${{matrix.combo.elixir-version}},
rebar3 ${{matrix.combo.rebar3-version}}
runs-on: ${{matrix.combo.os}}
strategy:
fail-fast: false
matrix:
combo:
- otp-version: '24.0.2'
rebar3-version: '3.16'
os: 'windows-2019'
- otp-version: '23.0'
rebar3-version: '3.15'
os: 'windows-2019'
- otp-version: '24.0.2'
rebar3-version: '3.16'
os: 'windows-2016'
- otp-version: '23.0'
rebar3-version: '3.15'
os: 'windows-2016'
- otp-version: '22.3'
rebar3-version: '3.15'
os: 'windows-2016'
- otp-version: '22.0'
rebar3-version: '3.15'
os: 'windows-2016'
- otp-version: '21.3'
rebar3-version: '3.15'
os: 'windows-2016'
- otp-version: '21.0'
rebar3-version: '3.15'
os: 'windows-2016'
- elixir-version: 'v1.10'
otp-version: '23'
rebar3-version: '3.14'
os: 'windows-latest'
- elixir-version: 'v1.11'
otp-version: '24'
rebar3-version: '3.15'
os: 'windows-latest'
steps:
- uses: actions/checkout@v2
- name: Use erlef/setup-beam
id: setup-beam
uses: ./
with:
otp-version: ${{matrix.combo.otp-version}}
elixir-version: ${{matrix.combo.elixir-version}}
rebar3-version: ${{matrix.combo.rebar3-version}}
- name: Erlang/OTP version (action)
run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}"
- name: Elixir version (action)
run: echo "Elixir ${{steps.setup-beam.outputs.elixir-version}}"
if: ${{matrix.combo.elixir-version}}
- name: rebar3 version (action)
run: echo "rebar3 ${{steps.setup-beam.outputs.rebar3-version}}"
- name: mix version and help (CLI)
run: |
mix -v
mix help local.rebar
mix help local.hex
if: ${{matrix.combo.elixir-version}}
- name: Run Mix project tests
run: |
cd test-projects/mix
mix deps.get
mix test
if: ${{matrix.combo.elixir-version}}
- name: Run rebar3 project tests
run: |
cd test-projects/rebar3
rebar3 ct
if: ${{matrix.combo.rebar3-version}}
+1 -1
View File
@@ -209,7 +209,7 @@ THE SOFTWARE.
----- -----
The following software may be included in this product: ansi-regex, ansi-styles, callsites, chalk, has-flag, is-fullwidth-code-point, parent-module, path-key, resolve-from, shebang-regex, string-width, strip-ansi, supports-color, type-fest. A copy of the source code may be downloaded from https://github.com/chalk/ansi-regex.git (ansi-regex), https://github.com/chalk/ansi-styles.git (ansi-styles), https://github.com/sindresorhus/callsites.git (callsites), https://github.com/chalk/chalk.git (chalk), https://github.com/sindresorhus/has-flag.git (has-flag), https://github.com/sindresorhus/is-fullwidth-code-point.git (is-fullwidth-code-point), https://github.com/sindresorhus/parent-module.git (parent-module), https://github.com/sindresorhus/path-key.git (path-key), https://github.com/sindresorhus/resolve-from.git (resolve-from), https://github.com/sindresorhus/shebang-regex.git (shebang-regex), https://github.com/sindresorhus/string-width.git (string-width), https://github.com/chalk/strip-ansi.git (strip-ansi), https://github.com/chalk/supports-color.git (supports-color), https://github.com/sindresorhus/type-fest.git (type-fest). This software contains the following license and notice below: The following software may be included in this product: ansi-regex, ansi-styles, callsites, chalk, has-flag, is-fullwidth-code-point, parent-module, path-key, resolve-from, shebang-regex, string-width, strip-ansi, supports-color. A copy of the source code may be downloaded from https://github.com/chalk/ansi-regex.git (ansi-regex), https://github.com/chalk/ansi-styles.git (ansi-styles), https://github.com/sindresorhus/callsites.git (callsites), https://github.com/chalk/chalk.git (chalk), https://github.com/sindresorhus/has-flag.git (has-flag), https://github.com/sindresorhus/is-fullwidth-code-point.git (is-fullwidth-code-point), https://github.com/sindresorhus/parent-module.git (parent-module), https://github.com/sindresorhus/path-key.git (path-key), https://github.com/sindresorhus/resolve-from.git (resolve-from), https://github.com/sindresorhus/shebang-regex.git (shebang-regex), https://github.com/sindresorhus/string-width.git (string-width), https://github.com/chalk/strip-ansi.git (strip-ansi), https://github.com/chalk/supports-color.git (supports-color). This software contains the following license and notice below:
MIT License MIT License
+84 -73
View File
@@ -1,10 +1,12 @@
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 -->
# setup-beam [![GitHub Actions Test][test-img]][test] [![GitHub Actions CI][ci-img]][ci] # setup-beam [![GitHub Actions][action-img]][action] [![GitHub Actions][ubuntu-img]][ubuntu] [![GitHub Actions][windows-img]][windows]
[test]: https://github.com/erlef/setup-beam [action]: https://github.com/erlef/setup-beam
[test-img]: https://github.com/erlef/setup-beam/workflows/test/badge.svg [action-img]: https://github.com/erlef/setup-beam/workflows/action/badge.svg
[ci]: https://github.com/erlef/setup-beam [ubuntu]: https://github.com/erlef/setup-beam
[ci-img]: https://github.com/erlef/setup-beam/workflows/ci/badge.svg [ubuntu-img]: https://github.com/erlef/setup-beam/workflows/ubuntu/badge.svg
[windows]: https://github.com/erlef/setup-beam
[windows-img]: https://github.com/erlef/setup-beam/workflows/windows/badge.svg
This action sets up an Erlang/OTP environment for use in a GitHub Actions This action sets up an Erlang/OTP environment for use in a GitHub Actions
workflow by: workflow by:
@@ -12,42 +14,78 @@ workflow by:
- installing Erlang/OTP - installing Erlang/OTP
- optionally, installing Elixir - optionally, installing Elixir
- optionally, installing `rebar3` - optionally, installing `rebar3`
- optionally, installing `hex`
**Note** Currently, this action only supports Actions' `ubuntu-` runtimes. **Note** Currently, this action only supports Actions' `ubuntu-` and `windows-` runtimes.
## Usage ## Usage
See [action.yml](action.yml). See [action.yml](action.yml) for the action's specification.
**Note** The Erlang/OTP release version specification is [relatively **Note**: The Erlang/OTP release version specification is [relatively
complex](http://erlang.org/doc/system_principles/versions.html#version-scheme). complex](http://erlang.org/doc/system_principles/versions.html#version-scheme).
For best results, we recommend specifying exact Erlang/OTP, Elixir versions, and For best results, we recommend specifying exact Erlang/OTP, Elixir versions, and
`rebar3` versions. `rebar3` versions, and setting option `version-type` to `strict`.
However, values like `22.x` are also accepted, and we attempt to resolve them However, values like `22.x`, or even `>22`, are also accepted, and we attempt to resolve them
according to semantic versioning rules. according to semantic versioning rules. This implicitly means `version-type` is `loose`,
which is also the default value for this option.
Additionally, it is recommended that one specifies Erlang/OTP, Elixir and `rebar3` versions Additionally, it is recommended that one specifies Erlang/OTP, Elixir and `rebar3` versions
using YAML strings, as these examples do, so that numbers like `23.0` don't using YAML strings, as these examples do, so that numbers like `23.0` don't
end up being parsed as `23`, which is not equivalent. end up being parsed as `23`, which is not equivalent.
For pre-release Elixir versions, such as `1.11.0-rc.0`, use the full version For pre-release versions, such as `v1.11.0-rc.0`, use the full version
specifier (`1.11.0-rc.0`). Pre-release versions are opt-in, so `1.11.x` will specifier (`v1.11.0-rc.0`) and set option `version-type` to `strict`. Pre-release versions are
not match a pre-release. opt-in, so `1.11.x` will not match a pre-release.
### Compatibility between Ubuntu and Erlang/OTP ### Compatibility between Operating System and Erlang/OTP
This list presents the known working version combos between Ubuntu This list presents the known working version combos between the target operating system
and Erlang/OTP. and Erlang/OTP.
| Ubuntu | Erlang/OTP | Status | Operating system | Erlang/OTP | Status
|- |- |- |- |- |-
| ubuntu-16.04 | 17 - 23 | ✅ | ubuntu-16.04 | 17 - 24 | ✅
| ubuntu-18.04 | 17 - 23 | ✅ | ubuntu-18.04 | 17 - 24 | ✅
| ubuntu-20.04 | 20 - 23 | ✅ | ubuntu-20.04 | 20 - 24 | ✅
| windows-2016 | 21* - 24 | ✅
| windows-2019 | 21* - 24 | ✅
### Basic example (Elixir) **Note** *: prior to 23, Windows builds are only available for minor versions, e.g. 21.0, 21.3, 22.0, etc.
### Self-hosted runners
Self-hosted runners need to set env. variable `ImageOS` to one of the following, since the action
uses that to download assets:
| ImageOS | Operating system
|- |-
| ubuntu16 | ubuntu-16.04
| ubuntu18 | ubuntu-18.04
| ubuntu20 | ubuntu-20.04
| win16 | windows-2016
| win19 | windows-2019
as per the following example:
```yaml ```yaml
...
jobs:
test:
runs-on: self-hosted
env:
ImageOS: ubuntu20 # equivalent to runs-on ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-beam@v1
...
```
### Basic example (Erlang/OTP + Elixir, on Ubuntu)
```yaml
# create this in .github/workflows/ci.yml
on: push on: push
jobs: jobs:
@@ -63,9 +101,10 @@ jobs:
- run: mix test - run: mix test
``` ```
### Basic example (`rebar3`) ### Basic example (Erlang/OTP + `rebar3`, on Ubuntu)
```yaml ```yaml
# create this in .github/workflows/ci.yml
on: push on: push
jobs: jobs:
@@ -80,9 +119,10 @@ jobs:
- run: rebar3 ct - run: rebar3 ct
``` ```
### Matrix example (Elixir) ### Matrix example (Erlang/OTP + Elixir, on Ubuntu)
```yaml ```yaml
# create this in .github/workflows/ci.yml
on: push on: push
jobs: jobs:
@@ -103,9 +143,10 @@ jobs:
- run: mix test - run: mix test
``` ```
### Matrix example (`rebar3`) ### Matrix example (Erlang/OTP + `rebar3`, on Ubuntu)
```yaml ```yaml
# create this in .github/workflows/ci.yml
on: push on: push
jobs: jobs:
@@ -125,70 +166,40 @@ jobs:
- run: rebar3 ct - run: rebar3 ct
``` ```
### Phoenix example ### Basic example (Erlang/OTP + `rebar3`, on Windows)
```yaml ```yaml
# create this in .github/workflows/ci.yml
on: push on: push
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: windows-2019
services:
db:
image: postgres:11
ports: ['5432:5432']
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: erlef/setup-beam@v1 - uses: erlef/setup-beam@v1
with: with:
otp-version: '22.2' otp-version: '24'
elixir-version: '1.9.4' rebar3-version: '3.16.1'
- run: mix deps.get - run: rebar3 ct
- run: mix test
```
#### Authenticating with Postgres in Phoenix
When using the Phoenix example above, the `postgres` container has some
default authentication set up. Specifically, it expects a username of
"postgres", and a password of "postgres". It will be available at
`localhost:5432`.
The simplest way of setting these auth values in CI is by checking for the
`GITHUB_ACTIONS` environment variable that is set in all workflows:
```elixir
# config/test.exs
use Mix.Config
# Configure the database for local testing
config :app, App.Repo,
database: "my_app_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
# Configure the database for GitHub Actions
if System.get_env("GITHUB_ACTIONS") do
config :app, App.Repo,
username: "postgres",
password: "postgres"
end
``` ```
## Elixir Problem Matchers ## Elixir Problem Matchers
The Elixir Problem Matchers in this repository are adapted from [here](https://github.com/fr1zle/vscode-elixir/blob/45eddb589acd7ac98e0c7305d1c2b24668ca709a/package.json#L70-L118). See [MATCHER_NOTICE](MATCHER_NOTICE.md) for license details. The Elixir Problem Matchers in this repository are adapted from [here](https://github.com/fr1zle/vscode-elixir/blob/45eddb589acd7ac98e0c7305d1c2b24668ca709a/package.json#L70-L118). See [MATCHER_NOTICE](MATCHER_NOTICE.md) for license details.
## Action versioning
`setup-beam` has three version paths, described below, for example version `1.8.0`:
- `@v1`: the latest in the `1.y.z` series (this tag is movable),
- `@v1.8`: the latest in the `1.8.z` series (this tag is movable),
- `@v1.8.0`: release `1.8.0` (this tag is not movable).
To prevent issues in CI (unless you're OK with potential incompatibility between versions - we
make real a effort to not introduce those without bumping major) we suggest you to use `@vx.y.z`,
whenever possible.
## License ## License
The scripts and documentation in this project are released under the [MIT license](LICENSE.md). The scripts and documentation in this project are released under the [MIT license](LICENSE.md).
+233 -49
View File
@@ -5,7 +5,7 @@ simulateInput('install-rebar', 'true')
simulateInput('install-hex', 'true') simulateInput('install-hex', 'true')
const assert = require('assert') const assert = require('assert')
const setupElixir = require('../src/setup-beam') const setupBeam = require('../src/setup-beam')
const installer = require('../src/installer') const installer = require('../src/installer')
async function all() { async function all() {
@@ -16,6 +16,8 @@ async function all() {
await testOTPVersions() await testOTPVersions()
await testElixirVersions() await testElixirVersions()
await testRebar3Versions() await testRebar3Versions()
await testGetVersionFromSpec()
} }
async function testFailInstallOTP() { async function testFailInstallOTP() {
@@ -81,47 +83,69 @@ async function testOTPVersions() {
let spec let spec
let osVersion let osVersion
spec = '19.3.x' if (process.platform === 'linux') {
osVersion = 'ubuntu-16.04' spec = '19.3.x'
expected = 'OTP-19.3.6' osVersion = 'ubuntu-16.04'
got = await setupElixir.getOTPVersion(spec, osVersion) expected = 'OTP-19.3.6.13'
assert.deepStrictEqual(got, expected) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '^19.3.6' spec = '^19.3.6'
osVersion = 'ubuntu-16.04' osVersion = 'ubuntu-16.04'
expected = 'OTP-19.3.6' expected = 'OTP-19.3.6.13'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '^19.3' spec = '^19.3'
osVersion = 'ubuntu-18.04' osVersion = 'ubuntu-18.04'
expected = 'OTP-19.3.6' expected = 'OTP-19.3.6.13'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '20' spec = '20'
osVersion = 'ubuntu-20.04' osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8' expected = 'OTP-20.3.8.26'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '20.x' spec = '20.x'
osVersion = 'ubuntu-20.04' osVersion = 'ubuntu-20.04'
expected = 'OTP-20.3.8' expected = 'OTP-20.3.8.26'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '20.0' spec = '20.0'
osVersion = 'ubuntu-20.04' osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5' expected = 'OTP-20.0.5'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '20.0.x' spec = '20.0.x'
osVersion = 'ubuntu-20.04' osVersion = 'ubuntu-20.04'
expected = 'OTP-20.0.5' expected = 'OTP-20.0.5'
got = await setupElixir.getOTPVersion(spec, osVersion) got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
}
if (process.platform === 'win32') {
spec = '24.0.1'
osVersion = 'windows-latest'
expected = '24.0.1'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.2.x'
osVersion = 'windows-2016'
expected = '23.2.7'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
spec = '23.0'
osVersion = 'windows-2019'
expected = '23.0.4'
got = await setupBeam.getOTPVersion(spec, osVersion)
assert.deepStrictEqual(got, expected)
}
} }
async function testElixirVersions() { async function testElixirVersions() {
@@ -133,20 +157,44 @@ async function testElixirVersions() {
spec = '1.1.x' spec = '1.1.x'
otpVersion = 'OTP-23' otpVersion = 'OTP-23'
expected = 'v1.1.1' expected = 'v1.1.1'
got = await setupElixir.getElixirVersion(spec, otpVersion) got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '1.10.4' spec = '1.10.4'
otpVersion = 'OTP-23' otpVersion = 'OTP-23'
expected = 'v1.10.4-otp-23' expected = 'v1.10.4-otp-23'
got = await setupElixir.getElixirVersion(spec, otpVersion) got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '1.11.0-rc.0' spec = '1.12.1'
otpVersion = 'OTP-24.0.2'
expected = 'v1.12.1-otp-24'
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict')
spec = 'v1.11.0-rc.0'
otpVersion = 'OTP-23' otpVersion = 'OTP-23'
expected = 'v1.11.0-rc.0-otp-23' expected = 'v1.11.0-rc.0-otp-23'
got = await setupElixir.getElixirVersion(spec, otpVersion) got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
simulateInput('version-type', 'strict')
spec = 'v1.11.0'
otpVersion = '22.3.4.2'
expected = 'v1.11.0-otp-22'
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
simulateInput('version-type', 'strict')
spec = 'master'
otpVersion = '23.1'
expected = 'master-otp-23'
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
} }
async function testRebar3Versions() { async function testRebar3Versions() {
@@ -154,22 +202,158 @@ async function testRebar3Versions() {
let expected let expected
let spec let spec
spec = '3.13.x' spec = '3.10.x'
expected = '3.13.2' expected = '3.10.0'
got = await setupElixir.getRebar3Version(spec) got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '3.13.2' spec = '3.11'
expected = '3.13.2' expected = '3.11.1'
got = await setupElixir.getRebar3Version(spec) got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '3.13' spec = '3.10'
expected = '3.13.2' expected = '3.10.0'
got = await setupElixir.getRebar3Version(spec) got = await setupBeam.getRebar3Version(spec)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
} }
async function testGetVersionFromSpec() {
let got
let expected
let spec
const versions = [
'3.2.30.5',
'3.2.3.5',
'1',
'2',
'3.2.3.4.1',
'1.0.9',
'3.2.3.40.1',
'1.0.2',
'2.0',
'2.10',
'2.9',
'1.0',
'3.2.3.4.2',
'1.1.0',
'3.4.5.4',
'3.4.5.3',
'3.4.5.4.1',
'24.0-rc3',
'24.0-rc2',
'24.0',
'23.3.4',
'23.3.3',
'22.3.4.9.1',
'22.3.4.12.1',
'22.3.4.10.1',
'master',
'v11.11.0-rc.0-otp-23',
'22.3.4.2',
]
spec = '1'
expected = '1.1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '1.0'
expected = '1.0.9'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict')
spec = '1'
expected = '1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
simulateInput('version-type', 'strict')
spec = '1.0'
expected = '1.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
spec = '2'
expected = '2.10'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '3'
expected = '3.4.5.4.1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '3.2'
expected = '3.2.30.5'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '>20'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '24.0'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict')
spec = '24.0-rc3'
expected = '24.0-rc3'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
spec = '24.0-rc2'
expected = '24.0-rc2'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3'
expected = '22.3.4.12.1'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '23.3.3'
expected = '23.3.3'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '24'
expected = '24.0'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '23.3'
expected = '23.3.4'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict')
spec = 'master'
expected = 'master'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
spec = 'master'
expected = 'master'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict')
spec = '22.3.4.2'
expected = '22.3.4.2'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
}
function simulateInput(key, value) { function simulateInput(key, value) {
process.env[`INPUT_${key.replace(/ /g, '_').toUpperCase()}`] = value process.env[`INPUT_${key.replace(/ /g, '_').toUpperCase()}`] = value
} }
+7 -3
View File
@@ -1,5 +1,5 @@
--- ---
name: Setup Erlang/OTP with optional Elixir and/or rebar3 name: Setup Erlang/OTP with optional Elixir (and mix) and/or rebar3
description: > description: >
Set up a specific version of Erlang/OTP, Elixir, and/or rebar3 Set up a specific version of Erlang/OTP, Elixir, and/or rebar3
and add the command-line tools to the PATH and add the command-line tools to the PATH
@@ -13,13 +13,17 @@ inputs:
otp-version: otp-version:
description: Version range or exact version of Erlang/OTP to use description: Version range or exact version of Erlang/OTP to use
install-hex: install-hex:
description: Whether to install Hex description: Whether to install Hex (with Mix)
default: true default: true
install-rebar: install-rebar:
description: Whether to install Rebar description: Whether to install Rebar (with Mix)
default: true default: true
rebar3-version: rebar3-version:
description: Version range or exact version of rebar3 to use description: Version range or exact version of rebar3 to use
version-type:
description: strict means the versions are take as-are; loose means we try
to guess versions based on semver rules
default: loose
outputs: outputs:
elixir-version: elixir-version:
description: Exact version of Elixir that was installed description: Exact version of Elixir that was installed
+232 -79
View File
@@ -4594,7 +4594,13 @@ const path = __nccwpck_require__(5622)
* @param {string} otpVersion * @param {string} otpVersion
*/ */
async function installOTP(osVersion, otpVersion) { async function installOTP(osVersion, otpVersion) {
await exec(__nccwpck_require__.ab + "install-otp", [osVersion, otpVersion]) const OS = process.platform
if (OS === 'linux') {
await exec(__nccwpck_require__.ab + "install-otp.sh", [osVersion, otpVersion])
} else if (OS === 'win32') {
const script = __nccwpck_require__.ab + "install-otp.ps1"
await exec(`pwsh.exe ${script} -VSN:${otpVersion}`)
}
} }
/** /**
@@ -4603,7 +4609,13 @@ async function installOTP(osVersion, otpVersion) {
* @param {string} elixirVersion * @param {string} elixirVersion
*/ */
async function installElixir(elixirVersion) { async function installElixir(elixirVersion) {
await exec(__nccwpck_require__.ab + "install-elixir", [elixirVersion]) const OS = process.platform
if (OS === 'linux') {
await exec(__nccwpck_require__.ab + "install-elixir.sh", [elixirVersion])
} else if (OS === 'win32') {
const script = __nccwpck_require__.ab + "install-elixir.ps1"
await exec(`pwsh.exe ${script} -VSN:${elixirVersion}`)
}
} }
/** /**
@@ -4612,12 +4624,20 @@ async function installElixir(elixirVersion) {
* @param {string} rebar3Version * @param {string} rebar3Version
*/ */
async function installRebar3(rebar3Version) { async function installRebar3(rebar3Version) {
await exec(__nccwpck_require__.ab + "install-rebar3", [rebar3Version]) const OS = process.platform
if (OS === 'linux') {
await exec(__nccwpck_require__.ab + "install-rebar3.sh", [rebar3Version])
} else if (OS === 'win32') {
const script = __nccwpck_require__.ab + "install-rebar3.ps1"
await exec(`pwsh.exe ${script} -VSN:${rebar3Version}`)
}
} }
function checkPlatform() { function checkPlatform() {
if (process.platform !== 'linux') { if (process.platform !== 'linux' && process.platform !== 'win32') {
throw new Error('@erlef/setup-beam only supports Ubuntu Linux at this time') throw new Error(
'@erlef/setup-beam only supports Ubuntu and Windows at this time',
)
} }
} }
@@ -4639,6 +4659,7 @@ const { exec } = __nccwpck_require__(1514)
const path = __nccwpck_require__(5622) const path = __nccwpck_require__(5622)
const semver = __nccwpck_require__(1383) const semver = __nccwpck_require__(1383)
const https = __nccwpck_require__(7211) const https = __nccwpck_require__(7211)
const fs = __nccwpck_require__(5747)
const installer = __nccwpck_require__(2127) const installer = __nccwpck_require__(2127)
main().catch((err) => { main().catch((err) => {
@@ -4666,7 +4687,7 @@ async function main() {
} }
const rebar3Spec = core.getInput('rebar3-version', { required: false }) const rebar3Spec = core.getInput('rebar3-version', { required: false })
maybeInstallRebar3(rebar3Spec) await maybeInstallRebar3(rebar3Spec)
} }
async function installOTP(otpSpec, osVersion) { async function installOTP(otpSpec, osVersion) {
@@ -4676,13 +4697,23 @@ async function installOTP(otpSpec, osVersion) {
) )
await installer.installOTP(osVersion, otpVersion) await installer.installOTP(osVersion, otpVersion)
core.setOutput('otp-version', otpVersion) core.setOutput('otp-version', otpVersion)
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) if (process.platform === 'linux') {
core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`)
} else if (process.platform === 'win32') {
const otpPath = fs.readFileSync(`${process.env.RUNNER_TEMP}/otp_path.txt`, {
encoding: 'utf8',
flag: 'r',
})
core.addPath(otpPath)
}
console.log('##[endgroup]') console.log('##[endgroup]')
return otpVersion return otpVersion
} }
async function maybeInstallElixir(elixirSpec, otpVersion) { async function maybeInstallElixir(elixirSpec, otpVersion) {
let installed = false
if (elixirSpec) { if (elixirSpec) {
const elixirVersion = await getElixirVersion(elixirSpec, otpVersion) const elixirVersion = await getElixirVersion(elixirSpec, otpVersion)
console.log(`##[group]Installing Elixir ${elixirVersion}`) console.log(`##[group]Installing Elixir ${elixirVersion}`)
@@ -4692,13 +4723,13 @@ async function maybeInstallElixir(elixirSpec, otpVersion) {
console.log( console.log(
`##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`, `##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`,
) )
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`)
console.log('##[endgroup]') console.log('##[endgroup]')
return true installed = true
} }
return false return installed
} }
async function mix(shouldMix, what) { async function mix(shouldMix, what) {
@@ -4712,18 +4743,20 @@ async function mix(shouldMix, what) {
} }
async function maybeInstallRebar3(rebar3Spec) { async function maybeInstallRebar3(rebar3Spec) {
let installed = false
if (rebar3Spec) { if (rebar3Spec) {
const rebar3Version = await getRebar3Version(rebar3Spec) const rebar3Version = await getRebar3Version(rebar3Spec)
console.log(`##[group]Installing rebar3 ${rebar3Version}`) console.log(`##[group]Installing rebar3 ${rebar3Version}`)
await installer.installRebar3(rebar3Version) await installer.installRebar3(rebar3Version)
core.setOutput('rebar3-version', rebar3Version) core.setOutput('rebar3-version', rebar3Version)
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`)
console.log('##[endgroup]') console.log('##[endgroup]')
return true installed = true
} }
return false return installed
} }
async function getOTPVersion(otpSpec0, osVersion) { async function getOTPVersion(otpSpec0, osVersion) {
@@ -4732,7 +4765,7 @@ async function getOTPVersion(otpSpec0, osVersion) {
let otpVersion let otpVersion
if (otpSpec[1]) { if (otpSpec[1]) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (from spec ${otpSpec0}) ` + `Requested Erlang/OTP version (${otpSpec0}) ` +
"should not contain 'OTP-'", "should not contain 'OTP-'",
) )
} }
@@ -4744,7 +4777,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
} }
if (otpVersion === null) { if (otpVersion === null) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (from spec ${otpSpec0}) not found in build listing`, `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -4755,11 +4789,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
const elixirVersions = await getElixirVersions() const elixirVersions = await getElixirVersions()
const semverVersions = Array.from(elixirVersions.keys()).sort() const semverVersions = Array.from(elixirVersions.keys()).sort()
const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/)
let elixirVersion let elixirVersion
if (exSpec[2]) { if (exSpec[2]) {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` +
"should not contain '-otp-...'", "should not contain '-otp-...'",
) )
} }
@@ -4768,7 +4802,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
} }
if (!exSpec || elixirVersion === null) { if (!exSpec || elixirVersion === null) {
throw new Error( throw new Error(
`Requested Elixir version (from spec ${exSpec0}) not found in build listing`, `Requested Elixir version (${exSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/)
@@ -4780,20 +4815,25 @@ async function getElixirVersion(exSpec0, otpVersion) {
if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) { if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) {
// ... and it's available: use it! // ... and it's available: use it!
elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}` elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}`
core.info(`Using Elixir / -otp- version ${elixirVersionWithOTP}`) core.info(
`Using Elixir ${elixirVersion} (built for OTP ${otpVersionMajor})`,
)
} else { } else {
// ... and it's not available: fallback to the "generic" version (v1.4.5 only). // ... and it's not available: fallback to the 'generic' version (v1.4.5 only).
elixirVersionWithOTP = elixirVersion elixirVersionWithOTP = elixirVersion
core.info(`Using Elixir ${elixirVersionWithOTP}`) core.info(`Using Elixir ${elixirVersion}`)
} }
} else { } else {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) not ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` +
'found in build listing', "found in version list (should you be using option 'version-type': 'strict'?)",
) )
} }
return elixirVersionWithOTP const DigitStart = new RegExp('^\\d+')
return DigitStart.test(elixirVersion)
? `v${elixirVersionWithOTP}`
: elixirVersionWithOTP
} }
async function getRebar3Version(r3Spec) { async function getRebar3Version(r3Spec) {
@@ -4801,7 +4841,8 @@ async function getRebar3Version(r3Spec) {
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
if (rebar3Version === null) { if (rebar3Version === null) {
throw new Error( throw new Error(
`Requested rebar3 version (from spec ${r3Spec}) not found in build listing`, `Requested rebar3 version (${r3Spec}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -4809,39 +4850,59 @@ async function getRebar3Version(r3Spec) {
} }
async function getOTPVersions(osVersion) { async function getOTPVersions(osVersion) {
const otpVersionsListing = await get( let originListing
`https://repo.hex.pm/builds/otp/${osVersion}/builds.txt`, let pageIdxs
) if (process.platform === 'linux') {
originListing = `https://repo.hex.pm/builds/otp/${osVersion}/builds.txt`
pageIdxs = [null]
} else if (process.platform === 'win32') {
originListing =
'https://api.github.com/repos/erlang/otp/releases?per_page=100'
pageIdxs = [1, 2, 3]
}
const otpVersionsListings = await get(originListing, pageIdxs)
const otpVersions = new Map() const otpVersions = new Map()
otpVersionsListing if (process.platform === 'linux') {
.trim() otpVersionsListings
.split('\n') .trim()
.forEach((line) => { .split('\n')
const otpMatch = line.match(/^(OTP-)?([^ ]+)/) .forEach((line) => {
const otpMatch = line.match(/^(OTP-)?([^ ]+)/)
let otpVersion = otpMatch[2] const otpVersion = otpMatch[2]
if (semver.validRange(otpVersion)) { otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference
otpVersion = semver.minVersion(otpVersion).version })
} } else if (process.platform === 'win32') {
otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference otpVersionsListings.forEach((otpVersionsListing) => {
JSON.parse(otpVersionsListing)
.map((x) => x.assets)
.flat()
.filter((x) => x.name.match(/^otp_win64_.*.exe$/))
.forEach((x) => {
const otpMatch = x.name.match(/^otp_win64_(.*).exe$/)
const otpVersion = otpMatch[1]
otpVersions.set(otpVersion, otpVersion)
})
}) })
}
return otpVersions return otpVersions
} }
async function getElixirVersions() { async function getElixirVersions() {
const elixirVersionsListing = await get( const elixirVersionsListings = await get(
'https://repo.hex.pm/builds/elixir/builds.txt', 'https://repo.hex.pm/builds/elixir/builds.txt',
[null],
) )
const otpVersionsForElixirMap = new Map() const otpVersionsForElixirMap = new Map()
elixirVersionsListing elixirVersionsListings
.trim() .trim()
.split('\n') .split('\n')
.forEach((line) => { .forEach((line) => {
const elixirMatch = const elixirMatch =
line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
const elixirVersion = elixirMatch[1] const elixirVersion = elixirMatch[1]
const otpVersion = elixirMatch[2] const otpVersion = elixirMatch[2]
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
@@ -4856,66 +4917,158 @@ async function getElixirVersions() {
} }
async function getRebar3Versions() { async function getRebar3Versions() {
const resultJSON = await get( const resultJSONs = await get(
'https://api.github.com/repos/erlang/rebar3/releases', 'https://api.github.com/repos/erlang/rebar3/releases?per_page=100',
[1, 2, 3],
) )
const rebar3VersionsListing = JSON.parse(resultJSON) const rebar3VersionsListing = []
.map((x) => x.tag_name) resultJSONs.forEach((resultJSON) => {
.sort() JSON.parse(resultJSON)
.map((x) => x.tag_name)
.sort()
.forEach((v) => rebar3VersionsListing.push(v))
})
return rebar3VersionsListing return rebar3VersionsListing
} }
function getVersionFromSpec(spec, versions) { function getVersionFromSpec(spec, versions) {
if (versions.includes(spec)) { let version = null
return spec
if (
spec.match(/rc/) ||
core.getInput('version-type', { required: false }) === 'strict'
) {
version = spec
} }
return semver.maxSatisfying(versions, semver.validRange(spec)) if (version === null) {
// We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions
const versionsMap = versions.sort(sortVersions).reduce((acc, v) => {
if (!v.match(/rc/)) {
// release candidates are opt-in
acc[maybeCoerced(v)] = v
}
return acc
}, {})
const rangeForMax = semver.validRange(spec)
if (rangeForMax) {
version =
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
} else {
version = versionsMap[maybeCoerced(spec)]
}
}
return version
}
function maybeCoerced(v) {
let ret
try {
ret = semver.coerce(v).version
} catch {
// some stuff can't be coerced, like 'master'
ret = v
}
return ret
}
function sortVersions(left, right) {
let ret = 0
const newL = verAsComparableStr(left)
const newR = verAsComparableStr(right)
function verAsComparableStr(ver) {
const matchGroups = 5
const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/
const matches = ver.match(verSpec).splice(1, matchGroups)
return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '')
}
if (newL < newR) {
ret = -1
} else if (newL > newR) {
ret = 1
}
return ret
} }
function getRunnerOSVersion() { function getRunnerOSVersion() {
const mapToUbuntuVersion = { const ImageOSToContainer = {
ubuntu16: 'ubuntu-16.04', ubuntu16: 'ubuntu-16.04',
ubuntu18: 'ubuntu-18.04', ubuntu18: 'ubuntu-18.04',
ubuntu20: 'ubuntu-20.04', ubuntu20: 'ubuntu-20.04',
win16: 'windows-2016',
win19: 'windows-2019',
}
const containerFromEnvImageOS = ImageOSToContainer[process.env.ImageOS]
if (!containerFromEnvImageOS) {
throw new Error(
"Tried to map a target OS from env. variable 'ImageOS', but failed. If you're using a " +
"self-hosted runner, you should set 'env': 'ImageOS': ... to one of the following: " +
"['" +
`${Object.keys(ImageOSToContainer).join("', '")}` +
"']",
)
} }
return mapToUbuntuVersion[process.env.ImageOS] || 'ubuntu-18.04' return containerFromEnvImageOS
} }
function get(url) { async function get(url0, pageIdxs) {
return new Promise((resolve, reject) => { function getPage(pageIdx) {
https return new Promise((resolve, reject) => {
.get( const url = new URL(url0)
url, if (pageIdx !== null) {
{ url.searchParams.append('page', pageIdx)
headers: { 'user-agent': 'setup-beam' }, }
}, https
(res) => { .get(
let data = '' url,
res.on('data', (chunk) => { {
data += chunk headers: { 'user-agent': 'setup-beam' },
}) },
res.on('end', () => { (res) => {
resolve(data) let data = ''
}) res.on('data', (chunk) => {
}, data += chunk
) })
.on('error', (err) => { res.on('end', () => {
reject(err) if (res.statusCode >= 400 && res.statusCode <= 599) {
}) reject(
}) new Error(
} `Got ${res.statusCode} from ${url}. Exiting with error`,
),
function prependToPath(what) { )
process.env.PATH = `${what}:${process.env.PATH}` } else {
resolve(data)
}
})
},
)
.on('error', (err) => {
reject(err)
})
})
}
let ret
if (pageIdxs[0] === null) {
ret = getPage(null)
} else {
ret = Promise.all(pageIdxs.map((pageIdx) => getPage(pageIdx)))
}
return ret
} }
module.exports = { module.exports = {
getOTPVersion, getOTPVersion,
getElixirVersion, getElixirVersion,
getRebar3Version, getRebar3Version,
getVersionFromSpec,
} }
+19
View File
@@ -0,0 +1,19 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_INPUT="${VSN}.zip"
$FILE_OUTPUT="elixir.zip"
$DIR_FOR_BIN=".setup-beam/elixir"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null
$ProgressPreference="SilentlyContinue"
Expand-Archive -DestinationPath "${DIR_FOR_BIN}" -Path "${FILE_OUTPUT}"
$ProgressPreference="Continue"
Write-Output "Installed Erlang/OTP version follows"
& "$DIR_FOR_BIN/bin/iex" "-v" | Write-Output
-4
View File
@@ -9,12 +9,8 @@ FILE_INPUT="${VSN}.zip"
FILE_OUTPUT=elixir.zip FILE_OUTPUT=elixir.zip
DIR_FOR_BIN=.setup-beam/elixir DIR_FOR_BIN=.setup-beam/elixir
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}" mkdir -p "${DIR_FOR_BIN}"
unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}" unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}"
rm -f "${FILE_OUTPUT}"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed Elixir version follows" echo "Installed Elixir version follows"
${DIR_FOR_BIN}/bin/iex -v ${DIR_FOR_BIN}/bin/iex -v
+20
View File
@@ -0,0 +1,20 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_OUTPUT="otp.exe"
$DIR_FOR_BIN=".setup-beam/otp"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://github.com/erlang/otp/releases/download/OTP-$VSN/otp_win64_$VSN.exe" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null
Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN"
Start-Process "./$DIR_FOR_BIN/$FILE_OUTPUT" /S -Wait
$ErlExec = Get-ChildItem -Path "C:/Program Files/" -Recurse -Depth 2 -Filter 'erl.exe' -Name | ForEach-Object { Write-Output "C:/Program Files/$_" }
$ErlPath = Split-Path -Path "$ErlExec"
Write-Output "$ErlPath" | Out-File -FilePath otp_path.txt -Encoding utf8 -NoNewline
Write-Output "Installed Erlang/OTP version follows"
& "$ErlPath/erl.exe" "+V" | Write-Output
-4
View File
@@ -10,13 +10,9 @@ FILE_INPUT="${VSN}.tar.gz"
FILE_OUTPUT=otp.tar.gz FILE_OUTPUT=otp.tar.gz
DIR_FOR_BIN=.setup-beam/otp DIR_FOR_BIN=.setup-beam/otp
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}" mkdir -p "${DIR_FOR_BIN}"
tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1 tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1
rm -f "${FILE_OUTPUT}"
"${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}" "${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed Erlang/OTP version follows" echo "Installed Erlang/OTP version follows"
${DIR_FOR_BIN}/bin/erl -version ${DIR_FOR_BIN}/bin/erl -version
+21
View File
@@ -0,0 +1,21 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_INPUT="rebar3"
$FILE_OUTPUT="rebar3"
$FILE_OUTPUT_PS1="rebar3.ps1"
$DIR_FOR_BIN=".setup-beam/rebar3"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN/bin" -ItemType Directory | Out-Null
Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN/bin"
Write-Output "& escript.exe $PWD/$DIR_FOR_BIN/bin/$FILE_OUTPUT `$args" | Out-File -FilePath "$FILE_OUTPUT_PS1" -Encoding utf8 -Append
type $FILE_OUTPUT_PS1
Move-Item "$FILE_OUTPUT_PS1" "$DIR_FOR_BIN/bin"
Write-Output "Installed rebar3 version follows"
& "$DIR_FOR_BIN/bin/rebar3" "version" | Write-Output
-3
View File
@@ -9,12 +9,9 @@ FILE_INPUT=rebar3
FILE_OUTPUT=rebar3 FILE_OUTPUT=rebar3
DIR_FOR_BIN=.setup-beam/rebar3 DIR_FOR_BIN=.setup-beam/rebar3
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}/bin" mkdir -p "${DIR_FOR_BIN}/bin"
chmod +x "${FILE_OUTPUT}" chmod +x "${FILE_OUTPUT}"
mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin" mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed rebar3 version follows" echo "Installed rebar3 version follows"
${DIR_FOR_BIN}/bin/rebar3 version ${DIR_FOR_BIN}/bin/rebar3 version
-1
View File
@@ -5,7 +5,6 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "1.7.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "1.2.6", "@actions/core": "1.2.6",
+1 -2
View File
@@ -1,6 +1,5 @@
{ {
"name": "setup-beam", "name": "setup-beam",
"version": "1.7.1",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -9,7 +8,7 @@
"jslint": "eslint src/**/*.js && eslint __tests__/**/*.js", "jslint": "eslint src/**/*.js && eslint __tests__/**/*.js",
"licenses": "yarn licenses generate-disclaimer > 3RD_PARTY_LICENSES", "licenses": "yarn licenses generate-disclaimer > 3RD_PARTY_LICENSES",
"markdownlint": "markdownlint *.md", "markdownlint": "markdownlint *.md",
"shellcheck": "shellcheck src/install-*", "shellcheck": "shellcheck src/install-*.sh",
"test": "node __tests__/setup-beam.test.js", "test": "node __tests__/setup-beam.test.js",
"yamllint": "yamllint .github/workflows/**.yml && yamllint .*.yml && yamllint *.yml" "yamllint": "yamllint .github/workflows/**.yml && yamllint .*.yml && yamllint *.yml"
}, },
+19
View File
@@ -0,0 +1,19 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_INPUT="${VSN}.zip"
$FILE_OUTPUT="elixir.zip"
$DIR_FOR_BIN=".setup-beam/elixir"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null
$ProgressPreference="SilentlyContinue"
Expand-Archive -DestinationPath "${DIR_FOR_BIN}" -Path "${FILE_OUTPUT}"
$ProgressPreference="Continue"
Write-Output "Installed Erlang/OTP version follows"
& "$DIR_FOR_BIN/bin/iex" "-v" | Write-Output
@@ -9,12 +9,8 @@ FILE_INPUT="${VSN}.zip"
FILE_OUTPUT=elixir.zip FILE_OUTPUT=elixir.zip
DIR_FOR_BIN=.setup-beam/elixir DIR_FOR_BIN=.setup-beam/elixir
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}" mkdir -p "${DIR_FOR_BIN}"
unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}" unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}"
rm -f "${FILE_OUTPUT}"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed Elixir version follows" echo "Installed Elixir version follows"
${DIR_FOR_BIN}/bin/iex -v ${DIR_FOR_BIN}/bin/iex -v
+20
View File
@@ -0,0 +1,20 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_OUTPUT="otp.exe"
$DIR_FOR_BIN=".setup-beam/otp"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://github.com/erlang/otp/releases/download/OTP-$VSN/otp_win64_$VSN.exe" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null
Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN"
Start-Process "./$DIR_FOR_BIN/$FILE_OUTPUT" /S -Wait
$ErlExec = Get-ChildItem -Path "C:/Program Files/" -Recurse -Depth 2 -Filter 'erl.exe' -Name | ForEach-Object { Write-Output "C:/Program Files/$_" }
$ErlPath = Split-Path -Path "$ErlExec"
Write-Output "$ErlPath" | Out-File -FilePath otp_path.txt -Encoding utf8 -NoNewline
Write-Output "Installed Erlang/OTP version follows"
& "$ErlPath/erl.exe" "+V" | Write-Output
-4
View File
@@ -10,13 +10,9 @@ FILE_INPUT="${VSN}.tar.gz"
FILE_OUTPUT=otp.tar.gz FILE_OUTPUT=otp.tar.gz
DIR_FOR_BIN=.setup-beam/otp DIR_FOR_BIN=.setup-beam/otp
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}" mkdir -p "${DIR_FOR_BIN}"
tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1 tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1
rm -f "${FILE_OUTPUT}"
"${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}" "${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed Erlang/OTP version follows" echo "Installed Erlang/OTP version follows"
${DIR_FOR_BIN}/bin/erl -version ${DIR_FOR_BIN}/bin/erl -version
+21
View File
@@ -0,0 +1,21 @@
param([Parameter(Mandatory=$true)][string]$VSN)
$ErrorActionPreference="Stop"
Set-Location $Env:RUNNER_TEMP
$FILE_INPUT="rebar3"
$FILE_OUTPUT="rebar3"
$FILE_OUTPUT_PS1="rebar3.ps1"
$DIR_FOR_BIN=".setup-beam/rebar3"
$ProgressPreference="SilentlyContinue"
Invoke-WebRequest "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" -OutFile "$FILE_OUTPUT"
$ProgressPreference="Continue"
New-Item "$DIR_FOR_BIN/bin" -ItemType Directory | Out-Null
Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN/bin"
Write-Output "& escript.exe $PWD/$DIR_FOR_BIN/bin/$FILE_OUTPUT `$args" | Out-File -FilePath "$FILE_OUTPUT_PS1" -Encoding utf8 -Append
type $FILE_OUTPUT_PS1
Move-Item "$FILE_OUTPUT_PS1" "$DIR_FOR_BIN/bin"
Write-Output "Installed rebar3 version follows"
& "$DIR_FOR_BIN/bin/rebar3" "version" | Write-Output
@@ -9,12 +9,9 @@ FILE_INPUT=rebar3
FILE_OUTPUT=rebar3 FILE_OUTPUT=rebar3
DIR_FOR_BIN=.setup-beam/rebar3 DIR_FOR_BIN=.setup-beam/rebar3
rm -f "${FILE_OUTPUT}"
rm -rf "${DIR_FOR_BIN}"
wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}"
mkdir -p "${DIR_FOR_BIN}/bin" mkdir -p "${DIR_FOR_BIN}/bin"
chmod +x "${FILE_OUTPUT}" chmod +x "${FILE_OUTPUT}"
mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin" mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin"
echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH"
echo "Installed rebar3 version follows" echo "Installed rebar3 version follows"
${DIR_FOR_BIN}/bin/rebar3 version ${DIR_FOR_BIN}/bin/rebar3 version
+25 -5
View File
@@ -8,7 +8,13 @@ const path = require('path')
* @param {string} otpVersion * @param {string} otpVersion
*/ */
async function installOTP(osVersion, otpVersion) { async function installOTP(osVersion, otpVersion) {
await exec(path.join(__dirname, 'install-otp'), [osVersion, otpVersion]) const OS = process.platform
if (OS === 'linux') {
await exec(path.join(__dirname, 'install-otp.sh'), [osVersion, otpVersion])
} else if (OS === 'win32') {
const script = path.join(__dirname, 'install-otp.ps1')
await exec(`pwsh.exe ${script} -VSN:${otpVersion}`)
}
} }
/** /**
@@ -17,7 +23,13 @@ async function installOTP(osVersion, otpVersion) {
* @param {string} elixirVersion * @param {string} elixirVersion
*/ */
async function installElixir(elixirVersion) { async function installElixir(elixirVersion) {
await exec(path.join(__dirname, 'install-elixir'), [elixirVersion]) const OS = process.platform
if (OS === 'linux') {
await exec(path.join(__dirname, 'install-elixir.sh'), [elixirVersion])
} else if (OS === 'win32') {
const script = path.join(__dirname, 'install-elixir.ps1')
await exec(`pwsh.exe ${script} -VSN:${elixirVersion}`)
}
} }
/** /**
@@ -26,12 +38,20 @@ async function installElixir(elixirVersion) {
* @param {string} rebar3Version * @param {string} rebar3Version
*/ */
async function installRebar3(rebar3Version) { async function installRebar3(rebar3Version) {
await exec(path.join(__dirname, 'install-rebar3'), [rebar3Version]) const OS = process.platform
if (OS === 'linux') {
await exec(path.join(__dirname, 'install-rebar3.sh'), [rebar3Version])
} else if (OS === 'win32') {
const script = path.join(__dirname, 'install-rebar3.ps1')
await exec(`pwsh.exe ${script} -VSN:${rebar3Version}`)
}
} }
function checkPlatform() { function checkPlatform() {
if (process.platform !== 'linux') { if (process.platform !== 'linux' && process.platform !== 'win32') {
throw new Error('@erlef/setup-beam only supports Ubuntu Linux at this time') throw new Error(
'@erlef/setup-beam only supports Ubuntu and Windows at this time',
)
} }
} }
+207 -74
View File
@@ -3,6 +3,7 @@ const { exec } = require('@actions/exec')
const path = require('path') const path = require('path')
const semver = require('semver') const semver = require('semver')
const https = require('https') const https = require('https')
const fs = require('fs')
const installer = require('./installer') const installer = require('./installer')
main().catch((err) => { main().catch((err) => {
@@ -30,7 +31,7 @@ async function main() {
} }
const rebar3Spec = core.getInput('rebar3-version', { required: false }) const rebar3Spec = core.getInput('rebar3-version', { required: false })
maybeInstallRebar3(rebar3Spec) await maybeInstallRebar3(rebar3Spec)
} }
async function installOTP(otpSpec, osVersion) { async function installOTP(otpSpec, osVersion) {
@@ -40,13 +41,23 @@ async function installOTP(otpSpec, osVersion) {
) )
await installer.installOTP(osVersion, otpVersion) await installer.installOTP(osVersion, otpVersion)
core.setOutput('otp-version', otpVersion) core.setOutput('otp-version', otpVersion)
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) if (process.platform === 'linux') {
core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`)
} else if (process.platform === 'win32') {
const otpPath = fs.readFileSync(`${process.env.RUNNER_TEMP}/otp_path.txt`, {
encoding: 'utf8',
flag: 'r',
})
core.addPath(otpPath)
}
console.log('##[endgroup]') console.log('##[endgroup]')
return otpVersion return otpVersion
} }
async function maybeInstallElixir(elixirSpec, otpVersion) { async function maybeInstallElixir(elixirSpec, otpVersion) {
let installed = false
if (elixirSpec) { if (elixirSpec) {
const elixirVersion = await getElixirVersion(elixirSpec, otpVersion) const elixirVersion = await getElixirVersion(elixirSpec, otpVersion)
console.log(`##[group]Installing Elixir ${elixirVersion}`) console.log(`##[group]Installing Elixir ${elixirVersion}`)
@@ -56,13 +67,13 @@ async function maybeInstallElixir(elixirSpec, otpVersion) {
console.log( console.log(
`##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`, `##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`,
) )
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`)
console.log('##[endgroup]') console.log('##[endgroup]')
return true installed = true
} }
return false return installed
} }
async function mix(shouldMix, what) { async function mix(shouldMix, what) {
@@ -76,18 +87,20 @@ async function mix(shouldMix, what) {
} }
async function maybeInstallRebar3(rebar3Spec) { async function maybeInstallRebar3(rebar3Spec) {
let installed = false
if (rebar3Spec) { if (rebar3Spec) {
const rebar3Version = await getRebar3Version(rebar3Spec) const rebar3Version = await getRebar3Version(rebar3Spec)
console.log(`##[group]Installing rebar3 ${rebar3Version}`) console.log(`##[group]Installing rebar3 ${rebar3Version}`)
await installer.installRebar3(rebar3Version) await installer.installRebar3(rebar3Version)
core.setOutput('rebar3-version', rebar3Version) core.setOutput('rebar3-version', rebar3Version)
prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`)
console.log('##[endgroup]') console.log('##[endgroup]')
return true installed = true
} }
return false return installed
} }
async function getOTPVersion(otpSpec0, osVersion) { async function getOTPVersion(otpSpec0, osVersion) {
@@ -96,7 +109,7 @@ async function getOTPVersion(otpSpec0, osVersion) {
let otpVersion let otpVersion
if (otpSpec[1]) { if (otpSpec[1]) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (from spec ${otpSpec0}) ` + `Requested Erlang/OTP version (${otpSpec0}) ` +
"should not contain 'OTP-'", "should not contain 'OTP-'",
) )
} }
@@ -108,7 +121,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
} }
if (otpVersion === null) { if (otpVersion === null) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (from spec ${otpSpec0}) not found in build listing`, `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -119,11 +133,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
const elixirVersions = await getElixirVersions() const elixirVersions = await getElixirVersions()
const semverVersions = Array.from(elixirVersions.keys()).sort() const semverVersions = Array.from(elixirVersions.keys()).sort()
const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/)
let elixirVersion let elixirVersion
if (exSpec[2]) { if (exSpec[2]) {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` +
"should not contain '-otp-...'", "should not contain '-otp-...'",
) )
} }
@@ -132,7 +146,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
} }
if (!exSpec || elixirVersion === null) { if (!exSpec || elixirVersion === null) {
throw new Error( throw new Error(
`Requested Elixir version (from spec ${exSpec0}) not found in build listing`, `Requested Elixir version (${exSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/)
@@ -144,20 +159,25 @@ async function getElixirVersion(exSpec0, otpVersion) {
if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) { if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) {
// ... and it's available: use it! // ... and it's available: use it!
elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}` elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}`
core.info(`Using Elixir / -otp- version ${elixirVersionWithOTP}`) core.info(
`Using Elixir ${elixirVersion} (built for OTP ${otpVersionMajor})`,
)
} else { } else {
// ... and it's not available: fallback to the "generic" version (v1.4.5 only). // ... and it's not available: fallback to the 'generic' version (v1.4.5 only).
elixirVersionWithOTP = elixirVersion elixirVersionWithOTP = elixirVersion
core.info(`Using Elixir ${elixirVersionWithOTP}`) core.info(`Using Elixir ${elixirVersion}`)
} }
} else { } else {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) not ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` +
'found in build listing', "found in version list (should you be using option 'version-type': 'strict'?)",
) )
} }
return elixirVersionWithOTP const DigitStart = new RegExp('^\\d+')
return DigitStart.test(elixirVersion)
? `v${elixirVersionWithOTP}`
: elixirVersionWithOTP
} }
async function getRebar3Version(r3Spec) { async function getRebar3Version(r3Spec) {
@@ -165,7 +185,8 @@ async function getRebar3Version(r3Spec) {
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
if (rebar3Version === null) { if (rebar3Version === null) {
throw new Error( throw new Error(
`Requested rebar3 version (from spec ${r3Spec}) not found in build listing`, `Requested rebar3 version (${r3Spec}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -173,39 +194,59 @@ async function getRebar3Version(r3Spec) {
} }
async function getOTPVersions(osVersion) { async function getOTPVersions(osVersion) {
const otpVersionsListing = await get( let originListing
`https://repo.hex.pm/builds/otp/${osVersion}/builds.txt`, let pageIdxs
) if (process.platform === 'linux') {
originListing = `https://repo.hex.pm/builds/otp/${osVersion}/builds.txt`
pageIdxs = [null]
} else if (process.platform === 'win32') {
originListing =
'https://api.github.com/repos/erlang/otp/releases?per_page=100'
pageIdxs = [1, 2, 3]
}
const otpVersionsListings = await get(originListing, pageIdxs)
const otpVersions = new Map() const otpVersions = new Map()
otpVersionsListing if (process.platform === 'linux') {
.trim() otpVersionsListings
.split('\n') .trim()
.forEach((line) => { .split('\n')
const otpMatch = line.match(/^(OTP-)?([^ ]+)/) .forEach((line) => {
const otpMatch = line.match(/^(OTP-)?([^ ]+)/)
let otpVersion = otpMatch[2] const otpVersion = otpMatch[2]
if (semver.validRange(otpVersion)) { otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference
otpVersion = semver.minVersion(otpVersion).version })
} } else if (process.platform === 'win32') {
otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference otpVersionsListings.forEach((otpVersionsListing) => {
JSON.parse(otpVersionsListing)
.map((x) => x.assets)
.flat()
.filter((x) => x.name.match(/^otp_win64_.*.exe$/))
.forEach((x) => {
const otpMatch = x.name.match(/^otp_win64_(.*).exe$/)
const otpVersion = otpMatch[1]
otpVersions.set(otpVersion, otpVersion)
})
}) })
}
return otpVersions return otpVersions
} }
async function getElixirVersions() { async function getElixirVersions() {
const elixirVersionsListing = await get( const elixirVersionsListings = await get(
'https://repo.hex.pm/builds/elixir/builds.txt', 'https://repo.hex.pm/builds/elixir/builds.txt',
[null],
) )
const otpVersionsForElixirMap = new Map() const otpVersionsForElixirMap = new Map()
elixirVersionsListing elixirVersionsListings
.trim() .trim()
.split('\n') .split('\n')
.forEach((line) => { .forEach((line) => {
const elixirMatch = const elixirMatch =
line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
const elixirVersion = elixirMatch[1] const elixirVersion = elixirMatch[1]
const otpVersion = elixirMatch[2] const otpVersion = elixirMatch[2]
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
@@ -220,64 +261,156 @@ async function getElixirVersions() {
} }
async function getRebar3Versions() { async function getRebar3Versions() {
const resultJSON = await get( const resultJSONs = await get(
'https://api.github.com/repos/erlang/rebar3/releases', 'https://api.github.com/repos/erlang/rebar3/releases?per_page=100',
[1, 2, 3],
) )
const rebar3VersionsListing = JSON.parse(resultJSON) const rebar3VersionsListing = []
.map((x) => x.tag_name) resultJSONs.forEach((resultJSON) => {
.sort() JSON.parse(resultJSON)
.map((x) => x.tag_name)
.sort()
.forEach((v) => rebar3VersionsListing.push(v))
})
return rebar3VersionsListing return rebar3VersionsListing
} }
function getVersionFromSpec(spec, versions) { function getVersionFromSpec(spec, versions) {
if (versions.includes(spec)) { let version = null
return spec
if (
spec.match(/rc/) ||
core.getInput('version-type', { required: false }) === 'strict'
) {
version = spec
} }
return semver.maxSatisfying(versions, semver.validRange(spec)) if (version === null) {
// We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions
const versionsMap = versions.sort(sortVersions).reduce((acc, v) => {
if (!v.match(/rc/)) {
// release candidates are opt-in
acc[maybeCoerced(v)] = v
}
return acc
}, {})
const rangeForMax = semver.validRange(spec)
if (rangeForMax) {
version =
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
} else {
version = versionsMap[maybeCoerced(spec)]
}
}
return version
}
function maybeCoerced(v) {
let ret
try {
ret = semver.coerce(v).version
} catch {
// some stuff can't be coerced, like 'master'
ret = v
}
return ret
}
function sortVersions(left, right) {
let ret = 0
const newL = verAsComparableStr(left)
const newR = verAsComparableStr(right)
function verAsComparableStr(ver) {
const matchGroups = 5
const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/
const matches = ver.match(verSpec).splice(1, matchGroups)
return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '')
}
if (newL < newR) {
ret = -1
} else if (newL > newR) {
ret = 1
}
return ret
} }
function getRunnerOSVersion() { function getRunnerOSVersion() {
const mapToUbuntuVersion = { const ImageOSToContainer = {
ubuntu16: 'ubuntu-16.04', ubuntu16: 'ubuntu-16.04',
ubuntu18: 'ubuntu-18.04', ubuntu18: 'ubuntu-18.04',
ubuntu20: 'ubuntu-20.04', ubuntu20: 'ubuntu-20.04',
win16: 'windows-2016',
win19: 'windows-2019',
}
const containerFromEnvImageOS = ImageOSToContainer[process.env.ImageOS]
if (!containerFromEnvImageOS) {
throw new Error(
"Tried to map a target OS from env. variable 'ImageOS', but failed. If you're using a " +
"self-hosted runner, you should set 'env': 'ImageOS': ... to one of the following: " +
"['" +
`${Object.keys(ImageOSToContainer).join("', '")}` +
"']",
)
} }
return mapToUbuntuVersion[process.env.ImageOS] || 'ubuntu-18.04' return containerFromEnvImageOS
} }
function get(url) { async function get(url0, pageIdxs) {
return new Promise((resolve, reject) => { function getPage(pageIdx) {
https return new Promise((resolve, reject) => {
.get( const url = new URL(url0)
url, if (pageIdx !== null) {
{ url.searchParams.append('page', pageIdx)
headers: { 'user-agent': 'setup-beam' }, }
}, https
(res) => { .get(
let data = '' url,
res.on('data', (chunk) => { {
data += chunk headers: { 'user-agent': 'setup-beam' },
}) },
res.on('end', () => { (res) => {
resolve(data) let data = ''
}) res.on('data', (chunk) => {
}, data += chunk
) })
.on('error', (err) => { res.on('end', () => {
reject(err) if (res.statusCode >= 400 && res.statusCode <= 599) {
}) reject(
}) new Error(
} `Got ${res.statusCode} from ${url}. Exiting with error`,
),
function prependToPath(what) { )
process.env.PATH = `${what}:${process.env.PATH}` } else {
resolve(data)
}
})
},
)
.on('error', (err) => {
reject(err)
})
})
}
let ret
if (pageIdxs[0] === null) {
ret = getPage(null)
} else {
ret = Promise.all(pageIdxs.map((pageIdx) => getPage(pageIdx)))
}
return ret
} }
module.exports = { module.exports = {
getOTPVersion, getOTPVersion,
getElixirVersion, getElixirVersion,
getRebar3Version, getRebar3Version,
getVersionFromSpec,
} }