Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand "CI best practicies" section to the guide #5656

Open
matklad opened this issue Jun 26, 2018 · 22 comments
Open

Expand "CI best practicies" section to the guide #5656

matklad opened this issue Jun 26, 2018 · 22 comments
Labels
A-documenting-cargo-itself Area: Cargo's documentation

Comments

@matklad
Copy link
Member

matklad commented Jun 26, 2018

Things to talk about:

Ideally, the section should contain copy-pastable config files for various CI providers.

@matklad
Copy link
Member Author

matklad commented Jun 26, 2018

@ehuss
Copy link
Contributor

ehuss commented Jun 26, 2018

I assume this means to expand https://github.com/rust-lang/cargo/blob/master/src/doc/src/guide/continuous-integration.md? Should it maybe leverage or at least point to japaric/trust?

@matklad
Copy link
Member Author

matklad commented Jun 26, 2018

Ah, I haven't realized that https://github.com/rust-lang/cargo/blob/master/src/doc/src/guide/continuous-integration.md exists.

Should it maybe leverage or at least point to japaric/trust?

Yep, we should definitely mention it!

@Eh2406
Copy link
Contributor

Eh2406 commented Jun 26, 2018

@ehuss That is a good place for it to go, thanks for the reminder!

The impetus for this is that when we stabilize --minimal-versions we want to craft a message about how to use it. Specifically as a small part of a thorough CI setup. Witch will just lead everyone to ask, "What showed the big parts of the CI setup be?" So we thought we should write up our opinion before we stabilize --minimal-versions.

@withoutboats
Copy link
Contributor

Relevant: rust-lang/rfcs#2483

@aturon
Copy link
Member

aturon commented Jul 26, 2018

I've just posted a blog post that talks about version selection in detail, and touches on the questions here as well.

@Eh2406
Copy link
Contributor

Eh2406 commented Jul 26, 2018

That is an excellent read. Thank you for writing this up so articulately. I especially liked the paragraph of:

In the long run, it could even make sense to combine the two approaches, allowing crates to state their toolchain requirements (and have that influence resolution), but encourage core crates to state “LTS” as their requirement.

That sounds like the tradeoff bending I know and love from the rust ethic. I.E. a careful and pedantic system to ensure you are correct but with a large pit of success to make it usable. It also involves the most design work, so it may not be feasible.

In our earlier discussion today you suggested that "equality constraints are rare in the ecosystem" and I resisted being pedantic and pointing out lock files. So I was glad to see them addressed in your blog post. But I do have one nit with:

Similarly, when dependencies are subsequently adjusted, Cargo will “unlock” the affected dependencies and again choose the maximum version.

Currently that adjustment is very conservative, if the version of the sub dependency that appears in the lock file satisfies the requirement of the new dependency then it is not unlocked. Meaning that I am often testing a combination not in the well tested set. So if the new dependency does not have lower-bound precision and my lockfile is old enough then I will brake.

Thanks for the thought provoking guidance of this community.

@Nemo157
Copy link
Member

Nemo157 commented Jul 26, 2018

There's also the https://crate-ci.github.io/ project (cc @epage). Maybe the Cargo Guide could have a small CI best practices section and link out to a larger exploration of the area like this?

@withoutboats
Copy link
Contributor

In our earlier discussion today you suggested that "equality constraints are rare in the ecosystem" and I resisted being pedantic and pointing out lock files.

The important difference with lock files is that you once you have one you should have a successful build, and because it was generated using max versions, it will not run into the too low min version problem. Ceiling'd constraints are a problem because they influence version resolution, whereas the lockfile is after version resolution.

You do identify exactly where a problem can arise, though: cargo update -p. It might be worth considering changing the behavior of cargo update -p to update the entire subgraph under the crate you're updating.

@Turbo87
Copy link
Member

Turbo87 commented Dec 10, 2018

Currently that adjustment is very conservative, if the version of the sub dependency that appears in the lock file satisfies the requirement of the new dependency then it is not unlocked. Meaning that I am often testing a combination not in the well tested set. So if the new dependency does not have lower-bound precision and my lockfile is old enough then I will brake.

I've run into this problem quite a few times lately. I'm using dependabot on some of my projects to automatically update dependencies, and the updates regularly break CI because the lockfile has an older transitive dependency, but some other dependency relies on it being the most recent release.

Examples:

It’s possible that we will eventually have workflows that depend on the accuracy of lower bounds in Cargo.toml. At the moment, however, this is purely speculative; the Cargo team does not have any ready examples.

Hopefully the examples above are helpful in that regard

@mathstuf
Copy link
Contributor

I've been working to improve CI times on crates I work on here. There's gitlab-ci examples there (minimum version, stable, nightly, with/without feature flags, -Z minimal-versions, and, because it affects us, testing against git master). It handles caching and artifacting between steps. The most comprehensive one I have is for git-checks.

For Cirrus CI, I have keyutils. Though it runs the -Z minimal-versions under nightly rather than the minimum compiler version.

Basic strategy for "optimal" builds (AFAICT):

  • run cargo generate-lockfile, cargo fetch --frozen, cargo vendor; cache the downloaded crates, artifact the vendored crates. This is important to avoid downloads, but also not inflating the build step with stale dependencies
  • run the build for each version (clippy goes here too); add the target/ directory to the artifact set
  • run the test suite for each version (this is split out because one of them gets run with git master in the PATH). Also good for testing against different DBs or other external dependencies without duplicating the build step.

@ehuss ehuss changed the title Add "CI best practicies" section to the guide Expand "CI best practicies" section to the guide Dec 8, 2019
@trevordmiller
Copy link

I recently added CI / CD for a Rust project and after a good amount of research this is what I ended up with (specific to GitHub Actions), in case it is helpful for this issue:

Run CI when pushing to branches for pull requeests

name: Pull request
on:
  push:
    branches-ignore:
      - master
jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Check
      run: cargo check
    - name: Test
      run: cargo test
    - name: Lint
      run: cargo clippy --all-targets -- -D warnings
    - name: Format
      run: cargo fmt -- --check
    - name: Publish
      run: cargo publish --dry-run

Run CI and CD when merging pull requests to master

name: Merge
on:
  push:
    branches:
      - master
jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Check
      run: cargo check
    - name: Test
      run: cargo test
    - name: Lint
      run: cargo clippy --all-targets -- -D warnings
    - name: Format
      run: cargo fmt -- --check
  executable:
    runs-on: ubuntu-latest
    needs: [verify]
    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Login
      run: cargo login ${{ secrets.CRATE_REGISTRY_PAT }}
    - name: Publish
      if: success()
      run: cargo publish

@mathstuf
Copy link
Contributor

So my feedback on this:

  • linting and formatting should be done in separate jobs (so that they fail fast)
  • cargo check should also be separate (building will have to do it anyways, so if you want the faster results, I'd just do that)
  • passing --tests and --examples to cargo check would be useful
  • if you're publishing to crates.io please push a tag to the repo as well
  • no guidelines for --features-based testing (probably out-of-scope, but a template/best practices should at least mention it)
  • acknowledgement of workspace repositories

@4ydan
Copy link

4ydan commented Jul 18, 2023

To reduce our pipeline time I have come up with following testing strategy:
parallel cargo doc, build, test and check
does that seem reasonable to you?

cargo check should also be separate (building will have to do it anyways, so if you want the faster results, I'd just do that)

When building is already doing cargo check can we just skip it or did I get that wrong?

@epage
Copy link
Contributor

epage commented Jul 18, 2023

imo cargo check and cargo build are mutually exclusive unless you want the artifact.

Similarly, cargo check and cargo test` may be mutually exclusive, depending on your needs.

My CI tends to be broken down into

  • parallel jobs of cargo test against different target platforms
  • an MSRV cargo check job
  • a cargo doc job
  • a cargo clippy job (sometimes this can supersede a cargo check job)
  • a cargo fmt job
  • a cargo deny job

@epage
Copy link
Contributor

epage commented Nov 28, 2023

#12382 documented a way to verify latest dependencies.

#13056 documents a way to verify MSRV

Holes I still see

@briansmith
Copy link

IMO, this project (cargo) isn't the place to document CI best practices for Rust projects, even ones that use Cargo. I do think that such guides need to exist but why burden the cargo project with maintaining these docs?

@epage
Copy link
Contributor

epage commented Nov 28, 2023

Because there isn't a better place at this time?

  • Guides need to exist somewhere
  • Ideally those guides would be evergreen, rather than blog posts
    • For myself, when I started my first projects, I pored over blog posts and had to infer from their contradictions and timestamps what was still valid and right
  • The guide needs to be someplace respectable that people will read and update so it stays relevant and not forgotten
  • The guide needs to be some place the cargo team can cross-link to and trusts to cross-link to as we have cases to refer to them (see the linked PRs)

bors added a commit that referenced this issue Nov 28, 2023
docs: Provide pointers for MSRV

### What does this PR try to resolve?

In today's cargo team meeting, we discussed the Pre-RFC for MSRV-aware resolver for #9930.
In that discussion, the question of recommending a policy came up. While we didn't feel the ecosystem has coalesced enough to set one (and we hope MSRV-aware resolver will avoid the need),
it became clear that some we can provide some basic help to the user, including
- Raising awareness of tools to find the actual MSRV
- The policy that they should verify it with examples on how to do so

### How should we test and review this PR?

While this recommends some specific third-party tools, I'm not aware of other tools within this for us to worry about at this time for us to create any guidelines on which we should include.

Explanations are given for the example CI job to discourage cargo culting and instead give people the information they need in making decisions relevant to their project.

### Additional information

I'd love to provide information to help users create their own MSRV policy but only if there was an automated way of collecting and reporting some of the data, like crates.io providing a dashboard of MSRVs set or rust-versions inferred from user-agents.

Without that, I felt it not worth getting into other policy discussions like reactive vs proactive updating of MSRV, automated MSRV updates, etc.  These can always be added later.

This also helps towards #5656.
@rursprung
Copy link

thanks for working on this!

would it be an option to provide a (set of) GitHub Workflows which contain these best practices (where possible)?
then users of other CIs can essentially copy whatever is being done there (with the GH Workflows being the canonical reference for the best practices at that time).

as workflows are versioned (semver) and come with documentation that might be a good way of rolling out new patterns over time (and if users have dependabot enabled for the workflows then they'll also notice if a new version comes around)

@mathstuf
Copy link
Contributor

Things I do in my Rust CI (though on GitLab-CI; the steps might be useful at least) that aren't mentioned here:

  • do generate-lockfile once per resolution ("standard", lock-file, -Zminimal-versions, etc.)
  • pull the crates and cache them
  • everything else does --frozen --locked to avoid skew between jobs if an index update comes in during the pipeline run
  • cargo audit each dependency resolution
  • cargo tarpaulin for coverage
  • cargo semver-checks
  • rather than doing "artifact cache"-friendly things like no incremental and no debuginfo, use sccache instead (though probably not better with cloud-based CI; we have our CI all on local machines with a Redis server we use for compile caching)

@epage
Copy link
Contributor

epage commented Nov 29, 2023

would it be an option to provide a (set of) GitHub Workflows which contain these best practices (where possible)?

For what we've included so far, we list a lot of trade offs so there isn't a single right solution.

Personally, I also am unsure of the value of providing an Action over people just calling the commands directly. The exception is I have a specific workflow in my own projects for clippy that leverages SARIF reporting in github.

  • do generate-lockfile once per resolution ("standard", lock-file, -Zminimal-versions, etc.)
  • pull the crates and cache them
  • everything else does --frozen --locked to avoid skew between jobs if an index update comes in during the pipeline run
  • rather than doing "artifact cache"-friendly things like no incremental and no debuginfo, use sccache instead (though probably not better with cloud-based CI; we have our CI all on local machines with a Redis server we use for compile caching)

Some of this gets in the question of how much we talk about principles vs CI specific practices (caching for that one CI provider).

  • cargo audit each dependency resolution

One question would be cargo deny vs cargo audit and whether we feel rustsec is GA enough for us to advertise

  • cargo tarpaulin for coverage

There are multiple solutions. We'd need to at least reference the main ones even if we point to one

  • cargo semver-checks

imo this isn't GA enough for us to include yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-documenting-cargo-itself Area: Cargo's documentation
Projects
None yet
Development

No branches or pull requests