defmodule GitHubActions do @moduledoc """ A little tool to write GitHub actions in Elixir. """ import Mix.Generator alias GitHubActions.Config alias GitHubActions.Workflow alias GitHubActions.Yaml @version Mix.Project.config()[:version] @workflow "default.exs" @config "config.exs" @dir ".gha" @doc """ Creates a GitHub actions workflow file. """ @spec run(keyword()) :: boolean() def run(opts) do read_config(opts) opts |> workflow() |> Yaml.encode() |> write(opts) end @doc false def copy(gen_global: true) do create_directory(global()) copy_file(priv(@config), global(@config)) copy_file(priv(@workflow), global(@workflow)) end def copy(gen_local: true) do create_directory(local()) copy_file(priv(@config), local(@config)) copy_file(priv(@workflow), local(@workflow)) end def copy(_opts) do raise GitHubActions.Error, "option --gen-local or --gen-global is required" end defp read_config(opts) do # read default config first Config.read(priv(@config)) if File.exists?(global(@config)), do: Config.read(global(@config)) if File.exists?(local(@config)), do: Config.read(local(@config)) case Keyword.fetch!(opts, :config) do :default -> :ok config -> Config.read(config) end end defp workflow do case default_input() do {:ok, path} -> workflow(path) {:error, script} -> raise GitHubActions.Error, "no workflow script found, sought: #{inspect(script)}" end end defp workflow(opts) when is_list(opts) do opts |> Keyword.fetch!(:workflow) |> workflow() end defp workflow(:default), do: workflow() defp workflow(path) do case Workflow.eval(path) do {:ok, workflow} -> workflow :error -> raise GitHubActions.Error, "invalid workflow script, script: #{inspect(path)}" end end defp default_input do workflow = Config.get([:input, :default], @workflow) Enum.find_value( [ local(workflow), global(workflow), priv(workflow) ], {:error, workflow}, fn path -> with true <- File.exists?(path) do {:ok, path} end end ) end defp write(yaml, opts) do path = output_path(opts) comment = case Config.get([:output, :comment], true) do true -> "# Created with GitHubActions version #{@version}\n" false -> "" end create_file( path, comment <> yaml, force: true ) end defp output_path(opts) do with :default <- Keyword.fetch!(opts, :output) do Path.join( Config.fetch!([:output, :path]), Config.fetch!([:output, :file]) ) end end defp local, do: @dir defp local(file), do: Path.join(@dir, file) defp global, do: Path.join(user_home(), @dir) defp global(file), do: Path.join([user_home(), @dir, file]) # The home directory can be overridden via application env (used in tests), # defaulting to the user's home directory. defp user_home do Application.get_env(:git_hub_actions, :user_home) || System.user_home!() end defp priv(file), do: Path.join(:code.priv_dir(:git_hub_actions), file) defmodule Error do defexception [:message] end end