diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml new file mode 100644 index 0000000..75fff78 --- /dev/null +++ b/.github/workflows/releases.yml @@ -0,0 +1,212 @@ +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 }} + permissions: + contents: read + 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<> $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: ${{ inputs.module_name }}/v${{ 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' }} diff --git a/.github/workflows/tofu-deploy.yaml b/.github/workflows/tofu-deploy.yaml index 2b1517b..10f734d 100644 --- a/.github/workflows/tofu-deploy.yaml +++ b/.github/workflows/tofu-deploy.yaml @@ -3,6 +3,9 @@ name: Infrastructure Deployment on Self Hosted Kubernetes Cluster on: workflow_dispatch: push: + paths: + - infrastructure/** + - modules/** branches: - task/** - bug/**