|
| 1 | +use std::fs; |
| 2 | +use std::process::Command; |
| 3 | + |
| 4 | +use tempfile::TempDir; |
| 5 | + |
| 6 | +fn init_git_repo(dir: &TempDir) { |
| 7 | + Command::new("git") |
| 8 | + .args(["init", "--initial-branch=main"]) |
| 9 | + .current_dir(dir.path()) |
| 10 | + .output() |
| 11 | + .expect("failed to init git repo"); |
| 12 | + |
| 13 | + Command::new("git") |
| 14 | + .args(["config", "user.email", "test@example.com"]) |
| 15 | + .current_dir(dir.path()) |
| 16 | + .output() |
| 17 | + .expect("failed to configure git email"); |
| 18 | + |
| 19 | + Command::new("git") |
| 20 | + .args(["config", "user.name", "Test"]) |
| 21 | + .current_dir(dir.path()) |
| 22 | + .output() |
| 23 | + .expect("failed to configure git name"); |
| 24 | +} |
| 25 | + |
| 26 | +fn git_add_and_commit(dir: &TempDir, message: &str) { |
| 27 | + Command::new("git") |
| 28 | + .args(["add", "-A"]) |
| 29 | + .current_dir(dir.path()) |
| 30 | + .output() |
| 31 | + .expect("failed to git add"); |
| 32 | + |
| 33 | + Command::new("git") |
| 34 | + .args(["commit", "-m", message]) |
| 35 | + .current_dir(dir.path()) |
| 36 | + .output() |
| 37 | + .expect("failed to git commit"); |
| 38 | +} |
| 39 | + |
| 40 | +fn lockfile_hash(dir: &TempDir) -> Vec<u8> { |
| 41 | + let output = Command::new("git") |
| 42 | + .args(["hash-object", "Cargo.lock"]) |
| 43 | + .current_dir(dir.path()) |
| 44 | + .output() |
| 45 | + .expect("failed to hash Cargo.lock"); |
| 46 | + |
| 47 | + output.stdout |
| 48 | +} |
| 49 | + |
| 50 | +fn write_changeset(dir: &TempDir, filename: &str, package: &str, bump: &str, summary: &str) { |
| 51 | + let content = format!( |
| 52 | + r#"--- |
| 53 | +"{package}": {bump} |
| 54 | +--- |
| 55 | +
|
| 56 | +{summary} |
| 57 | +"# |
| 58 | + ); |
| 59 | + fs::write( |
| 60 | + dir.path().join(".changeset/changesets").join(filename), |
| 61 | + content, |
| 62 | + ) |
| 63 | + .expect("write changeset"); |
| 64 | +} |
| 65 | + |
| 66 | +fn setup_workspace_with_dependency() -> TempDir { |
| 67 | + let dir = TempDir::new().expect("create temp dir"); |
| 68 | + |
| 69 | + init_git_repo(&dir); |
| 70 | + |
| 71 | + fs::write( |
| 72 | + dir.path().join("Cargo.toml"), |
| 73 | + r#"[workspace] |
| 74 | +members = ["crates/*"] |
| 75 | +resolver = "2" |
| 76 | +
|
| 77 | +[workspace.dependencies] |
| 78 | +crate-a = { path = "crates/crate-a", version = "1.0.0" } |
| 79 | +"#, |
| 80 | + ) |
| 81 | + .expect("write workspace Cargo.toml"); |
| 82 | + |
| 83 | + fs::create_dir_all(dir.path().join("crates/crate-a/src")).expect("create crate-a dir"); |
| 84 | + fs::write( |
| 85 | + dir.path().join("crates/crate-a/Cargo.toml"), |
| 86 | + r#"[package] |
| 87 | +name = "crate-a" |
| 88 | +version = "1.0.0" |
| 89 | +edition = "2021" |
| 90 | +"#, |
| 91 | + ) |
| 92 | + .expect("write crate-a Cargo.toml"); |
| 93 | + fs::write(dir.path().join("crates/crate-a/src/lib.rs"), "").expect("write crate-a lib.rs"); |
| 94 | + |
| 95 | + fs::create_dir_all(dir.path().join("crates/crate-b/src")).expect("create crate-b dir"); |
| 96 | + fs::write( |
| 97 | + dir.path().join("crates/crate-b/Cargo.toml"), |
| 98 | + r#"[package] |
| 99 | +name = "crate-b" |
| 100 | +version = "2.0.0" |
| 101 | +edition = "2021" |
| 102 | +
|
| 103 | +[dependencies] |
| 104 | +crate-a = { workspace = true } |
| 105 | +"#, |
| 106 | + ) |
| 107 | + .expect("write crate-b Cargo.toml"); |
| 108 | + fs::write(dir.path().join("crates/crate-b/src/lib.rs"), "").expect("write crate-b lib.rs"); |
| 109 | + |
| 110 | + fs::create_dir_all(dir.path().join(".changeset/changesets")) |
| 111 | + .expect("create .changeset/changesets dir"); |
| 112 | + |
| 113 | + let output = Command::new("cargo") |
| 114 | + .args(["generate-lockfile"]) |
| 115 | + .current_dir(dir.path()) |
| 116 | + .output() |
| 117 | + .expect("failed to run cargo generate-lockfile"); |
| 118 | + assert!( |
| 119 | + output.status.success(), |
| 120 | + "cargo generate-lockfile failed: {}", |
| 121 | + String::from_utf8_lossy(&output.stderr) |
| 122 | + ); |
| 123 | + |
| 124 | + git_add_and_commit(&dir, "Initial commit"); |
| 125 | + |
| 126 | + dir |
| 127 | +} |
| 128 | + |
| 129 | +macro_rules! cargo_changeset { |
| 130 | + () => { |
| 131 | + assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset") |
| 132 | + }; |
| 133 | +} |
| 134 | + |
| 135 | +#[test] |
| 136 | +fn release_lockfile_not_dirtied_by_cargo_check() { |
| 137 | + let workspace = setup_workspace_with_dependency(); |
| 138 | + write_changeset(&workspace, "fix.md", "crate-a", "patch", "Fix a bug"); |
| 139 | + git_add_and_commit(&workspace, "Add changeset"); |
| 140 | + |
| 141 | + cargo_changeset!() |
| 142 | + .arg("release") |
| 143 | + .current_dir(workspace.path()) |
| 144 | + .assert() |
| 145 | + .success(); |
| 146 | + |
| 147 | + let hash_before = lockfile_hash(&workspace); |
| 148 | + |
| 149 | + let check_output = Command::new("cargo") |
| 150 | + .args(["check"]) |
| 151 | + .current_dir(workspace.path()) |
| 152 | + .output() |
| 153 | + .expect("failed to run cargo check"); |
| 154 | + assert!( |
| 155 | + check_output.status.success(), |
| 156 | + "cargo check failed: {}", |
| 157 | + String::from_utf8_lossy(&check_output.stderr) |
| 158 | + ); |
| 159 | + |
| 160 | + let hash_after = lockfile_hash(&workspace); |
| 161 | + |
| 162 | + assert_eq!( |
| 163 | + hash_before, hash_after, |
| 164 | + "cargo check should not change Cargo.lock after release — \ |
| 165 | + the release commit must include an up-to-date lockfile" |
| 166 | + ); |
| 167 | +} |
| 168 | + |
| 169 | +#[test] |
| 170 | +fn release_lockfile_correct_after_minor_bump_with_workspace_dep() { |
| 171 | + let workspace = setup_workspace_with_dependency(); |
| 172 | + write_changeset(&workspace, "feat.md", "crate-a", "minor", "Add a feature"); |
| 173 | + git_add_and_commit(&workspace, "Add changeset"); |
| 174 | + |
| 175 | + cargo_changeset!() |
| 176 | + .arg("release") |
| 177 | + .current_dir(workspace.path()) |
| 178 | + .assert() |
| 179 | + .success(); |
| 180 | + |
| 181 | + let hash_before = lockfile_hash(&workspace); |
| 182 | + |
| 183 | + let check_output = Command::new("cargo") |
| 184 | + .args(["check"]) |
| 185 | + .current_dir(workspace.path()) |
| 186 | + .output() |
| 187 | + .expect("failed to run cargo check"); |
| 188 | + assert!( |
| 189 | + check_output.status.success(), |
| 190 | + "cargo check failed: {}", |
| 191 | + String::from_utf8_lossy(&check_output.stderr) |
| 192 | + ); |
| 193 | + |
| 194 | + let hash_after = lockfile_hash(&workspace); |
| 195 | + |
| 196 | + assert_eq!( |
| 197 | + hash_before, hash_after, |
| 198 | + "cargo check should not change Cargo.lock after minor bump release" |
| 199 | + ); |
| 200 | +} |
0 commit comments