arcature-cli 2026.1.1

Developer lifecycle CLI for Arcature applications.
Documentation
use std::path::{Component, Path};

use crate::process::{ProcessSpec, run};
use crate::project::ProjectConfig;

use super::ScriptError;
use super::file::ScriptFile;

pub(crate) fn execute(
    project: &ProjectConfig,
    name: &str,
    forwarded: &[String],
) -> Result<(), ScriptError> {
    if !super::name::valid(name) {
        return Err(ScriptError::InvalidName(name.to_owned()));
    }
    let file = ScriptFile::load(project.root())?;
    let entry = file.scripts.get(name).ok_or_else(|| ScriptError::Unknown {
        name: name.to_owned(),
        available: file.scripts.keys().cloned().collect(),
    })?;
    if entry.program.is_empty() {
        return Err(ScriptError::MissingProgram(name.to_owned()));
    }
    let relative = entry.cwd.as_deref().unwrap_or_else(|| Path::new("."));
    if relative.is_absolute()
        || relative.components().any(|part| {
            matches!(
                part,
                Component::ParentDir | Component::RootDir | Component::Prefix(_)
            )
        })
    {
        return Err(ScriptError::InvalidWorkingDirectory(relative.to_path_buf()));
    }
    let root =
        project
            .root()
            .canonicalize()
            .map_err(|source| ScriptError::ResolveWorkingDirectory {
                path: project.root().to_path_buf(),
                source,
            })?;
    let directory_path = project.root().join(relative);
    let directory =
        directory_path
            .canonicalize()
            .map_err(|source| ScriptError::ResolveWorkingDirectory {
                path: directory_path,
                source,
            })?;
    if !directory.starts_with(&root) {
        return Err(ScriptError::InvalidWorkingDirectory(relative.to_path_buf()));
    }
    let mut specification = ProcessSpec::new(&entry.program, directory)
        .args(entry.args.iter())
        .args(forwarded.iter());
    for (key, value) in &entry.env {
        specification = specification.env(key, value);
    }
    run(&specification)?;
    Ok(())
}