lib/pakman/bootstrap.ex

defmodule Pakman.Bootstrap do
  alias Pakman.Environment
  alias Pakman.Setup
  alias Pakman.Bootstrap.Templates

  @system Application.compile_env(:pakman, :system) || System

  def perform(options \\ []) do
    workspace = System.get_env("GITHUB_WORKSPACE")

    @system.cmd("sudo", ["chown", "-R", "runner:abuild", workspace])

    %{organization: namespace, name: name, slug: slug} =
      Environment.repository()

    {version, _} = System.cmd("git", ["describe", "--tags", "--always"])

    version =
      version
      |> String.split("-")
      |> List.first()

    build = System.get_env("GITHUB_RUN_NUMBER")

    base_path = Path.join(workspace, ".apk/#{namespace}/#{name}")

    config_file =
      Keyword.get(options, :config) ||
        Path.join(workspace, "instellar.yml")

    config = YamlElixir.read_from_file!(config_file)

    options =
      if config["dependencies"]["trace"] do
        ["!check"]
      else
        ["!check", "!tracedeps"]
      end

    strip_binary = Map.get(config["build"], "strip_binary", true)

    options =
      if strip_binary == false do
        options ++ ["!strip"]
      else
        options
      end

    config =
      Map.merge(config, %{
        "options" => options,
        "dependencies" =>
          Map.merge(config["dependencies"], %{
            "runtime" =>
              merge_runtime_dependencies(
                config["dependencies"]["runtime"],
                config["type"]
              )
          })
      })

    File.mkdir_p!(base_path)

    case config["type"] do
      "custom" ->
        create_apkbuild(
          base_path,
          slug,
          String.trim(version),
          String.trim(build),
          config
        )

        create_file(base_path, slug, :pre_install)

        Map.get(config, "hook", %{})
        |> Enum.map(&create_hook_file(&1, base_path, slug))

      _ ->
        create_apkbuild(
          base_path,
          slug,
          String.trim(version),
          String.trim(build),
          config
        )

        if run_config = Map.get(config, "run") do
          create_life_cycle_files(base_path, run_config)
        end

        create_build_files(base_path, slug, config["type"])

        Map.get(config, "hook", %{})
        |> Enum.map(&create_hook_file(&1, base_path, slug))
    end

    Setup.perform()
  end

  defp create_apkbuild(base_path, name, version, build, configuration) do
    [base_path, "APKBUILD"]
    |> Enum.join("/")
    |> File.write!(
      Templates.apkbuild(
        name,
        determine_version(version),
        build,
        configuration
      )
    )
  end

  defp determine_version(version) do
    if String.match?(
         version,
         ~r/^([0-9]|[1-9][0-9]*)\.([0-9]|[1-9][0-9]*)\.([0-9]|[1-9][0-9]*)/
       ),
       do: version,
       else: "0.0.0"
  end

  defp merge_runtime_dependencies(nil, type),
    do: merge_runtime_dependencies([], type)

  defp merge_runtime_dependencies(original, "static") when is_list(original),
    do: original ++ ["nginx"]

  defp merge_runtime_dependencies(original, _) when is_list(original),
    do: original

  defp create_build_files(base_path, name, "static"),
    do: create_file(base_path, name, :pre_install)

  defp create_build_files(base_path, name, _) do
    create_file(base_path, name, :profile)
    create_file(base_path, name, :pre_install)
    create_file(base_path, name, :environment)
    create_file(base_path, name, :env_exec)
  end

  def create_life_cycle_files(base_path, %{"name" => name} = configuration) do
    [base_path, "#{name}.initd"]
    |> Path.join()
    |> File.write!(Templates.initd(configuration))

    [base_path, "#{name}.run"]
    |> Path.join()
    |> File.write!(Templates.run(configuration))

    [base_path, "#{name}.finish"]
    |> Path.join()
    |> File.write!(Templates.finish(configuration))

    [base_path, "#{name}.log"]
    |> Path.join()
    |> File.write!(Templates.log(name))
  end

  defp create_hook_file({hook_name, content}, base_path, name) do
    [base_path, "#{name}.#{hook_name}"]
    |> Path.join()
    |> File.write!(Templates.hook(content))
  end

  defp create_file(base_path, name, type) do
    file_type = generate_file_type(type)

    [base_path, "#{name}.#{file_type}"]
    |> Path.join()
    |> File.write!(apply(Templates, type, [name]))
  end

  defp generate_file_type(type) do
    type
    |> Atom.to_string()
    |> String.replace("_", "-")
  end
end