From 775f44e84cd272aba97ff3186684a83856d5c6d4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 19 Jul 2023 10:46:59 -0500 Subject: [PATCH 1/5] docs(ci): Remove Travis example When the guide was written, it only included Travis CI. For while, it was *the* CI service people used. However, since git hosting services have been integrating CI support and with Travis CI's new owners, market share has been going down. One estimate I found said that Travis CI's market share is 0.94%. If we had a compelling reason to include it, independent of that, I would. However, including it and making it first in the list is a distraction. There are CI services in more common use (e.g. CircleCI, Jenkins) to include at this point over Travis. --- src/doc/src/guide/continuous-integration.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/doc/src/guide/continuous-integration.md b/src/doc/src/guide/continuous-integration.md index 38d8ae20e80..66a6a2f3a66 100644 --- a/src/doc/src/guide/continuous-integration.md +++ b/src/doc/src/guide/continuous-integration.md @@ -1,26 +1,5 @@ ## Continuous Integration -### Travis CI - -To test your [package][def-package] on Travis CI, here is a sample -`.travis.yml` file: - -```yaml -language: rust -rust: - - stable - - beta - - nightly -matrix: - allow_failures: - - rust: nightly -``` - -This will test all three release channels, but any breakage in nightly -will not fail your overall build. Please see the [Travis CI Rust -documentation](https://docs.travis-ci.com/user/languages/rust/) for more -information. - ### GitHub Actions To test your package on GitHub Actions, here is a sample `.github/workflows/ci.yml` file: From b9216c14ec0a485fd54fb5113dad24994342c58e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 19 Jul 2023 10:50:46 -0500 Subject: [PATCH 2/5] docs(ci): Add section heading to open the way for more headings --- src/doc/src/guide/continuous-integration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/src/guide/continuous-integration.md b/src/doc/src/guide/continuous-integration.md index 66a6a2f3a66..ed441a51ed8 100644 --- a/src/doc/src/guide/continuous-integration.md +++ b/src/doc/src/guide/continuous-integration.md @@ -1,5 +1,9 @@ ## Continuous Integration +### Getting Started + +A basic CI will build and test your projects: + ### GitHub Actions To test your package on GitHub Actions, here is a sample `.github/workflows/ci.yml` file: From eb1a257204e4e0c19e03ba98b1bd3587533f1b62 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 19 Jul 2023 10:54:00 -0500 Subject: [PATCH 3/5] docs(ci): Remove unused link --- src/doc/src/guide/continuous-integration.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/doc/src/guide/continuous-integration.md b/src/doc/src/guide/continuous-integration.md index ed441a51ed8..159e024f39d 100644 --- a/src/doc/src/guide/continuous-integration.md +++ b/src/doc/src/guide/continuous-integration.md @@ -104,5 +104,3 @@ This will test and build documentation on the stable channel and nightly channel, but any breakage in nightly will not fail your overall build. Please see the [builds.sr.ht documentation](https://man.sr.ht/builds.sr.ht/) for more information. - -[def-package]: ../appendix/glossary.md#package '"package" (glossary entry)' From 57aa352da74b49edd6243a9cb57df2cd5f51b52b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 19 Jul 2023 11:35:29 -0500 Subject: [PATCH 4/5] 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". --- src/doc/src/faq.md | 62 +++++++++++-------- src/doc/src/guide/cargo-toml-vs-cargo-lock.md | 13 ++-- src/doc/src/guide/continuous-integration.md | 56 +++++++++++++++++ 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/src/doc/src/faq.md b/src/doc/src/faq.md index e4a753413f6..37578b74097 100644 --- a/src/doc/src/faq.md +++ b/src/doc/src/faq.md @@ -102,33 +102,45 @@ issue][cargo-issues]. [cargo-issues]: https://github.com/rust-lang/cargo/issues -### Why do binaries have `Cargo.lock` in version control, but not libraries? +### Why have `Cargo.lock` in version control? + +Whether you do is dependent on the needs of your package. The purpose of a `Cargo.lock` lockfile is to describe the state of the world at -the time of a successful build. Cargo uses the lockfile to provide -deterministic builds on different times and different systems, by ensuring that -the exact same dependencies and versions are used as when the `Cargo.lock` file -was originally generated. - -This property is most desirable from applications and packages which are at the -very end of the dependency chain (binaries). As a result, it is recommended that -all binaries check in their `Cargo.lock`. - -For libraries the situation is somewhat different. A library is not only used by -the library developers, but also any downstream consumers of the library. Users -dependent on the library will not inspect the library’s `Cargo.lock` (even if it -exists). This is precisely because a library should **not** be deterministically -recompiled for all users of the library. - -If a library ends up being used transitively by several dependencies, it’s -likely that just a single copy of the library is desired (based on semver -compatibility). If Cargo used all of the dependencies' `Cargo.lock` files, -then multiple copies of the library could be used, and perhaps even a version -conflict. - -In other words, libraries specify SemVer requirements for their dependencies but -cannot see the full picture. Only end products like binaries have a full -picture to decide what versions of dependencies should be used. +the time of a successful build. +Cargo uses the lockfile to provide deterministic builds at different times and +on different systems, +by ensuring that the exact same dependencies and versions are used as when the +`Cargo.lock` file was originally generated. + +Deterministic builds help with +- Running `git bisect` to find the root cause of a bug +- Ensuring CI only fails due to new commits and not external factors +- Reducing confusion when contributors see different behavior as compared to + other contributors or CI + +Having this snapshot of dependencies can also help when projects need to be +verified against consistent versions of dependencies, like when +- Verifying a minimum-supported Rust version (MSRV) that is less than the latest + version of a dependency supports +- Verifying human readable output which won't have compatibility guarantees + (e.g. snapshot testing error messages to ensure they are "understandable", a + metric too fuzzy to automate) + +However, this determinism can give a false sense of security because +`Cargo.lock` does not affect the consumers of your package, only `Cargo.toml` does that. +For example: +- [`cargo install`] will select the latest dependencies unless `--locked` is + passed in. +- New dependencies, like those added with [`cargo add`], will be locked to the latest version + +The lockfile can also be a source of merge conflicts. + +For strategies to verify newer versions of dependencies via CI, +see [Verifying Latest Dependencies](guide/continuous-integration.md#verifying-latest-dependencies). + +[`cargo add`]: commands/cargo-add.md +[`cargo install`]: commands/cargo-install.md ### Can libraries use `*` as a version for their dependencies? diff --git a/src/doc/src/guide/cargo-toml-vs-cargo-lock.md b/src/doc/src/guide/cargo-toml-vs-cargo-lock.md index 84d697f66a3..131139d7cd9 100644 --- a/src/doc/src/guide/cargo-toml-vs-cargo-lock.md +++ b/src/doc/src/guide/cargo-toml-vs-cargo-lock.md @@ -8,14 +8,11 @@ about them, here’s a summary: * `Cargo.lock` contains exact information about your dependencies. It is maintained by Cargo and should not be manually edited. -If you’re building a non-end product, such as a rust library that other rust -[packages][def-package] will depend on, put `Cargo.lock` in your -`.gitignore`. If you’re building an end product, which are executable like -command-line tool or an application, or a system library with crate-type of -`staticlib` or `cdylib`, check `Cargo.lock` into `git`. If you're curious -about why that is, see -["Why do binaries have `Cargo.lock` in version control, but not libraries?" in the -FAQ](../faq.md#why-do-binaries-have-cargolock-in-version-control-but-not-libraries). +When in doubt, check `Cargo.lock` into git. +For a better understanding of why and what the alternatives might be, see +[“Why have Cargo.lock in version control?” in the FAQ](../faq.md#why-have-cargolock-in-version-control). +We recommend pairing this with +[Verifying Latest Dependencies](continuous-integration.md#verifying-latest-dependencies) Let’s dig in a little bit more. diff --git a/src/doc/src/guide/continuous-integration.md b/src/doc/src/guide/continuous-integration.md index 159e024f39d..13f6c30abbb 100644 --- a/src/doc/src/guide/continuous-integration.md +++ b/src/doc/src/guide/continuous-integration.md @@ -104,3 +104,59 @@ This will test and build documentation on the stable channel and nightly channel, but any breakage in nightly will not fail your overall build. Please see the [builds.sr.ht documentation](https://man.sr.ht/builds.sr.ht/) for more information. + +### Verifying Latest Dependencies + +When [specifying dependencies](../reference/specifying-dependencies.md) in +`Cargo.toml`, they generally match a range of versions. +Exhaustively testing all version combination would be unwieldy. +Verifying the latest versions would at least test for users who run [`cargo +add`] or [`cargo install`]. + +When testing the latest versions some considerations are: +- Minimizing external factors affecting local development or CI +- Rate of new dependencies being published +- Level of risk a project is willing to accept +- CI costs, including indirect costs like if a CI service has a maximum for + parallel runners, causing new jobs to be serialized when at the maxium. + +Some potential solutions include: +- [Not checking in the `Cargo.lock`](../faq.md#why-have-cargolock-in-version-control) + - Depending on PR velocity, many versions may go untested + - This comes at the cost of determinism +- Have a CI job verify the latest dependencies but mark it to "continue on failure" + - Depending on the CI service, failures might not be obvious + - Depending on PR velocity, may use more resources than necessary +- Have a scheduled CI job to verify latest dependencies + - A hosted CI service may disable scheduled jobs for repositories that + haven't been touched in a while, affecting passively maintained packages + - Depending on the CI service, notifications might not be routed to people + who can act on the failure + - If not balanced with dependency publish rate, may not test enough versions + or may do redundant testing +- Regularly update dependencies through PRs, like with [Dependabot] or [RenovateBot] + - Can isolate dependencies to their own PR or roll them up into a single PR + - Only uses the resources necessary + - Can configure the frequency to balance CI resources and coverage of dependency versions + +An example CI job to verify latest dependencies, using Github Actions: +```yaml +jobs: + latest_deps: + name: Latest Dependencies + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v3 + - run: rustup update stable && rustup default stable + - run: cargo update --verbose + - run: cargo build --verbose + - run: cargo test --verbose +``` +For projects with higher risks of per-platform or per-Rust version failures, +more combinations may want to be tested. + +[`cargo add`]: ../commands/cargo-add.md +[`cargo install`]: ../commands/cargo-install.md +[Dependabot]: https://docs.github.com/en/code-security/dependabot/working-with-dependabot +[RenovateBot]: https://renovatebot.com/ From 54ad4a01df49bbdffc72a2ae4563ed78c2b1ac15 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Fri, 16 Jun 2023 03:45:43 +0200 Subject: [PATCH 5/5] fix(new): Don't ignore `Cargo.lock` for all package types Following the default guidance to commit a lockfile, this updates `cargo new` to do it by default. --- src/cargo/ops/cargo_new.rs | 6 ------ src/doc/src/faq.md | 4 +++- tests/testsuite/cargo_init/auto_git/out/.gitignore | 1 - .../fossil_autodetect/out/.fossil-settings/clean-glob | 1 - .../fossil_autodetect/out/.fossil-settings/ignore-glob | 1 - tests/testsuite/cargo_init/git_autodetect/out/.gitignore | 1 - .../git_ignore_exists_no_conflicting_entries/out/.gitignore | 1 - .../cargo_init/inferred_lib_with_git/out/.gitignore | 1 - .../testsuite/cargo_init/mercurial_autodetect/out/.hgignore | 1 - tests/testsuite/cargo_init/pijul_autodetect/out/.ignore | 1 - tests/testsuite/cargo_init/simple_git/out/.gitignore | 1 - .../cargo_init/simple_git_ignore_exists/out/.gitignore | 1 - tests/testsuite/cargo_init/simple_hg/out/.hgignore | 1 - .../cargo_init/simple_hg_ignore_exists/out/.hgignore | 1 - tests/testsuite/new.rs | 4 ++-- 15 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs index 0809cefc373..740232063ac 100644 --- a/src/cargo/ops/cargo_new.rs +++ b/src/cargo/ops/cargo_new.rs @@ -93,7 +93,6 @@ struct MkOptions<'a> { path: &'a Path, name: &'a str, source_files: Vec, - bin: bool, edition: Option<&'a str>, registry: Option<&'a str>, } @@ -448,7 +447,6 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> { path, name, source_files: vec![plan_new_source_file(opts.kind.is_bin(), name.to_string())], - bin: is_bin, edition: opts.edition.as_deref(), registry: opts.registry.as_deref(), }; @@ -553,7 +551,6 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult { version_control, path, name, - bin: has_bin, source_files: src_paths_types, edition: opts.edition.as_deref(), registry: opts.registry.as_deref(), @@ -745,9 +742,6 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> { // for all mutually-incompatible VCS in terms of syntax are in sync. let mut ignore = IgnoreList::new(); ignore.push("/target", "^target$", "target"); - if !opts.bin { - ignore.push("/Cargo.lock", "^Cargo.lock$", "Cargo.lock"); - } let vcs = opts.version_control.unwrap_or_else(|| { let in_existing_vcs = existing_vcs_repo(path.parent().unwrap_or(path), config.cwd()); diff --git a/src/doc/src/faq.md b/src/doc/src/faq.md index 37578b74097..f8a758babe0 100644 --- a/src/doc/src/faq.md +++ b/src/doc/src/faq.md @@ -104,7 +104,8 @@ issue][cargo-issues]. ### Why have `Cargo.lock` in version control? -Whether you do is dependent on the needs of your package. +While [`cargo new`] defaults to tracking `Cargo.lock` in version control, +whether you do is dependent on the needs of your package. The purpose of a `Cargo.lock` lockfile is to describe the state of the world at the time of a successful build. @@ -139,6 +140,7 @@ The lockfile can also be a source of merge conflicts. For strategies to verify newer versions of dependencies via CI, see [Verifying Latest Dependencies](guide/continuous-integration.md#verifying-latest-dependencies). +[`cargo new`]: commands/cargo-new.md [`cargo add`]: commands/cargo-add.md [`cargo install`]: commands/cargo-install.md diff --git a/tests/testsuite/cargo_init/auto_git/out/.gitignore b/tests/testsuite/cargo_init/auto_git/out/.gitignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/tests/testsuite/cargo_init/auto_git/out/.gitignore +++ b/tests/testsuite/cargo_init/auto_git/out/.gitignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob b/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob index a9d37c560c6..eb5a316cbd1 100644 --- a/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob +++ b/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob @@ -1,2 +1 @@ target -Cargo.lock diff --git a/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob b/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob index a9d37c560c6..eb5a316cbd1 100644 --- a/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob +++ b/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob @@ -1,2 +1 @@ target -Cargo.lock diff --git a/tests/testsuite/cargo_init/git_autodetect/out/.gitignore b/tests/testsuite/cargo_init/git_autodetect/out/.gitignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/tests/testsuite/cargo_init/git_autodetect/out/.gitignore +++ b/tests/testsuite/cargo_init/git_autodetect/out/.gitignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/.gitignore b/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/.gitignore index e2e02f22f3c..914d4ecb721 100644 --- a/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/.gitignore +++ b/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/.gitignore @@ -3,4 +3,3 @@ # Added by cargo /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/inferred_lib_with_git/out/.gitignore b/tests/testsuite/cargo_init/inferred_lib_with_git/out/.gitignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/tests/testsuite/cargo_init/inferred_lib_with_git/out/.gitignore +++ b/tests/testsuite/cargo_init/inferred_lib_with_git/out/.gitignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/mercurial_autodetect/out/.hgignore b/tests/testsuite/cargo_init/mercurial_autodetect/out/.hgignore index 0cc56e7ff73..3b11056a129 100644 --- a/tests/testsuite/cargo_init/mercurial_autodetect/out/.hgignore +++ b/tests/testsuite/cargo_init/mercurial_autodetect/out/.hgignore @@ -1,2 +1 @@ ^target$ -^Cargo.lock$ diff --git a/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore b/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore +++ b/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/simple_git/out/.gitignore b/tests/testsuite/cargo_init/simple_git/out/.gitignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/tests/testsuite/cargo_init/simple_git/out/.gitignore +++ b/tests/testsuite/cargo_init/simple_git/out/.gitignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/simple_git_ignore_exists/out/.gitignore b/tests/testsuite/cargo_init/simple_git_ignore_exists/out/.gitignore index 4447742e0eb..5d097ae696c 100644 --- a/tests/testsuite/cargo_init/simple_git_ignore_exists/out/.gitignore +++ b/tests/testsuite/cargo_init/simple_git_ignore_exists/out/.gitignore @@ -6,4 +6,3 @@ # already existing elements were commented out #/target -/Cargo.lock diff --git a/tests/testsuite/cargo_init/simple_hg/out/.hgignore b/tests/testsuite/cargo_init/simple_hg/out/.hgignore index 0cc56e7ff73..3b11056a129 100644 --- a/tests/testsuite/cargo_init/simple_hg/out/.hgignore +++ b/tests/testsuite/cargo_init/simple_hg/out/.hgignore @@ -1,2 +1 @@ ^target$ -^Cargo.lock$ diff --git a/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/.hgignore b/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/.hgignore index dd9ddffeb23..a23820d48ff 100644 --- a/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/.hgignore +++ b/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/.hgignore @@ -3,4 +3,3 @@ # Added by cargo ^target$ -^Cargo.lock$ diff --git a/tests/testsuite/new.rs b/tests/testsuite/new.rs index b9ddcf2d7c9..91a2871e97e 100644 --- a/tests/testsuite/new.rs +++ b/tests/testsuite/new.rs @@ -95,7 +95,7 @@ fn simple_git() { let fp = paths::root().join("foo/.gitignore"); let contents = fs::read_to_string(&fp).unwrap(); - assert_eq!(contents, "/target\n/Cargo.lock\n",); + assert_eq!(contents, "/target\n",); cargo_process("build").cwd(&paths::root().join("foo")).run(); } @@ -112,7 +112,7 @@ fn simple_hg() { let fp = paths::root().join("foo/.hgignore"); let contents = fs::read_to_string(&fp).unwrap(); - assert_eq!(contents, "^target$\n^Cargo.lock$\n",); + assert_eq!(contents, "^target$\n",); cargo_process("build").cwd(&paths::root().join("foo")).run(); }