-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
base: master
Are you sure you want to change the base?
Fix cargo add overwriting symlinked Cargo.toml files #15281
Conversation
r? @weihanglo rustbot has assigned @weihanglo. Use |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
a45d668
to
53fdb96
Compare
There was a problem hiding this comment.
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.)
tests/testsuite/cargo_add/symlink.rs
Outdated
} | ||
|
||
// Add a dependency | ||
project.cargo("add rand").run(); |
There was a problem hiding this comment.
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
cargo/tests/testsuite/rename_deps.rs
Line 12 in ab1463d
Package::new("bar", "0.2.0").publish(); cargo/tests/testsuite/cargo_add/features/mod.rs
Lines 10 to 16 in ab1463d
cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") .feature("nose", &[]) .feature("mouth", &[]) .feature("eyes", &[]) .feature("ears", &[]) .publish();
There was a problem hiding this comment.
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?
2f5fc06
to
465de3c
Compare
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
465de3c
to
1708e12
Compare
/// Write changes back to the file. | ||
/// Write changes back to the file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the double comment?
// 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) |
There was a problem hiding this comment.
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
?
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 andcargo 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:cargo add
to add a dependencyI've manually tested this fix and confirmed it works correctly.