Skip to content

Commit 6af7497

Browse files
committed
docs: Generalize Cargo.lock guidance
Before, we were fairly prescriptive of when to check-in the `Cargo.lock` file. The guidance has been updated to instead explain the trade offs and to mostly leave it in users hands. As a starting point, we do say to check it in for those that want an "easy answer".
1 parent 82a78e3 commit 6af7497

File tree

3 files changed

+96
-33
lines changed

3 files changed

+96
-33
lines changed

src/doc/src/faq.md

+35-25
Original file line numberDiff line numberDiff line change
@@ -102,33 +102,43 @@ issue][cargo-issues].
102102

103103
[cargo-issues]: https://github.com/rust-lang/cargo/issues
104104

105-
### Why do binaries have `Cargo.lock` in version control, but not libraries?
105+
### Why have `Cargo.lock` in version control?
106+
107+
Whether you do is dependent on the needs of your package.
106108

107109
The purpose of a `Cargo.lock` lockfile is to describe the state of the world at
108-
the time of a successful build. Cargo uses the lockfile to provide
109-
deterministic builds on different times and different systems, by ensuring that
110-
the exact same dependencies and versions are used as when the `Cargo.lock` file
111-
was originally generated.
112-
113-
This property is most desirable from applications and packages which are at the
114-
very end of the dependency chain (binaries). As a result, it is recommended that
115-
all binaries check in their `Cargo.lock`.
116-
117-
For libraries the situation is somewhat different. A library is not only used by
118-
the library developers, but also any downstream consumers of the library. Users
119-
dependent on the library will not inspect the library’s `Cargo.lock` (even if it
120-
exists). This is precisely because a library should **not** be deterministically
121-
recompiled for all users of the library.
122-
123-
If a library ends up being used transitively by several dependencies, it’s
124-
likely that just a single copy of the library is desired (based on semver
125-
compatibility). If Cargo used all of the dependencies' `Cargo.lock` files,
126-
then multiple copies of the library could be used, and perhaps even a version
127-
conflict.
128-
129-
In other words, libraries specify SemVer requirements for their dependencies but
130-
cannot see the full picture. Only end products like binaries have a full
131-
picture to decide what versions of dependencies should be used.
110+
the time of a successful build.
111+
Cargo uses the lockfile to provide deterministic builds at different times and
112+
on different systems,
113+
by ensuring that the exact same dependencies and versions are used as when the
114+
`Cargo.lock` file was originally generated.
115+
116+
Deterministic builds help with
117+
- Running `git bisect` to find the root cause of a bug
118+
- Ensuring CI only fails due to new commits and not external factors
119+
- Reducing confusion when contributors see different behavior as compared to
120+
other contributors or CI
121+
122+
Having this snapshot of dependencies can also help when projects need to be
123+
verified against consistent versions of dependencies, like when
124+
- Verifying a minimum-supported Rust version that is less than the what latest
125+
version of a dependency supports
126+
- Verifying human readable output which won't have compatibility guarantees
127+
(e.g. snapshot testing error messages to ensure they are "understandable", a
128+
metric too fuzzy to automate)
129+
130+
However, this determinism can give a false sense of security because
131+
`Cargo.lock` does not affect the consumers of your package, only `Cargo.toml` does that.
132+
For example:
133+
- [`cargo install`] will select the latest dependencies unless `--locked` is
134+
passed in.
135+
- New dependencies, like those added with [`cargo add`], will be locked to the latest version
136+
137+
For strategies to verify newer dependencies,
138+
see [Verifying Latest Dependencies](guide/continuous-integration.md#verifying-latest-dependencies).
139+
140+
[`cargo add`]: commands/cargo-add.md
141+
[`cargo install`]: commands/cargo-install.md
132142

133143
### Can libraries use `*` as a version for their dependencies?
134144

src/doc/src/guide/cargo-toml-vs-cargo-lock.md

+5-8
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ about them, here’s a summary:
88
* `Cargo.lock` contains exact information about your dependencies. It is
99
maintained by Cargo and should not be manually edited.
1010

11-
If you’re building a non-end product, such as a rust library that other rust
12-
[packages][def-package] will depend on, put `Cargo.lock` in your
13-
`.gitignore`. If you’re building an end product, which are executable like
14-
command-line tool or an application, or a system library with crate-type of
15-
`staticlib` or `cdylib`, check `Cargo.lock` into `git`. If you're curious
16-
about why that is, see
17-
["Why do binaries have `Cargo.lock` in version control, but not libraries?" in the
18-
FAQ](../faq.md#why-do-binaries-have-cargolock-in-version-control-but-not-libraries).
11+
When in doubt, check `Cargo.lock` into git.
12+
For a better understanding of why and what the alternatives might be, see
13+
[“Why have Cargo.lock in version control?” in the FAQ](../faq.md#why-have-cargolock-in-version-control).
14+
We recommend pairing this with
15+
[Verifying Latest Dependencies](continuous-integration.md#verifying-latest-dependencies)
1916

2017
Let’s dig in a little bit more.
2118

src/doc/src/guide/continuous-integration.md

+56
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,59 @@ This will test and build documentation on the stable channel and nightly
104104
channel, but any breakage in nightly will not fail your overall build. Please
105105
see the [builds.sr.ht documentation](https://man.sr.ht/builds.sr.ht/) for more
106106
information.
107+
108+
### Verifying Latest Dependencies
109+
110+
When [specifying dependencies](../reference/specifying-dependencies.md) in
111+
`Cargo.toml`, they generally match a range of versions.
112+
Exhaustively testing all version combination would be unwieldy.
113+
Verifying the latest versions would at least test for users who run [`cargo
114+
add`] or [`cargo install`].
115+
116+
When testing the latest versions some considerations are:
117+
- Minimizing external factors affecting local development or CI
118+
- Rate of new dependencies being published
119+
- Level of risk a project is willing to accept
120+
- CI costs, including indirect costs like if a CI service has a maximum for
121+
parallel runners, causing new jobs to be serialized when at the maxium.
122+
123+
Some potential solutions include:
124+
- [Not checking in the `Cargo.lock`](../faq.md#why-have-cargolock-in-version-control)
125+
- Depending on PR velocity, many versions may go untested
126+
- This comes at the cost of determinism
127+
- Have a CI job verify the latest dependencies but mark it to "continue on failure"
128+
- Depending on the CI service, failures might not be obvious
129+
- Depending on PR velocity, may use more resources than necessary
130+
- Have a scheduled CI job to verify latest dependencies
131+
- A hosted CI service may disable scheduled jobs for repositories that
132+
haven't been touched in a while, affecting passively maintained packages
133+
- Depending on the CI service, notifications might not be routed to people
134+
who can act on the failure
135+
- If not balanced with dependency publish rate, may not test enough versions
136+
or may do redundant testing
137+
- Regularly update dependencies through PRs, like with [Dependabot] or [RenovateBot]
138+
- Can isolate dependencies to their own PR or roll them up into a single PR
139+
- Only uses the resources necessary
140+
- Can configure the frequency to balance CI resources and coverage of dependency versions
141+
142+
An example CI job to verify latest dependencies, using Github Actions:
143+
```yaml
144+
jobs:
145+
latest_deps:
146+
name: Latest Dependencies
147+
runs-on: ubuntu-latest
148+
continue-on-error: true
149+
steps:
150+
- uses: actions/checkout@v3
151+
- run: rustup update stable && rustup default stable
152+
- run: cargo update --verbose
153+
- run: cargo build --verbose
154+
- run: cargo test --verbose
155+
```
156+
For projects with higher risks of per-platform or per-Rust version failures,
157+
more combinations may want to be tested.
158+
159+
[`cargo add`]: ../commands/cargo-add.md
160+
[`cargo install`]: ../commands/cargo-install.md
161+
[Dependabot]: https://docs.github.com/en/code-security/dependabot/working-with-dependabot
162+
[RenovateBot]: https://renovatebot.com/

0 commit comments

Comments
 (0)