Open epage opened 5 years ago
One approach I just came across via awesome-rust is semantic which uses clog to generate the changelog and determine the bump level based on the commit messages.
Personally I prefer an isolation between commit log, which is dev-facing, and release notes, which is user facing. So I didn't included auto changelog generation from very beginning.
However, I agree it's possible to determine the release level from commit log or public api changes. I wish semantic could offer a binary of library just for determining bump level so we can add optional support for that.
Personally I prefer an isolation between commit log, which is dev-facing, and release notes, which is user facing. So I didn't included auto changelog generation from very beginning.
Ideally, yes but I maintain too many crates to always worry about the changelog. Maybe switching to cargo-release will help. However, I do tend to make my commits more user focused (if they are feat/fix rather than refactor/chore).
And too often I see projects, major projects, without them and I'm at a loss digging through commits when they make breaking changes. Something is better than nothing.
However, I agree it's possible to determine the release level from commit log or public api changes. I wish semantic could offer a binary of library just for determining bump level so we can add optional support for that.
At my quick glance, it looked like semantic was just calling into clog-cli
's crate to see if anything was considered breaking from conventional-changelog's perspective, so that should be relatively easy to pull out.
The downside to semantic
's approach is you have to hope you did things right for it to correctly guess what version field to bump. If you mess up, you might end up needing to yank a release. I have also found that while it is easy to get people to do checklist tasks in the source code (like update changelog), it is harder to be programmatically precise tasks like with commit messages.
Another approach is to instead have the source always represent what the next version will be. If a commit makes a breaking change, then that commit is responsible for updating the version number without doing a release. The CI then releases that version. To support this, we might want a --no-release
flag that is a shortcut for --no-push
, --no-tag
, etc that a developer will call in prep for their PR.
The use cases we'd want to consider supporting
--no-release
would helpunchanged
post-level
arg.increment
level that increments pre-release, if present, and otherwise increments patch.... I have more thoughts and there is some problem areas but I wanted to post this without a long delay as I write it all up
we recently did this at my work and i figured it would be helpful to share since it was not straight forward to figure out. the workflow we have relies on two main actions, one to ensure that pr titles(and thus the merge commit) have a valid bump level and one that reads that commit and creates the tag and bumps everything.
a MAJOR caveat with this is that adding those secrets makes them available for use by anyone opening a pr on your repo. if you google "push to protected branch github action", you will get a bunch of results like this: https://github.community/t/allowing-github-actions-bot-to-push-to-protected-branch/16536 . we are using this on a private repo, so we're not really concerned with someone doing some fishy. i think there must be ways to mitigate that since rust itself has something like this, but i haven't looked into it at all
name: Check PR Title
on:
pull_request:
types: [opened, edited, synchronize, reopened]
jobs:
check-pr-title:
runs-on: ubuntu-latest
steps:
- uses: deepakputhraya/action-pr-title@master
with:
allowed_prefixes: "#major,#minor,#patch,#none" # title should start with the given prefix
prefix_case_sensitive: false # title prefix are case insensitive
github_token: ${{ github.token }} # Default: ${{ github.token }}
name: Bump version, create new tag and release point
on:
push:
branches:
- "main"
jobs:
bump_version:
name: Bump version, create tag/release point
runs-on: ubuntu-latest
# prevent recursively starting actions. it will only start one extra and then fail on the
# `bump_version` step if it is removed, but it's good to have for other github actions that
# might trigger on pushes to main
if: "!startsWith(github.event.head_commit.message, '[RELEASE]')"
steps:
# cache cargo release so we don't have to install it every time
- name: Cache cargo release and rust install steps
id: cache-release
uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
~/target/
key: ${{ runner.os }}-cargo-release
- name: Checkout code
uses: actions/checkout@v2
with:
repository: pdq/rover
# if you have branch protection on, you will need to a secret PAT for a repo admin or add
# an exception for user who generated the PAT
# this action caches the authentication it uses and makes that the default for future
# steps. adding the token here means you don't have to add it manually in a later step
token: ${{ secrets.PAT_GIT_BOT }}
fetch-depth: "0"
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v4
with:
# if you are requiring signed commits, you need to add a secret private key that will be
# used to sign the commits/tags
gpg_private_key: ${{ secrets.GPG_RSA_PRIVATE_KEY }}
git_user_signingkey: true
git_commit_gpgsign: true
# this is an easy way to get the bump level from the pr title once it has been merged. it
# makes the bump level available to use in the cargo release step
- name: Get version from PR
id: bump_version
uses: anothrNick/github-tag-action@1.36.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: false
DEFAULT_BUMP: none
DRY_RUN: true
- name: install cargo-release
uses: actions-rs/cargo@v1
if: steps.cache-release.outputs.cache-hit != 'true'
with:
command: install
args: cargo-release
- name: Setup Git info
run: |
git config user.name "git-bot"
git config user.email "git-bot@your-domain.com"
- name: Create tag and update Cargo.toml
uses: actions-rs/cargo@v1
with:
command: release
args: ${{ steps.bump_version.outputs.part }} --execute --no-confirm
For what it's worth, if you don't need signed commits or are worried about protected branches, you can get away with a much more out-of-the-box release process.
name: Release
env:
CARGO_TERM_COLOR: always
defaults:
run:
shell: bash
on:
workflow_dispatch:
inputs:
level:
type: choice
description: Bump level
options:
- patch
- minor
- major
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: cache cargo cache
id: cache-release
uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
~/target/
key: ${{ runner.os }}-cargo-release
- name: checkout code
uses: actions/checkout@v3
- name: git config
run: |
git config user.name "Github Actions: Release"
git config user.email "<>"
# fail early if tests fail
- name: run tests
run: |
make test
- name: install cargo-release
uses: actions-rs/cargo@v1
if: steps.cache-release.outputs.cache-hit != 'true'
with:
command: install
args: cargo-release
- name: cargo release ${{ github.event.inputs.level }}
uses: actions-rs/cargo@v1
with:
command: release
args: ${{ github.event.inputs.level }} --execute --no-confirm --config .github/workflows/cargo-release.toml
With 0.22 out, we now have cargo release --unpublished
which would allow CI to automatically release crates if a commit is pushed/merged with the version fields changed.
While this isn't fully automatic, this gets as close as I think is safe at this point.
I've started building a tool that I called semverlog
. The idea is to eventually have one-click style releases.
semverlog
has a compute-bump-level
command which will look for a .changes
directory in the current working dir and print "major", "minor" or "patch" to stdout, based on the changes found.
I was envisioning the following integration with cargo release
:
cargo release version --level-from-command="semverlog compute-bump-level"
cargo release
already figures out which dependencies to release in which order in a workspace. To compute the level, it would execute the given command with the crate-to-be-released as the working directory.
What do you think of this idea?
I can't fully articulate why but that handshake doesn't feel quite right to me.
I can't fully articulate why but that handshake doesn't feel quite right to me.
Do you see any possible integration that would compute the bump level dynamically?
Something I've been considering is the idea of auto-releasing after each PR but the question is "how?"