forklaunch 1.16.0

Launch faster with forklaunch
use std::io::Write;

use anyhow::{Context, Result};
use clap::{Arg, ArgAction, ArgMatches, Command};
use serde_json::json;
use termcolor::{ColorChoice, StandardStream, WriteColor};

use crate::{
    CliCommand,
    core::command::command,
    managed::{
        client::{Missing, post_json, print_dryrun, require_managed_mode, resolve_managed_auth},
        types::StateAccepted,
    },
};

#[derive(Debug)]
pub(super) struct ApplyVariablesCommand;

impl ApplyVariablesCommand {
    pub(super) fn new() -> Self {
        Self
    }
}

impl CliCommand for ApplyVariablesCommand {
    fn command(&self) -> Command {
        command(
            "apply-variables",
            "Redeploy the current version so newly written variables reach the tasks",
        )
        .long_about(
            "Redeploy the current version so newly written variables reach the tasks.\n\n\
             Writing a variable (`instance vars set`, `template vars set`, or the application's\n\
             environment editor) does not restart anything. This records pendingUpdate=variables\n\
             and redeploys the same version with the current values; the marker clears when the\n\
             platform reports the deploy finished, and stays (with lastError set) if it failed,\n\
             so a retry is the same command again.\n\n\
             Allowed while the instance is awaiting_claim, active or suspended; refused while a\n\
             fleet rollout is updating it.",
        )
        .arg(
            Arg::new("id")
                .long("id")
                .required(true)
                .help("Id of the instance"),
        )
        .arg(
            Arg::new("dryrun")
                .long("dryrun")
                .help("Print the request that would be sent without sending it")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("json")
                .long("json")
                .help("Output raw JSON instead of formatted terminal output")
                .action(ArgAction::SetTrue),
        )
    }

    fn handler(&self, matches: &ArgMatches) -> Result<()> {
        let mut stdout = StandardStream::stdout(ColorChoice::Always);
        let id = matches
            .get_one::<String>("id")
            .context("--id is required")?;
        let path = format!("/instances/{}/apply-variables", urlencoding::encode(id));

        if matches.get_flag("dryrun") {
            return print_dryrun("POST", &path, Some(&json!({})));
        }

        let auth_mode = resolve_managed_auth()?;
        require_managed_mode(&auth_mode)?;

        let accepted: StateAccepted = post_json(
            &auth_mode,
            &path,
            json!({}),
            Missing::Resource(format!("instance '{}'", id)),
        )?;

        if matches.get_flag("json") {
            println!("{}", serde_json::to_string_pretty(&accepted)?);
            return Ok(());
        }

        log_ok!(
            stdout,
            "Applying variables: redeploying the current version (state {})",
            accepted.state.as_deref().unwrap_or("unchanged")
        );
        log_info!(
            stdout,
            "Follow it with `forklaunch managed instance get --id {}`; pendingUpdate clears when the deploy lands.",
            id
        );
        writeln!(stdout)?;
        Ok(())
    }
}