Generate GH Release with Proper Release Notes for OpenTofu Modules #21
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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' }} |