Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ruff version with long version display #8034

Merged
merged 13 commits into from
Oct 20, 2023
2 changes: 1 addition & 1 deletion crates/ruff_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ wild = { version = "2" }
assert_cmd = { version = "2.0.8" }
# Avoid writing colored snapshots when running tests from the terminal
colored = { workspace = true, features = ["no-color"]}
insta = { workspace = true, features = ["filters"] }
insta = { workspace = true, features = ["filters", "json"] }
insta-cmd = { version = "0.4.0" }
tempfile = "3.6.0"
test-case = { workspace = true }
Expand Down
80 changes: 80 additions & 0 deletions crates/ruff_cli/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::{fs, path::Path, process::Command};

fn main() {
// The workspace root directory is not available without walking up the tree
// https://github.com/rust-lang/cargo/issues/3946
let workspace_root = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("..");

commit_info(&workspace_root);

#[allow(clippy::disallowed_methods)]
let target = std::env::var("TARGET").unwrap();
println!("cargo:rustc-env=RUST_HOST_TARGET={target}");
}

fn commit_info(workspace_root: &Path) {
// If not in a git repository, do not attempt to retrieve commit information
let git_dir = workspace_root.join(".git");
if !git_dir.exists() {
return;
}

let git_head_path = git_dir.join("HEAD");
println!(
"cargo:rerun-if-changed={}",
git_head_path.as_path().display()
);

let git_head_contents = fs::read_to_string(git_head_path);
if let Ok(git_head_contents) = git_head_contents {
// The contents are either a commit or a reference in the following formats
// - "<commit>" when the head is detached
// - "ref <ref>" when working on a branch
// If a commit, checking if the HEAD file has changed is sufficient
// If a ref, we need to add the head file for that ref to rebuild on commit
let mut git_ref_parts = git_head_contents.split_whitespace();
git_ref_parts.next();
if let Some(git_ref) = git_ref_parts.next() {
let git_ref_path = git_dir.join(git_ref);
println!(
"cargo:rerun-if-changed={}",
git_ref_path.as_path().display()
);
}
}

let output = match Command::new("git")
zanieb marked this conversation as resolved.
Show resolved Hide resolved
.arg("log")
.arg("-1")
.arg("--date=short")
.arg("--abbrev=9")
.arg("--format=%H %h %cd %(describe)")
.output()
{
Ok(output) if output.status.success() => output,
_ => return,
};
let stdout = String::from_utf8(output.stdout).unwrap();
let mut parts = stdout.split_whitespace();
let mut next = || parts.next().unwrap();
println!("cargo:rustc-env=RUFF_COMMIT_HASH={}", next());
println!("cargo:rustc-env=RUFF_COMMIT_SHORT_HASH={}", next());
println!("cargo:rustc-env=RUFF_COMMIT_DATE={}", next());

// Describe can fail for some commits
// https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emdescribeoptionsem
if let Some(describe) = parts.next() {
let mut describe_parts = describe.split('-');
println!(
"cargo:rustc-env=RUFF_LAST_TAG={}",
describe_parts.next().unwrap()
);
// If this is the tagged commit, this component will be missing
println!(
"cargo:rustc-env=RUFF_LAST_TAG_DISTANCE={}",
describe_parts.next().unwrap_or("0")
);
}
}
5 changes: 5 additions & 0 deletions crates/ruff_cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ pub enum Command {
#[doc(hidden)]
#[clap(hide = true)]
Format(FormatCommand),
/// Display Ruff's version
Version {
#[arg(long, value_enum, default_value = "text")]
output_format: HelpFormat,
},
}

// The `Parser` derive is for ruff_dev, for ruff_cli `Args` would be sufficient
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pub(crate) mod linter;
pub(crate) mod rule;
pub(crate) mod show_files;
pub(crate) mod show_settings;
pub(crate) mod version;
21 changes: 21 additions & 0 deletions crates/ruff_cli/src/commands/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::io::{self, BufWriter, Write};

use anyhow::Result;

use crate::args::HelpFormat;

/// Display version information
pub(crate) fn version(output_format: HelpFormat) -> Result<()> {
let mut stdout = BufWriter::new(io::stdout().lock());
let version_info = crate::version::version();

match output_format {
HelpFormat::Text => {
writeln!(stdout, "ruff {}", &version_info)?;
}
HelpFormat::Json => {
serde_json::to_writer_pretty(stdout, &version_info)?;
}
};
Ok(())
}
7 changes: 7 additions & 0 deletions crates/ruff_cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::print_stdout)]
zanieb marked this conversation as resolved.
Show resolved Hide resolved

use std::fs::File;
use std::io::{self, stdout, BufWriter, Write};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -27,6 +29,7 @@ mod panic;
mod printer;
pub mod resolve;
mod stdin;
mod version;

#[derive(Copy, Clone)]
pub enum ExitStatus {
Expand Down Expand Up @@ -134,6 +137,10 @@ pub fn run(
set_up_logging(&log_level)?;

match command {
Command::Version { output_format } => {
commands::version::version(output_format)?;
Ok(ExitStatus::Success)
}
Command::Rule { rule, all, format } => {
if all {
commands::rule::rules(format)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0 (53b0f5d92 2023-10-19)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/ruff_cli/src/version.rs
expression: version
---
0.0.0+24 (53b0f5d92 2023-10-19)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: crates/ruff_cli/src/version.rs
expression: version
---
{
"version": "0.0.0",
"commit_info": {
"short_commit_hash": "53b0f5d92",
"commit_hash": "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7",
"commit_date": "2023-10-19",
"last_tag": "v0.0.1",
"commits_since_last_tag": 0
}
}
130 changes: 130 additions & 0 deletions crates/ruff_cli/src/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//! Code for representing Ruff's release version number.
use serde::Serialize;
use std::fmt;

/// Information about the git repository where Ruff was built from.
#[derive(Serialize)]
pub(crate) struct CommitInfo {
short_commit_hash: String,
commit_hash: String,
commit_date: String,
last_tag: Option<String>,
commits_since_last_tag: u32,
}

/// Ruff's version.
#[derive(Serialize)]
pub(crate) struct VersionInfo {
/// Ruff's version, such as "0.5.1"
version: String,
/// Information about the git commit we may have been built from.
///
/// `None` if not built from a git repo or if retrieval failed.
commit_info: Option<CommitInfo>,
}

impl fmt::Display for VersionInfo {
/// Formatted version information: "<version>[+<commits>] (<commit> <date>)"
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.version)?;

if let Some(ref ci) = self.commit_info {
if ci.commits_since_last_tag > 0 {
write!(f, "+{}", ci.commits_since_last_tag)?;
}
write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?;
}

Ok(())
}
}

/// Returns information about Ruff's version.
pub(crate) fn version() -> VersionInfo {
// Environment variables are only read at compile-time
macro_rules! option_env_str {
($name:expr) => {
option_env!($name).map(|s| s.to_string())
};
}

// This version is pulled from Cargo.toml and set by Cargo
let version = option_env_str!("CARGO_PKG_VERSION").unwrap();

// Commit info is pulled from git and set by `build.rs`
let commit_info = option_env_str!("RUFF_COMMIT_HASH").map(|commit_hash| CommitInfo {
short_commit_hash: option_env_str!("RUFF_COMMIT_SHORT_HASH").unwrap(),
commit_hash,
commit_date: option_env_str!("RUFF_COMMIT_DATE").unwrap(),
last_tag: option_env_str!("RUFF_LAST_TAG"),
commits_since_last_tag: option_env_str!("RUFF_LAST_TAG_DISTANCE")
.as_deref()
.map_or(0, |value| value.parse::<u32>().unwrap_or(0)),
});

VersionInfo {
version,
commit_info,
}
}

#[cfg(test)]
mod tests {
use insta::{assert_display_snapshot, assert_json_snapshot};

use super::{CommitInfo, VersionInfo};

#[test]
fn version_formatting() {
let version = VersionInfo {
version: "0.0.0".to_string(),
commit_info: None,
};
assert_display_snapshot!(version);
}

#[test]
fn version_formatting_with_commit_info() {
let version = VersionInfo {
version: "0.0.0".to_string(),
commit_info: Some(CommitInfo {
short_commit_hash: "53b0f5d92".to_string(),
commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
last_tag: Some("v0.0.1".to_string()),
commit_date: "2023-10-19".to_string(),
commits_since_last_tag: 0,
}),
};
assert_display_snapshot!(version);
}

#[test]
fn version_formatting_with_commits_since_last_tag() {
let version = VersionInfo {
version: "0.0.0".to_string(),
commit_info: Some(CommitInfo {
short_commit_hash: "53b0f5d92".to_string(),
commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
last_tag: Some("v0.0.1".to_string()),
commit_date: "2023-10-19".to_string(),
commits_since_last_tag: 24,
}),
};
assert_display_snapshot!(version);
}

#[test]
fn version_serializable() {
let version = VersionInfo {
version: "0.0.0".to_string(),
commit_info: Some(CommitInfo {
short_commit_hash: "53b0f5d92".to_string(),
commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
last_tag: Some("v0.0.1".to_string()),
commit_date: "2023-10-19".to_string(),
commits_since_last_tag: 0,
}),
};
assert_json_snapshot!(version);
}
}
13 changes: 7 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,13 @@ Ruff: An extremely fast Python linter.
Usage: ruff [OPTIONS] <COMMAND>

Commands:
check Run Ruff on the given files or directories (default)
rule Explain a rule (or all rules)
config List or describe the available configuration options
linter List all supported upstream linters
clean Clear any caches in the current directory and any subdirectories
help Print this message or the help of the given subcommand(s)
check Run Ruff on the given files or directories (default)
rule Explain a rule (or all rules)
config List or describe the available configuration options
linter List all supported upstream linters
clean Clear any caches in the current directory and any subdirectories
version Display Ruff's version
help Print this message or the help of the given subcommand(s)

Options:
-h, --help Print help
Expand Down