Skip to content

Commit 1b94e1b

Browse files
committed
fix(package): report if workspace manifest is dirty
Workspace manifest might not be under the package root, but workspace inheritance may still affect what is packaged into the `.crate.` tarball. We take a conservative action that if the workspace manifest is dirty, then all workspace members are considered dirty.
1 parent 2375b19 commit 1b94e1b

File tree

3 files changed

+17
-19
lines changed

3 files changed

+17
-19
lines changed

src/cargo/ops/cargo_package/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ fn prepare_archive(
384384
let src_files = src.list_files(pkg)?;
385385

386386
// Check (git) repository state, getting the current commit hash.
387-
let vcs_info = vcs::check_repo_state(pkg, &src_files, gctx, &opts)?;
387+
let vcs_info = vcs::check_repo_state(pkg, &src_files, ws, &opts)?;
388388

389389
build_ar_list(ws, pkg, src_files, vcs_info)
390390
}

src/cargo/ops/cargo_package/vcs.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use serde::Serialize;
1010
use tracing::debug;
1111

1212
use crate::core::Package;
13+
use crate::core::Workspace;
1314
use crate::sources::PathEntry;
1415
use crate::CargoResult;
1516
use crate::GlobalContext;
@@ -44,9 +45,10 @@ pub struct GitVcsInfo {
4445
pub fn check_repo_state(
4546
p: &Package,
4647
src_files: &[PathEntry],
47-
gctx: &GlobalContext,
48+
ws: &Workspace<'_>,
4849
opts: &PackageOpts<'_>,
4950
) -> CargoResult<Option<VcsInfo>> {
51+
let gctx = ws.gctx();
5052
let Ok(repo) = git2::Repository::discover(p.root()) else {
5153
gctx.shell().verbose(|shell| {
5254
shell.warn(format_args!(
@@ -103,7 +105,7 @@ pub fn check_repo_state(
103105
path.display(),
104106
workdir.display(),
105107
);
106-
let Some(git) = git(p, gctx, src_files, &repo, &opts)? else {
108+
let Some(git) = git(ws, p, src_files, &repo, &opts)? else {
107109
// If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning,
108110
// then don't generate the corresponding file in order to maintain consistency with past behavior.
109111
return Ok(None);
@@ -166,8 +168,8 @@ fn warn_symlink_checked_out_as_plain_text_file(
166168

167169
/// The real git status check starts from here.
168170
fn git(
171+
ws: &Workspace<'_>,
169172
pkg: &Package,
170-
gctx: &GlobalContext,
171173
src_files: &[PathEntry],
172174
repo: &git2::Repository,
173175
opts: &PackageOpts<'_>,
@@ -188,12 +190,12 @@ fn git(
188190
// Find the intersection of dirty in git, and the src_files that would
189191
// be packaged. This is a lazy n^2 check, but seems fine with
190192
// thousands of files.
191-
let cwd = gctx.cwd();
193+
let cwd = ws.gctx().cwd();
192194
let mut dirty_src_files: Vec<_> = src_files
193195
.iter()
194196
.filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path)))
195197
.map(|p| p.as_ref())
196-
.chain(dirty_files_outside_pkg_root(pkg, repo, src_files)?.iter())
198+
.chain(dirty_files_outside_pkg_root(ws, pkg, repo, src_files)?.iter())
197199
.map(|path| {
198200
pathdiff::diff_paths(path, cwd)
199201
.as_ref()
@@ -232,11 +234,13 @@ fn git(
232234
///
233235
/// * `package.readme` and `package.license-file` pointing to paths outside package root
234236
/// * symlinks targets reside outside package root
237+
/// * Any change in the root workspace manifest, regardless of what has changed.
235238
///
236239
/// This is required because those paths may link to a file outside the
237240
/// current package root, but still under the git workdir, affecting the
238241
/// final packaged `.crate` file.
239242
fn dirty_files_outside_pkg_root(
243+
ws: &Workspace<'_>,
240244
pkg: &Package,
241245
repo: &git2::Repository,
242246
src_files: &[PathEntry],
@@ -255,8 +259,9 @@ fn dirty_files_outside_pkg_root(
255259
for rel_path in src_files
256260
.iter()
257261
.filter(|p| p.is_symlink_or_under_symlink())
258-
.map(|p| p.as_ref())
259-
.chain(metadata_paths.iter())
262+
.map(|p| p.as_ref().as_path())
263+
.chain(metadata_paths.iter().map(AsRef::as_ref))
264+
.chain([ws.root_manifest()])
260265
// If inside package root. Don't bother checking git status.
261266
.filter(|p| paths::strip_prefix_canonical(p, pkg_root).is_err())
262267
// Handle files outside package root but under git workdir,

tests/testsuite/package.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -1212,15 +1212,6 @@ fn vcs_status_check_for_each_workspace_member() {
12121212
});
12131213
git::commit(&repo);
12141214

1215-
p.change_file(
1216-
"Cargo.toml",
1217-
r#"
1218-
[workspace]
1219-
members = ["isengard", "mordor"]
1220-
[workspace.package]
1221-
edition = "2021"
1222-
"#,
1223-
);
12241215
// Dirty file outside won't affect packaging.
12251216
p.change_file("hobbit", "changed!");
12261217
p.change_file("mordor/src/lib.rs", "changed!");
@@ -1357,7 +1348,8 @@ fn dirty_file_outside_pkg_root_considered_dirty() {
13571348
p.change_file("original-dir/file", "after");
13581349
// * Changes in files outside pkg root that `license-file`/`readme` point to
13591350
p.change_file("LICENSE", "after");
1360-
// * When workspace inheritance is involved and changed
1351+
// * When workspace root manifest has changned,
1352+
// no matter whether workspace inheritance is involved.
13611353
p.change_file(
13621354
"Cargo.toml",
13631355
r#"
@@ -1378,8 +1370,9 @@ fn dirty_file_outside_pkg_root_considered_dirty() {
13781370
p.cargo("package --workspace --no-verify")
13791371
.with_status(101)
13801372
.with_stderr_data(str![[r#"
1381-
[ERROR] 4 files in the working directory contain changes that were not yet committed into git:
1373+
[ERROR] 5 files in the working directory contain changes that were not yet committed into git:
13821374
1375+
Cargo.toml
13831376
LICENSE
13841377
README.md
13851378
lib.rs

0 commit comments

Comments
 (0)