diff --git a/.github/workflows/release_drafter.yaml b/.github/workflows/release_drafter.yaml new file mode 100644 index 0000000000..cf6a73fe2f --- /dev/null +++ b/.github/workflows/release_drafter.yaml @@ -0,0 +1,69 @@ +name: Prepare New Helm Chart Release + +# Description: +# This workflow prepares a new release of the helm chart by updating chart and app versions as well as creating a PR. +# A release PR will be created in these cases. +# - When a user kicks offs this workflow manually. A user can specify the CHART_VERSION and APP_VERSION used for the new release. +# - When the cron schedule kicks off the job and there is a major or minor version different for the APP_VERSION. The CHART_VERSION will be automatically incremented appropriately. + +on: + schedule: + # Run every 12 hours at 55 minutes past the hour. + - cron: "55 */12 * * *" + workflow_dispatch: + inputs: + CREATE_BRANCH: + description: 'Whether to create a remote branch for the release. Defaults to false since peter-evans/create-pull-request steps will handle branch creation.' + required: false + default: 'false' + CHART_VERSION: + description: 'Optionally overrides the chart version in Chart.yaml.' + required: false + default: '' + APP_VERSION: + description: 'Optionally overrides the app version in Chart.yaml.' + required: false + default: '' + DEBUG: + description: 'Enable debug mode for the script.' + required: false + default: 'false' + +jobs: + prepare_release: + runs-on: ubuntu-latest + env: + CREATE_BRANCH: ${{ github.event.inputs.CREATE_BRANCH }} + CHART_VERSION: ${{ github.event.inputs.CHART_VERSION }} + APP_VERSION: ${{ github.event.inputs.APP_VERSION }} + DEBUG: ${{ github.event.inputs.DEBUG }} + steps: + - uses: actions/checkout@v4 + + - name: Install tools + run: make install-tools + + - name: Prepare Release + id: prepare_release + run: | + make prepare-release CHART_VERSION=$CHART_VERSION APP_VERSION=$APP_VERSION CREATE_BRANCH=$CREATE_BRANCH DEBUG=$DEBUG + + - name: Check if PR is already open + id: check_if_pr_open + run: | + DIFF=1 + git fetch origin + ((git show-ref --verify --quiet refs/heads/update-release) || (git diff --no-ext-diff --quiet main..update-release -- helm-charts)) && DIFF=0 + echo "PR_NEEDED=$DIFF" >> "$GITHUB_OUTPUT" + + - name: Open PR for Version Update + id: open_pr + if: ${{ steps.prepare_release.outputs.NEED_UPDATE == 1 && steps.check_if_pr_open.outputs.PR_NEEDED == 1 }} + uses: peter-evans/create-pull-request@v6 + with: + commit-message: Prepare release v${{ steps.prepare_release.outputs.LATEST_CHART_VERSION }} + title: Prepare release v${{ steps.prepare_release.outputs.LATEST_CHART_VERSION }} + body: Updating Helm chart from version ${{ steps.prepare_release.outputs.CURRENT_CHART_VERSION }} to ${{ steps.prepare_release.outputs.LATEST_CHART_VERSION }} and App version from ${{ steps.prepare_release.outputs.CURRENT_APP_VERSION }} to ${{ steps.prepare_release.outputs.LATEST_APP_VERSION }} + branch: "update-release" # Same branch name for all PRs + base: main + delete-branch: true diff --git a/Makefile b/Makefile index 6779bb5d4d..52f14e5c9c 100644 --- a/Makefile +++ b/Makefile @@ -181,3 +181,15 @@ update-chart-dep: dep-update ## Updates the dependency version in the Chart.yaml exit 1; \ fi ci_scripts/update-chart-dependency.sh $(CHART_PATH) $(SUBCHART_NAME) $(DEBUG_MODE) + +# Usage Examples: +# make prepare-release +# - Prepares for a new release using the current chart and app version in Chart.yaml. +# Automatically increments the chart version. +# Commits and pushes changes to a new branch unless CREATE_BRANCH is set to "false". +# make prepare-release CHART_VERSION=1.2.3 APP_VERSION=1.2.0 CREATE_BRANCH=false +# - Prepares for a new release with the chart version explicitly set to 1.2.3 and the app version to 1.2.0. +# Changes remain local if CREATE_BRANCH is set to "false", suitable for workflows that do not immediately push to remote. +.PHONY: prepare-release +prepare-release: ## Prepares for a new release of the helm chart. Optionally specify CHART_VERSION and APP_VERSION. + ci_scripts/prepare-release.sh CREATE_BRANCH=${CREATE_BRANCH} CHART_VERSION=${CHART_VERSION} APP_VERSION=${APP_VERSION} diff --git a/RELEASE.md b/RELEASE.md index 2ed2919d34..4226a0e8f6 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -13,10 +13,24 @@ of `version` field. ### Release Procedure -To make a new release of the helm chart: -1. Bump the chart `version` in [Chart.yaml](helm-charts/splunk-otel-collector/Chart.yaml) -1. Run `make render` to update Helm dependencies and render all the examples with the latest changes. -1. Run `git add .` to stage the rendered differences without requiring a chloggen entry -1. Run `make chlog-update` to update CHANGELOG.md for the release. -1. Create PR and request review from the team. -1. When the PR gets merged, the release will automatically be made and the helm repo updated. +#### Using GitHub Workflows + +- **Manual Github Worfklow:** + - Navigate to the **[Draft Release](https://github.com/signalfx/splunk-otel-collector-chart/actions/workflows/release_drafter.yaml** workflow under GitHub Actions. + - Manually trigger the workflow. It automatically drafts a PR for the release. + - Review code changes, validate chart functionality, approve the PR, and merge the PR. +- **Scheduled PRs:** Automatically generated PRs are scheduled for new versions of the collector that introduce major or minor version changes. + - Review code changes, validate chart functionality, approve the PR, and merge the PR. + +#### Manually Making a Release + +1. **Version Update:** Manually edit [Chart.yaml](helm-charts/splunk-otel-collector/Chart.yaml) to update the `version` field. +1. **Dependencies & Rendering:** Execute `make render` to update Helm dependencies and apply changes. +1. **CHANGELOG Update:** Run `make chlog-update` to incorporate changes into the CHANGELOG. +1. **Stage & Commit Changes:** + 1. Stage all changes: `git add .` + 1. Commit with a message: `git commit -m "Prepare release {version}"` +1. **Create a pull request:** + 1. Push your commits to the signalfx owned remote repository. + 1. Create a PR for your changes against the main branch. +1. Review code changes, validate chart functionality, approve the PR, and merge the PR. diff --git a/ci_scripts/base_util.sh b/ci_scripts/base_util.sh index 18b3489131..da925ad425 100755 --- a/ci_scripts/base_util.sh +++ b/ci_scripts/base_util.sh @@ -108,6 +108,7 @@ emit_output() { fi } +# ---- CI/CD Methods ---- # Function: setup_git # Description: Configures git so commits are published under the bot user. # Usage: setup_git @@ -117,6 +118,80 @@ setup_git() { echo "set git config for release-bot (ssg-srv-gh-o11y-gdi@splunk.com)" } +# Function: get_release_version +# Description: Removes the leading 'v' from a version string. +# Usage: get_release_version "v1.2.3" +get_release_version() { + local release_tag="$1" + echo "$release_tag" | cut -c2- +} + +# Function: get_major_version +# Description: Extracts the major version number from a version string. +# Usage: get_major_version "v1.2.3" +get_major_version() { + local release_tag="$1" + get_release_version "$release_tag" | awk -F'.' '{print $1}' +} + +# Function: get_minor_version +# Description: Extracts the minor version number from a version string. +# Usage: get_minor_version "v1.2.3" +get_minor_version() { + local release_tag="$1" + get_release_version "$release_tag" | awk -F'.' '{print $2}' +} + +# Function: get_patch_version +# Description: Extracts the patch version number from a version string. +# Usage: get_patch_version "v1.2.3" +get_patch_version() { + local release_tag="$1" + get_release_version "$release_tag" | awk -F'.' '{print $3}' +} + +# Function: validate_version +# Description: Validates that the provided version string follows the major.minor.patch format. +# Usage: validate_version "1.2.3" +validate_version() { + local version="$1" + if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] + then + echo "Invalid release version: $version" + echo "Release version must follow the pattern major.minor.patch, e.g. 1.2.3" + exit 1 + fi +} + +# Function: setup_branch +# Description: Checks if the specified branch exists and has open PRs. If it does and has no open PRs, it resets the branch to main. If it doesn't exist, it creates a new branch. +# Usage: setup_branch "release-1.2.3" "github.com/example/repo" +setup_branch() { + local branch="$1" + local repo_url="$2" + + # check if the branch exists + if git ls-remote --exit-code --heads origin "$branch"; then + # get number of open PRs for the branch + pr_count="$( gh pr list --repo "$repo_url" --head "$branch" --state open --json id --jq length )" + if [[ ! "$pr_count" =~ ^[0-9]+$ ]]; then + echo "ERROR: Failed to get PRs for the $branch branch!" >&2 + echo "$pr_count" >&2 + exit 1 + fi + if [[ "$pr_count" != "0" ]]; then + echo ">>> The $branch branch exists and has $pr_count open PR(s)." + echo ">>> Nothing to do." + exit 0 + fi + echo ">>> Resetting the $branch branch to main ..." + git checkout "$branch" + git reset --hard origin/main + else + git checkout -b "$branch" + fi +} + # ---- Docker Methods ---- # These methods provide functions for managing docker images. # To use this utility, source it in your script as shown in the example below: diff --git a/ci_scripts/prepare-release.sh b/ci_scripts/prepare-release.sh new file mode 100755 index 0000000000..955d171a8f --- /dev/null +++ b/ci_scripts/prepare-release.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# Simplifies the process of preparing a new helm chart release. +# Usage: +# ./prepare-release.sh +# Environment Variables: +# CREATE_BRANCH - If set to "false", changes remain local. Default is "true" to push changes. +# CHART_VERSION - Optionally overrides the chart version in Chart.yaml. +# APP_VERSION - Optionally overrides the app version in Chart.yaml. + +# Include the base utility functions for setting and debugging variables +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$SCRIPT_DIR/base_util.sh" + +# Function to update Chart.yaml with the new chart and app version. +function update_versions() { + echo "Updating Chart.yaml with chart version: $1 and app version: $2" + yq e ".version = \"$1\"" -i "${CHART_FILE_PATH}" + yq e ".appVersion = \"$2\"" -i "${CHART_FILE_PATH}" +} + +# Function to notify Github workflows when to execute downstream release jobs. +function notify_workflows_for_need_update() { + local current_chart_version="$1" + local latest_chart_version="$2" + + # Emit the NEED_UPDATE variable to either GitHub output or stdout. + setd "NEED_UPDATE" 1 + setd "CURRENT_CHART_VERSION" "$current_chart_version" + setd "LATEST_CHART_VERSION" "$latest_chart_version" + setd "CURRENT_APP_VERSION" "$current_chart_version" + setd "LATEST_APP_VERSION" "$latest_chart_version" + + # Notify possible downstream CI/CD tasks about needed info. + emit_output "NEED_UPDATE" + emit_output "CURRENT_CHART_VERSION" + emit_output "LATEST_CHART_VERSION" + emit_output "CURRENT_APP_VERSION" + emit_output "LATEST_APP_VERSION" +} + +# Prepare the release by updating versions, creating a branch, and committing changes. +function prepare_release() { + local version="$1" appVersion="$2" create_branch="$3" + + echo "Preparing release: $version" + echo "Release chart version: $version" + echo "Release app version: $appVersion" + # Update Chart.yaml with the new versions. + update_versions "$version" "$appVersion" + + # Generate new configs and update the changelog. + make render + make chlog-update + + # Stage any changes. + git add . + + # Check if there are staged changes and if the create_branch flag is true. + if git diff --staged --quiet; then + echo "No changes to commit." + else + if [[ "$create_branch" == "true" ]]; then + local branch_name="release-$version" + echo "Creating branch: $branch_name" + # Ensure the branch is correctly set up, either by creating or resetting it. + setup_branch "$branch_name" "jvoravong/splunk-otel-collector-chart" + # Commit and push only if create_branch is true. + git commit -m "Prepare release $version" + git push -u origin "$branch_name" + else + echo "Changes are staged but not committed or pushed because CREATE_BRANCH is not set to true." + # Optionally, you might still want to commit locally even if not pushing. + git commit -m "Prepare release $version" + fi + fi +} + +# Detect if CHART_VERSION is overridden via an environment variable. +CHART_VERSION_OVERRIDDEN=false +if [ -n "$CHART_VERSION" ]; then + CHART_VERSION_OVERRIDDEN=true + debug "Chart version overridden to: $CHART_VERSION" +fi + +# Fetch chart and app versions, either from environment variables or Chart.yaml. +CHART_VERSION=${CHART_VERSION:-$(grep "^version:" "${CHART_FILE_PATH}" | awk '{print $2}')} +APP_VERSION=${APP_VERSION:-$(yq e ".appVersion" "${CHART_FILE_PATH}")} +# Defaults CREATE_BRANCH to true if not set. +CREATE_BRANCH=${CREATE_BRANCH:-true} + +# Increment chart version if it matches the app version's major and minor. +chart_major=$(get_major_version "v$CHART_VERSION") +chart_minor=$(get_minor_version "v$CHART_VERSION") +app_major=$(get_major_version "v$APP_VERSION") +app_minor=$(get_minor_version "v$APP_VERSION") + +# Notify downstream Github workflow to create a release PR if needed. +# Create a PR if the job was manually created by a human. +if [[ "$WORKFLOW_TRIGGER_TYPE" = "dispatch" ]]; then + notify_workflows_for_need_update "$CHART_VERSION" "$APP_VERSION" +fi + +# Conditional logic to increment chart version or align it based on app version. +if [[ "$CHART_VERSION_OVERRIDDEN" = true ]]; then + debug "Using overridden chart version: $CHART_VERSION" +elif [[ "$chart_major" -eq "$app_major" && "$chart_minor" -eq "$app_minor" ]]; then + chart_patch=$(get_patch_version "v$CHART_VERSION") + CHART_VERSION="$chart_major.$chart_minor.$((chart_patch + 1))" + debug "Incrementing chart version to $chart_version" +else + CHART_VERSION="$app_major.$app_minor.0" + debug "Aligning chart version to $chart_version due to major.minor mismatch with app version" + # Notify downstream Github workflow to create a release PR if needed. + # Create a PR if the there is a major or minor version difference for the chart. + notify_workflows_for_need_update "$CHART_VERSION" "$APP_VERSION" +fi + +prepare_release "$CHART_VERSION" "$APP_VERSION" "$CREATE_BRANCH"