Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/changesets/constantly-notable-frog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: added
changeset-operations: minor
---
Add integration tests for target-specific dependency handling during release
4 changes: 4 additions & 0 deletions .changeset/changesets/diffusely-dandy-goldcrest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
changeset-manifest: patch
---
Handle simple string dependency entries during version updates
5 changes: 5 additions & 0 deletions .changeset/changesets/giddily-valid-gaur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: added
cargo-changeset: minor
---
Add end-to-end tests for target-specific dependency version updates during release
5 changes: 5 additions & 0 deletions .changeset/changesets/imminently-exuberant-teal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: added
changeset-manifest: minor
---
Update dependency versions in `[target.'...'.dependencies]`, `[target.'...'.dev-dependencies]`, and `[target.'...'.build-dependencies]` sections
4 changes: 4 additions & 0 deletions .changeset/changesets/mundanely-spacious-magpie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
cargo-changeset: patch
---
Handle simple string dependency entries during version updates
5 changes: 5 additions & 0 deletions .changeset/changesets/youthfully-exultant-krill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: added
changeset-project: minor
---
Support `[target.'...'.dependencies]` and `[target.'...'.build-dependencies]` sections in the workspace dependency graph
44 changes: 28 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,41 @@ For detailed usage beyond what is covered here, run

### When to add a changeset

Add a changeset whenever your changes affect the behavior, API, or
dependencies of one or more crates in this workspace. Do NOT add a changeset
for changes that are invisible to users of the crate, such as CI
configuration, documentation-only edits, or test-only refactors.
Run `cargo changeset verify` to determine which crates need changeset
coverage. It is the single source of truth — add changesets for every crate
it reports as uncovered.

### IMPORTANT: Changeset workflow

`cargo changeset verify` operates in two modes:

1. **Dirty working directory** — verifies only the uncommitted changes
2. **Clean working directory** — verifies all branch changes against the base branch

The correct workflow is:

1. Make code changes (working directory is now dirty)
2. Run `cargo changeset verify` — dirty mode reports which crates need coverage
3. Add changesets for uncovered crates
4. Run `cargo changeset verify` again — should now pass (dirty state includes both code + changesets)
5. Commit everything together

**NEVER commit without `cargo changeset verify` passing first.**

### How to add a changeset

Run the following command (no interactive prompts):

```bash
cargo changeset add \
--package-bump <crate-name>:<major|minor|patch> \
--package-bump <crate-name>:<major|minor|patch|none> \
--category <added|changed|deprecated|removed|fixed|security> \
-m "<description>"
```

The `--category` flag defaults to `changed` if omitted. It determines which
CHANGELOG section the entry appears under.

For changes affecting multiple crates, repeat `--package-bump` for each:

```bash
Expand All @@ -99,28 +119,20 @@ cargo changeset add \
- `major` — breaking changes to the public API
- `minor` — new functionality that is backwards compatible
- `patch` — bug fixes and backwards-compatible corrections
- `none` — internal changes invisible to crate consumers

### Writing the description

The changeset description appears in the CHANGELOG and is read by users of
the crate, not its developers. Write it from the perspective of someone who
depends on the crate and wants to know what changed and how it affects them.
Keep it to a single sentence when possible.
Keep it to a single sentence when possible. Use markdown notation where
appropriate (e.g. backticks for code references).

Good: "Add `--timeout` flag to control request deadline"
Good: "Fix panic when parsing empty configuration files"
Bad: "Refactored the timeout module and added a CLI flag"
Bad: "Fixed bug in config.rs line 42"

### Verifying coverage

After adding a changeset, verify that all affected crates are covered:

```bash
cargo changeset verify --base main
```

Exit code 0 means all changed crates have coverage.
````

---
Expand Down
164 changes: 163 additions & 1 deletion crates/cargo-changeset/tests/release_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::process::Command;
use changeset_test_helpers::changesets::{write_changeset, write_multi_changeset};
use changeset_test_helpers::git::{git_add_and_commit, init_git_repo};
use changeset_test_helpers::workspaces::{
add_helm_chart_config, create_workspace_with_cascade_chain,
WorkspaceBuilder, add_helm_chart_config, create_workspace_with_cascade_chain,
create_workspace_with_circular_version_tracking,
create_workspace_with_deeply_nested_json_field, create_workspace_with_duplicate_dependency,
create_workspace_with_helm_chart, create_workspace_with_invalid_version_field_path,
Expand Down Expand Up @@ -675,3 +675,165 @@ fn release_version_tracking_deeply_nested_field_paths() {
"expected deeply nested upstream_crate version updated to 1.0.1, got:\n{manifest_json}"
);
}

#[test]
fn release_updates_target_specific_dependency_versions() {
let workspace = WorkspaceBuilder::virtual_workspace()
.with_git()
.with_changeset_dir()
.crate_member("crate-a", "1.0.0")
.crate_member("crate-b", "2.0.0")
.crate_toml_extra(
"crate-b",
r#"
[dependencies]
crate-a = { path = "../crate-a", version = "1.0.0" }

[target.'cfg(target_os = "linux")'.dependencies]
crate-a = { path = "../crate-a", version = "1.0.0" }
"#,
)
.build();

let lockfile_output = Command::new("cargo")
.args(["generate-lockfile"])
.current_dir(workspace.path())
.output()
.expect("failed to run cargo generate-lockfile");
assert!(
lockfile_output.status.success(),
"cargo generate-lockfile failed: {}",
String::from_utf8_lossy(&lockfile_output.stderr)
);

git_add_and_commit(&workspace, "Initial commit");
write_changeset(&workspace, "bump-a.md", "crate-a", "minor", "Add feature");
git_add_and_commit(&workspace, "Add changeset");

assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
.arg("release")
.current_dir(workspace.path())
.assert()
.success();

let content = fs::read_to_string(workspace.path().join("crates/crate-b/Cargo.toml"))
.expect("read crate-b Cargo.toml");

assert!(
!content.contains(r#"version = "1.0.0""#),
"old version should not remain in crate-b Cargo.toml, got:\n{content}"
);
assert_eq!(
content.matches(r#"version = "1.1.0""#).count(),
2,
"both [dependencies] and target-specific section should have 1.1.0, got:\n{content}"
);
}

#[test]
fn release_skips_path_only_dependency() {
let workspace = WorkspaceBuilder::virtual_workspace()
.with_git()
.with_changeset_dir()
.crate_member("crate-a", "1.0.0")
.crate_member("crate-b", "2.0.0")
.crate_toml_extra(
"crate-b",
r#"
[dependencies]
crate-a = { path = "../crate-a" }
"#,
)
.build();

let lockfile_output = Command::new("cargo")
.args(["generate-lockfile"])
.current_dir(workspace.path())
.output()
.expect("failed to run cargo generate-lockfile");
assert!(
lockfile_output.status.success(),
"cargo generate-lockfile failed: {}",
String::from_utf8_lossy(&lockfile_output.stderr)
);

git_add_and_commit(&workspace, "Initial commit");
write_changeset(&workspace, "bump-a.md", "crate-a", "minor", "Add feature");
git_add_and_commit(&workspace, "Add changeset");

assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
.arg("release")
.current_dir(workspace.path())
.assert()
.success();

let content = fs::read_to_string(workspace.path().join("crates/crate-b/Cargo.toml"))
.expect("read crate-b Cargo.toml");

assert!(
content.contains(r#"path = "../crate-a""#),
"path-only dependency should still have path, got:\n{content}"
);
assert!(
!content.contains("1.1.0"),
"path-only dependency should not get a version added, got:\n{content}"
);
}

#[test]
fn release_skips_workspace_true_dependency() {
let workspace = WorkspaceBuilder::virtual_workspace()
.with_git()
.with_changeset_dir()
.crate_member("crate-a", "1.0.0")
.crate_member("crate-b", "2.0.0")
.workspace_toml_extra(
r#"
[workspace.dependencies]
crate-a = { path = "crates/crate-a", version = "1.0.0" }
"#,
)
.crate_toml_extra(
"crate-b",
r#"
[dependencies]
crate-a = { workspace = true }
"#,
)
.build();

let lockfile_output = Command::new("cargo")
.args(["generate-lockfile"])
.current_dir(workspace.path())
.output()
.expect("failed to run cargo generate-lockfile");
assert!(
lockfile_output.status.success(),
"cargo generate-lockfile failed: {}",
String::from_utf8_lossy(&lockfile_output.stderr)
);

git_add_and_commit(&workspace, "Initial commit");
write_changeset(&workspace, "bump-a.md", "crate-a", "minor", "Add feature");
git_add_and_commit(&workspace, "Add changeset");

assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
.arg("release")
.current_dir(workspace.path())
.assert()
.success();

let crate_b_content = fs::read_to_string(workspace.path().join("crates/crate-b/Cargo.toml"))
.expect("read crate-b Cargo.toml");
assert!(
crate_b_content.contains("workspace = true"),
"workspace = true should remain unchanged in crate-b, got:\n{crate_b_content}"
);

let root_content =
fs::read_to_string(workspace.path().join("Cargo.toml")).expect("read root Cargo.toml");
assert!(
root_content.contains(r#"version = "1.1.0""#),
"workspace.dependencies version should be updated to 1.1.0, got:\n{root_content}"
);
}
Loading
Loading