Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- The action now detects whether the commit is a release rollback rather than a release per se
- A release rollback commit occurs when the version of the package is downgraded rather than bumped
- A new output, `COMMIT_TYPE`, has been added which can be either `release` (same as `IS_RELEASE=true`), `release-rollback`, or `normal`

### Deprecated

- The `IS_RELEASE` output is deprecated; use `COMMIT_TYPE` instead

## [2.0.0]

### Changed
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

## Description

Check whether the current commit is a release commit. Primarily this action looks at the `.version` key in a repository's `package.json` to see whether it has changed. Optionally, it can also validate that the commit message starts with a specific string.
Check whether the current commit is a release or release rollback commit. Primarily this action looks at the `.version` key in a repository's `package.json` to see whether it has been bumped, downgraded, or untouched. Optionally, if the version has been upgraded, it can also validate that the commit message starts with a specific string.

![image](https://user-images.githubusercontent.com/675259/181828020-b54ef521-20f1-477c-83b4-3e9ac5b91398.png)

## Usage

## Basic usage

This will look at the current commit, comparing it to `github.event.before` to see whether the `version` field of the `package.json` file in the root directory of the repository has changed. If the version has been updated, `IS_RELEASE` will be set to `true`. Otherwise, it will be set to `false`.
This will look at the current commit, comparing it to `github.event.before` to see whether the `version` field of the `package.json` file in the root directory of the repository has changed. If the version has been upgraded, `COMMIT_TYPE` will be set to `release`. If the version has been downgraded, `COMMIT_TYPE` will be set to `release-rollback`. Otherwise, it will be set to `normal`.

```yaml
jobs:
is-release:
outputs:
IS_RELEASE: ${{ steps.is-release.outputs.IS_RELEASE }}
COMMIT_TYPE: ${{ steps.is-release.outputs.COMMIT_TYPE }}
runs-on: ubuntu-latest
steps:
- uses: MetaMask/action-is-release@v2
Expand All @@ -33,7 +33,7 @@ jobs:
# Filter by commits made by the author "github-actions"
if: github.event.head_commit.author.name == 'github-actions'
outputs:
IS_RELEASE: ${{ steps.is-release.outputs.IS_RELEASE }}
COMMIT_TYPE: ${{ steps.is-release.outputs.COMMIT_TYPE }}
runs-on: ubuntu-latest
steps:
- uses: MetaMask/action-is-release@v2
Expand All @@ -48,7 +48,7 @@ Here is an example of how to use the `commit-starts-with` option.
jobs:
is-release:
outputs:
IS_RELEASE: ${{ steps.is-release.outputs.IS_RELEASE }}
COMMIT_TYPE: ${{ steps.is-release.outputs.COMMIT_TYPE }}
runs-on: ubuntu-latest
steps:
- uses: MetaMask/action-is-release@v2
Expand All @@ -63,16 +63,14 @@ This field can support multiple patterns separated by a comma. For example, if `

### Conditionally running release jobs

You can then add filters in following jobs so those will skip if the `IS_RELEASE` criteria isn't met:
You can then add filters in following jobs so those will skip if the `COMMIT_TYPE` criteria isn't met:

```yaml
jobs:
is-release:
< insert example from above >
publish-release:
if: needs.is-release.outputs.IS_RELEASE == 'true'
if: needs.is-release.outputs.COMMIT_TYPE == 'release'
runs-on: ubuntu-latest
needs: is-release
```


13 changes: 8 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
name: 'Is this a release?'
description: "This action checks to see if the .version key in a repository's package.json has changed in order to determine if this commit is a release commit"
name: "Is this a release or a release rollback?"
description: "This action checks to see if the .version key in a repository's package.json has changed in order to determine if this commit is a release or release rollback commit."

inputs:
commit-starts-with:
description: "Validate that the release commit starts with a string in this comma-separated list. Use '[version]' to refer to the current release version."
description: "If a release commit, validate that the message starts with a string in this comma-separated list. Use '[version]' to refer to the current release version."
required: false

outputs:
IS_RELEASE:
description: 'Is this a release? can be either "true" or "false".'
description: 'Is this a release? Can be either "true" or "false". (Deprecated in favor of COMMIT_TYPE.)'
value: ${{ steps.is-release.outputs.IS_RELEASE }}
COMMIT_TYPE:
description: 'What kind of commit is this? Can be "release", "release-rollback", or "normal".'
value: ${{ steps.is-release.outputs.COMMIT_TYPE }}

runs:
using: 'composite'
using: "composite"
steps:
- uses: actions/checkout@v4
with:
Expand Down
103 changes: 103 additions & 0 deletions scripts/compare-semver-versions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env bash

set -euo pipefail

# Returns: "gt" if version1 > version2, "lt" if version1 < version2, "eq" if version1 == version2
compare-semver-versions() {
local version1="$1"
local version2="$2"

# Split versions into major.minor.patch and prerelease
IFS='.' read -a v1_parts <<< "$version1"
IFS='.' read -a v2_parts <<< "$version2"

# Extract prerelease parts if they exist
local v1_prerelease=""
local v2_prerelease=""

# Check if version1 has prerelease (contains hyphen)
if [[ "$version1" == *"-"* ]]; then
v1_prerelease="${version1#*-}"
# Remove prerelease from parts array
v1_parts=("${v1_parts[@]%-*}")
fi

# Check if version2 has prerelease (contains hyphen)
if [[ "$version2" == *"-"* ]]; then
v2_prerelease="${version2#*-}"
# Remove prerelease from parts array
v2_parts=("${v2_parts[@]%-*}")
fi

# Ensure we have at least 3 parts (major.minor.patch)
local v1_major="${v1_parts[0]:-0}"
local v1_minor="${v1_parts[1]:-0}"
local v1_patch="${v1_parts[2]:-0}"

local v2_major="${v2_parts[0]:-0}"
local v2_minor="${v2_parts[1]:-0}"
local v2_patch="${v2_parts[2]:-0}"

# Compare major version
if [[ "$v1_major" -gt "$v2_major" ]]; then
echo "gt"
return
elif [[ "$v1_major" -lt "$v2_major" ]]; then
echo "lt"
return
fi

# Major versions are equal, compare minor
if [[ "$v1_minor" -gt "$v2_minor" ]]; then
echo "gt"
return
elif [[ "$v1_minor" -lt "$v2_minor" ]]; then
echo "lt"
return
fi

# Minor versions are equal, compare patch
if [[ "$v1_patch" -gt "$v2_patch" ]]; then
echo "gt"
return
elif [[ "$v1_patch" -lt "$v2_patch" ]]; then
echo "lt"
return
fi

# All numeric parts are equal, compare prereleases
# If one has prerelease and the other doesn't, the one without prerelease is greater
if [[ -z "$v1_prerelease" && -n "$v2_prerelease" ]]; then
echo "gt" # no prerelease > prerelease
return
elif [[ -n "$v1_prerelease" && -z "$v2_prerelease" ]]; then
echo "lt" # prerelease < no prerelease
return
elif [[ -z "$v1_prerelease" && -z "$v2_prerelease" ]]; then
echo "eq" # both have no prerelease
return
else
# Both have prereleases, compare alphabetically
if [[ "$v1_prerelease" > "$v2_prerelease" ]]; then
echo "gt"
return
elif [[ "$v1_prerelease" < "$v2_prerelease" ]]; then
echo "lt"
return
else
echo "eq" # prereleases are equal
return
fi
fi
}

# Only run main function if this script is called directly (not sourced)
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <version1> <version2>"
echo "Returns: gt if version1 > version2, lt if version1 < version2, eq if version1 == version2"
exit 1
fi

compare-semver-versions "$@"
fi
29 changes: 25 additions & 4 deletions scripts/is-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ set -x
set -e
set -o pipefail

# Source the SemVer comparison utility
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/compare-semver-versions.sh"

BEFORE="${1}"
COMMIT_STARTS_WITH="${2}"

Expand All @@ -14,11 +18,25 @@ fi

VERSION_BEFORE="$(git show "$BEFORE":package.json | jq --raw-output .version)"
VERSION_AFTER="$(jq --raw-output .version package.json)"
if [[ "$VERSION_BEFORE" == "$VERSION_AFTER" ]]; then
echo "Notice: version unchanged. Skipping release."

if [[ "$VERSION_AFTER" == "$VERSION_BEFORE" ]]; then
echo "Version unchanged, so this is not a release commit."
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
echo "COMMIT_TYPE=normal" >> $GITHUB_OUTPUT
exit 0
elif [[ -n $COMMIT_STARTS_WITH ]]; then
else
# Get the comparison result using the sourced function
COMPARISON_WITH_BEFORE="$(compare-semver-versions "$VERSION_AFTER" "$VERSION_BEFORE")"

if [[ "$COMPARISON_WITH_BEFORE" == "lt" ]]; then
echo "Version downgraded, so this is a release rollback."
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
echo "COMMIT_TYPE=release-rollback" >> $GITHUB_OUTPUT
exit 0
fi
fi

if [[ -n $COMMIT_STARTS_WITH ]]; then
COMMIT_MESSAGE="$(git log --max-count=1 --format=%s)"
match_found=false

Expand All @@ -32,10 +50,13 @@ elif [[ -n $COMMIT_STARTS_WITH ]]; then
done

if [[ $match_found == false ]]; then
echo "Notice: commit message does not start with \"${COMMIT_STARTS_WITH}\". Skipping release."
echo "Commit message does not start with \"${COMMIT_STARTS_WITH}\", so this is not a release commit."
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
echo "COMMIT_TYPE=normal" >> $GITHUB_OUTPUT
exit 0
fi
fi

echo "This is a release commit!"
echo "IS_RELEASE=true" >> $GITHUB_OUTPUT
echo "COMMIT_TYPE=release" >> $GITHUB_OUTPUT