feat: add nix manifest profile foundation
This commit is contained in:
+87
-6
@@ -60,7 +60,9 @@ fn resolve_socket(pod_name: &str, override_path: Option<PathBuf>) -> PathBuf {
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Mode {
|
||||
Spawn,
|
||||
Spawn {
|
||||
profile_path: Option<PathBuf>,
|
||||
},
|
||||
/// `insomnia <name>` / `insomnia --pod <name>`: attach to a live Pod by name if
|
||||
/// possible; otherwise launch `insomnia-pod --pod <name>` so the pod process
|
||||
/// resumes from name-keyed state or creates a fresh same-name Pod.
|
||||
@@ -111,6 +113,7 @@ where
|
||||
let mut multi = false;
|
||||
let mut session: Option<SegmentId> = None;
|
||||
let mut pod: Option<String> = None;
|
||||
let mut profile_path: Option<PathBuf> = None;
|
||||
let mut socket_override: Option<PathBuf> = None;
|
||||
let mut socket_seen = false;
|
||||
let mut positional: Option<String> = None;
|
||||
@@ -141,6 +144,13 @@ where
|
||||
pod = Some(raw.clone());
|
||||
i += 2;
|
||||
}
|
||||
"--profile" => {
|
||||
let raw = args
|
||||
.get(i + 1)
|
||||
.ok_or(ParseError::MissingValue("--profile"))?;
|
||||
profile_path = Some(PathBuf::from(raw));
|
||||
i += 2;
|
||||
}
|
||||
"--socket" => {
|
||||
socket_seen = true;
|
||||
let raw = args
|
||||
@@ -187,6 +197,11 @@ where
|
||||
"--multi and --socket are mutually exclusive",
|
||||
));
|
||||
}
|
||||
if profile_path.is_some() {
|
||||
return Err(ParseError::Conflict(
|
||||
"--multi and --profile are mutually exclusive",
|
||||
));
|
||||
}
|
||||
return Ok(Mode::Multi);
|
||||
}
|
||||
|
||||
@@ -205,6 +220,13 @@ where
|
||||
"--pod and --resume are mutually exclusive",
|
||||
));
|
||||
}
|
||||
if profile_path.is_some()
|
||||
&& (resume || session.is_some() || pod.is_some() || positional.is_some() || socket_seen)
|
||||
{
|
||||
return Err(ParseError::Conflict(
|
||||
"--profile can only be used for fresh spawn",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(pod_name) = pod {
|
||||
return Ok(Mode::PodName {
|
||||
@@ -224,7 +246,7 @@ where
|
||||
socket_override,
|
||||
});
|
||||
}
|
||||
Ok(Mode::Spawn)
|
||||
Ok(Mode::Spawn { profile_path })
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -248,13 +270,13 @@ async fn main() -> ExitCode {
|
||||
}
|
||||
|
||||
let result = match mode {
|
||||
Mode::Spawn => run_spawn(None).await,
|
||||
Mode::Spawn { profile_path } => run_spawn(None, profile_path).await,
|
||||
Mode::PodName {
|
||||
pod_name,
|
||||
socket_override,
|
||||
} => run_pod_name(pod_name, socket_override).await,
|
||||
Mode::Resume => run_resume().await,
|
||||
Mode::ResumeWithSession(id) => run_spawn(Some(id)).await,
|
||||
Mode::ResumeWithSession(id) => run_spawn(Some(id), None).await,
|
||||
Mode::Multi => run_multi().await,
|
||||
};
|
||||
|
||||
@@ -449,8 +471,11 @@ fn is_recoverable_multi_open_error(error: &(dyn std::error::Error + 'static)) ->
|
||||
error.is::<spawn::SpawnError>() || error.is::<NestedOpenCancelled>()
|
||||
}
|
||||
|
||||
async fn run_spawn(resume_from: Option<SegmentId>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let ready = match spawn::run(resume_from).await? {
|
||||
async fn run_spawn(
|
||||
resume_from: Option<SegmentId>,
|
||||
profile_path: Option<PathBuf>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let ready = match spawn::run(resume_from, profile_path).await? {
|
||||
SpawnOutcome::Ready(r) => r,
|
||||
SpawnOutcome::Cancelled => return Ok(()),
|
||||
};
|
||||
@@ -1154,6 +1179,62 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_profile_spawn_mode() {
|
||||
match parse_args_from(["--profile", "/profiles/coder.nix"]).unwrap() {
|
||||
Mode::Spawn { profile_path } => {
|
||||
assert_eq!(profile_path, Some(PathBuf::from("/profiles/coder.nix")));
|
||||
}
|
||||
_ => panic!("expected Spawn mode"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_profile_rejects_resume_attach_modes() {
|
||||
let segment_id = session_store::new_segment_id().to_string();
|
||||
let cases = [
|
||||
(
|
||||
vec![
|
||||
"--profile".to_string(),
|
||||
"p.nix".to_string(),
|
||||
"--resume".to_string(),
|
||||
],
|
||||
"--profile can only be used for fresh spawn",
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"--profile".to_string(),
|
||||
"p.nix".to_string(),
|
||||
"--session".to_string(),
|
||||
segment_id,
|
||||
],
|
||||
"--profile can only be used for fresh spawn",
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"--profile".to_string(),
|
||||
"p.nix".to_string(),
|
||||
"--socket".to_string(),
|
||||
"/tmp/insomnia/sock".to_string(),
|
||||
],
|
||||
"--profile can only be used for fresh spawn",
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"--profile".to_string(),
|
||||
"p.nix".to_string(),
|
||||
"agent".to_string(),
|
||||
],
|
||||
"--profile can only be used for fresh spawn",
|
||||
),
|
||||
];
|
||||
|
||||
for (args, message) in cases {
|
||||
let err = parse_args_from(args).unwrap_err();
|
||||
assert_eq!(err.to_string(), message);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multi_mode() {
|
||||
match parse_args_from(["--multi"]).unwrap() {
|
||||
|
||||
+27
-2
@@ -91,13 +91,20 @@ type InlineTerminal = Terminal<CrosstermBackend<io::Stdout>>;
|
||||
/// Source session for a resume run. `None` = fresh spawn (current
|
||||
/// behaviour); `Some(id)` swaps the dialog into "Resume Pod" mode and
|
||||
/// passes `--session <id>` to the spawned `insomnia-pod` child.
|
||||
pub async fn run(resume_from: Option<SegmentId>) -> Result<SpawnOutcome, SpawnError> {
|
||||
pub async fn run(
|
||||
resume_from: Option<SegmentId>,
|
||||
profile_path: Option<PathBuf>,
|
||||
) -> Result<SpawnOutcome, SpawnError> {
|
||||
let defaults = load_spawn_defaults()?;
|
||||
let scope_origin = match profile_path.as_ref() {
|
||||
Some(path) => ScopeOrigin::FromProfile(path.clone()),
|
||||
None => defaults.scope_origin,
|
||||
};
|
||||
|
||||
let mut form = Form {
|
||||
cwd: defaults.cwd.clone(),
|
||||
cascade_has_scope: defaults.cascade_has_scope,
|
||||
scope_origin: defaults.scope_origin,
|
||||
scope_origin,
|
||||
name_cursor: defaults.default_name.chars().count(),
|
||||
name: defaults.default_name,
|
||||
message: None,
|
||||
@@ -105,6 +112,7 @@ pub async fn run(resume_from: Option<SegmentId>) -> Result<SpawnOutcome, SpawnEr
|
||||
resume_from,
|
||||
resume_by_pod_name: false,
|
||||
resume_scope: None,
|
||||
profile_path,
|
||||
};
|
||||
|
||||
let mut terminal = make_inline_terminal()?;
|
||||
@@ -279,6 +287,7 @@ fn form_for_pod_name(pod_name: String, defaults: SpawnDefaults) -> Form {
|
||||
resume_from: None,
|
||||
resume_by_pod_name: true,
|
||||
resume_scope: None,
|
||||
profile_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +361,7 @@ async fn wait_for_ready(
|
||||
|
||||
let config = SpawnConfig {
|
||||
pod_name: form.name.clone(),
|
||||
profile_path: form.profile_path.clone(),
|
||||
overlay_toml: overlay_toml.to_string(),
|
||||
cwd,
|
||||
resume_from: form.resume_from,
|
||||
@@ -428,6 +438,7 @@ enum ScopeOrigin {
|
||||
FromUser,
|
||||
FromProject,
|
||||
CwdDefault,
|
||||
FromProfile(PathBuf),
|
||||
}
|
||||
|
||||
struct Form {
|
||||
@@ -462,6 +473,10 @@ struct Form {
|
||||
/// resume runs, and serialized into the overlay instead of cwd-default
|
||||
/// scope so resume does not silently broaden access.
|
||||
resume_scope: Option<ScopeConfig>,
|
||||
/// Optional Nix profile passed to `insomnia-pod --profile` for fresh spawns.
|
||||
/// This is not used for resume/attach flows because those must restore Pod
|
||||
/// state rather than re-evaluate a profile source.
|
||||
profile_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Form {
|
||||
@@ -593,6 +608,15 @@ fn context_line(form: &Form) -> Line<'_> {
|
||||
),
|
||||
Span::styled(" (write, default)", Style::default().fg(Color::DarkGray)),
|
||||
]),
|
||||
ScopeOrigin::FromProfile(ref path) => Line::from(vec![
|
||||
Span::raw(" "),
|
||||
Span::styled("profile: ", Style::default().fg(Color::DarkGray)),
|
||||
Span::styled(
|
||||
path.display().to_string(),
|
||||
Style::default().fg(Color::Green),
|
||||
),
|
||||
Span::styled(" (resolved by pod)", Style::default().fg(Color::DarkGray)),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,6 +663,7 @@ mod tests {
|
||||
resume_from: None,
|
||||
resume_by_pod_name: false,
|
||||
resume_scope: None,
|
||||
profile_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user