Skip to content

Conversation

@etraut-openai
Copy link
Collaborator

We already support reading from config.toml through a symlink, but the code was not properly handling updates to a symlinked config file. This PR generalizes safe symlink-chain resolution and atomic writes into path_utils, updating all config write paths to use the shared logic (including set_default_oss_provider, which previously didn't use the common path), and adds tests for symlink chains and cycles.

This resolves #6646.

Notes:

  • Symlink cycles or resolution failures replace the top-level symlink with a real file.
  • Shared config write path now handles symlinks consistently across edits, defaults, and empty-user-layer creation.

This PR was inspired by #9437, which was contributed by @ryoppippi

We already support reading from `config.toml` through a symlink, but the code was not properly handling updates to a symlinked config file. This PR generalizes safe symlink-chain resolution and atomic writes into path_utils, updating all config write paths to use the shared logic (including set_default_oss_provider, which previously didn't use the common path), and adds tests for symlink chains and cycles.

This resolves #6646.

Notes:
* Symlink cycles or resolution failures replace the top-level symlink with a real file.
* Shared config write path now handles symlinks consistently across edits, defaults, and empty-user-layer creation.
@etraut-openai
Copy link
Collaborator Author

@codex review

@chatgpt-codex-connector
Copy link
Contributor

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

pub write_path: PathBuf,
}

pub fn resolve_symlink_write_paths(path: &Path) -> io::Result<SymlinkWritePaths> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a docstring for this? It looks like it guards against cycles but has no max number of symlink resolutions?

Also, can you please add a unit test?

}

async fn write_empty_user_config(write_path: PathBuf) -> Result<(), ConfigServiceError> {
task::spawn_blocking(move || write_atomically(&write_path, ""))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make an async version of write_atomically() instead?

std::fs::create_dir_all(parent)?;
let tmp = NamedTempFile::new_in(parent)?;
std::fs::write(tmp.path(), contents)?;
tmp.persist(write_path)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is the real reason why this needs to be sync? It doesn't appear as though there is an async version readily available...

Incidentally, here is how the tempfile crate creates a file on UNIX:

https://github.com/Stebalien/tempfile/blob/8e5ba93488ad0a6354c19776a0cb89b95273a491/src/file/imp/unix.rs#L47-L65

Ok(normalize_for_wsl(canonical))
}

pub struct SymlinkWritePaths {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type feels a little weird to me: symlinks are relatively rare, but you get this type back even when symlinks are not involved.

Should this be an enum like:

pub enum WritePath {
  RegularFile {
    write_path: PathBuf,
  }
  Symlink {
    read_path: PathBuf,
    write_path: PathBuf,
  }
}

@etraut-openai etraut-openai merged commit 1271d45 into main Jan 19, 2026
51 of 53 checks passed
@etraut-openai etraut-openai deleted the etraut/config_symlink branch January 19, 2026 03:22
@github-actions github-actions bot locked and limited conversation to collaborators Jan 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Honor config.toml symlinks when creating or updating config

3 participants