Skip to content

Commit

Permalink
Add workspace config
Browse files Browse the repository at this point in the history
This adds support for per-workspace configuration as was
described as a TODO in the LayeredConfig struct. Like repo
configuration in `.jj/repo/config.toml`, the workspace config
goes in `.jj/config.toml` in the workspace root, and its
settings take precedence over repo, user and default configs.

The config subcommands that take `--user` and `--repo` are
additionally given `--workspace` which behaves as one would
expect.
  • Loading branch information
HybridEidolon committed May 12, 2024
1 parent 256988d commit 01b8970
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 12 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* `ui.color = "debug"` prints active labels alongside the regular colored output.

* Workspaces may have an additional layered configuration, located at
`.jj/config.toml`. `jj config` subcommands which took layer options like
`--repo` now also support `--workspace`.

### Fixed bugs

## [0.17.1] - 2024-05-07
Expand Down
7 changes: 7 additions & 0 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,11 @@ pub fn get_new_config_file_path(
new_config_path()?.ok_or_else(|| user_error("No repo config path found to edit"))?
}
ConfigSource::Repo => command.workspace_loader()?.repo_path().join("config.toml"),
ConfigSource::Workspace => command
.workspace_loader()?
.workspace_root()
.join(".jj")
.join("config.toml"),
_ => {
return Err(user_error(format!(
"Can't get path for config source {config_source:?}"
Expand Down Expand Up @@ -2846,6 +2851,7 @@ impl CliRunner {
layered_configs.read_user_config()?;
if let Ok(loader) = &maybe_cwd_workspace_loader {
layered_configs.read_repo_config(loader.repo_path())?;
layered_configs.read_workspace_config(&loader.workspace_root().join(".jj"))?;
}
let config = layered_configs.merge();
ui.reset(&config)?;
Expand All @@ -2868,6 +2874,7 @@ impl CliRunner {
let loader = WorkspaceLoader::init(&cwd.join(path))
.map_err(|err| map_workspace_load_error(err, Some(path)))?;
layered_configs.read_repo_config(loader.repo_path())?;
layered_configs.read_workspace_config(&loader.workspace_root().join(".jj"))?;
Ok(loader)
} else {
maybe_cwd_workspace_loader
Expand Down
6 changes: 6 additions & 0 deletions cli/src/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub(crate) struct ConfigLevelArgs {
/// Target the repo-level config
#[arg(long, group = "config_level")]
repo: bool,

/// Target the workspace-level config
#[arg(long, group = "config_level")]
workspace: bool,
}

impl ConfigLevelArgs {
Expand All @@ -51,6 +55,8 @@ impl ConfigLevelArgs {
Some(ConfigSource::User)
} else if self.repo {
Some(ConfigSource::Repo)
} else if self.workspace {
Some(ConfigSource::Workspace)
} else {
None
}
Expand Down
15 changes: 14 additions & 1 deletion cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum ConfigSource {
// TODO: Track explicit file paths, especially for when user config is a dir.
User,
Repo,
Workspace,
CommandArg,
}

Expand All @@ -59,7 +60,7 @@ pub struct AnnotatedValue {
/// 2. Base environment variables
/// 3. [User config](https://github.com/martinvonz/jj/blob/main/docs/config.md#configuration)
/// 4. Repo config `.jj/repo/config.toml`
/// 5. TODO: Workspace config `.jj/config.toml`
/// 5. Workspace config `.jj/config.toml`
/// 6. Override environment variables
/// 7. Command-line arguments `--config-toml`
#[derive(Clone, Debug)]
Expand All @@ -68,6 +69,7 @@ pub struct LayeredConfigs {
env_base: config::Config,
user: Option<config::Config>,
repo: Option<config::Config>,
workspace: Option<config::Config>,
env_overrides: config::Config,
arg_overrides: Option<config::Config>,
}
Expand All @@ -80,6 +82,7 @@ impl LayeredConfigs {
env_base: env_base(),
user: None,
repo: None,
workspace: None,
env_overrides: env_overrides(),
arg_overrides: None,
}
Expand All @@ -99,6 +102,12 @@ impl LayeredConfigs {
Ok(())
}

#[instrument]
pub fn read_workspace_config(&mut self, workspace_path: &Path) -> Result<(), ConfigError> {
self.workspace = Some(read_config_file(&workspace_path.join("config.toml"))?);
Ok(())
}

pub fn parse_config_args(&mut self, toml_strs: &[String]) -> Result<(), ConfigError> {
let config = toml_strs
.iter()
Expand Down Expand Up @@ -128,6 +137,7 @@ impl LayeredConfigs {
(ConfigSource::Env, Some(&self.env_base)),
(ConfigSource::User, self.user.as_ref()),
(ConfigSource::Repo, self.repo.as_ref()),
(ConfigSource::Workspace, self.workspace.as_ref()),
(ConfigSource::Env, Some(&self.env_overrides)),
(ConfigSource::CommandArg, self.arg_overrides.as_ref()),
];
Expand Down Expand Up @@ -603,6 +613,7 @@ mod tests {
env_base: empty_config.to_owned(),
user: None,
repo: None,
workspace: None,
env_overrides: empty_config,
arg_overrides: None,
};
Expand All @@ -629,6 +640,7 @@ mod tests {
env_base: env_base_config,
user: None,
repo: Some(repo_config),
workspace: None,
env_overrides: empty_config,
arg_overrides: None,
};
Expand Down Expand Up @@ -704,6 +716,7 @@ mod tests {
env_base: empty_config.to_owned(),
user: Some(user_config),
repo: Some(repo_config),
workspace: None,
env_overrides: empty_config,
arg_overrides: None,
};
Expand Down
22 changes: 19 additions & 3 deletions cli/tests/cli-reference@.md.snap
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,10 @@ List variables set in config file, along with their values
Possible values: `true`, `false`
* `--workspace` — Target the workspace-level config
Possible values: `true`, `false`
* `-T`, `--template <TEMPLATE>` — Render each variable using the given template
Expand Down Expand Up @@ -532,7 +536,7 @@ Martin von Zweigbergk
Update config file to set the given option to a given value
**Usage:** `jj config set <--user|--repo> <NAME> <VALUE>`
**Usage:** `jj config set <--user|--repo|--workspace> <NAME> <VALUE>`
###### **Arguments:**
Expand All @@ -549,6 +553,10 @@ Update config file to set the given option to a given value
Possible values: `true`, `false`
* `--workspace` — Target the workspace-level config
Possible values: `true`, `false`
Expand All @@ -558,7 +566,7 @@ Start an editor on a jj config file.
Creates the file if it doesn't already exist regardless of what the editor does.
**Usage:** `jj config edit <--user|--repo>`
**Usage:** `jj config edit <--user|--repo|--workspace>`
###### **Options:**
Expand All @@ -570,6 +578,10 @@ Creates the file if it doesn't already exist regardless of what the editor does.
Possible values: `true`, `false`
* `--workspace` — Target the workspace-level config
Possible values: `true`, `false`
Expand All @@ -581,7 +593,7 @@ A config file at that path may or may not exist.
See `jj config edit` if you'd like to immediately edit the file.
**Usage:** `jj config path <--user|--repo>`
**Usage:** `jj config path <--user|--repo|--workspace>`
###### **Options:**
Expand All @@ -593,6 +605,10 @@ See `jj config edit` if you'd like to immediately edit the file.
Possible values: `true`, `false`
* `--workspace` — Target the workspace-level config
Possible values: `true`, `false`
Expand Down
52 changes: 44 additions & 8 deletions cli/tests/test_config_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,22 @@ fn test_config_list_layer() {
],
);

test_env.jj_cmd_ok(
&repo_path,
&[
"config",
"set",
"--user",
"test-layered-workspace-key",
"test-original-val",
],
);

let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--user"]);
insta::assert_snapshot!(stdout, @r###"
test-key="test-val"
test-layered-key="test-original-val"
test-layered-workspace-key="test-original-val"
"###);

// Repo
Expand All @@ -177,6 +189,18 @@ fn test_config_list_layer() {
],
);

// Workspace
test_env.jj_cmd_ok(
&repo_path,
&[
"config",
"set",
"--workspace",
"test-layered-workspace-key",
"test-layered-val",
],
);

let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--user"]);
insta::assert_snapshot!(stdout, @r###"
test-key="test-val"
Expand All @@ -186,6 +210,11 @@ fn test_config_list_layer() {
insta::assert_snapshot!(stdout, @r###"
test-layered-key="test-layered-val"
"###);

let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--workspace"]);
insta::assert_snapshot!(stdout, @r###"
test-layered-workspace-key="test-layered-val"
"###);
}

#[test]
Expand Down Expand Up @@ -364,16 +393,23 @@ fn test_config_layer_workspace() {
// Repo
std::fs::write(
main_path.join(".jj/repo/config.toml"),
format!("{config_key} = {value:?}\n", value = "main-repo"),
format!("ui.mergetool = \"foo\"\n{config_key} = {value:?}\n", value = "main-repo"),
)
.unwrap();
// Secondary workspace
std::fs::write(
secondary_path.join(".jj/config.toml"),
format!("{config_key} = {value:?}\n", value = "workspace"),
)
.unwrap();
let stdout = test_env.jj_cmd_success(&main_path, &["config", "list", config_key]);

let stdout = test_env.jj_cmd_success(&main_path, &["config", "list", "ui.mergetool"]);
insta::assert_snapshot!(stdout, @r###"
ui.editor="main-repo"
ui.mergetool="foo"
"###);
let stdout = test_env.jj_cmd_success(&secondary_path, &["config", "list", config_key]);
insta::assert_snapshot!(stdout, @r###"
ui.editor="main-repo"
ui.editor="workspace"
"###);
}

Expand All @@ -383,11 +419,11 @@ fn test_config_set_missing_opts() {
let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "set"]);
insta::assert_snapshot!(stderr, @r###"
error: the following required arguments were not provided:
<--user|--repo>
<--user|--repo|--workspace>
<NAME>
<VALUE>
Usage: jj config set <--user|--repo> <NAME> <VALUE>
Usage: jj config set <--user|--repo|--workspace> <NAME> <VALUE>
For more information, try '--help'.
"###);
Expand Down Expand Up @@ -546,9 +582,9 @@ fn test_config_edit_missing_opt() {
let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "edit"]);
insta::assert_snapshot!(stderr, @r###"
error: the following required arguments were not provided:
<--user|--repo>
<--user|--repo|--workspace>
Usage: jj config edit <--user|--repo>
Usage: jj config edit <--user|--repo|--workspace>
For more information, try '--help'.
"###);
Expand Down
4 changes: 4 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ config path --user`.
- The repo settings. These can be edited with `jj config edit --repo` and are
located in `.jj/repo/config.toml`.

- The workspace settings. These can be edited with `jj config edit --workspace`
and are located in `.jj/config.toml` in the workspace root. The first working
directory created in a fresh `jj` repo is itself a workspace.

- Settings [specified in the command-line](#specifying-config-on-the-command-line).

These are listed in the order they are loaded; the settings from earlier items
Expand Down

0 comments on commit 01b8970

Please sign in to comment.