ref:main
use crate::client::Client;
use crate::config;
use crate::output;
use clap::{Args, Subcommand};
#[derive(Args)]
pub struct DeployArgs {
#[command(subcommand)]
pub command: DeployCommand,
}
#[derive(Subcommand)]
pub enum DeployCommand {
/// List deployments for an environment
List {
/// Repository (org/repo)
repo: Option<String>,
/// Environment name (required)
#[arg(long)]
env: String,
},
/// Create a deployment
Create {
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
/// Environment name
#[arg(long)]
env: String,
/// Ref/branch/tag to deploy
#[arg(long, name = "ref")]
deploy_ref: String,
/// Description
#[arg(long, default_value = "")]
description: String,
},
/// Show deployment status
Status {
/// Repository (org/repo)
repo: Option<String>,
},
/// Manage environments
Env(DeployEnvArgs),
}
#[derive(Args)]
pub struct DeployEnvArgs {
#[command(subcommand)]
pub command: DeployEnvCommand,
}
#[derive(Subcommand)]
pub enum DeployEnvCommand {
/// List environments
List {
/// Repository (org/repo)
repo: Option<String>,
},
/// Create an environment
Create {
/// Repository (org/repo)
#[arg(long)]
repo: Option<String>,
/// Environment name
#[arg(long)]
name: String,
},
}
pub async fn run(args: DeployArgs) -> Result<(), Box<dyn std::error::Error>> {
match args.command {
DeployCommand::List { repo, env } => list(repo.as_deref(), &env).await,
DeployCommand::Create {
repo,
env,
deploy_ref,
description,
} => create(repo.as_deref(), &env, &deploy_ref, &description).await,
DeployCommand::Status { repo } => status(repo.as_deref()).await,
DeployCommand::Env(env_args) => match env_args.command {
DeployEnvCommand::List { repo } => env_list(repo.as_deref()).await,
DeployEnvCommand::Create { repo, name } => env_create(repo.as_deref(), &name).await,
},
}
}
async fn list(repo: Option<&str>, env: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let resp: serde_json::Value = client
.get(&format!("/{org}/{name}/environments/{env}/deployments"))
.await?;
let deployments: Vec<serde_json::Value> = if let Some(arr) = resp.get("deployments") {
serde_json::from_value(arr.clone()).unwrap_or_default()
} else if let Some(arr) = resp.get("data") {
serde_json::from_value(arr.clone()).unwrap_or_default()
} else {
serde_json::from_value(resp).unwrap_or_default()
};
output::header(&format!("Deployments ({org}/{name})"));
let rows: Vec<Vec<String>> = deployments
.iter()
.map(|d| {
vec![
d.get("environment")
.and_then(|v| v.as_str())
.unwrap_or("?")
.to_string(),
output::colorize_status(d.get("status").and_then(|v| v.as_str()).unwrap_or("?")),
d.get("ref")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string(),
d.get("inserted_at")
.and_then(|v| v.as_str())
.map(output::format_time)
.unwrap_or_default(),
]
})
.collect();
output::print_table(&["ENV", "STATUS", "REF", "CREATED"], &rows);
Ok(())
}
async fn create(
repo: Option<&str>,
env: &str,
deploy_ref: &str,
description: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let _resp: serde_json::Value = client
.post(
&format!("/{org}/{name}/environments/{env}/deployments"),
&serde_json::json!({
"ref": deploy_ref,
"description": description,
}),
)
.await?;
output::success(&format!("Created deployment to {env}"));
output::detail("Ref", deploy_ref);
Ok(())
}
async fn status(repo: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let resp: serde_json::Value = client
.get(&format!("/{org}/{name}/deployments/status"))
.await?;
output::header(&format!("Deployment status ({org}/{name})"));
println!("{}", serde_json::to_string_pretty(&resp)?);
Ok(())
}
async fn env_list(repo: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, name) = config::resolve_repo(repo)?;
let resp: serde_json::Value = client.get(&format!("/{org}/{name}/environments")).await?;
let envs: Vec<serde_json::Value> = if let Some(arr) = resp.get("environments") {
serde_json::from_value(arr.clone()).unwrap_or_default()
} else if let Some(arr) = resp.get("data") {
serde_json::from_value(arr.clone()).unwrap_or_default()
} else {
serde_json::from_value(resp).unwrap_or_default()
};
output::header(&format!("Environments ({org}/{name})"));
let rows: Vec<Vec<String>> = envs
.iter()
.map(|e| {
vec![e
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("?")
.to_string()]
})
.collect();
output::print_table(&["NAME"], &rows);
Ok(())
}
async fn env_create(repo: Option<&str>, name: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_config()?;
let (org, repo_name) = config::resolve_repo(repo)?;
let _resp: serde_json::Value = client
.post(
&format!("/{org}/{repo_name}/environments"),
&serde_json::json!({ "name": name }),
)
.await?;
output::success(&format!("Created environment {name}"));
Ok(())
}