arcature-cli 2026.1.1

Developer lifecycle CLI for Arcature applications.
Documentation
#![cfg(unix)]

use std::fs;
use std::path::PathBuf;
use std::process::Command;

fn root(label: &str) -> PathBuf {
    std::env::temp_dir().join(format!("arcature-script-{label}-{}", std::process::id()))
}

fn create_project(path: &std::path::Path, scripts: &str) {
    fs::create_dir_all(path).expect("project should be created");
    fs::write(path.join("arcature.toml"), "frontend = \"react\"\nfrontend_dir = \"frontend\"\nbackend_package = \"demo\"\nbackend_binary = \"demo\"\nbackend_port = 3000\n").expect("config should be written");
    fs::write(path.join("s.script"), scripts).expect("scripts should be written");
}

#[test]
fn executes_explicit_namespaced_script_and_propagates_status() {
    let project = root("status");
    create_project(
        &project,
        "[scripts.\"release:test\"]\nprogram = \"/bin/true\"\n\n[scripts.fail]\nprogram = \"/bin/false\"\n",
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_arc"))
            .current_dir(&project)
            .args(["s", "release:test"])
            .status()
            .expect("arc should run")
            .success()
    );
    assert!(
        !Command::new(env!("CARGO_BIN_EXE_arc"))
            .current_dir(&project)
            .args(["s", "fail"])
            .status()
            .expect("arc should run")
            .success()
    );
    assert!(
        !Command::new(env!("CARGO_BIN_EXE_arc"))
            .current_dir(&project)
            .args(["s", "missing"])
            .status()
            .expect("arc should run")
            .success()
    );
    fs::remove_dir_all(project).expect("project should be removed");
}

#[test]
fn forwards_metacharacters_as_argv_without_shell_execution() {
    let project = root("injection");
    let marker = project.join("injected");
    create_project(
        &project,
        "[scripts.echo]\nprogram = \"/bin/echo\"\nargs = [\"fixed\"]\n",
    );
    let payload = format!(";touch {}", marker.display());
    assert!(
        Command::new(env!("CARGO_BIN_EXE_arc"))
            .current_dir(&project)
            .args(["s", "echo", "--", &payload])
            .status()
            .expect("arc should run")
            .success()
    );
    assert!(!marker.exists());
    fs::remove_dir_all(project).expect("project should be removed");
}

#[test]
fn rejects_parent_working_directory() {
    let project = root("cwd");
    create_project(
        &project,
        "[scripts.escape]\nprogram = \"/bin/true\"\ncwd = \"..\"\n",
    );
    assert!(
        !Command::new(env!("CARGO_BIN_EXE_arc"))
            .current_dir(&project)
            .args(["s", "escape"])
            .status()
            .expect("arc should run")
            .success()
    );
    fs::remove_dir_all(project).expect("project should be removed");
}