use std::fs;
use std::path::Path;
use anyhow::{Context, Result};
use serde_json::{Value, json};
use crate::catalog::quality_checks::{ci_workflow, render_check};
use crate::config::PackageWorkflow;
use crate::graph::TestRunner;
use crate::steps::{probe, run_in};
use crate::ui;
pub fn ensure_luaurc(project_dir: &Path) -> Result<()> {
let path = project_dir.join(".luaurc");
let original = if path.exists() {
Some(fs::read_to_string(&path)?)
} else {
None
};
match planned_luaurc(original.as_deref())? {
LuaurcUpdate::Unchanged => ui::ok(".luaurc already configured"),
LuaurcUpdate::Unparseable => {
ui::skip(".luaurc exists but couldn't be parsed, leaving it alone");
}
LuaurcUpdate::Write(contents) => {
fs::write(&path, contents)
.with_context(|| format!("failed to write {}", path.display()))?;
ui::ok("wrote .luaurc");
}
}
Ok(())
}
pub(crate) enum LuaurcUpdate {
Unchanged,
Unparseable,
Write(String),
}
pub(crate) fn planned_luaurc(original: Option<&str>) -> Result<LuaurcUpdate> {
planned_luaurc_setting(original, "languageMode", json!("strict"))
}
pub(super) fn planned_luaurc_setting(
original: Option<&str>,
key: &str,
value: Value,
) -> Result<LuaurcUpdate> {
let mut config = match original {
Some(text) => match serde_json::from_str::<Value>(text) {
Ok(Value::Object(map)) => map,
_ => return Ok(LuaurcUpdate::Unparseable),
},
None => serde_json::Map::new(),
};
if config.contains_key(key) {
return Ok(LuaurcUpdate::Unchanged);
}
config.insert(key.to_owned(), value);
Ok(LuaurcUpdate::Write(format!(
"{}\n",
serde_json::to_string_pretty(&Value::Object(config))?
)))
}
pub fn ensure_check_script(
project_dir: &Path,
selected_tools: &[String],
has_tests: bool,
) -> Result<bool> {
let Some(contents) = render_check(selected_tools, has_tests) else {
return Ok(false);
};
let path = project_dir.join(".lute").join("check.luau");
if path.exists() {
ui::ok(".lute/check.luau already exists");
return Ok(true);
}
fs::create_dir_all(path.parent().expect("joined path has a parent"))?;
fs::write(&path, contents).with_context(|| format!("failed to write {}", path.display()))?;
ui::ok("wrote .lute/check.luau");
Ok(true)
}
pub fn ensure_ci_workflow(
project_dir: &Path,
workflow: PackageWorkflow,
has_server_packages: bool,
runner: Option<TestRunner>,
backend: crate::graph::JestBackend,
) -> Result<()> {
let path = project_dir.join(".github").join("workflows").join("ci.yml");
if path.exists() {
ui::ok(".github/workflows/ci.yml already exists");
return Ok(());
}
fs::create_dir_all(path.parent().expect("joined path has a parent"))?;
fs::write(
&path,
ci_workflow(workflow, has_server_packages, runner, backend),
)
.with_context(|| format!("failed to write {}", path.display()))?;
ui::ok("wrote .github/workflows/ci.yml");
Ok(())
}
pub fn lute_setup(project_dir: &Path) -> Result<()> {
if !probe("lute", &["--version"]) {
ui::skip(
"lute isn't on PATH yet - run `lute setup --with-luaurc` in the project once it is",
);
return Ok(());
}
if let Err(err) = run_in("lute", &["setup", "--with-luaurc"], Some(project_dir)) {
ui::warn(&format!("lute setup failed, continuing - {err}"));
}
Ok(())
}