Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cli/src/error_report.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use api::client::ClientErrorExplanation;
use http::StatusCode;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};

use crate::exit_code::ExitCode;

Expand Down Expand Up @@ -94,7 +94,7 @@ impl ErrorReport {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ErrorKind {
/// Couldn't connect to Trunk.
Expand All @@ -105,6 +105,8 @@ pub enum ErrorKind {
GitRepository,
/// The run stopped early, such as when it found no test reports and empty results aren't allowed.
Interrupted,
/// Also what a kind from a newer CLI reads as.
#[serde(other)]
Other,
}

Expand Down
22 changes: 21 additions & 1 deletion cli/src/exit_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{borrow::Cow, fmt};

use clap::builder::{StyledStr, styling::Styles};
use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
use serde::{Serialize, Serializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use strum::{EnumIter, EnumMessage, IntoEnumIterator};

/// Two exit codes are equal when the process exits with the same number, so
Expand Down Expand Up @@ -131,6 +131,18 @@ impl Serialize for ExitCode {
}
}

/// A number that matches a named code reads back as that code, and any other as `PassedThrough`.
impl<'de> Deserialize<'de> for ExitCode {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let code = i32::deserialize(deserializer)?;
Ok(Self::iter()
.find(|exit_code| {
!matches!(exit_code, Self::PassedThrough(_)) && exit_code.code() == code
})
.unwrap_or(Self::PassedThrough(code)))
}
}

impl JsonSchema for ExitCode {
fn schema_name() -> Cow<'static, str> {
"ExitCode".into()
Expand Down Expand Up @@ -168,6 +180,14 @@ mod tests {
assert!(ExitCode::PassedThrough(0).is_success());
}

#[test]
fn deserializes_from_its_number() {
let read = |json: &str| serde_json::from_str::<ExitCode>(json).unwrap();
assert!(matches!(read("70"), ExitCode::Error));
assert!(matches!(read("0"), ExitCode::Success));
assert!(matches!(read("3"), ExitCode::PassedThrough(3)));
}

#[test]
fn serializes_as_its_number() {
assert_eq!(serde_json::to_string(&ExitCode::Error).unwrap(), "70");
Expand Down
46 changes: 37 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
thread::JoinHandle,
};

use clap::{Parser, Subcommand};
use clap::{Args, Parser, Subcommand};
use clap_verbosity_flag::{InfoLevel, Verbosity, log::LevelFilter};
use display::{
message::{DisplayMessage, send_message},
Expand All @@ -20,7 +20,7 @@ use trunk_analytics_cli::{
error_report::ErrorReport,
exit_code::ExitCode,
summary_output::{
ErrorSummary, UploadCommand, UploadReport, ValidateReport,
ErrorSummary, UploadCommand, UploadReport, ValidateReport, github,
json::{self, Report},
text::{SummaryFileText, TextOptions, UploadText},
},
Expand All @@ -47,7 +47,9 @@ impl Cli {
let token = match &self.command {
Commands::Test(args) => args.token(),
Commands::Upload(args) => args.token.clone(),
Commands::Validate(..) | Commands::SummaryOutputSchema => None,
Commands::Validate(..)
| Commands::SummaryOutputSchema
| Commands::GithubAnnotations(..) => None,
};

token.map_or(
Expand All @@ -62,6 +64,7 @@ impl Cli {
Commands::Upload(..) => "upload",
Commands::Validate(..) => "validate",
Commands::SummaryOutputSchema => "summary-output-schema",
Commands::GithubAnnotations(..) => "github-annotations",
}
}

Expand All @@ -74,23 +77,29 @@ impl Cli {
match &self.command {
Commands::Test(args) => args.org_url_slug(),
Commands::Upload(args) => args.org_url_slug.clone(),
Commands::Validate(..) | Commands::SummaryOutputSchema => String::from("not used"),
Commands::Validate(..)
| Commands::SummaryOutputSchema
| Commands::GithubAnnotations(..) => String::from("not used"),
}
}

pub fn summary_output_file(&self) -> Option<PathBuf> {
match &self.command {
Commands::Test(args) => args.summary_output_file(),
Commands::Upload(args) => args.summary_output_file.clone(),
Commands::Validate(..) | Commands::SummaryOutputSchema => None,
Commands::Validate(..)
| Commands::SummaryOutputSchema
| Commands::GithubAnnotations(..) => None,
}
}

pub fn repo_root(&self) -> String {
let explicit_root = match &self.command {
Commands::Test(args) => args.repo_root(),
Commands::Upload(args) => args.repo_root.clone(),
Commands::Validate(..) | Commands::SummaryOutputSchema => None,
Commands::Validate(..)
| Commands::SummaryOutputSchema
| Commands::GithubAnnotations(..) => None,
};
explicit_root
.or(std::env::current_dir()
Expand All @@ -114,6 +123,17 @@ enum Commands {
Validate(ValidateArgs),
/// Print the JSON Schema of the summary that `--summary-output-file` writes.
SummaryOutputSchema,
/// Print GitHub Actions annotations for why a run failed, from its `--summary-output-file`.
/// Exits 0 only when it printed one. Used by the analytics-uploader action.
#[command(hide = true)]
GithubAnnotations(GithubAnnotationsArgs),
}

#[derive(Debug, Args)]
struct GithubAnnotationsArgs {
/// The summary that `upload` or `test` wrote to `--summary-output-file`.
#[arg(long)]
summary_file: PathBuf,
}

fn close_out_and_exit(
Expand All @@ -131,14 +151,19 @@ fn close_out_and_exit(
// "the Sentry client must be initialized before starting an async runtime or spawning threads"
// https://docs.sentry.io/platforms/rust/#async-main-function
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
// Before Sentry and logging start, so stdout carries only the workflow commands.
if let Commands::GithubAnnotations(args) = &cli.command {
std::process::exit(github::run(&args.summary_file).code());
}

let release_name = format!("analytics-cli@{}", env!("CARGO_PKG_VERSION"));
let guard = third_party::sentry::init(release_name.into(), None);

tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(async {
let cli = Cli::parse();
let org_url_slug = cli.org_url_slug();
let summary_output_file = cli.summary_output_file();
let log_level_filter = cli.verbose.log_level_filter();
Expand Down Expand Up @@ -222,7 +247,7 @@ fn main() -> anyhow::Result<()> {
close_out_and_exit(exit_code, guard, render_sender, render_handle)
}
MainOutcome::Finished(Ok(RunResult::SummaryOutputSchema)) => {
println!("{}", serde_json::to_string_pretty(&json::schema())?);
println!("{}", serde_json::to_string_pretty(&json::Report::schema())?);
close_out_and_exit(ExitCode::Success, guard, render_sender, render_handle)
}
MainOutcome::Finished(Err(error)) => {
Expand Down Expand Up @@ -251,7 +276,7 @@ fn write_summary_file(path: Option<&Path>, report: Report, render_sender: &Sende
let Some(path) = path else {
return;
};
let text = SummaryFileText::new(path, json::write_file(path, report));
let text = SummaryFileText::new(path, report.write_file(path));
send_message(
DisplayMessage::Final(Arc::new(text), String::from("summary file display")),
render_sender,
Expand Down Expand Up @@ -301,6 +326,9 @@ async fn run(cli: Cli, render_sender: Sender<DisplayMessage>) -> anyhow::Result<
Ok(RunResult::Validate(result))
}
Commands::SummaryOutputSchema => Ok(RunResult::SummaryOutputSchema),
Commands::GithubAnnotations(..) => {
anyhow::bail!("github-annotations runs before the async runtime starts")
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions cli/src/summary_output/error.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use api::client::get_api_host;
use schemars::JsonSchema;
use serde::Serialize;
use serde::{Deserialize, Serialize};

use crate::{
error_report::{ErrorKind, ErrorReport, get_client_error_explanation, settings_url},
exit_code::ExitCode,
};

#[derive(Debug, Clone, Serialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ErrorSummary {
pub kind: ErrorKind,
Expand Down
Loading
Loading