diff --git a/CHANGELOG.md b/CHANGELOG.md index a2187b67da6..317ef50e8f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index f2849a85142..1bc25201a1e 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -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:?}" @@ -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)?; @@ -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 diff --git a/cli/src/commands/config.rs b/cli/src/commands/config.rs index 91fb74d1646..1f5bbf22250 100644 --- a/cli/src/commands/config.rs +++ b/cli/src/commands/config.rs @@ -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 { @@ -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 } diff --git a/cli/src/config.rs b/cli/src/config.rs index 453d7dabedf..62c879e0dca 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -41,6 +41,7 @@ pub enum ConfigSource { // TODO: Track explicit file paths, especially for when user config is a dir. User, Repo, + Workspace, CommandArg, } @@ -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)] @@ -68,6 +69,7 @@ pub struct LayeredConfigs { env_base: config::Config, user: Option, repo: Option, + workspace: Option, env_overrides: config::Config, arg_overrides: Option, } @@ -80,6 +82,7 @@ impl LayeredConfigs { env_base: env_base(), user: None, repo: None, + workspace: None, env_overrides: env_overrides(), arg_overrides: None, } @@ -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() @@ -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()), ]; @@ -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, }; @@ -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, }; @@ -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, }; diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index c74cbbfa50c..de42d92506e 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -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