Motivation 🪴
Maintianing many open source packages is time consuming, you have pull requests to review, security issues to concern about and also patch, open tracking issues to continuosly improve and also keep an eye on updates and improvements.
This makes any automation improvements a big deal, whenever you can automate these tasks will allow you to have more time to focus in actual work.
Dependency management can be addressed with Dependabot which is pretty easy to
setup, just setup a .github/dependabot.yml
with the following contents and
you will get weekly updates on dependencies:
version: 2
updates:
- package-ecosystem: 'cargo'
directory: '/'
schedule:
interval: 'weekly'
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: 'weekly'
This will open a bunch on PRs every monday, so its recommeded to setup a CI workflow to at least check tests and build integrity.
By having these CI workflows settled, you can then provide a couple workflows for approving and merging PRs.
The following workflow will approve PRs from dependabot when CI passes:
name: Dependabot auto-approve
on: pull_request_target
permissions:
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-[email protected]
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
And this other will merge approved PRs:
name: Dependabot auto-merge
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-[email protected]
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
Kudos to GitHub 🍻 for providing these workflows! The official guide is here
But after this setup I still had the need for releasing patch releases by hand, my task list was:
- Pull latest contents from GitHub
- Update tag in
Cargo.toml
- Create tag using
git tag
- Push to
main
using such tag - GitHub Workflow ran to publish to crates.io
But that workflow had many issues, repetition af manual tasks are error prone,
sometimes I had to cancel publish work because a mistaken version number was
bumped, or because I forgot to update Cargo.toml
.
So I had to provide one more workflow, one that automates my task list.
Preparing a Workflow to Bump Version and Push 🪴
I was inspired by NPM’s version
command, it basically covered my needs, when
you run npm version <major | minor | patch>
, you have the version
field
from package.json
updated with a +1
on the desired version digit, you also
get git tag
run with the new version, thats great!
With that in mind, and as an acceptance criteria for my workflow, I started to
investigate on GitHub, looking for a cargo
plugin that covers this for me.
And thats how I found cargo-set-version
which was part of cargo-edit
crate.
So the set of tasks to perform before publishing are:
- Check crate integrity with a dry run on
cargo publish
- Setup
cargo-set-version
as part of the CI Workflow (This involves setting up Rust) - Update
Cargo.toml
version - Commit changes to upstream pushing with tags included
- Login to
Crates.io
- Upload the crate
- Create GitHub Release
Let me walk you through the proccess.
Create a
Release
workflow on.github/workflow/release.yml
Provide workflow dispatch support with an input for version number to bump
name: Release
on:
workflow_dispatch:
inputs:
version:
type: choice
required: true
description: 'Version number to bump'
options:
- patch
- minor
- major
- As we need to commit and push, permissions to write must be granted:
permissions:
contents: write
issues: write
pull-requests: write
- Now lets implement jobs, the first job (
publish-dry-run
) will check for crate integrity, the second (release
) will perform release work:
jobs:
publish-dry-run:
name: "Runs cargo publish --dry-run"
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v1
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Check crate publish
run: cargo publish --dry-run
Then release
job would look like this:
release:
name: Create Release
needs: publish-dry-run
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Install `cargo-edit`
run: cargo install cargo-edit
- id: cargo-set-version
name: Set Version
run: cargo set-version --bump ${{ inputs.version }}
- name: Set Crate Version as Environment Variable
run: |
CARGO_TOML_VERSION=$(awk -F ' = ' '$1 ~ /version/ { gsub(/["]/, "", $2); printf("%s",$2) }' Cargo.toml)
echo "CRATE_VERSION=$CARGO_TOML_VERSION" >> $GITHUB_ENV
- name: Create Commit
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add .
git commit -m "chore: bump version to v$CRATE_VERSION"
git push origin main --follow-tags
- name: Login to Crates.io
run: cargo login ${CRATES_IO_TOKEN}
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
- name: Publish crate
run: cargo publish
- name: Create Release
id: create_release
uses: actions/github-script@v5
with:
script: |
await github.request(`POST /repos/${{ github.repository }}/releases`, {
tag_name: "v${{ env.CRATE_VERSION }}",
generate_release_notes: true
});
I have been using this workflow since July 2023 and it works pretty nice! You can see how it performs here.
Here is the full source if you get lost!
Conclusion 🪴
Automating repetitive tasks will always allow you to focus on more interesting ones and at the same time will reduce errors dramatically. Releasing new versions should be easy to achieve, even if you are AFK you should be able to merge PRs and release versions of your packages, you never know who may need a patch on one of your crates urgently!