forked from mirrors/jj
feat(config): add jj config get
for scripting
The motivating use-case was this `jj signoff` script: https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6 Which includes lines like this: ```sh NAME=$(jj config list user.name | awk '{split($0, a, "="); print a[2];}' | tr -d '"') MAIL=$(jj config list user.email | awk '{split($0, a, "="); print a[2];}' | tr -d '"') ``` There is no reason that we should have to clumsily parse out the config values. This `jj config get` command supports scripting use-cases like this.
This commit is contained in:
parent
d1701a5d95
commit
24ea8478cb
3 changed files with 110 additions and 0 deletions
|
@ -54,6 +54,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* `jj git fetch` now supports a `--branch` argument to fetch some of the
|
||||
branches only.
|
||||
|
||||
* `jj config get` command allows retrieving config values for use in scripting.
|
||||
|
||||
* `jj config set` command allows simple config edits like
|
||||
`jj config set --repo user.email "somebody@example.com"`
|
||||
|
||||
|
|
|
@ -189,6 +189,8 @@ impl ConfigArgs {
|
|||
enum ConfigSubcommand {
|
||||
#[command(visible_alias("l"))]
|
||||
List(ConfigListArgs),
|
||||
#[command(visible_alias("g"))]
|
||||
Get(ConfigGetArgs),
|
||||
#[command(visible_alias("s"))]
|
||||
Set(ConfigSetArgs),
|
||||
#[command(visible_alias("e"))]
|
||||
|
@ -208,6 +210,22 @@ struct ConfigListArgs {
|
|||
// TODO(#1047): Support ConfigArgs (--user or --repo).
|
||||
}
|
||||
|
||||
/// Get the value of a given config option.
|
||||
///
|
||||
/// Unlike `jj config list`, the result of `jj config get` is printed without
|
||||
/// extra formatting and therefore is usable in scripting. For example:
|
||||
///
|
||||
/// $ jj config list user.name
|
||||
/// user.name="Martin von Zweigbergk"
|
||||
/// $ jj config get user.name
|
||||
/// Martin von Zweigbergk
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(verbatim_doc_comment)]
|
||||
struct ConfigGetArgs {
|
||||
#[arg(required = true)]
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// Update config file to set the given option to a given value.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct ConfigSetArgs {
|
||||
|
@ -1096,6 +1114,7 @@ fn cmd_config(
|
|||
) -> Result<(), CommandError> {
|
||||
match subcommand {
|
||||
ConfigSubcommand::List(sub_args) => cmd_config_list(ui, command, sub_args),
|
||||
ConfigSubcommand::Get(sub_args) => cmd_config_get(ui, command, sub_args),
|
||||
ConfigSubcommand::Set(sub_args) => cmd_config_set(ui, command, sub_args),
|
||||
ConfigSubcommand::Edit(sub_args) => cmd_config_edit(ui, command, sub_args),
|
||||
}
|
||||
|
@ -1143,6 +1162,43 @@ fn cmd_config_list(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_config_get(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigGetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let value = command
|
||||
.settings()
|
||||
.config()
|
||||
.get_string(&args.name)
|
||||
.map_err(|err| match err {
|
||||
config::ConfigError::Type {
|
||||
origin,
|
||||
unexpected,
|
||||
expected,
|
||||
key,
|
||||
} => {
|
||||
let expected = format!("a value convertible to {expected}");
|
||||
// Copied from `impl fmt::Display for ConfigError`. We can't use
|
||||
// the `Display` impl directly because `expected` is required to
|
||||
// be a `'static str`.
|
||||
let mut buf = String::new();
|
||||
use std::fmt::Write;
|
||||
write!(buf, "invalid type: {unexpected}, expected {expected}").unwrap();
|
||||
if let Some(key) = key {
|
||||
write!(buf, " for key `{key}`").unwrap();
|
||||
}
|
||||
if let Some(origin) = origin {
|
||||
write!(buf, " in {origin}").unwrap();
|
||||
}
|
||||
CommandError::ConfigError(buf.to_string())
|
||||
}
|
||||
err => err.into(),
|
||||
})?;
|
||||
writeln!(ui, "{value}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_config_set(
|
||||
_ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
|
|
|
@ -438,6 +438,58 @@ fn test_config_edit_repo_outside_repo() {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_get() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config(
|
||||
r###"
|
||||
[table]
|
||||
string = "some value 1"
|
||||
int = 123
|
||||
list = ["list", "value"]
|
||||
overridden = "foo"
|
||||
"###,
|
||||
);
|
||||
test_env.add_config(
|
||||
r###"
|
||||
[table]
|
||||
overridden = "bar"
|
||||
"###,
|
||||
);
|
||||
|
||||
let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "nonexistent"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Config error: configuration property "nonexistent" not found
|
||||
For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
"###);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.string"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
some value 1
|
||||
"###);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.int"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
123
|
||||
"###);
|
||||
|
||||
let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "table.list"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Config error: invalid type: sequence, expected a value convertible to a string
|
||||
For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
"###);
|
||||
|
||||
let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "table"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Config error: invalid type: map, expected a value convertible to a string
|
||||
For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
"###);
|
||||
|
||||
let stdout =
|
||||
test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.overridden"]);
|
||||
insta::assert_snapshot!(stdout, @"bar");
|
||||
}
|
||||
|
||||
fn find_stdout_lines(keyname_pattern: &str, stdout: &str) -> String {
|
||||
let key_line_re = Regex::new(&format!(r"(?m)^{keyname_pattern}=.*$")).unwrap();
|
||||
key_line_re
|
||||
|
|
Loading…
Reference in a new issue