feat(#2554): add hivectl forge reconcile-config to reconcile local applied config against forge main
This commit is contained in:
parent
11df4a1bf5
commit
d124dd205a
9 changed files with 335 additions and 1 deletions
|
|
@ -281,6 +281,41 @@ pub enum ForgeCmd {
|
|||
#[arg(long, conflicts_with = "password")]
|
||||
password_stdin: bool,
|
||||
},
|
||||
/// Show + reconcile the divergence between an agent's local applied
|
||||
/// config checkout and its forge `agent-configs/<agent>` main.
|
||||
///
|
||||
/// Always prints the diff first. `--from forge` resets the local
|
||||
/// checkout to forge main (effective on the next deploy); `--from local`
|
||||
/// is not supported yet. With no `--from`, prompts for the direction.
|
||||
ReconcileConfig {
|
||||
/// Agent whose config branches to reconcile.
|
||||
agent: String,
|
||||
/// Which side to reconcile from. Omit to be prompted after the diff.
|
||||
#[arg(long, value_enum)]
|
||||
from: Option<ReconcileFrom>,
|
||||
/// Include the full diff (not just `--stat`) in the report.
|
||||
#[arg(long)]
|
||||
verbose: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// Which side to reconcile config branches from (`hivectl forge
|
||||
/// reconcile-config --from`). Maps to [`hive_host_sock::ReconcileDirection`].
|
||||
#[derive(Clone, Copy, clap::ValueEnum)]
|
||||
pub enum ReconcileFrom {
|
||||
/// Reset the local applied checkout to forge main.
|
||||
Forge,
|
||||
/// Advance forge main from local — not supported yet.
|
||||
Local,
|
||||
}
|
||||
|
||||
impl From<ReconcileFrom> for hive_host_sock::ReconcileDirection {
|
||||
fn from(from: ReconcileFrom) -> Self {
|
||||
match from {
|
||||
ReconcileFrom::Forge => Self::Forge,
|
||||
ReconcileFrom::Local => Self::Local,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
//! daemon (which owns the forge admin token + account-token persistence);
|
||||
//! hivectl just resolves the password client-side and relays the request.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use hive_host_sock::{HostRequest, ReconcileDirection};
|
||||
|
||||
use crate::cli::ReconcileFrom;
|
||||
use crate::util::{daemon_request, resolve_password};
|
||||
|
||||
pub(crate) async fn forge_create_user(
|
||||
|
|
@ -29,3 +32,58 @@ pub(crate) async fn forge_create_user(
|
|||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `hivectl forge reconcile-config <agent> [--from <forge|local>] [--verbose]`.
|
||||
/// Always shows the divergence first (daemon computes it read-only), then
|
||||
/// applies the chosen direction — from `--from` or an interactive prompt.
|
||||
pub(crate) async fn forge_reconcile_config(
|
||||
socket: &Path,
|
||||
agent: &str,
|
||||
from: Option<ReconcileFrom>,
|
||||
verbose: bool,
|
||||
) -> Result<()> {
|
||||
daemon_request(
|
||||
socket,
|
||||
HostRequest::ReconcileConfigStatus {
|
||||
agent: agent.to_owned(),
|
||||
verbose,
|
||||
},
|
||||
"forge",
|
||||
)
|
||||
.await?;
|
||||
let direction = match from {
|
||||
Some(f) => Some(f.into()),
|
||||
None => prompt_direction()?,
|
||||
};
|
||||
let Some(direction) = direction else {
|
||||
println!("forge: aborted — no changes made");
|
||||
return Ok(());
|
||||
};
|
||||
daemon_request(
|
||||
socket,
|
||||
HostRequest::ReconcileConfigApply {
|
||||
agent: agent.to_owned(),
|
||||
direction,
|
||||
},
|
||||
"forge",
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Prompt the operator for the reconcile direction after showing the diff.
|
||||
/// Returns `None` to abort (empty / `q` / unrecognized input).
|
||||
fn prompt_direction() -> Result<Option<ReconcileDirection>> {
|
||||
print!("reconcile from [forge/local] (or q to abort): ");
|
||||
io::stdout().flush()?;
|
||||
let mut line = String::new();
|
||||
io::stdin().read_line(&mut line)?;
|
||||
match line.trim().to_ascii_lowercase().as_str() {
|
||||
"forge" | "f" => Ok(Some(ReconcileDirection::Forge)),
|
||||
"local" | "l" => Ok(Some(ReconcileDirection::Local)),
|
||||
"" | "q" | "quit" => Ok(None),
|
||||
other => {
|
||||
println!("forge: unrecognized direction {other:?} — aborting");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ use choom::choom;
|
|||
mod github;
|
||||
use github::github_set_token;
|
||||
mod forge;
|
||||
use forge::forge_create_user;
|
||||
use forge::{forge_create_user, forge_reconcile_config};
|
||||
mod agents;
|
||||
use agents::run_agents;
|
||||
mod power;
|
||||
|
|
@ -69,6 +69,11 @@ async fn main() -> Result<()> {
|
|||
password,
|
||||
password_stdin,
|
||||
} => forge_create_user(&socket, &name, password.as_deref(), password_stdin).await,
|
||||
ForgeCmd::ReconcileConfig {
|
||||
agent,
|
||||
from,
|
||||
verbose,
|
||||
} => forge_reconcile_config(&socket, &agent, from, verbose).await,
|
||||
},
|
||||
Cmd::Matrix { cmd } => run_matrix_cmd(&socket, cmd).await,
|
||||
Cmd::Github { cmd } => match cmd {
|
||||
|
|
|
|||
Loading…
Reference in a new issue