Standard-version: Detect if release is necessary, exit if not

Created on 7 Jun 2017  路  12Comments  路  Source: conventional-changelog/standard-version

semantic-release has a sweet way of detecting whether or not it should follow through with a release. Some commits are just docs, style, and even refactor, that don't really affect the published build, hence a release shouldn't really take place. Currently, standard-version tags the release regardless of what the commits are -- for commits that don't affect the build, standard-version bumps the version number as a patch. We use standard-version at work for our CI builds and it works great for publishing to our internal registry, but it overpopulates our registry with unnecessary releases. The same with Bitbucket and unnecessary release tags.

Is this a viable feature that can be implemented into standard-version? Or is always releasing the correct behavior and should always be expected?

Most helpful comment

@stevemao I could see an argument for having a --force option, that creates a release even if there's no changes in the history (which is something I sometimes do). Perhaps the default behavior could be to warn and exit though? (with a tip as to how to use --force).

@enriquecaballero what do you think? (also sorry for this incredibly slow reply).

All 12 comments

@enriquecaballero sounds like good idea, i like it too.

Btw are you saying that you integrated the standard-version in the CI? What CI you use? Travis, Circle? I'm just thinking how can set it on CI, so i won't need to do it manually, but just like every needed commit (like in SR).

I just tested this, and confirmed that standard-version will create a release _even if there are no new commits at all_. Even if the immediate previous commit is in fact just the last release from `standard-version, it will create a new one, with a patch-level version bump.

tldr
If you do npm version it will also create a release even if there are no new commits at all. Reasons including you might messed up with your preview release or docs changes etc. And this is supposed to be a better npm version

@stevemao I could see an argument for having a --force option, that creates a release even if there's no changes in the history (which is something I sometimes do). Perhaps the default behavior could be to warn and exit though? (with a tip as to how to use --force).

@enriquecaballero what do you think? (also sorry for this incredibly slow reply).

Sounds good.
Side: do you think that npm and yarn should behave the same?

Is this going to be a feature in the future? This is handy for CI especially when your CI pushes tags and commits back to Git. This will cause an endless cycle as each push will trigger a new version that's going to push a new change log again and so on.

Jumping in on this, it'd be really nice if standard-version supported this.

In the meantime, if you're stuck on this, I wrote/use a small naive bash script to perform this type of check on CircleCI:

#!/bin/bash

set -e

{
  git describe --tags --exact-match > /dev/null 2>&1 && {
    echo "Release is already associated to a tag. Skipping..."
    exit 0
  }
} || {
  echo "Running new release"
  npm run release && {
    git push origin $CIRCLE_BRANCH && git push origin --tags
  } || {
    echo "Release aborted as branch was updated during build."
  }
}

exit 0

Where npm run release calls standard version, and an assumption is made that all git tags are generated by standard-version.

should be easily portable to other CI providers (only specific part is$CIRCLE_BRANCH)

I'm going to take a look into getting this done this week :+1:

In case it helps someone, here's what I have in my CI script. In commits since the previous tag, it'll proceed if any commit subject matches ^(feat|fix|perf|refactor|revert): or !:, or if the body contains BREAKING CHANGE:. Improvements or suggestions welcomed!

if \
  { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%s' | cut -d: -f1 | sort -u | sed -e 's/([^)]*)//' | grep -q -i -E '^feat|fix|perf|refactor|revert$' ; } || \
  { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%s' | cut -d: -f1 | sort -u | sed -e 's/([^)]*)//' | grep -q -E '\!$' ; } || \
  { git log "$( git describe --tags --abbrev=0 )..HEAD" --format='%b' | grep -q -E '^BREAKING CHANGE:' ; }
then
  npx --quiet [email protected]
else
  echo "No applicable changes since the previous tag, skipping..."
fi

+1

@bcoe any follow up on this? Could this change be made?

I'm looking in to using standard-version for Slate, but people are often contributing small docs fixes which would result in tons of no-op patch releases going out which would be very frustrating.

Here's a proposal for a possible solution that wouldn't involve file blacklists etc:

Use Presets

This might actually solve two issues with standard-version at the moment:

  1. If you run standard-version twice, back to back, without _any_ commits in between, the second run will still generate a new tagged commit and update the change log. Maybe there is some edge case where this is desirable, but it does seem a little silly.
  2. We run standard-version blindly in CI, and one or more commits have been made since the last tagged release commit, but they may not deem a new release necessary. (ie: this issue)

In both cases, the solution seems to be to make standard-version smart enough to know when to generate a release commit and when it should no-op.

One way this could be achieved while not imposing any particular, strict process on users is via presets. At the moment, presets are used primarily to translate various commit types into the headings they will appear under in a project's CHANGELOG.md, but also support many of the standard-version CLI options. standard-version uses this one by default. The full spec can be found here.

The definition for the types option reads:

An array of type objects representing the explicitly supported commit message types, and whether they should show up in generated CHANGELOGs.

And a typical value for types looks like this:

"types": [
    {"type": "feat", "section": "Features"},
    {"type": "fix", "section": "Bug Fixes"},
    {"type": "chore", "hidden": true},
    {"type": "docs", "hidden": true},
    {"type": "style", "hidden": true},
    {"type": "refactor", "hidden": true},
    {"type": "perf", "hidden": true},
    {"type": "test", "hidden": true}
]

So, what if standard-version _only_ considers the registered commit types in whatever preset its using to determine if a release should take place?

It already does something like this when determining _what type of release_ to create, the difference I'm proposing is that if it finds (1) no commits at all or (2) no commits that match one of the "explicitly supported commit message types", it exits without creating a release.

This proposal still maintains the usefulness of the hidden option, which indicates a supported commit type that _should_ trigger a release, but will not appear in CHANGELOG.md.

Omitting a commit type from types entirely would then indicate that not only should commits of that type not appear in the change log, but that they should not even be considered as candidates when determining if a release is necessary.

This would also allow users to continue following the same commit message format (conventional, for example) and run tools like commitlint, they would just need to decide on one or more commit types to use for commits that should not trigger a release. Working within the current implementation of the default preset, a commit type like misc could be used to bypass releasing.


Addendum:

  • I would also suggest dropping docs as a registered commit type since, as @ianstormtaylor pointed out, such commits should almost never result in the creation of a release.
  • Additionally, a --force option could be added to give users the behavior we have today, where a new release commit is _always_ created when standard-version is invoked, regardless of the commit history since the last release commit.
Was this page helpful?
0 / 5 - 0 ratings