Skip to content

Commit

Permalink
Add auto semver tagging for release automation (#712)
Browse files Browse the repository at this point in the history
Adds basic action, built on top of
[this](https://github.com/anothrNick/github-tag-action)... Put simply,
merge to main will trigger a new `SNAPSHOT` version, one minor version
up from the latest. A manual trigger will allow for a "clean tag"
release which will publish assets.

note: this does not yet publish a github release, this will remain
manual for now
  • Loading branch information
elliotmjackson authored Nov 2, 2022
1 parent fe29465 commit 353096c
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 3 deletions.
233 changes: 233 additions & 0 deletions .github/actions/github-tag-action/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#!/bin/bash

set -o pipefail

# config
default_semvar_bump=${DEFAULT_BUMP:-minor}
with_v=${WITH_V:-true}
release_branches=${RELEASE_BRANCHES:-}
custom_tag=${CUSTOM_TAG:-}
source=${SOURCE:-.}
dryrun=${DRY_RUN:-false}
initial_version=${INITIAL_VERSION:-0.0.0}
tag_context=${TAG_CONTEXT:-repo}
prerelease=${PRERELEASE:-false}
suffix=${PRERELEASE_SUFFIX:-beta}
verbose=${VERBOSE:-false}
major_string_token=${MAJOR_STRING_TOKEN:-#major}
minor_string_token=${MINOR_STRING_TOKEN:-#minor}
patch_string_token=${PATCH_STRING_TOKEN:-#patch}
none_string_token=${NONE_STRING_TOKEN:-#none}
# since https://github.blog/2022-04-12-git-security-vulnerability-announced/ runner uses?
git config --global --add safe.directory /github/workspace

cd "${GITHUB_WORKSPACE}/${source}" || exit 1

echo "*** CONFIGURATION ***"
echo -e "\tDEFAULT_BUMP: ${default_semvar_bump}"
echo -e "\tWITH_V: ${with_v}"
echo -e "\tRELEASE_BRANCHES: ${release_branches}"
echo -e "\tCUSTOM_TAG: ${custom_tag}"
echo -e "\tSOURCE: ${source}"
echo -e "\tDRY_RUN: ${dryrun}"
echo -e "\tINITIAL_VERSION: ${initial_version}"
echo -e "\tTAG_CONTEXT: ${tag_context}"
echo -e "\tPRERELEASE: ${prerelease}"
echo -e "\tPRERELEASE_SUFFIX: ${suffix}"
echo -e "\tVERBOSE: ${verbose}"
echo -e "\tMAJOR_STRING_TOKEN: ${major_string_token}"
echo -e "\tMINOR_STRING_TOKEN: ${minor_string_token}"
echo -e "\tPATCH_STRING_TOKEN: ${patch_string_token}"
echo -e "\tNONE_STRING_TOKEN: ${none_string_token}"

# verbose, show everything
if $verbose
then
set -x
fi

setOutput() {
echo "${1}=${2}" >> "${GITHUB_OUTPUT}"
}

current_branch=$(git rev-parse --abbrev-ref HEAD)

pre_release="$prerelease"
IFS=',' read -ra branch <<< "$release_branches"
for b in "${branch[@]}"; do
# check if ${current_branch} is in ${release_branches} | exact branch match
if [[ "$current_branch" == "$b" ]]
then
pre_release="false"
fi
# verify non specific branch names like .* release/* if wildcard filter then =~
if [ "$b" != "${b//[\[\]|.? +*]/}" ] && [[ "$current_branch" =~ $b ]]
then
pre_release="false"
fi
done
echo "pre_release = $pre_release"

# fetch tags
git fetch --tags

tagFmt="^v?[0-9]+\.[0-9]+\.[0-9]+$"
preTagFmt="^v?[0-9]+\.[0-9]+\.[0-9]+(-$suffix\.[0-9]+)$"

# get latest tag that looks like a semver (with or without v)
case "$tag_context" in
*repo*)
tag="$(git for-each-ref --sort=-v:refname --format '%(refname:lstrip=2)' | grep -E "$tagFmt" | head -n 1)"
pre_tag="$(git for-each-ref --sort=-v:refname --format '%(refname:lstrip=2)' | grep -E "$preTagFmt" | head -n 1)"
;;
*branch*)
tag="$(git tag --list --merged HEAD --sort=-v:refname | grep -E "$tagFmt" | head -n 1)"
pre_tag="$(git tag --list --merged HEAD --sort=-v:refname | grep -E "$preTagFmt" | head -n 1)"
;;
* ) echo "Unrecognised context"
exit 1;;
esac

echo "tag_context=$tag_context"

# if there are none, start tags at INITIAL_VERSION
if [ -z "$tag" ]
then
if $with_v
then
tag="v$initial_version"
else
tag="$initial_version"
fi
if [ -z "$pre_tag" ] && $pre_release
then
if $with_v
then
pre_tag="v$initial_version"
else
pre_tag="$initial_version"
fi
fi
fi

# get current commit hash for tag
tag_commit=$(git rev-list -n 1 "$tag")
echo "tag_commit=$tag_commit"

# get current commit hash
commit=$(git rev-parse HEAD)
echo "commit=$commit"

if [ "$tag_commit" == "$commit" ]
then
echo "No new commits since previous tag. Skipping..."
setOutput "new_tag" "$tag"
setOutput "tag" "$tag"
exit 0
fi

# get the merge commit message looking for #bumps
log=$(git show -s --format=%s)
echo "Last commit message: $log"

case "$log" in
*$major_string_token* ) new=$(semver -i major "$tag"); part="major";;
*$minor_string_token* ) new=$(semver -i minor "$tag"); part="minor";;
*$patch_string_token* ) new=$(semver -i patch "$tag"); part="patch";;
*$none_string_token* )
echo "Default bump was set to none. Skipping..."
setOutput "new_tag" "$tag"
setOutput "tag" "$tag"
exit 0;;
* )
if [ "$default_semvar_bump" == "none" ]
then
echo "Default bump was set to none. Skipping..."
setOutput "new_tag" "$tag"
setOutput "tag" "$tag"
exit 0
else
new=$(semver -i "${default_semvar_bump}" "$tag")
part=$default_semvar_bump
fi
;;
esac

if $pre_release
then
# already a pre-release available, bump it
newPreTagFmt="$new+(-$suffix\.[0-9]+)$"
exists="$(git tag --list --merged HEAD --sort=-v:refname | grep -E "$newPreTagFmt" | head -n 1)"
if [[ $exists != "" ]]
then
echo -e "Found parent to ${new} pre-tag ${exists}..."
new=$(semver -i prerelease "${exists}" --preid "${suffix}")
echo -e "Bumping ${suffix} pre-tag ${exists}. New pre-tag ${new}"
elif [[ "$pre_tag" =~ $new ]] && [[ "$pre_tag" =~ $suffix ]]
then
new=$(semver -i prerelease "${pre_tag}" --preid "${suffix}")
echo -e "Bumping ${suffix} pre-tag ${pre_tag}. New pre-tag ${new}"
else
new="$new-$suffix.0"
echo -e "Setting ${suffix} pre-tag ${pre_tag} - With pre-tag ${new}"
fi
part="pre-$part"
fi

if $with_v
then
new="v$new"
fi

echo -e "Bumping tag ${tag} - New tag ${new}"

# as defined in readme if CUSTOM_TAG is used any semver calculations are irrelevant.
if [ -n "$custom_tag" ]
then
new="$custom_tag"
fi

# set outputs
setOutput "new_tag" "$new"
setOutput "part" "$part"
setOutput "tag" "$new" # this needs to go in v2 is breaking change
setOutput "old_tag" "$tag"

# dry run exit without real changes
if $dryrun
then
exit 0
fi

# create local git tag
git tag "$new"

# push new tag ref to github
dt=$(date '+%Y-%m-%dT%H:%M:%SZ')
full_name=$GITHUB_REPOSITORY
git_refs_url=$(jq .repository.git_refs_url "$GITHUB_EVENT_PATH" | tr -d '"' | sed 's/{\/sha}//g')

echo "$dt: **pushing tag $new to repo $full_name"

git_refs_response=$(
curl -s -X POST "$git_refs_url" \
-H "Authorization: token $GITHUB_TOKEN" \
-d @- << EOF
{
"ref": "refs/tags/$new",
"sha": "$commit"
}
EOF
)

git_ref_posted=$( echo "${git_refs_response}" | jq .ref | tr -d '"' )

echo "::debug::${git_refs_response}"
if [ "${git_ref_posted}" = "refs/tags/${new}" ]
then
exit 0
else
echo "::error::Tag was not created properly."
exit 1
fi
2 changes: 1 addition & 1 deletion .github/workflows/goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: goreleaser
on:
push:
tags:
- 'v*.*.*'
- 'v[0-9]+.[0-9]+.[0-9]+'

permissions:
contents: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: Maven Deploy
on:
push:
tags:
- 'v*.*.*'
- 'v[0-9]+.[0-9]+.[0-9]+'

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Release Python Package
on:
push:
tags:
- 'v*.*.*'
- 'v[0-9]+.[0-9]+.[0-9]+'

env:
IMAGE_TAG: protoc-gen-validate:${{ github.sha }}
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/semver.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Version Bump
on:
push:
branches:
- main
workflow_dispatch:
inputs:
prerelease:
type: boolean
description: Pre-release?
default: true
bump:
type: choice
description: Which version?
default: 'minor'
options:
- minor
- patch

env:
PRERELEASE: ${{github.event.inputs.prerelease}}
DEFAULT_BUMP: ${{github.event.inputs.bump}}
APP_ID: 257305

jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ env.APP_ID }}
private_key: ${{ secrets.TOKEN_EXCHANGE_GH_APP_PRIVATE_KEY }}
repository: ${{ github.repository }}
permissions: >-
{"contents": "write"}
- uses: actions/checkout@v3
with:
fetch-depth: "0"
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm install -g semver
- name: Bump version and push tag
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
run: .github/actions/github-tag-action/entrypoint.sh

0 comments on commit 353096c

Please sign in to comment.