Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cargo add overwriting symlinked Cargo.toml files #15281

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

RaghavenderSingh
Copy link

What does this PR try to resolve?

This PR fixes a bug where cargo add breaks symlinks to Cargo.toml files. Currently, when Cargo.toml is a symlink and cargo add is used to add a dependency, the symlink is replaced with a regular file, breaking the link to the original target file.

This issue was reported in #15241 where a user who relies on symlinked Cargo.toml files found that cargo add breaks their workflow.

Fixes #15241

How should we test and review this PR?

I've modified LocalManifest::write() to check if the path is a symlink, and if so, follow it to get the actual target path. This ensures we write to the actual file rather than replacing the symlink.

I've also added a test in tests/testsuite/cargo_add/symlink.rs that:

  1. Creates a symlinked Cargo.toml file
  2. Runs cargo add to add a dependency
  3. Verifies the symlink is preserved and the dependency is added to the target file

I've manually tested this fix and confirmed it works correctly.

@rustbot
Copy link
Collaborator

rustbot commented Mar 8, 2025

r? @weihanglo

rustbot has assigned @weihanglo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-manifest Area: Cargo.toml issues S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 8, 2025
Copy link
Member

Choose a reason for hiding this comment

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

nit: we prefer contributions split tests into the first commit, with them passing. That way the fix commit will show the change in behavior by how the test changes. This helps test the test and provides an illustration of the PR description for the reviewer and the community at large.

We linked to examples in the contrib docs explaining this

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the guidance. I've restructured the PR.

Copy link
Member

Choose a reason for hiding this comment

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

Note that the first commit is best to pass CI. That means you'll assert the problematic behavior. The "fix" commit then contains both the fix, and the change in test. See also this PR as an example.

In addition, your test is a functional test, but is placed along with other UI tests. Is it possible to make it a pure file-based UI test, and change the test name to case in order to follow the convention?

(It might not work because cross-platform symlinks are tricky, and git is not good at handling it.)

Copy link
Member

Choose a reason for hiding this comment

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

cc @epage, any thoughts on how to organize these?

Copy link
Contributor

Choose a reason for hiding this comment

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

This mixing of functional with UI tests seems inline with how we do it elesewhere

@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch from a45d668 to 53fdb96 Compare March 8, 2025 19:25
Copy link
Member

Choose a reason for hiding this comment

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

Note that the first commit is best to pass CI. That means you'll assert the problematic behavior. The "fix" commit then contains both the fix, and the change in test. See also this PR as an example.

In addition, your test is a functional test, but is placed along with other UI tests. Is it possible to make it a pure file-based UI test, and change the test name to case in order to follow the convention?

(It might not work because cross-platform symlinks are tricky, and git is not good at handling it.)

}

// Add a dependency
project.cargo("add rand").run();
Copy link
Member

Choose a reason for hiding this comment

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

This is pulling an exteranl dependency from crates.io. We avoid that in cargo's testsuite and instead host a local registry fixure, like

Copy link
Member

Choose a reason for hiding this comment

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

cc @epage, any thoughts on how to organize these?

@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch 2 times, most recently from 2f5fc06 to 465de3c Compare March 9, 2025 19:57
When Cargo.toml is a symlink, cargo add was overwriting it with a regular
file. This change follows the symlink and writes to the target file instead,
preserving the symlink structure.

Fixes rust-lang#15241
@RaghavenderSingh RaghavenderSingh force-pushed the fix-cargo-add-symlinks branch from 465de3c to 1708e12 Compare March 9, 2025 20:04
Comment on lines +300 to 301
/// Write changes back to the file.
/// Write changes back to the file.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the double comment?

Comment on lines +327 to +335
// Check if the path is a symlink and follow it if it is
let actual_path = if self.path.is_symlink() {
std::fs::read_link(&self.path)?
} else {
self.path.clone()
};

// Write to the actual target path instead of the symlink
cargo_util::paths::write_atomic(&actual_path, new_contents_bytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be put in write_atomic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-manifest Area: Cargo.toml issues S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cargo add overwrites a symlink Cargo.toml
4 participants