Skip to main content

ralph/commands/init/
workflow.rs

1//! Initialization workflow orchestration.
2//!
3//! Purpose:
4//! - Implement the end-to-end `ralph init` workflow.
5//!
6//! Responsibilities:
7//! - Create `.ralph` state, acquire the queue lock, and run the optional wizard.
8//! - Write queue, done, config, and optional README files.
9//! - Update `.gitignore`, warn on pending migrations, and build the final report.
10//!
11//! Scope:
12//! - Workflow orchestration only; individual writers and README helpers live in sibling modules.
13//!
14//! Usage:
15//! - Re-exported as `crate::commands::init::run_init`.
16//!
17//! Invariants/assumptions:
18//! - New projects always initialize `.jsonc` queue/done/config files.
19//! - Gitignore updates remain best-effort and idempotent.
20
21use crate::config;
22use crate::queue;
23use anyhow::{Context, Result};
24use std::fs;
25
26use super::migration_check::check_pending_migrations;
27use super::{InitOptions, InitReport, gitignore, readme, wizard, writers};
28
29pub fn run_init(resolved: &config::Resolved, opts: InitOptions) -> Result<InitReport> {
30    let ralph_dir = resolved.repo_root.join(".ralph");
31    fs::create_dir_all(&ralph_dir).with_context(|| format!("create {}", ralph_dir.display()))?;
32
33    let _queue_lock = queue::acquire_queue_lock(&resolved.repo_root, "init", opts.force_lock)?;
34
35    let wizard_answers = if opts.interactive {
36        Some(wizard::run_wizard()?)
37    } else {
38        None
39    };
40
41    let queue_path = resolved
42        .repo_root
43        .join(crate::constants::queue::DEFAULT_QUEUE_FILE);
44    let done_path = resolved
45        .repo_root
46        .join(crate::constants::queue::DEFAULT_DONE_FILE);
47    let config_path = resolved
48        .repo_root
49        .join(crate::constants::queue::DEFAULT_CONFIG_FILE);
50
51    let queue_status = writers::write_queue(
52        &queue_path,
53        opts.force,
54        &resolved.id_prefix,
55        resolved.id_width,
56        wizard_answers.as_ref(),
57    )?;
58    let done_status = writers::write_done(
59        &done_path,
60        opts.force,
61        &resolved.id_prefix,
62        resolved.id_width,
63    )?;
64    let config_status = writers::write_config(&config_path, opts.force, wizard_answers.as_ref())?;
65
66    let mut readme_status = None;
67    if crate::prompts::prompts_reference_readme(&resolved.repo_root)? {
68        let readme_path = resolved.repo_root.join(".ralph/README.md");
69        let (status, version) = readme::write_readme(&readme_path, opts.force, opts.update_readme)?;
70        readme_status = Some((status, version));
71    }
72
73    if let Err(e) = gitignore::ensure_ralph_gitignore_entries(&resolved.repo_root) {
74        log::warn!(
75            "Failed to update .gitignore: {}. You may need to manually add '.ralph/workspaces/' to your .gitignore.",
76            e
77        );
78    }
79
80    check_pending_migrations(resolved)?;
81
82    if opts.interactive {
83        wizard::print_completion_message(wizard_answers.as_ref(), &resolved.queue_path);
84    }
85
86    Ok(InitReport {
87        queue_status,
88        done_status,
89        config_status,
90        readme_status,
91        queue_path,
92        done_path,
93        config_path,
94    })
95}