Skip to content

Commit

Permalink
Add make prepare-release and automatic Github workflow (#1197)
Browse files Browse the repository at this point in the history
* Add `make prepare-release` and automatic Github workflow

* patch

* patch
  • Loading branch information
jvoravong authored Mar 7, 2024
1 parent 0e9bdbb commit 7f3cf29
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 7 deletions.
69 changes: 69 additions & 0 deletions .github/workflows/release_drafter.yaml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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}
28 changes: 21 additions & 7 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
75 changes: 75 additions & 0 deletions ci_scripts/base_util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -117,6 +118,80 @@ setup_git() {
echo "set git config for release-bot ([email protected])"
}

# 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:
Expand Down
118 changes: 118 additions & 0 deletions ci_scripts/prepare-release.sh
Original file line number Diff line number Diff line change
@@ -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"

0 comments on commit 7f3cf29

Please sign in to comment.