mz_deploy/cli/commands/
log.rs1use crate::cli::CliError;
13use crate::client::{Client, DeploymentHistoryEntry};
14use crate::config::Settings;
15use crate::{info, info_nonl, log};
16use chrono::{DateTime, Local};
17use owo_colors::{OwoColorize, Stream, Style};
18use std::fmt;
19use std::io::{IsTerminal, Write};
20use std::process::{Command, Stdio};
21
22#[derive(serde::Serialize)]
24#[serde(transparent)]
25struct HistoryOutput {
26 entries: Vec<DeploymentHistoryEntry>,
27}
28
29impl fmt::Display for HistoryOutput {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 writeln!(f, "Deployment history (promoted):\n")?;
32
33 let deployment_style = Style::new().yellow().bold();
34 for entry in &self.entries {
35 let datetime: DateTime<Local> = entry.promoted_at.with_timezone(&Local);
36 let date_str = datetime.format("%a %b %d %H:%M:%S %Y %z").to_string();
37
38 writeln!(
39 f,
40 "{} {} [{}]",
41 "deployment".if_supports_color(Stream::Stderr, |t| deployment_style.style(t)),
42 entry
43 .deploy_id
44 .if_supports_color(Stream::Stderr, |t| t.cyan()),
45 entry
46 .kind
47 .to_string()
48 .if_supports_color(Stream::Stderr, |t| t.dimmed())
49 )?;
50 if let Some(commit_sha) = &entry.git_commit {
51 writeln!(
52 f,
53 "{}: {}",
54 "Commit".if_supports_color(Stream::Stderr, |t| t.dimmed()),
55 commit_sha
56 )?;
57 }
58 writeln!(
59 f,
60 "{}: {}",
61 "Promoted by".if_supports_color(Stream::Stderr, |t| t.dimmed()),
62 entry
63 .deployed_by
64 .if_supports_color(Stream::Stderr, |t| t.yellow())
65 )?;
66 writeln!(
67 f,
68 "{}: {}",
69 "Date".if_supports_color(Stream::Stderr, |t| t.dimmed()),
70 date_str
71 )?;
72 writeln!(f)?;
73
74 for sq in &entry.schemas {
75 writeln!(
76 f,
77 " {}.{}",
78 sq.database
79 .if_supports_color(Stream::Stderr, |t| t.dimmed()),
80 sq.schema
81 )?;
82 }
83 writeln!(f)?;
84 }
85
86 Ok(())
87 }
88}
89
90pub async fn run(settings: &Settings, limit: Option<usize>) -> Result<(), CliError> {
110 let profile = settings.connection();
111 let client = Client::connect_with_profile(profile.clone())
112 .await
113 .map_err(CliError::Connection)?;
114
115 super::setup::verify(&client, settings.emulator()).await?;
116 super::setup::validate_connection(&client, settings.emulator()).await?;
117 let history = client.deployments().list_deployment_history(limit).await?;
118
119 let output = HistoryOutput { entries: history };
120
121 if log::json_output_enabled() {
122 log::output(&output);
123 } else if output.entries.is_empty() {
124 info!("No deployment history found.");
125 info!();
126 info!("To create and promote a deployment, run:");
127 info!(
128 " {} {} {}",
129 "mz-deploy".if_supports_color(Stream::Stderr, |t| t.cyan()),
130 "stage".if_supports_color(Stream::Stderr, |t| t.cyan()),
131 ".".if_supports_color(Stream::Stderr, |t| t.cyan())
132 );
133 info!(
134 " {} {} {}",
135 "mz-deploy".if_supports_color(Stream::Stderr, |t| t.cyan()),
136 "apply".if_supports_color(Stream::Stderr, |t| t.cyan()),
137 "--staging-env <name>".if_supports_color(Stream::Stderr, |t| t.cyan())
138 );
139 } else {
140 let formatted = format!("{output}");
141 if std::io::stderr().is_terminal() {
142 display_with_pager(&formatted);
143 } else {
144 info_nonl!("{}", formatted);
145 }
146 }
147
148 Ok(())
149}
150
151fn display_with_pager(content: &str) {
153 if let Ok(mut child) = Command::new("less")
158 .args(["-RFX"])
159 .stdin(Stdio::piped())
160 .spawn()
161 {
162 if let Some(mut stdin) = child.stdin.take() {
163 let _ = stdin.write_all(content.as_bytes());
165 }
166 let _ = child.wait();
168 } else {
169 info_nonl!("{}", content);
171 }
172}