Skip to content

Commit

Permalink
cli version: Show UTC date as part of the version next to the commi…
Browse files Browse the repository at this point in the history
…t id

The date comes from the commiter date of the commit. This is so that
it's easy to tell at a glance how old a version is.

To make it easier to get UTC date, we first obtain it as a Unix
timestamp, which should always be UTC. This will make the
interaction with Nix in a subsequent commit easier.
  • Loading branch information
ilyagr committed Oct 27, 2023
1 parent eeb93cc commit 1de3123
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 13 deletions.
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ required-features = ["test-fakes"]

[build-dependencies]
cargo_metadata = { workspace = true }
chrono = { workspace = true }

[dependencies]
chrono = { workspace = true }
Expand Down
46 changes: 37 additions & 9 deletions cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate chrono;

use std::path::Path;
use std::process::Command;
use std::str;

use cargo_metadata::MetadataCommand;
use chrono::prelude::*;

const GIT_HEAD_PATH: &str = "../.git/HEAD";
const JJ_OP_HEADS_PATH: &str = "../.jj/repo/op_heads/heads";
Expand All @@ -41,21 +44,44 @@ fn main() -> std::io::Result<()> {
}
println!("cargo:rerun-if-env-changed=NIX_JJ_GIT_HASH");

if let Some(git_hash) = get_git_hash() {
println!("cargo:rustc-env=JJ_VERSION={}-{}", version, git_hash);
// TODO: timestamp can be "nix"
if let Some((maybe_date, git_hash)) = get_git_timestamp_and_hash() {
println!(
"cargo:rustc-env=JJ_VERSION={}-{}-{}",
version,
maybe_date
.map(|d| d.format("%Y%m%d").to_string())
.unwrap_or_else(|| "dateunknown".to_string()),
git_hash
);
} else {
println!("cargo:rustc-env=JJ_VERSION={}", version);
}

Ok(())
}

fn get_git_hash() -> Option<String> {
/// Convert a string with a unix timestamp to a date
fn timestamp_to_date(ts_str: &str) -> Option<DateTime<Utc>> {
ts_str
.parse::<i64>()
.ok()
.and_then(|ts| DateTime::<Utc>::from_timestamp(ts, 0))
}

/// Return the UTC committer date (maybe) and the git hash
fn get_git_timestamp_and_hash() -> Option<(Option<DateTime<Utc>>, String)> {
if let Some(nix_hash) = std::env::var("NIX_JJ_GIT_HASH")
.ok()
.filter(|s| !s.is_empty())
{
return Some(nix_hash);
return Some((None, nix_hash));
}

fn parse_timestamp_vbar_hash(bytes: &[u8]) -> (Option<DateTime<Utc>>, String) {
let s = str::from_utf8(bytes).unwrap().trim_end();
let (ts_str, id) = s.split_once('|').unwrap();
(timestamp_to_date(ts_str), id.to_owned())
}
if let Ok(output) = Command::new("jj")
.args([
Expand All @@ -64,19 +90,21 @@ fn get_git_hash() -> Option<String> {
"log",
"--no-graph",
"-r=@-",
"-T=commit_id",
r#"-T=committer.timestamp().utc().format("%s") ++ "|" ++ commit_id"#,
])
.output()
{
if output.status.success() {
return Some(String::from_utf8(output.stdout).unwrap());
return Some(parse_timestamp_vbar_hash(&output.stdout));
}
}

if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
if let Ok(output) = Command::new("git")
.args(["log", "-1", "--format=%ct|%H", "HEAD"])
.output()
{
if output.status.success() {
let line = str::from_utf8(&output.stdout).unwrap();
return Some(line.trim_end().to_owned());
return Some(parse_timestamp_vbar_hash(&output.stdout));
}
}

Expand Down
12 changes: 8 additions & 4 deletions cli/tests/test_global_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

use std::ffi::OsString;

use assert_matches::assert_matches;

use crate::common::{get_stderr_string, TestEnvironment};

pub mod common;
Expand Down Expand Up @@ -67,10 +69,12 @@ fn test_no_subcommand() {

let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--version"]);
let sanitized = stdout.replace(|c: char| c.is_ascii_hexdigit(), "?");
assert!(
sanitized == "jj ?.??.?\n"
|| sanitized == "jj ?.??.?-????????????????????????????????????????\n",
"{sanitized}"
assert_matches!(
sanitized.as_str(),
"jj ?.??.?\n"
| "jj ?.??.?-????????-????????????????????????????????????????\n"
// `dateunknown` turns into `??t?unknown` since d,a,e are hex digits.
| "jj ?.??.?-??t?unknown-????????????????????????????????????????\n"
);

let stdout = test_env.jj_cmd_success(test_env.env_root(), &["-R", "repo"]);
Expand Down

0 comments on commit 1de3123

Please sign in to comment.