View Source Defaults
The defaults create a workflow file that runs on ubuntu-20.04
. The matrix is
created with the setting of elixir
from the relevant project and the OTP
versions >= 20.
The linux
job gets the following steps:
checkout
setup_elixir
, usingerlef/setup-elixir@v1
restore(:deps)
, can be suppressed by configrestore(:_build)
, can be suppressed by configrestore(:dialyxir)
, when:dialyxir
is part of the dependencies, can be suppressed by configget_deps
compile_deps
compile
, with--warnings-as-errors
check_code_format
, can be suppressed by config, executes only for the latest Elixir/OTP versionlint_code
, when:credo
is part of the dependencies, executes only for the latest Elixir/OTP versionrun_tests
dialyxir
, when:dialyxir
is part of the dependencies
The workflow script also contains jobs for macOS
and Windows
. To activate
these jobs, set config :jobs, [:linux, :macos, :windows]
in the config. The
jobs for macOS
and Windows
use just the latest Elixir/OTP version.
config.exs
import GitHubActions.Config
config :output,
comment: true,
path: ".github/workflows",
file: "ci.yml"
config :input,
default: "default.exs"
config :steps,
refresh: true,
check_code_format: true,
dialyxir: true,
coveralls: :github
config :mix,
env: nil
# Specifies for which OSs jobs are generated. Possible values :linux,
# :windows and :macos.
config :jobs, [:linux]
# Specifies the linux distribution.
config :linux,
name: "Ubuntu",
runs_on: "ubuntu-20.04"
# Specifies the macos version.
config :macos,
name: "macOS",
runs_on: "macos-latest"
# Specifies the windows version.
config :windows,
name: "Windows",
runs_on: "windows-latest"
config versions: [
[
otp: ["17.0/5"],
elixir: [
"1.0.0/5",
"1.1.0/1"
]
],
[
otp: ["18.0/3"],
elixir: [
"1.0.5",
"1.1.0/1",
"1.2.0/6",
"1.3.0/4",
"1.4.0/5",
"1.5.0/3"
]
],
[
otp: ["19.0/3"],
elixir: [
"1.2.6",
"1.3.0/4",
"1.4.0/5",
"1.5.0/3",
"1.6.0/6",
"1.7.0/4"
]
],
[
otp: ["20.0/3"],
elixir: [
"1.4.5",
"1.5.0/3",
"1.6.0/6",
"1.7.0/4",
"1.8.0/2",
"1.9.0/4"
]
],
[
otp: ["21.0/3"],
elixir: [
"1.6.6",
"1.7.0/4",
"1.8.0/2",
"1.9.0/4",
"1.10.0/4",
"1.11.0/4"
]
],
[
otp: ["22.0/3"],
elixir: [
"1.7.0/4",
"1.8.0/2",
"1.9.0/4",
"1.10.0/4",
"1.11.0/4",
"1.12.0/2"
]
],
[
otp: ["23.0/3"],
elixir: [
"1.10.3/4",
"1.11.0/4",
"1.12.0/2"
]
],
[
otp: ["24.0/1"],
elixir: [
"1.11.4",
"1.12.0/2"
]
]
]
default.exs
defmodule GitHubActions.Default do
use GitHubActions.Workflow
def workflow do
[
name: "CI",
env: [
GITHUB_TOKEN: ~e[secrets.GITHUB_TOKEN]
],
on: ~w(pull_request push),
jobs: [
linux: job(:linux),
windows: job(:windows),
macos: job(:macos)
]
]
end
defp job(:linux = os) do
job(os,
name: """
Test on #{Config.get([os, :name])} (\
Elixir #{~e[matrix.elixir]}, \
OTP #{~e[matrix.otp]})\
""",
runs_on: Config.get([os, :runs_on]),
strategy: [
matrix: Versions.matrix(elixir: Project.elixir(), otp: "> 20.0.0")
],
steps: [
checkout(),
setup_elixir(os),
restore(:deps),
restore(:_build),
restore(:dialyxir),
get_deps(),
compile_deps(os),
compile(os),
check_code_format(),
lint_code(),
run_tests(os),
dialyxir()
]
)
end
defp job(:windows = os) do
job(os,
name: "Test on #{Config.get([os, :name])}",
runs_on: Config.get([os, :runs_on]),
steps: [
checkout(),
restore(:chocolatey),
setup_elixir(os),
install_hex(),
install_rebar(),
get_deps(),
compile_deps(os),
compile(os),
run_tests(os)
]
)
end
defp job(:macos = os) do
job(os,
name: "Test on #{Config.get([os, :name])}",
runs_on: Config.get([os, :runs_on]),
steps: [
checkout(),
setup_elixir(os),
install_hex(),
install_rebar(),
restore(:deps),
restore(:_build),
get_deps(),
compile_deps(os),
compile(os),
run_tests(os)
]
)
end
defp job(os, config) do
case member?(:jobs, os) do
true -> config
false -> :skip
end
end
defp checkout do
[
name: "Checkout",
uses: "actions/checkout@v4"
]
end
defp setup_elixir(:linux) do
[
name: "Setup Elixir",
uses: "erlef/setup-elixir@v1",
with: [
elixir_version: ~e[matrix.elixir],
otp_version: ~e[matrix.otp]
]
]
end
defp setup_elixir(:macos) do
[
name: "Setup Elixir",
run: "brew install elixir"
]
end
defp setup_elixir(:windows) do
echo = ~S(echo "C:\ProgramData\chocolatey\lib\Elixir\bin;C:\ProgramData\chocolatey\bin")
path = ~S(Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append)
[
name: "Setup Elixir",
run: """
cinst elixir --no-progress
#{echo} | #{path}
"""
]
end
defp install_hex do
[
name: "Install hex",
run: mix(:local, :hex, force: true)
]
end
defp install_rebar do
[
name: "Install rebar",
run: mix(:local, :rebar, force: true)
]
end
defp restore(:chocolatey) do
[
name: "Restore chocolatey",
uses: "actions/cache@v4",
with: [
path: ~S"C:\Users\runneradmin\AppData\Local\Temp\chocolatey",
key: "#{~e[runner.os]}-chocolatey-#{~e[github.sha]}",
restore_keys: """
#{~e[runner.os]}-chocolatey-
"""
]
]
end
defp restore(:dialyxir) do
case Project.has_dep?(:dialyxir) and Config.get([:steps, :dialyxir]) do
false ->
:skip
true ->
case Project.fetch([:dialyzer, :plt_file]) do
:error ->
:skip
{:ok, {_, file}} ->
file |> Path.dirname() |> restore()
end
end
end
defp restore(path) do
case Config.fetch!([:steps, :refresh]) do
false ->
:skip
true ->
[
name: "Restore #{path}",
uses: "actions/cache@v4",
with: [
path: "#{path}",
key: key(path)
]
]
end
end
defp get_deps do
[
name: "Get dependencies",
run: mix(:deps, :get)
]
end
defp compile_deps(os) do
[
name: "Compile dependencies",
run: mix(:deps, :compile, env: :test, os: os)
]
end
defp compile(os) do
[
name: "Compile project",
run: mix(:compile, warnings_as_errors: true, env: :test, os: os)
]
end
defp check_code_format do
case Config.get(:check_code_format, true) do
false ->
:skip
true ->
[
name: "Check code format",
if: ~e"""
contains(matrix.elixir, '#{Versions.latest(:elixir)}') && \
contains(matrix.otp, '#{Versions.latest(:otp)}')\
""",
run: mix(:format, check_formatted: true, env: :test)
]
end
end
defp lint_code do
case Project.has_dep?(:credo) do
false ->
:skip
true ->
[
name: "Lint code",
if: ~e"""
contains(matrix.elixir, '#{Versions.latest(:elixir)}') && \
contains(matrix.otp, '#{Versions.latest(:otp)}')\
""",
run: mix(:credo, strict: true, env: :test)
]
end
end
defp run_tests(:windows) do
[
name: "Run tests",
run: mix(:test)
]
end
defp run_tests(_nix) do
case Project.has_dep?(:excoveralls) do
true ->
[
name: "Run tests with coverage",
run: mix(:coveralls, Config.get([:steps, :coveralls]), env: :test)
]
false ->
[
name: "Run tests",
run: mix(:test)
]
end
end
defp dialyxir do
case Project.has_dep?(:dialyxir) do
false ->
:skip
true ->
[
name: "Static code analysis",
run: mix(:dialyzer)
]
end
end
defp key(key) do
os = ~e[runner.os]
elixir = ~e[matrix.elixir]
otp = ~e[matrix.otp]
lock = ~e[hashFiles(format('{0}{1}', github.workspace, '/mix.lock'))]
"#{key}-#{os}-#{elixir}-#{otp}-#{lock}"
end
defp member?(key, value), do: key |> Config.get() |> Enum.member?(value)
end