-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
1,937 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,338 @@ | ||
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 elixir_version do | ||
elixir = Config.get(:elixir) | ||
if elixir, do: "~> #{Version.minor(elixir)}", else: Project.elixir() | ||
end | ||
|
||
defp otp_version, do: "> 21.0.0" | ||
|
||
defp matrix do | ||
Versions.matrix( | ||
elixir: elixir_version(), | ||
otp: otp_version() | ||
) | ||
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: matrix()], | ||
steps: [ | ||
checkout(), | ||
setup_elixir(os), | ||
restore(:deps), | ||
restore(:_build), | ||
restore(:dialyxir), | ||
get_deps(), | ||
compile_deps(os), | ||
compile(os), | ||
check_unused_deps(), | ||
check_code_format(), | ||
lint_code(), | ||
run_tests(os), | ||
run_coverage(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(), | ||
setup_elixir(os), | ||
get_deps(), | ||
compile_deps(os), | ||
compile(os), | ||
run_tests(os), | ||
run_coverage(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), | ||
run_coverage(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", | ||
id: "setup-beam", | ||
uses: "erlef/setup-beam@v1", | ||
with: [ | ||
elixir_version: ~e[matrix.elixir], | ||
otp_version: ~e[matrix.otp] | ||
] | ||
] | ||
end | ||
|
||
defp setup_elixir(:macos) do | ||
[ | ||
name: "Setup Elixir", | ||
id: "setup-beam", | ||
run: "brew install elixir" | ||
] | ||
end | ||
|
||
defp setup_elixir(:windows) do | ||
[ | ||
name: "Setup Elixir", | ||
id: "setup-beam", | ||
uses: "erlef/setup-beam@v1", | ||
with: [ | ||
elixir_version: Versions.latest(:elixir), | ||
otp_version: Versions.latest(:otp) | ||
] | ||
] | ||
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(: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(if: latest_version(true)) | ||
end | ||
end | ||
end | ||
|
||
defp restore(path, opts \\ []) do | ||
case Config.fetch!([:steps, :refresh]) do | ||
false -> | ||
:skip | ||
|
||
true -> | ||
{opt_if, opts} = Keyword.pop(opts, :if) | ||
opt_if = if opt_if, do: [if: opt_if], else: [] | ||
|
||
Keyword.merge( | ||
[name: "Restore #{path}"] ++ | ||
opt_if ++ | ||
[ | ||
uses: "actions/cache@v4", | ||
with: [ | ||
path: path, | ||
key: key(path) | ||
] | ||
], | ||
opts | ||
) | ||
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_unused_deps do | ||
case Config.get(:check_unused_deps, true) do | ||
false -> | ||
:skip | ||
|
||
true -> | ||
[ | ||
name: "Check unused dependencies", | ||
if: latest_version(true), | ||
run: mix(:deps, :unlock, check_unused: true) | ||
] | ||
end | ||
end | ||
|
||
defp check_code_format do | ||
case Config.get(:check_code_format, true) do | ||
false -> | ||
:skip | ||
|
||
true -> | ||
[ | ||
name: "Check code format", | ||
if: latest_version(true), | ||
run: mix(:format, check_formatted: true) | ||
] | ||
end | ||
end | ||
|
||
defp lint_code do | ||
case Project.has_dep?(:credo) do | ||
false -> | ||
:skip | ||
|
||
true -> | ||
[ | ||
name: "Lint code", | ||
if: latest_version(true), | ||
run: mix(:credo, strict: true) | ||
] | ||
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", | ||
if: latest_version(false), | ||
run: mix(:test) | ||
] | ||
|
||
false -> | ||
[ | ||
name: "Run tests", | ||
run: mix(:test) | ||
] | ||
end | ||
end | ||
|
||
defp run_coverage(:windows), do: :skip | ||
|
||
defp run_coverage(_nix) do | ||
case Project.has_dep?(:excoveralls) do | ||
true -> | ||
[ | ||
name: "Run tests with coverage", | ||
if: latest_version(true), | ||
run: mix(:coveralls, Config.get([:steps, :coveralls])) | ||
] | ||
|
||
false -> | ||
:skip | ||
end | ||
end | ||
|
||
defp dialyxir do | ||
case Project.has_dep?(:dialyxir) do | ||
false -> | ||
:skip | ||
|
||
true -> | ||
[ | ||
name: "Static code analysis", | ||
if: latest_version(true), | ||
run: mix(:dialyzer, force_check: true, format: "github") | ||
] | ||
end | ||
end | ||
|
||
defp key(key) do | ||
os = ~e[runner.os] | ||
elixir = ~e[matrix.elixir] | ||
otp = ~e[matrix.otp] | ||
setup_beam_version = ~e{steps.setup-beam.outputs.setup-beam-version} | ||
lock = ~e[hashFiles(format('{0}{1}', github.workspace, '/mix.lock'))] | ||
"#{key}-#{os}-#{elixir}-#{otp}-#{lock}-#{setup_beam_version}" | ||
end | ||
|
||
defp latest_version(true) do | ||
~e""" | ||
matrix.elixir == '#{Versions.latest(:elixir)}' && \ | ||
matrix.otp == '#{Versions.latest(:otp)}'\ | ||
""" | ||
end | ||
|
||
defp latest_version(false) do | ||
~e""" | ||
!(matrix.elixir == '#{Versions.latest(:elixir)}' && \ | ||
matrix.otp == '#{Versions.latest(:otp)}')\ | ||
""" | ||
end | ||
|
||
defp member?(key, value), do: key |> Config.get() |> Enum.member?(value) | ||
end |
Oops, something went wrong.