config: manage repo config path by ConfigEnv

This will help migrate LayeredConfigs methods to ConfigEnv.
This commit is contained in:
Yuya Nishihara 2024-11-23 19:03:13 +09:00
parent 6ec3fcc359
commit 1d485b75b6
7 changed files with 54 additions and 28 deletions

View File

@ -2767,15 +2767,16 @@ impl LogContentFormat {
pub fn get_new_config_file_path(
config_source: ConfigSource,
command: &CommandHelper,
) -> Result<PathBuf, CommandError> {
) -> Result<&Path, CommandError> {
let config_env = command.config_env();
let edit_path = match config_source {
// TODO(#531): Special-case for editors that can't handle viewing directories?
ConfigSource::User => command
.config_env()
ConfigSource::User => config_env
.new_user_config_path()?
.ok_or_else(|| user_error("No user config path found to edit"))?
.to_owned(),
ConfigSource::Repo => command.workspace_loader()?.repo_path().join("config.toml"),
.ok_or_else(|| user_error("No user config path found to edit"))?,
ConfigSource::Repo => config_env
.new_repo_config_path()
.ok_or_else(|| user_error("No repo config path found to edit"))?,
_ => {
return Err(user_error(format!(
"Can't get path for config source {config_source:?}"
@ -3553,7 +3554,7 @@ impl CliRunner {
"Did you update to a commit where the directory doesn't exist?",
)
})?;
let config_env = ConfigEnv::from_environment()?;
let mut config_env = ConfigEnv::from_environment()?;
// Use cwd-relative workspace configs to resolve default command and
// aliases. WorkspaceLoader::init() won't do any heavy lifting other
// than the path resolution.
@ -3562,15 +3563,15 @@ impl CliRunner {
.create(find_workspace_dir(&cwd))
.map_err(|err| map_workspace_load_error(err, None));
layered_configs.read_user_config(&config_env)?;
let mut repo_config_path = None;
if let Ok(loader) = &maybe_cwd_workspace_loader {
layered_configs.read_repo_config(loader.repo_path())?;
repo_config_path = Some(layered_configs.repo_config_path(loader.repo_path()));
config_env.reset_repo_path(loader.repo_path());
layered_configs.read_repo_config(&config_env)?;
}
let config = layered_configs.merge();
ui.reset(&config).map_err(|e| {
let user_config_path = config_env.existing_user_config_path();
let paths = [repo_config_path.as_deref(), user_config_path]
let repo_config_path = config_env.existing_repo_config_path();
let paths = [repo_config_path, user_config_path]
.into_iter()
.flatten()
.map(|path| format!("- {}", path.display()))
@ -3601,7 +3602,8 @@ impl CliRunner {
.workspace_loader_factory
.create(&cwd.join(path))
.map_err(|err| map_workspace_load_error(err, Some(path)))?;
layered_configs.read_repo_config(loader.repo_path())?;
config_env.reset_repo_path(loader.repo_path());
layered_configs.read_repo_config(&config_env)?;
Ok(loader)
} else {
maybe_cwd_workspace_loader

View File

@ -38,5 +38,5 @@ pub fn cmd_config_edit(
args: &ConfigEditArgs,
) -> Result<(), CommandError> {
let config_path = get_new_config_file_path(args.level.expect_source_kind(), command)?;
run_ui_editor(command.settings(), &config_path)
run_ui_editor(command.settings(), config_path)
}

View File

@ -73,7 +73,7 @@ pub fn cmd_config_set(
check_wc_author(ui, command, &value, AuthorChange::Email)?;
};
write_config_value_to_file(&args.name, value, &config_path)
write_config_value_to_file(&args.name, value, config_path)
}
/// Returns the commit of the working copy if it exists.

View File

@ -48,5 +48,5 @@ pub fn cmd_config_unset(
)));
}
remove_config_value_from_file(&args.name, &config_path)
remove_config_value_from_file(&args.name, config_path)
}

View File

@ -493,11 +493,12 @@ fn get_jj_command() -> Result<(JjBuilder, Config), CommandError> {
let cwd = std::env::current_dir()
.and_then(|cwd| cwd.canonicalize())
.map_err(user_error)?;
let config_env = ConfigEnv::from_environment()?;
let mut config_env = ConfigEnv::from_environment()?;
let maybe_cwd_workspace_loader = DefaultWorkspaceLoaderFactory.create(find_workspace_dir(&cwd));
let _ = layered_configs.read_user_config(&config_env);
if let Ok(loader) = &maybe_cwd_workspace_loader {
let _ = layered_configs.read_repo_config(loader.repo_path());
config_env.reset_repo_path(loader.repo_path());
let _ = layered_configs.read_repo_config(&config_env);
}
let mut config = layered_configs.merge();
// skip 2 because of the clap_complete prelude: jj -- jj <actual args...>
@ -514,7 +515,8 @@ fn get_jj_command() -> Result<(JjBuilder, Config), CommandError> {
if let Some(repository) = args.repository {
// Try to update repo-specific config on a best-effort basis.
if let Ok(loader) = DefaultWorkspaceLoaderFactory.create(&cwd.join(&repository)) {
let _ = layered_configs.read_repo_config(loader.repo_path());
config_env.reset_repo_path(loader.repo_path());
let _ = layered_configs.read_repo_config(&config_env);
config = layered_configs.merge();
}
cmd_args.push("--repository".into());

View File

@ -143,19 +143,14 @@ impl LayeredConfigs {
}
#[instrument]
pub fn read_repo_config(&mut self, repo_path: &Path) -> Result<(), ConfigEnvError> {
pub fn read_repo_config(&mut self, env: &ConfigEnv) -> Result<(), ConfigError> {
self.inner.remove_layers(ConfigSource::Repo);
let path = self.repo_config_path(repo_path);
if path.exists() {
if let Some(path) = env.existing_repo_config_path() {
self.inner.load_file(ConfigSource::Repo, path)?;
}
Ok(())
}
pub fn repo_config_path(&self, repo_path: &Path) -> PathBuf {
repo_path.join("config.toml")
}
pub fn parse_config_args(&mut self, toml_strs: &[String]) -> Result<(), ConfigEnvError> {
self.inner.remove_layers(ConfigSource::CommandArg);
let config = toml_strs
@ -314,6 +309,7 @@ impl UnresolvedConfigEnv {
#[derive(Clone, Debug)]
pub struct ConfigEnv {
user_config_path: ConfigPath,
repo_config_path: ConfigPath,
}
impl ConfigEnv {
@ -326,6 +322,7 @@ impl ConfigEnv {
};
Ok(ConfigEnv {
user_config_path: env.resolve()?,
repo_config_path: ConfigPath::Unavailable,
})
}
@ -353,6 +350,32 @@ impl ConfigEnv {
ConfigPath::Unavailable => Ok(None),
}
}
/// Sets the directory where repo-specific config file is stored. The path
/// is usually `.jj/repo`.
pub fn reset_repo_path(&mut self, path: &Path) {
self.repo_config_path = ConfigPath::new(Some(path.join("config.toml")));
}
/// Returns a path to the existing repo-specific config file.
pub fn existing_repo_config_path(&self) -> Option<&Path> {
match &self.repo_config_path {
ConfigPath::Existing(path) => Some(path),
_ => None,
}
}
/// Returns a path to the repo-specific config file.
pub fn new_repo_config_path(&self) -> Option<&Path> {
match &self.repo_config_path {
ConfigPath::Existing(path) => Some(path),
ConfigPath::New(path) => {
// TODO: should we create new file?
Some(path)
}
ConfigPath::Unavailable => None,
}
}
}
/// Environment variables that should be overridden by config values
@ -1185,6 +1208,7 @@ mod tests {
};
Ok(ConfigEnv {
user_config_path: env.resolve()?,
repo_config_path: ConfigPath::Unavailable,
})
}

View File

@ -797,9 +797,7 @@ fn test_config_path() {
fn test_config_edit_repo_outside_repo() {
let test_env = TestEnvironment::default();
let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["config", "edit", "--repo"]);
insta::assert_snapshot!(stderr, @r###"
Error: There is no jj repo in "."
"###);
insta::assert_snapshot!(stderr, @"Error: No repo config path found to edit");
}
#[test]