Repository: guppy-rs/guppy (language: rust)

Issue:
Gotcha using `workspace-dotted` without specifying a version
## Setup

My `hakari.toml` has:

```
workspace-hack-line-style = "workspace-dotted"
```

My root `Cargo.toml` has:

```
[workspace.dependencies]
my-workspace-hack = { path = "/path/to/my-workspace-hack" }
```

After initial setup (`generate` then `manage-deps`) all workspace packages have the following added as expected:

```
[dependencies]
my-workspace-hack.workspace = true
```

## Issue

Later, when re-running `cargo hakari manage-deps` (or running `cargo hakari --dry-run --quiet` in CI), `hakari` reports that many packages are missing the `my-workspace-hack` dependency and attempts to add them again (or fail in the case of the CI job).

I debugged this and discovered the error, namely that the workspace dependency must specify a version as well as a path, i.e.:

```
[workspace.dependencies]
my-workspace-hack = { version = "0.1.0", path = "/path/to/my-workspace-hack" }
```

I traced this to the following code, which in my case always hits the first else clause (`link.version_req() == &VersionReq::STAR`) when the `hakari` package has no version:

https://github.com/guppy-rs/guppy/blob/07c17f96f37ebd75d11e30428a1a3bfa4e23cdfa/tools/hakari/src/cli_ops/manage_deps.rs#L153-L164

So whilst this is not a bug, it is a gotcha and the cause is non-obvious (I had to step through in the debugger).  Perhaps something could be added to surface this (likely not what the user intended) behaviour more directly?  Perhaps running without an given version should be an explicit opt-in?

## Documentation

The [documentation](https://docs.rs/cargo-hakari/0.9.29/cargo_hakari/patch_directive/index.html) does talk about this to an extent, though in a different context (I skipped reading the section initially as it did not apply to my circumstances).  

It say (emphasis mine):

> This option lets you specify the _**path**_ to the workspace-hack crate, once, in the root Cargo.toml

And then shows an example where only the `version` is given:

```
[workspace.dependencies]
my-workspace-hack = "0.1.0"  # or another version number if you've changed it
```

So perhaps the need to specify the version could be made more prominent in the documentation?


Discussion / hints:
Thanks, good catch! I think we can update the check to not require that the version be specified -- I don't think there's a particular reason to require it unless the crate is being published.

Fix patch:
diff --git a/tools/cargo-hakari/src/command.rs b/tools/cargo-hakari/src/command.rs
index cd67ddb2027..a9ec3f83e08 100644
--- a/tools/cargo-hakari/src/command.rs
+++ b/tools/cargo-hakari/src/command.rs
@@ -199,7 +199,7 @@ enum CommandWithBuilder {
         dry_run: bool,
 
         /// Proceed with the operation without prompting for confirmation.
-        #[clap(long, short, conflicts_with = "dry-run")]
+        #[clap(long, short, conflicts_with = "dry_run")]
         yes: bool,
     },
 
@@ -216,7 +216,7 @@ enum CommandWithBuilder {
         dry_run: bool,
 
         /// Proceed with the operation without prompting for confirmation.
-        #[clap(long, short, conflicts_with = "dry-run")]
+        #[clap(long, short, conflicts_with = "dry_run")]
         yes: bool,
     },
 
diff --git a/tools/cargo-hakari/src/docs/publishing.rs b/tools/cargo-hakari/src/docs/publishing.rs
index 25b44268e70..85081fdfc3d 100644
--- a/tools/cargo-hakari/src/docs/publishing.rs
+++ b/tools/cargo-hakari/src/docs/publishing.rs
@@ -53,7 +53,25 @@
 //!
 //! The rest of this section assumes the crate is called `my-workspace-hack`.
 //!
-//! ## 2. Ensure the latest `dep-format-version` is set in `.config/hakari.toml`
+//! ## 2. Ensure that workspace-hack dependencies have a version set
+//!
+//! Depending on how workspace-hack dependencies are set up:
+//!
+//! ### i. Using `workspace-dotted`
+//!
+//! If you're using [the `workspace-dotted` line
+//! style](crate::docs::config#workspace-hack-line-style), ensure that the `workspace-hack` line in
+//! the root `Cargo.toml` has a `version` field set.
+//!
+//! ```toml
+//! [workspace.dependencies]
+//! my-workspace-hack = { version = "0.1", path = "..." }
+//! ```
+//!
+//! ### ii. Specifying dependencies directly
+//!
+//! If you're using a different line style, ensure that [the latest
+//! `dep-format-version`](crate::docs::config#dep-format-version) is set in `.config/hakari.toml`.
 //!
 //! `dep-format-version = "2"` and higher add the `version` field to the `my-workspace-hack = ...`
 //! lines in other `Cargo.toml` files. `cargo publish` uses the `version` field to recognize
@@ -101,3 +119,9 @@
 //!
 //! Run `cargo hakari generate` to restore the workspace-hack's contents. You can also use your
 //! source control system's commands to do so, such as with `git restore`.
+//!
+//! ## 7. Consider using a `[patch]` directive
+//!
+//! To allow Cargo workspaces that depend on a Git or path dependency to use the published
+//! workspace-hack, consider using a `[patch]` directive. Steps to do so are described in the [patch
+//! directive](crate::docs::patch_directive) section.
diff --git a/tools/hakari/src/cli_ops/manage_deps.rs b/tools/hakari/src/cli_ops/manage_deps.rs
index 9275e384d3b..4d75958541e 100644
--- a/tools/hakari/src/cli_ops/manage_deps.rs
+++ b/tools/hakari/src/cli_ops/manage_deps.rs
@@ -6,7 +6,7 @@
 use crate::{
     cli_ops::{WorkspaceOp, WorkspaceOps},
     hakari::DepFormatVersion,
-    HakariBuilder,
+    HakariBuilder, WorkspaceHackLineStyle,
 };
 use guppy::{
     graph::{DependencyDirection, PackageLink, PackageMetadata, PackageSet},
@@ -38,7 +38,8 @@ impl<'g> HakariBuilder<'g> {
                     (Some(link), true) => match self.dep_format_version {
                         DepFormatVersion::V1 => None,
                         DepFormatVersion::V2 | DepFormatVersion::V3 | DepFormatVersion::V4 => {
-                            needs_update_v2(hakari_package, link).then_some(true)
+                            needs_update_v2(hakari_package, link, self.workspace_hack_line_style)
+                                .then_some(true)
                         }
                     },
                     (None, false) => None,
@@ -90,7 +91,9 @@ impl<'g> HakariBuilder<'g> {
                     .link_to(hakari_package.id())
                     .expect("valid package ID");
                 match link_opt {
-                    Some(link) => needs_update_v2(hakari_package, link),
+                    Some(link) => {
+                        needs_update_v2(hakari_package, link, self.workspace_hack_line_style)
+                    }
                     None => true,
                 }
             })
@@ -151,13 +154,20 @@ impl<'g> HakariBuilder<'g> {
 }
 
 #[allow(clippy::if_same_then_else, clippy::needless_bool)]
-fn needs_update_v2(hakari_package: &PackageMetadata<'_>, link: PackageLink<'_>) -> bool {
+fn needs_update_v2(
+    hakari_package: &PackageMetadata<'_>,
+    link: PackageLink<'_>,
+    line_style: WorkspaceHackLineStyle,
+) -> bool {
     if !link.version_req().matches(hakari_package.version()) {
         // The version number doesn't match: it must be updated.
         true
     } else if link.version_req() == &VersionReq::STAR {
-        // The version number isn't specified and force_version is true.
-        true
+        // The version number isn't specified. Require it in case line_style isn't workspace-dotted.
+        match line_style {
+            WorkspaceHackLineStyle::Full | WorkspaceHackLineStyle::VersionOnly => true,
+            WorkspaceHackLineStyle::WorkspaceDotted => false,
+        }
     } else {
         false
     }


Test patch: