Skip to content

Commit

Permalink
templates: add cryptographic_signature display to default formats
Browse files Browse the repository at this point in the history
Cryptographic signature support in templates was added in
c99c97c (#4853), but has to be manually
configured. This adds some defaults to the built-in config.

Instead of having separate `builtin_*_with_sig` aliases, this adds to
the aliases that actually format commits. Since signature verification
is slow, this is disabled by default. To enable it, override the
`should_show_cryptographic_signature()` template alias like so:

    [ui]
    show-cryptographic-signatures = true
    [template-aliases]
    'format_short_cryptographic_signature(signature)' = ...
    'format_detailed_cryptographic_signature(signature)' = ...

Note that the two formatting functions take
`Option<CryptographicSignature>`, not `CryptographicSignature`. This
allows you to display a custom message if a signature is not found, but
will emit an error if you do not check for signature presence.

    [template-aliases]
    'format_detailed_cryptographic_signature(signature)' = '''
      if(signature,
        "message if present",
        "message if missing",
      )
    '''
  • Loading branch information
bryceberger committed Jan 13, 2025
1 parent 9872427 commit 8ee936f
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* New template function `config(name)` to access to configuration variable from
template.

* Add a config option `ui.show-cryptographic-signatures`. When set to `"true"`, the
builtin templates will show signataure information if available. The signature display
can be customized using `format_detailed_cryptographic_signature(signature)` and
`format_short_cryptographic_signature(signature)`.

### Fixed bugs

* Fixed diff selection by external tools with `jj split`/`commit -i FILESETS`.
Expand Down
5 changes: 5 additions & 0 deletions cli/src/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@
},
"conflict-marker-style": {
"$ref": "#/properties/ui/definitions/conflict-marker-style"
},
"show-cryptographic-signatures": {
"type": "boolean",
"default": false,
"description": "Whether the built-in templates should show cryptographic signature information"
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions cli/src/config/colors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,10 @@
"node current_operation" = { fg = "green", bold = true }
"node immutable" = { fg = "bright cyan", bold = true }
"node conflict" = { fg = "red", bold = true }

"signature display" = "yellow"
"signature key" = "cyan"
"signature status good" = "green"
"signature status unknown" = "yellow"
"signature status bad" = "red"
"signature status invalid" = "red"
2 changes: 2 additions & 0 deletions cli/src/config/misc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ quiet = false
log-word-wrap = false
log-synthetic-elided-nodes = true
conflict-marker-style = "diff"
# signature verification is slow, disable by default
show-cryptographic-signatures = false

[ui.movement]
edit = false
Expand Down
31 changes: 31 additions & 0 deletions cli/src/config/templates.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ if(root,
if(git_head, label("git_head", "git_head()")),
format_short_commit_id(commit_id),
if(conflict, label("conflict", "conflict")),
if(config("ui.show-cryptographic-signatures").as_boolean(),
format_short_cryptographic_signature(signature)),
if(empty, label("empty", "(empty)")),
if(description,
description.first_line(),
Expand Down Expand Up @@ -134,6 +136,8 @@ concat(
surround("Tags : ", "\n", tags),
"Author : " ++ format_detailed_signature(author) ++ "\n",
"Committer: " ++ format_detailed_signature(committer) ++ "\n",
if(config("ui.show-cryptographic-signatures").as_boolean(),
"Signature: " ++ format_detailed_cryptographic_signature(signature) ++ "\n"),
"\n",
indent(" ",
coalesce(description, label(if(empty, "empty"), description_placeholder) ++ "\n")),
Expand Down Expand Up @@ -281,9 +285,36 @@ separate(" ",
if(commit.git_head(), label("git_head", "git_head()")),
format_short_commit_id(commit.commit_id()),
if(commit.conflict(), label("conflict", "conflict")),
if(config("ui.show-cryptographic-signatures").as_boolean(),
format_short_cryptographic_signature(commit.signature())),
)
'''

'format_detailed_cryptographic_signature(signature)' = '''
if(signature,
separate(" ",
label("signature status " ++ signature.status(), signature.status()),
"signature by",
coalesce(signature.display(), "(unknown)"),
signature.key(),
),
"(no signature)",
)
'''
'format_short_cryptographic_signature(signature)' = '''
if(signature,
label("signature status", concat(
"[",
label(signature.status(), coalesce(
if(signature.status() == "good", "✓︎"),
if(signature.status() == "unknown", "?"),
"x",
)),
"]",
))
)
'''

builtin_log_node = '''
coalesce(
if(!self, label("elided", "~")),
Expand Down
83 changes: 71 additions & 12 deletions cli/tests/test_commit_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,22 +1214,81 @@ fn test_log_diff_predefined_formats() {
fn test_signature_templates() {
let test_env = TestEnvironment::default();

test_env.add_config(r#"signing.sign-all = true"#);
test_env.add_config(r#"signing.backend = "test""#);

test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");

let template = r#"if(signature,
signature.status() ++ " " ++ signature.display(),
"no"
) ++ " signature""#;
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "unsigned"]);
test_env.add_config("signing.sign-all = true");
test_env.add_config("signing.backend = 'test'");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "signed"]);

let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r"
@ good test-display signature
◆ no signature");
let template = r#"
if(signature,
signature.status() ++ " " ++ signature.display(),
"no",
) ++ " signature""#;

// show that signatures can render
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r#"
@ good test-display signature
○ no signature
◆ no signature
"#);
let stdout = test_env.jj_cmd_success(&repo_path, &["show", "-T", template]);
insta::assert_snapshot!(stdout, @"good test-display signature");
insta::assert_snapshot!(stdout, @r#"good test-display signature"#);

// builtin templates
test_env.add_config("ui.show-cryptographic-signatures = true");

let args: &[_] = &["log", "-r", "..", "-T"];

let stdout = test_env.jj_cmd_success(&repo_path, &[args, &["builtin_log_oneline"]].concat());
insta::assert_snapshot!(stdout, @r#"
@ rlvkpnrz test.user 2001-02-03 08:05:09 a0909ee9 [✓︎] (empty) signed
○ qpvuntsm test.user 2001-02-03 08:05:08 879d5d20 (empty) unsigned
~
"#);

let stdout = test_env.jj_cmd_success(&repo_path, &[args, &["builtin_log_compact"]].concat());
insta::assert_snapshot!(stdout, @r#"
@ rlvkpnrz [email protected] 2001-02-03 08:05:09 a0909ee9 [✓︎]
│ (empty) signed
○ qpvuntsm [email protected] 2001-02-03 08:05:08 879d5d20
│ (empty) unsigned
~
"#);

let stdout = test_env.jj_cmd_success(&repo_path, &[args, &["builtin_log_detailed"]].concat());
insta::assert_snapshot!(stdout, @r#"
@ Commit ID: a0909ee96bb5c66311a0c579dc8ebed4456dfc1b
│ Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
│ Author : Test User <[email protected]> (2001-02-03 08:05:09)
│ Committer: Test User <[email protected]> (2001-02-03 08:05:09)
│ Signature: good signature by test-display
│ signed
○ Commit ID: 879d5d20fea5930f053e0817033ad4aba924a361
│ Change ID: qpvuntsmwlqtpsluzzsnyyzlmlwvmlnu
~ Author : Test User <[email protected]> (2001-02-03 08:05:08)
Committer: Test User <[email protected]> (2001-02-03 08:05:08)
Signature: (no signature)
unsigned
"#);

// customization point
let config_val = r#"template-aliases."format_short_cryptographic_signature(signature)"="'status: ' ++ signature.status()""#;
let stdout = test_env.jj_cmd_success(
&repo_path,
&[args, &["builtin_log_oneline", "--config", config_val]].concat(),
);
insta::assert_snapshot!(stdout, @r#"
@ rlvkpnrz test.user 2001-02-03 08:05:09 a0909ee9 status: good (empty) signed
○ qpvuntsm test.user 2001-02-03 08:05:08 879d5d20 status: <Error: No CryptographicSignature available> (empty) unsigned
~
"#);
}
27 changes: 26 additions & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,32 @@ formatted with `format_timestamp()`.
'commit_timestamp(commit)' = 'commit.author().timestamp()'
```

### Allow "large" revsets by default
### Signature format

Can be enabled with `ui.show-cryptographic-signatures`, and
customized with `format_short_cryptographic_signature(sig)` and
`format_detailed_cryptographic_signature(sig)`.

Note that the formatting functions take an `Option<CryptographicSignature>`.
This allows you to emit a custom message if a signature is not present, but
will raise an error if you try to access methods on a signature that is not
available.

```toml
[ui]
# default is false
show-cryptographic-signatures = true

[template-aliases]
'format_short_cryptographic_signature(sig)' = '''
if(sig,
sig.status(),
"(no sig)",
)
'''
```

## Allow "large" revsets by default

Certain commands (such as `jj rebase`) can take multiple revset arguments, but
default to requiring each of those revsets to expand to a *single* revision.
Expand Down

0 comments on commit 8ee936f

Please sign in to comment.