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
31 changes: 31 additions & 0 deletions .changeset/all-pumas-stop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@biomejs/biome": minor
---

Added support for multiple reporters, and the ability to save reporters on arbitrary files.

#### Combine two reporters in CI

If you run Biome on GitHub, take advantage of the reporter and still see the errors in console, you can now use both reporters:

```shell
biome ci --reporter=default --reporter=github
```

#### Save reporter output to a file

With the new `--reporter-file` CLI option, it's now possible to save the output of all reporters to a file. The file is a path,
so you can pass a relative or an absolute path:

```shell
biome ci --reporter=rdjson --reporter-file=/etc/tmp/report.json
biome ci --reporter=summary --reporter-file=./reports/file.txt
```

You can combine these two features. For example, have the `default` reporter written on terminal, and the `rdjson` reporter written on file:

```shell
biome ci --reporter=default --reporter=rdjson --reporter-file=/etc/tmp/report.json
```

**The `--reporter` and `--reporter-file` flags must appear next to each other, otherwise an error is thrown.**
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/biome_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ biome_js_analyze = { workspace = true }
biome_js_formatter = { workspace = true }
biome_js_syntax = { workspace = true }
biome_json_analyze = { workspace = true }
biome_json_factory = { workspace = true }
biome_json_formatter = { workspace = true }
biome_json_parser = { workspace = true }
biome_json_syntax = { workspace = true }
Expand Down
57 changes: 37 additions & 20 deletions crates/biome_cli/src/cli_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,8 @@ pub struct CliOptions {
pub error_on_warnings: bool,

/// Allows to change how diagnostics and summary are reported.
#[bpaf(
long("reporter"),
argument("json|json-pretty|github|junit|summary|gitlab|checkstyle|rdjson|sarif"),
fallback(CliReporter::default())
)]
pub reporter: CliReporter,
#[bpaf(external, many)]
pub cli_reporter: Vec<CliReporter>,

/// The level of diagnostics to show. In order, from the lowest to the most important: info, warn, error. Passing `--diagnostic-level=error` will cause Biome to print only diagnostics that contain only errors.
#[bpaf(
Expand Down Expand Up @@ -119,8 +115,28 @@ impl FromStr for ColorsArg {
}
}

#[derive(Debug, Default, Clone, Eq, PartialEq, Bpaf)]
#[bpaf(adjacent)]
pub struct CliReporter {
#[bpaf(
long("reporter"),
argument("default|json|json-pretty|github|junit|summary|gitlab|checkstyle|rdjson|sarif"),
fallback(CliReporterKind::default())
)]
pub(crate) kind: CliReporterKind,

#[bpaf(long("reporter-file"), argument("PATH"))]
pub(crate) destination: Option<Utf8PathBuf>,
}

impl CliReporter {
pub(crate) fn is_file_report(&self) -> bool {
self.destination.is_some()
}
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub enum CliReporter {
pub enum CliReporterKind {
/// The default reporter
#[default]
Default,
Expand All @@ -146,15 +162,16 @@ pub enum CliReporter {

impl CliReporter {
pub(crate) const fn is_default(&self) -> bool {
matches!(self, Self::Default)
matches!(self.kind, CliReporterKind::Default)
}
}

impl FromStr for CliReporter {
impl FromStr for CliReporterKind {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"default" => Ok(Self::Default),
"json" => Ok(Self::Json),
"json-pretty" => Ok(Self::JsonPretty),
"summary" => Ok(Self::Summary),
Expand All @@ -171,19 +188,19 @@ impl FromStr for CliReporter {
}
}

impl Display for CliReporter {
impl Display for CliReporterKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Default => f.write_str("default"),
Self::Json => f.write_str("json"),
Self::JsonPretty => f.write_str("json-pretty"),
Self::Summary => f.write_str("summary"),
Self::GitHub => f.write_str("github"),
Self::Junit => f.write_str("junit"),
Self::GitLab => f.write_str("gitlab"),
Self::Checkstyle => f.write_str("checkstyle"),
Self::RdJson => f.write_str("rdjson"),
Self::Sarif => f.write_str("sarif"),
Self::Default { .. } => f.write_str("default"),
Self::Json { .. } => f.write_str("json"),
Self::JsonPretty { .. } => f.write_str("json-pretty"),
Self::Summary { .. } => f.write_str("summary"),
Self::GitHub { .. } => f.write_str("github"),
Self::Junit { .. } => f.write_str("junit"),
Self::GitLab { .. } => f.write_str("gitlab"),
Self::Checkstyle { .. } => f.write_str("checkstyle"),
Self::RdJson { .. } => f.write_str("rdjson"),
Self::Sarif { .. } => f.write_str("sarif"),
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions crates/biome_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::changed::{get_changed_files, get_staged_files};
use crate::cli_options::{CliOptions, CliReporter, ColorsArg, cli_options};
use crate::cli_options::{CliOptions, CliReporterKind, ColorsArg, cli_options};
use crate::logging::log_options;
use crate::logging::{LogOptions, LoggingKind};
use crate::{CliDiagnostic, LoggingLevel, VERSION};
Expand Down Expand Up @@ -708,11 +708,15 @@ impl BiomeCommand {
}
}

pub const fn get_color(&self) -> Option<&ColorsArg> {
pub fn get_color(&self) -> Option<&ColorsArg> {
match self.cli_options() {
Some(cli_options) => {
// To properly display GitHub annotations we need to disable colors
if matches!(cli_options.reporter, CliReporter::GitHub) {
if cli_options
.cli_reporter
.iter()
.any(|r| r.kind == CliReporterKind::GitHub)
{
return Some(&ColorsArg::Off);
}
// We want force colors in CI, to give e better UX experience
Expand Down
4 changes: 0 additions & 4 deletions crates/biome_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ pub(crate) const VERSION: &str = match option_env!("BIOME_VERSION") {
None => env!("CARGO_PKG_VERSION"),
};

/// JSON file that is temporarily to handle internal files via [Workspace].
/// When using this file, make sure to close it via [Workspace::close_file].
pub const TEMPORARY_INTERNAL_REPORTER_FILE: &str = "__BIOME_INTERNAL_FILE__.json";

/// Global context for an execution of the CLI
pub struct CliSession<'app> {
/// Instance of [App] used by this run of the CLI
Expand Down
33 changes: 16 additions & 17 deletions crates/biome_cli/src/reporter/checkstyle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::reporter::{Reporter, ReporterVisitor};
use crate::reporter::{Reporter, ReporterVisitor, ReporterWriter};
use crate::runner::execution::Execution;
use crate::{DiagnosticsPayload, TraversalSummary};
use biome_console::{Console, ConsoleExt, markup};
use biome_console::markup;
use biome_diagnostics::display::SourceFile;
use biome_diagnostics::{Error, PrintDescription, Resource, Severity};
use camino::{Utf8Path, Utf8PathBuf};
Expand All @@ -10,16 +10,21 @@ use std::io::{self, Write};

pub struct CheckstyleReporter<'a> {
pub summary: TraversalSummary,
pub diagnostics_payload: DiagnosticsPayload,
pub diagnostics_payload: &'a DiagnosticsPayload,
pub execution: &'a dyn Execution,
pub verbose: bool,
pub(crate) working_directory: Option<Utf8PathBuf>,
}

impl Reporter for CheckstyleReporter<'_> {
fn write(self, visitor: &mut dyn ReporterVisitor) -> io::Result<()> {
visitor.report_summary(self.execution, self.summary, self.verbose)?;
fn write(
self,
writer: &mut dyn ReporterWriter,
visitor: &mut dyn ReporterVisitor,
) -> io::Result<()> {
visitor.report_summary(writer, self.execution, self.summary, self.verbose)?;
visitor.report_diagnostics(
writer,
self.execution,
self.diagnostics_payload,
self.verbose,
Expand All @@ -29,19 +34,12 @@ impl Reporter for CheckstyleReporter<'_> {
}
}

pub struct CheckstyleReporterVisitor<'a> {
console: &'a mut dyn Console,
}

impl<'a> CheckstyleReporterVisitor<'a> {
pub fn new(console: &'a mut dyn Console) -> Self {
Self { console }
}
}
pub struct CheckstyleReporterVisitor;

impl<'a> ReporterVisitor for CheckstyleReporterVisitor<'a> {
impl ReporterVisitor for CheckstyleReporterVisitor {
fn report_summary(
&mut self,
_writer: &mut dyn ReporterWriter,
_execution: &dyn Execution,
_summary: TraversalSummary,
_verbose: bool,
Expand All @@ -51,8 +49,9 @@ impl<'a> ReporterVisitor for CheckstyleReporterVisitor<'a> {

fn report_diagnostics(
&mut self,
writer: &mut dyn ReporterWriter,
_execution: &dyn Execution,
payload: DiagnosticsPayload,
payload: &DiagnosticsPayload,
verbose: bool,
_working_directory: Option<&Utf8Path>,
) -> io::Result<()> {
Expand Down Expand Up @@ -109,7 +108,7 @@ impl<'a> ReporterVisitor for CheckstyleReporterVisitor<'a> {
writeln!(output, " </file>")?;
}
writeln!(output, "</checkstyle>")?;
self.console.log(markup! {{
writer.log(markup! {{
(String::from_utf8_lossy(&output))
}});
Ok(())
Expand Down
30 changes: 19 additions & 11 deletions crates/biome_cli/src/reporter/github.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
use crate::reporter::{Reporter, ReporterVisitor};
use crate::reporter::{Reporter, ReporterVisitor, ReporterWriter};
use crate::runner::execution::Execution;
use crate::{DiagnosticsPayload, TraversalSummary};
use biome_console::{Console, ConsoleExt, markup};
use biome_console::markup;
use biome_diagnostics::PrintGitHubDiagnostic;
use camino::{Utf8Path, Utf8PathBuf};
use std::io;

pub(crate) struct GithubReporter<'a> {
pub(crate) diagnostics_payload: DiagnosticsPayload,
pub diagnostics_payload: &'a DiagnosticsPayload,
pub(crate) execution: &'a dyn Execution,
pub(crate) verbose: bool,
pub(crate) working_directory: Option<Utf8PathBuf>,
}

impl Reporter for GithubReporter<'_> {
fn write(self, visitor: &mut dyn ReporterVisitor) -> io::Result<()> {
fn write(
self,
writer: &mut dyn ReporterWriter,

visitor: &mut dyn ReporterVisitor,
) -> io::Result<()> {
visitor.report_diagnostics(
writer,
self.execution,
self.diagnostics_payload,
self.verbose,
Expand All @@ -24,11 +30,12 @@ impl Reporter for GithubReporter<'_> {
Ok(())
}
}
pub(crate) struct GithubReporterVisitor<'a>(pub(crate) &'a mut dyn Console);
pub(crate) struct GithubReporterVisitor;

impl ReporterVisitor for GithubReporterVisitor<'_> {
impl ReporterVisitor for GithubReporterVisitor {
fn report_summary(
&mut self,
_writer: &mut dyn ReporterWriter,
_execution: &dyn Execution,
_summary: TraversalSummary,
_verbose: bool,
Expand All @@ -38,17 +45,18 @@ impl ReporterVisitor for GithubReporterVisitor<'_> {

fn report_diagnostics(
&mut self,
writer: &mut dyn ReporterWriter,
_execution: &dyn Execution,
diagnostics_payload: DiagnosticsPayload,
diagnostics_payload: &DiagnosticsPayload,
verbose: bool,
_working_directory: Option<&Utf8Path>,
) -> io::Result<()> {
for diagnostic in &diagnostics_payload.diagnostics {
if diagnostic.severity() >= diagnostics_payload.diagnostic_level {
if diagnostic.tags().is_verbose() && verbose {
self.0.log(markup! {{PrintGitHubDiagnostic(diagnostic)}});
} else if !verbose {
self.0.log(markup! {{PrintGitHubDiagnostic(diagnostic)}});
if !diagnostic.tags().is_verbose() {
writer.log(markup! {{PrintGitHubDiagnostic(diagnostic)}});
} else if diagnostic.tags().is_verbose() && verbose {
writer.log(markup! {{PrintGitHubDiagnostic(diagnostic)}});
}
}
}
Expand Down
Loading
Loading