Skip to content

Release Helm Chart

Release Helm Chart #50

Workflow file for this run

name: Release Helm Chart
on:
workflow_dispatch:
inputs:
version:
description: 'Semver version number (e.g., 1.2.3, 1.2.3-prerelease)'
required: true
type: string
services_version:
description: 'Lock onto Braintrust services version (e.g., 1.2.3)'
required: false
type: string
force_republish:
description: 'Re-publish an existing version'
required: false
type: boolean
default: false
env:
CHART_PATH: ./braintrust
# Note: The Chart name is appended automatically by helm to the registry path
ECR_REGISTRY: public.ecr.aws/braintrust/helm
CHART_NAME: braintrust
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
steps:
- name: Create GitHub App Token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
id: bot-token
with:
app-id: ${{ secrets.GH_BOT_APP_ID }}
private-key: ${{ secrets.GH_BOT_APP_PRIVATE_KEY }}
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
token: ${{ steps.bot-token.outputs.token }}
fetch-tags: true
- name: Configure Git
run: |
git config --global user.name "Braintrust Bot"
git config --global user.email "215900051+braintrust-bot[bot]@users.noreply.github.com"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Login to Amazon ECR Public
id: login-ecr-public
uses: aws-actions/amazon-ecr-login@c962da2960ed15f492addc26fffa274485265950 # v2.0.2
with:
registry-type: public
- name: Validate and fixup Helm version
run: |
INPUT_VERSION="${{ github.event.inputs.version }}"
# Strip v prefix if present for helm chart version
if [[ $INPUT_VERSION == v* ]]; then
VERSION="${INPUT_VERSION#v}"
echo "ℹ️ Stripped 'v' prefix from version: $INPUT_VERSION -> $VERSION"
else
VERSION="$INPUT_VERSION"
fi
# Check if version matches semver pattern (x.y.z with optional pre-release and build metadata)
if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "❌ Error: Version '$INPUT_VERSION' is not a valid semver format"
echo "Expected format: x.y.z[-prerelease][+build] (e.g., 1.2.3, v1.2.3, 1.2.3-alpha.1)"
exit 1
fi
if [[ "${{ github.event.inputs.force_republish }}" != "true" ]]; then
if [[ -n "$(git tag --list "$VERSION")" ]]; then
echo "❌ Error: Version $VERSION already exists"
echo "Please use a different version number"
exit 1
fi
else
if [[ -z "$(git tag --list "$VERSION")" ]]; then
echo "❌ Error: force_republish=true but tag $VERSION does not exist"
echo "There is no existing release to republish"
exit 1
fi
echo "⚠️ force_republish=true — will republish from existing tag $VERSION"
fi
# Store the cleaned version for later use
echo "CHART_VERSION=$VERSION" >> $GITHUB_ENV
- name: Validate and fixup services version
run: |
if [ "${{ inputs.services_version }}" != "" ]; then
INPUT_SERVICES_VERSION="${{ inputs.services_version }}"
if [[ $INPUT_SERVICES_VERSION == v* ]]; then
SERVICES_VERSION="$INPUT_SERVICES_VERSION"
else
SERVICES_VERSION="v$INPUT_SERVICES_VERSION"
echo "ℹ️ Added 'v' prefix to services version: ${{ inputs.services_version }} -> $SERVICES_VERSION"
fi
echo "SERVICES_VERSION=$SERVICES_VERSION" >> $GITHUB_ENV
fi
- name: Prepare release branch
run: |
if [[ "${{ github.event.inputs.force_republish }}" == "true" ]]; then
# Capture the SHA from the existing (broken) tag before we delete anything
START_POINT=$(git rev-parse $CHART_VERSION)
echo "ℹ️ Republishing from existing tag $CHART_VERSION @ $START_POINT"
# Delete the existing GitHub release if present
if gh release view $CHART_VERSION &>/dev/null; then
echo "⚠️ Deleting existing release $CHART_VERSION"
gh release delete $CHART_VERSION --yes
fi
# Always delete the tag explicitly
if git ls-remote --exit-code origin refs/tags/$CHART_VERSION &>/dev/null; then
echo "⚠️ Deleting existing tag $CHART_VERSION"
git push origin --delete refs/tags/$CHART_VERSION
fi
# Delete old release branch if it exists
if git ls-remote --exit-code origin refs/heads/release/$CHART_VERSION &>/dev/null; then
echo "⚠️ Deleting existing release branch release/$CHART_VERSION"
git push origin --delete release/$CHART_VERSION
fi
else
# Normal flow publishes from main
START_POINT=origin/main
# Refresh origin/main — it's both the branch point (START_POINT)
# and the fast-forward target at the end of this step.
git fetch origin main
# Fail fast if release branch already exists — indicates a partial release
if git ls-remote --exit-code origin refs/heads/release/$CHART_VERSION &>/dev/null; then
echo "❌ Error: release/$CHART_VERSION already exists — this release may be partially complete"
echo "Use force_republish=true to redo it from scratch"
exit 1
fi
fi
# Create the release branch from the chosen starting point
git checkout -b release/$CHART_VERSION $START_POINT
# Apply services version lock if provided
if [ "$SERVICES_VERSION" != "" ]; then
./lock_versions $SERVICES_VERSION
git add .
if ! git diff --staged --quiet; then
git commit -m "Update Braintrust Services versions to $SERVICES_VERSION"
else
echo "No changes to commit for services version update"
fi
fi
# Update Chart version
sed -i "s/^version: .*/version: $CHART_VERSION/" $CHART_PATH/Chart.yaml
git add $CHART_PATH/Chart.yaml
if ! git diff --staged --quiet; then
git commit -m "Update Chart version to $CHART_VERSION"
else
echo "ℹ️ Chart.yaml already at version $CHART_VERSION"
fi
git push origin release/$CHART_VERSION
# In normal mode, fast-forward main to include the version bumps.
# (Skipped for force_republish: the release branch diverges from main.)
if [[ "${{ github.event.inputs.force_republish }}" != "true" ]]; then
git push origin release/$CHART_VERSION:main
fi
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
- name: Create GitHub Release
run: |
gh release create $CHART_VERSION \
--target release/$CHART_VERSION \
--draft \
--title "$CHART_VERSION" \
--generate-notes
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}
- name: Package Helm chart
run: |
helm package $CHART_PATH
mv $CHART_NAME-$CHART_VERSION.tgz $CHART_NAME.tgz
- name: Push chart to ECR Public
id: push-chart
run: |
# Push the chart to ECR Public
helm push $CHART_NAME.tgz oci://$ECR_REGISTRY
# Store chart URL for release notes
echo "chart_url=oci://$ECR_REGISTRY" >> $GITHUB_OUTPUT
# Create job summary
cat >> $GITHUB_STEP_SUMMARY << EOF
## 📦 Chart Details
- **Chart Name**: ${{ env.CHART_NAME }}
- **Version**: $CHART_VERSION
- **Chart URL**: \`oci://${{ env.ECR_REGISTRY }}\`
EOF
- name: Update Release with Chart Link
run: |
# Get the current release notes
gh release view $CHART_VERSION --json body --jq .body > current_notes.md
# Add Braintrust Services version information if provided
if [ "$SERVICES_VERSION" != "" ]; then
cat >> current_notes.md << EOF
## 🔧 Braintrust Services
* Updated Braintrust Services to \`$SERVICES_VERSION\`
EOF
fi
# Add chart information to release notes
cat >> current_notes.md << EOF
## 📦 Helm Chart
- **Chart Name**: ${{ env.CHART_NAME }}
- **Version**: $CHART_VERSION
- **Chart URL**: \`oci://${{ env.ECR_REGISTRY }}\`
### Installation
\`\`\`bash
helm install braintrust ${{ steps.push-chart.outputs.chart_url }}/${{ env.CHART_NAME }} --version $CHART_VERSION
\`\`\`
EOF
# Update the release with new notes
gh release edit $CHART_VERSION --notes-file current_notes.md --draft=false
env:
GH_TOKEN: ${{ steps.bot-token.outputs.token }}