Does anyone use Github Actions to run cargo publish?

I'm looking into automating our release process, and I had planned on using Github Actions to publish to cargo, rather than having devs run it manually. But in looking for prior art, I haven't found any projects that do that in GitHub Actions.

Is this unusual? Is there a reason that I might not want to publish from GitHub Actions?

Heya, I do that with a workspace with about 25 crates, see peace: publish.yml.

This lets me go git tag $tag; git push origin $tag, and it publishes it, so it works well for a team.

Things to set it up:

  • Copy the publish.yml and edit it for your workflow (e.g. what other things you want to build, if any).
  • In Repo > Settings > Security > Actions, I have a CRATES_IO_API_TOKEN which you can generate from https://crates.io/settings/tokens
  • if you have more than 10 crates that get published within an hour, you'll hit the crates.io API rate limit, and the error message tells you the address to email to get it raised

I've got that for single crate repos as well, see fn_graph: publish.yml. I think it's nearly identical.

enjoy :v:

10 Likes

@azriel91 I really like that setup. Besides just publishing crates to crates.io, does your release process include anything else like updating changelogs, creating GitHub Releases, or generating pre-compiled binaries?

1 Like

oh mine doesn't, but Embark has a workflow like that in cargo-about: rust-ci.yml, which:

  • packages the cargo-about binary (windows, linux, mac)
  • creates a github release
  • (I think) also updates their changelog, but I can't find what uses release.toml
2 Likes

I am using Google's release-please for GitHub Actions. Out of the box, it can:

  • adjust version numbers (either via built-in support [it understands most popular package ecosystems] or auxiliary comments) automatically, derived from conventional commits; the version number in Cargo.toml won't have to be modified manually ever again, it's automatically SemVer-compliant;
  • (automatically) update changelogs via conventional commits, and
  • create GitHub releases,

as @Michael-F-Bryan asked.

I just set up a repository of mine to use that workflow. The main GitHub Actions file looks like:

name: Main

on:
  push:

env:
  CARGO_TERM_COLOR: always

jobs:
  build-test:
    name: Build and test (${{ matrix.os }})

    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3
      - uses: swatinem/rust-cache@v2
      - name: Build
        run: >
          cargo build
          --locked
          --verbose

      - name: Run tests (without coverage)
        if: matrix.os != 'ubuntu-latest'
        run: >
          cargo test
          --verbose

      - name: Run tests (with coverage)
        if: matrix.os == 'ubuntu-latest'
        run: >
          cargo install cargo-tarpaulin
          && cargo tarpaulin
          --verbose
          --out Xml
          --engine llvm
          --skip-clean
      - name: Upload coverage reports to Codecov
        if: matrix.os == 'ubuntu-latest'
        uses: codecov/codecov-action@v3

  release-please:
    name: Execute release chores

    permissions:
      contents: write
      pull-requests: write

    runs-on: ubuntu-latest
    needs: build-test

    outputs:
      created: ${{ steps.release.outputs.release_created }}

    steps:
      - uses: google-github-actions/release-please-action@v3
        id: release
        with:
          release-type: rust

  publish:
    name: Publish to crates.io

    runs-on: ubuntu-latest
    needs: release-please
    if: needs.release-please.outputs.created

    environment: crates.io

    steps:
      - uses: actions/checkout@v3
      - uses: swatinem/rust-cache@v2

      - name: Publish
      # https://doc.rust-lang.org/cargo/reference/config.html?highlight=CARGO_REGISTRY_TOKEN#credentials
        run: >
          cargo publish
          --verbose
          --locked
          --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

It tests on all three major OSes and also uploads code coverage test results to codecov. Note how release-please can work with Rust out of the box (release-type: rust). I've found caching to be indispensable for acceptable build times, hence swatinem/rust-cache, which I've found to work well.

Note also that currently, release-please will require additional permissions (contents and pull-requests as write), and additionally, it needs to be allowed to create pull requests in the UI as well (at least that's what I had to do), else it'll complain with:

Error: release-please failed: GitHub Actions is not permitted to create or approve pull requests.

Lastly, the above runs in an environment, a great tool for more secure deployments, with additional rules and UI sugar:

This lets one pinpoint the exact time the crate was published to crates.io, who authorized it etc. One can create additional environments for container image registries, for example. The secrets.CARGO_REGISTRY_TOKEN is an environment secret, in the case of this sample repo.

release-please works through release pull requests, like this one. One caveat in this is that when running under the GITHUB_TOKEN, your releases will be made as github-actions[bot]; this bot cannot trigger actions, so workflows will not run on this PR, including required ones. Workarounds are

  • specifying a normal, personal token of yours (releases etc. will then be in your name),
  • rebasing the PR, then force-pushing, to the same effect but much less autonomously,
  • using a separate GitHub account, acting as a bot,
  • or lastly, force-merging the PR, with no regards to pipeline checks (the cowboy way).

Note that the release-please action will run under GITHUB_TOKEN per default, when nothing else is specified.

Once the "release PR" is merged, anything gated behind if: needs.release-please.outputs.created runs in the next GH Actions run. A release (example, automatically generated from this same PR) is then also created. Anything you'd like to do, like building binaries as artifacts, can be done here. These binaries can then be attached to the created release (use the upload_url output of the release-please action). I hope this addresses all points raised in this thread!

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.