use super::Provider;
use crate::nixpacks::{
app::App,
environment::{Environment, EnvironmentVariables},
nix::pkg::Pkg,
plan::{
phase::{Phase, StartPhase},
BuildPlan,
},
};
use anyhow::Result;
use regex::{Match, Regex};
const DEFAULT_ELIXIR_PKG_NAME: &str = "elixir";
pub struct ElixirProvider {}
impl Provider for ElixirProvider {
fn name(&self) -> &str {
"elixir"
}
fn detect(&self, app: &App, _env: &Environment) -> Result<bool> {
Ok(app.includes_file("mix.exs"))
}
fn get_build_plan(&self, app: &App, env: &Environment) -> Result<Option<BuildPlan>> {
let mut plan = BuildPlan::default();
plan.add_variables(EnvironmentVariables::from([(
"MIX_ENV".to_string(),
"prod".to_string(),
)]));
let elixir_pkg = ElixirProvider::get_nix_elixir_package(app, env)?;
let setup_phase = Phase::setup(Some(vec![elixir_pkg]));
plan.add_phase(setup_phase);
let mut install_phase = Phase::install(Some("mix local.hex --force".to_string()));
install_phase.add_cmd("mix local.rebar --force");
install_phase.add_cmd("mix deps.get --only prod");
plan.add_phase(install_phase);
let mut build_phase = Phase::build(Some("mix compile".to_string()));
let mix_exs_content = app.read_file("mix.exs")?;
if mix_exs_content.contains("assets.deploy") {
build_phase.add_cmd("mix assets.deploy".to_string());
}
if mix_exs_content.contains("postgrex") && mix_exs_content.contains("ecto") {
build_phase.add_cmd("mix ecto.migrate");
build_phase.add_cmd("mix run priv/repo/seeds.exs");
}
plan.add_phase(build_phase);
let start_phase = StartPhase::new("mix phx.server".to_string());
plan.set_start_phase(start_phase);
Ok(Some(plan))
}
}
impl ElixirProvider {
fn get_nix_elixir_package(app: &App, env: &Environment) -> Result<Pkg> {
fn as_default(v: Option<Match>) -> &str {
match v {
Some(m) => m.as_str(),
None => "_",
}
}
let mix_exs_content = app.read_file("mix.exs")?;
let custom_version = env.get_config_variable("ELIXIR_VERSION");
let mix_elixir_version_regex = Regex::new(r#"(elixir:[\s].*[> ])([0-9|\.]*)"#)?;
let custom_version = if custom_version.is_some() {
custom_version
} else if custom_version.is_none() && app.includes_file(".elixir-version") {
Some(app.read_file(".elixir-version")?)
} else {
mix_elixir_version_regex
.captures(&mix_exs_content)
.map(|c| c.get(2).unwrap().as_str().to_owned())
};
if custom_version.is_none() {
return Ok(Pkg::new(DEFAULT_ELIXIR_PKG_NAME));
}
let custom_version = custom_version.unwrap();
let elixir_version_regex =
Regex::new(r#"^(?:[\sa-zA-Z-"']*)(\d*)(?:\.*)(\d*)(?:\.*\d*)(?:["']?)$"#)?;
let matches = elixir_version_regex.captures(custom_version.as_str().trim());
if matches.is_none() {
return Ok(Pkg::new(DEFAULT_ELIXIR_PKG_NAME));
}
let matches = matches.unwrap();
let parsed_version = (as_default(matches.get(1)), as_default(matches.get(2)));
match parsed_version {
("1", "9") => Ok(Pkg::new("elixir_1_9")),
("1", "10") => Ok(Pkg::new("elixir_1_10")),
("1", "11") => Ok(Pkg::new("elixir_1_11")),
("1", "12") => Ok(Pkg::new("elixir_1_12")),
_ => Ok(Pkg::new(DEFAULT_ELIXIR_PKG_NAME)),
}
}
}