-
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?
465de3c
to
1708e12
Compare
src/cargo/util/toml_mut/manifest.rs
Outdated
/// 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
?
1708e12
to
4f2006f
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.
This commit does not change the test. Does the previous commit fail cargo test
or is it testing the wrong thing? Each commit should be atomic which includes passing tests. When we ask for a test commit to be split out first, it is to show the current behavior. The commit with the fix will also change the test and that diff shows how behavior changes.
eec6ec3
to
b0850ce
Compare
b0850ce
to
26bd6b5
Compare
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.