Skip to content

Generate GH Release with Proper Release Notes for OpenTofu Modules #21

Generate GH Release with Proper Release Notes for OpenTofu Modules

Generate GH Release with Proper Release Notes for OpenTofu Modules #21

Workflow file for this run

name: Generate GH Release with Proper Release Notes for OpenTofu Modules
on:
workflow_dispatch:
inputs:
module_name:
description: Name of the module to perform a release for
type: environment
required: true
release_type:
description: Type of release that to be performed
type: choice
options:
- patch
- minor
- major
default: patch
is_dev_release:
description: Perform a development release than a full release?
type: boolean
default: false
jobs:
prepare_release_details:
name: Preparing release details such as next release version and changelogs
runs-on: ubuntu-latest
environment: ${{ inputs.module_name }}
env:
MODULE: ${{ inputs.module_name }}
RELEASE_TYPE: ${{ inputs.release_type }}
IS_DEV_RELEASE: ${{ inputs.is_dev_release }}
CURRENT_FULL_VERSION: ${{ vars.FULL_VERSION || '0.0.0' }}
outputs:
version: ${{ steps.version.outputs.NEXT_RELEASE_VERSION }}
changelog: ${{ steps.changelog.outputs.NOTES }}
steps:
- name: Checking out the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
- name: Checking if there's anything committed to create a new release
run: |
CURRENT_TAG=$(git tag -l "$MODULE/v*" --sort=-v:refname | head -n 1)
CURRENT_TAG=${CURRENT_TAG:-$(git rev-list --max-parents=0 HEAD)}
# Count commits in the module path excluding [INF]
CHANGE_COUNT=$(git log $CURRENT_TAG..HEAD --oneline -- "modules/$MODULE" | grep -v "\[INF\]" | wc -l)
if [ "$CHANGE_COUNT" -eq "0" ]; then
echo "::error title=No Changes Detected::No non-[INF] commits found in modules/$MODULE since $CURRENT_FULL_VERSION."
exit 1
fi
echo "Found $CHANGE_COUNT relevant changes."
- id: version
name: Calculating the next release version from the previous if it exists
run: |
# Fetch the raw version without any development tags if it exists
BASE_VERSION=$(echo "$CURRENT_FULL_VERSION" | cut -d- -f1)
# If the previous release was a development version, extract the release number
DEV_VERSION=$(echo "$CURRENT_FULL_VERSION" | grep -oP 'dev\.\K\d+' || echo "0")
# Calculating the next release version
# Case 1: if the current release was a dev version and the next one is not, the next
# release version will be the base version without any dev tags added onto it
if [[ "$CURRENT_FULL_VERSION" == *"dev."* && "$IS_DEV_RELEASE" == "false" ]]; then
NEXT_RELEASE_VERSION="$BASE_VERSION"
# Case 2: if the current release was a dev version and the next one is also a dev
# release, the next release version will be the same with the dev version incremented
elif [[ "$CURRENT_FULL_VERSION" == *"dev."* && "$IS_DEV_RELEASE" == "true" ]]; then
DEV_VERSION=$(( DEV_VERSION + 1 ))
NEXT_RELEASE_VERSION="$BASE_VERSION-dev.$DEV_VERSION"
# Case 3: if the current release is not a dev version and the next too is not
# According to the provided inputs, increment major/minor/patch version and
# make that as the next release version to be performed
else
# Split the basen version up using period
# and extract the individual versions
IFS='.' read -ra VERSIONS <<< "$BASE_VERSION"
MAJOR_VERSION=${VERSIONS[0]}
MINOR_VERSION=${VERSIONS[1]}
PATCH_VERSION=${VERSIONS[2]}
# Increment the version according to the inputs and create the final version
if [[ "$RELEASE_TYPE" == "major" ]]; then MAJOR_VERSION=$(( MAJOR_VERSION + 1 )); MINOR_VERSION=0; PATCH_VERSION=0;
elif [[ "$RELEASE_TYPE" == "minor" ]]; then MINOR_VERSION=$(( MINOR_VERSION + 1 )); PATCH_VERSION=0;
else PATCH_VERSION=$(( PATCH_VERSION + 1 )); fi
NEXT_RELEASE_VERSION="$MAJOR_VERSION.$MINOR_VERSION.$PATCH_VERSION"
# If its a development release, attach the tag accordingly
if [[ "$IS_DEV_RELEASE" == "true" ]]; then
NEXT_RELEASE_VERSION="$NEXT_RELEASE_VERSION-dev.1"
fi
fi
# Outputting the version as a GH Output
echo "NEXT_RELEASE_VERSION=$NEXT_RELEASE_VERSION" >> $GITHUB_OUTPUT
- id: changelog
name: Generating Change log from the previous versions if exists for the new release
run: |
# We will output the change log
# as one big blob of text
echo "NOTES<<EOF" >> $GITHUB_OUTPUT
# Fetching the current release tag if it exists
# If it does not, we are starting from scratch
CURRENT_TAG=$(git tag -l "$MODULE/v*" --sort=-v:refname | head -n 1)
CURRENT_TAG=${CURRENT_TAG:-$(git rev-list --max-parents=0 HEAD)}
# Fetch all commits since the previous release for this specific module
COMMITS=$(git log $CURRENT_TAG..HEAD --format="%H" -- "modules/$MODULE")
# Looping across all commits to pull out
# Change log for the next release
for SHASUM in $COMMITS; do
# Fetch the body of the commit
# Since all commits on the main
# branch is PRs getting squashed
# and merged, the body will contain
# the changes made
BODY=$(git log -1 --format="%b" $SHASUM)
# If the body is garbage, DO NOT INCLUDE
# IN THE CHANGELOG
if [[ ! "$BODY" == *"# Implemented Changes"* ]]; then
continue
fi
# Fetch the commit message
SUBJECT=$(git log -1 --format="%s" $SHASUM)
# If the commit message is for an infrastructure
# folder update, ignore it safely
if [[ $SUBJECT == "[INF]"* ]]; then
continue
fi
# Fetch the PR number from the commit message
PR_REF=$(echo "$SUBJECT" | grep -o '(#[0-9]\+)' | sed 's/[()]//g' || echo "N/A")
# Fetch the issue number from the body of the commit
CLOSE_REF=$(echo "$BODY" | grep -i "Closes:" | sed 's/.*Closes://I' | sed 's/[*]//g' | xargs || echo "N/A")
CLOSE_REF=${CLOSE_REF:-"N/A"}
# Fetch all the changes made in the body and ignore everything apart from the changes points
CHANGES=$(echo "$BODY" | awk '/# Implemented Changes/{flag=1; next} /\*\*Closes:/{flag=0} flag' | sed '/^[[:space:]]*$/d')
# Take the changes points and output it as the changelogs
if [ ! -z "$CHANGES" ]; then
echo "#### From PR: $PR_REF (Issues closed: $CLOSE_REF)" >> $GITHUB_OUTPUT
echo "$CHANGES" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
fi
done
echo "EOF" >> $GITHUB_OUTPUT
- name: Printing out the release information as a step summary for validation
run: |
echo "### Release Preview: ${{ inputs.module_name }}" >> $GITHUB_STEP_SUMMARY
echo "**New Version:** \`${{ steps.version.outputs.NEXT_RELEASE_VERSION }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Release Type:** ${{ inputs.is_dev_release && 'In Development (Dev)' || 'Stable' }}" >> $GITHUB_STEP_SUMMARY
echo "#### Proposed Changelogs:" >> $GITHUB_STEP_SUMMARY
if [ -z "${{ steps.changelog.outputs.NOTES }}" ]; then
echo "*No descriptive changes found (commits may lack the 'Implemented Changes' section).*" >> $GITHUB_STEP_SUMMARY
else
echo "${{ steps.changelog.outputs.NOTES }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **Note:** Please review the details above. If correct, approve the next job to finalize the release." >> $GITHUB_STEP_SUMMARY
perform_release:
name: Create the release for the ${{ inputs.module_name }} module
runs-on: ubuntu-latest
environment: "manual-approval"
needs: prepare_release_details
env:
version: ${{ needs.prepare_release_details.outputs.version }}
changelog: ${{ needs.prepare_release_details.outputs.changelog }}
permissions:
contents: write
steps:
- name: Checking out the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
- name: Finalize the release version in GitHub Environment for ${{ inputs.module_name }}
env:
GITHUB_TOKEN: ${{ secrets.RELEASES_TOKEN }}
run: gh variable set MODULE_VERSION --body "${{ env.version }}" --env "${{ inputs.module_name }}"
- name: Create GitHub Release for the ${{ inputs.module_name }}
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe
with:
tag_name: ${{ env.version }}
name: ${{ inputs.module_name }} v${{ env.version }}
body: |
## Changes in ${{ github.event.inputs.module_name }}
${{ env.changelog }}
prerelease: ${{ inputs.is_dev_release }}
make_latest: ${{ inputs.is_dev_release == 'false' }}