From 252ff57edcf109afdfe111532e1f1be76e957640 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 2 Oct 2025 00:20:24 -0600 Subject: [PATCH 1/5] release actions/scripts outline --- .../workflows/create-and-publish-release.yml | 10 ++++++++ .../workflows/publish-existing-release.yml | 11 +++++++++ README.md | 7 ++++++ scripts/create-new-release.sh | 24 +++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 .github/workflows/create-and-publish-release.yml create mode 100644 .github/workflows/publish-existing-release.yml create mode 100644 scripts/create-new-release.sh diff --git a/.github/workflows/create-and-publish-release.yml b/.github/workflows/create-and-publish-release.yml new file mode 100644 index 0000000..498e238 --- /dev/null +++ b/.github/workflows/create-and-publish-release.yml @@ -0,0 +1,10 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to Sonatype in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/braintrustdata/braintrust-java/actions/workflows/publish-sonatype.yml +name: Create And Publish Release +on: + workflow_dispatch: + +# TODO +# - run CI -- abort on failure +# - run $REPO_ROOT/scripts/create-new-release.sh --push=true --branch=main diff --git a/.github/workflows/publish-existing-release.yml b/.github/workflows/publish-existing-release.yml new file mode 100644 index 0000000..554ec3b --- /dev/null +++ b/.github/workflows/publish-existing-release.yml @@ -0,0 +1,11 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to Sonatype in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/braintrustdata/braintrust-java/actions/workflows/publish-sonatype.yml +name: Publish Existing Release + +# TODO +# run on main when a new tag is pushed (or a manual run with a valid tag as an input) +# - run CI -- abort on failure +# - run $REPO_ROOT/scripts/create-new-release.sh --push=true --branch=main + +# --generate-notes diff --git a/README.md b/README.md index cc6ba68..46f8cfc 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,13 @@ The remaining sections document developing the SDK itself. Nothing below is requ - These hooks automatically run common checks for you but CI also runs the same checks before merging to the main branch is allowed - NOTE: this will overwrite existing hooks. Take backups before running +## Releasing the SDK + +TODO: polish this section + +- summary: push the button on github +- bumping a major + ## Running a local OpenTelemetry collector OpenTelemetry provides a local collector with a debug exporter which logs all traces, logs, and metrics to stdout. diff --git a/scripts/create-new-release.sh b/scripts/create-new-release.sh new file mode 100644 index 0000000..79a995e --- /dev/null +++ b/scripts/create-new-release.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# TODO +# takes falgs +# * --push=true|false (required) +# * --branch=branch-name (defaults to main) +# * --bump=MAJOR|MINOR|PATCH (required) +# cd $REPO_ROOT +# checkout latest $BRANCH +# ASSERT +# - git state is clean +# look in ./src/main/resources/braintrust.properties for sdk.version prop +# CURRENT_SNAPSHOT = (whatever is in the props file) +# CURRENT_RELEASE_VERSION = ($CURRENT_SNAPSHOT without the -SNAPSHOT) +# NEW_SNAPSHOT = (bump semver depending on $BUMP) +# NEW_SNAPSHOT_RELEASE_VERSION = ($NEW_SNAPSHOT without the -SNAPSHOT) +# +# write $CURRENT_RELEASE_VERSION over the existing value in braintrust.properties +# git commit -m "release $CURRENT_RELEASE_VERSION" +# tag "v$CURRENT_RELEASE_VERSION" +# write $NEW_SNAPSHOT over the existing value in braintrust.properties +# git commit -m "begin $NEW_SNAPSHOT" +# if $PUSH +# push commits and tag (or fail if we're out of date) From 9e031372e6c1c8e31b1a75c5752b793904a56435 Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 2 Oct 2025 07:11:57 -0600 Subject: [PATCH 2/5] feat: implement create-new-release.sh script for managing release versions Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) --- scripts/create-new-release.sh | 193 ++++++++++++++++++++++++++++++---- 1 file changed, 171 insertions(+), 22 deletions(-) mode change 100644 => 100755 scripts/create-new-release.sh diff --git a/scripts/create-new-release.sh b/scripts/create-new-release.sh old mode 100644 new mode 100755 index 79a995e..8c81029 --- a/scripts/create-new-release.sh +++ b/scripts/create-new-release.sh @@ -1,24 +1,173 @@ #!/usr/bin/env bash -# TODO -# takes falgs -# * --push=true|false (required) -# * --branch=branch-name (defaults to main) -# * --bump=MAJOR|MINOR|PATCH (required) -# cd $REPO_ROOT -# checkout latest $BRANCH -# ASSERT -# - git state is clean -# look in ./src/main/resources/braintrust.properties for sdk.version prop -# CURRENT_SNAPSHOT = (whatever is in the props file) -# CURRENT_RELEASE_VERSION = ($CURRENT_SNAPSHOT without the -SNAPSHOT) -# NEW_SNAPSHOT = (bump semver depending on $BUMP) -# NEW_SNAPSHOT_RELEASE_VERSION = ($NEW_SNAPSHOT without the -SNAPSHOT) -# -# write $CURRENT_RELEASE_VERSION over the existing value in braintrust.properties -# git commit -m "release $CURRENT_RELEASE_VERSION" -# tag "v$CURRENT_RELEASE_VERSION" -# write $NEW_SNAPSHOT over the existing value in braintrust.properties -# git commit -m "begin $NEW_SNAPSHOT" -# if $PUSH -# push commits and tag (or fail if we're out of date) +set -euo pipefail + +# Default values +BRANCH="main" +PUSH="" +SEMVER_SEGMENT_TO_BUMP="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --push=*) + PUSH="${1#*=}" + shift + ;; + --branch=*) + BRANCH="${1#*=}" + shift + ;; + --semver-segment-to-bump=*) + SEMVER_SEGMENT_TO_BUMP="${1#*=}" + shift + ;; + *) + echo "Unknown option $1" + exit 1 + ;; + esac +done + +# Validate required arguments +if [[ -z "$PUSH" ]]; then + echo "Error: --push=true|false is required" + exit 1 +fi + +if [[ -z "$SEMVER_SEGMENT_TO_BUMP" ]]; then + echo "Error: --semver-segment=MAJOR|MINOR|PATCH is required" + exit 1 +fi + +if [[ "$PUSH" != "true" && "$PUSH" != "false" ]]; then + echo "Error: --push must be 'true' or 'false'" + exit 1 +fi + +if [[ "$SEMVER_SEGMENT_TO_BUMP" != "MAJOR" && "$SEMVER_SEGMENT_TO_BUMP" != "MINOR" && "$SEMVER_SEGMENT_TO_BUMP" != "PATCH" ]]; then + echo "Error: --semver-segment must be 'MAJOR', 'MINOR', or 'PATCH'" + exit 1 +fi + +# Find repository root +REPO_ROOT=$(git rev-parse --show-toplevel) +cd "$REPO_ROOT" + +# Checkout and update the specified branch +echo "Checking out branch: $BRANCH" +git checkout "$BRANCH" +git pull origin "$BRANCH" + +# Assert git state is clean +if [[ -n $(git status --porcelain) ]]; then + echo "Error: Git working directory is not clean" + git status + exit 1 +fi + +# Read current version from braintrust.properties +PROPERTIES_FILE="./src/main/resources/braintrust.properties" +if [[ ! -f "$PROPERTIES_FILE" ]]; then + echo "Error: $PROPERTIES_FILE not found" + exit 1 +fi + +CURRENT_SNAPSHOT=$(grep "^sdk.version=" "$PROPERTIES_FILE" | cut -d'=' -f2) +if [[ -z "$CURRENT_SNAPSHOT" ]]; then + echo "Error: sdk.version not found in $PROPERTIES_FILE" + exit 1 +fi + +echo "Current snapshot version: $CURRENT_SNAPSHOT" + +# Validate current version ends with -SNAPSHOT +if [[ ! "$CURRENT_SNAPSHOT" =~ -SNAPSHOT$ ]]; then + echo "Error: Current version '$CURRENT_SNAPSHOT' does not end with '-SNAPSHOT'" + exit 1 +fi + +# Remove -SNAPSHOT to get current release version +CURRENT_RELEASE_VERSION="${CURRENT_SNAPSHOT%-SNAPSHOT}" +echo "Current release version: $CURRENT_RELEASE_VERSION" + +# Parse version components +if [[ ! "$CURRENT_RELEASE_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + echo "Error: Version '$CURRENT_RELEASE_VERSION' is not in semver format (x.y.z)" + exit 1 +fi + +MAJOR="${BASH_REMATCH[1]}" +MINOR="${BASH_REMATCH[2]}" +PATCH="${BASH_REMATCH[3]}" + +# Calculate new version based on semver segment to bump +case "$SEMVER_SEGMENT_TO_BUMP" in + "MAJOR") + NEW_MAJOR=$((MAJOR + 1)) + NEW_MINOR=0 + NEW_PATCH=0 + ;; + "MINOR") + NEW_MAJOR=$MAJOR + NEW_MINOR=$((MINOR + 1)) + NEW_PATCH=0 + ;; + "PATCH") + NEW_MAJOR=$MAJOR + NEW_MINOR=$MINOR + NEW_PATCH=$((PATCH + 1)) + ;; +esac + +case "$SEMVER_SEGMENT_TO_BUMP" in + # For MAJOR/MINOR bumps, release the .0 version and prepare the next patch snapshot + "MAJOR"|"MINOR") + # Release version is the bumped version with patch 0 + CURRENT_RELEASE_VERSION="${NEW_MAJOR}.${NEW_MINOR}.0" + # Next snapshot is patch 1 + NEW_SNAPSHOT="${NEW_MAJOR}.${NEW_MINOR}.1-SNAPSHOT" + ;; + "PATCH") + # For patch bumps, use the original logic + NEW_SNAPSHOT="${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}-SNAPSHOT" + ;; +esac + +echo "New snapshot version: $NEW_SNAPSHOT" + +# Update properties file with current release version +echo "Creating release commit for version: $CURRENT_RELEASE_VERSION" +sed -i "s/^sdk.version=.*/sdk.version=$CURRENT_RELEASE_VERSION/" "$PROPERTIES_FILE" + +# Commit release version +git add "$PROPERTIES_FILE" +git commit -m "release $CURRENT_RELEASE_VERSION" + +# Create tag +git tag "v$CURRENT_RELEASE_VERSION" +echo "Created tag: v$CURRENT_RELEASE_VERSION" + +# Update properties file with new snapshot version +echo "Creating snapshot commit for version: $NEW_SNAPSHOT" +sed -i "s/^sdk.version=.*/sdk.version=$NEW_SNAPSHOT/" "$PROPERTIES_FILE" + +# Commit new snapshot version +git add "$PROPERTIES_FILE" +git commit -m "begin $NEW_SNAPSHOT" + +git log --oneline -n 3 # print out what we just did +# Push if requested +if [[ "$PUSH" == "true" ]]; then + echo "Pushing commits and tags to origin..." + git push origin "$BRANCH" + git push origin "v$CURRENT_RELEASE_VERSION" + echo "Successfully pushed release $CURRENT_RELEASE_VERSION and new snapshot $NEW_SNAPSHOT" +else + echo "Skipping push (--push=false)" + echo "To push manually, run:" + echo " git push origin $BRANCH" + echo " git push origin v$CURRENT_RELEASE_VERSION" +fi + +echo "Release process completed successfully!" From f8242b063e0957929edf78aa3548ad8ed3ae89bd Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 2 Oct 2025 08:36:47 -0600 Subject: [PATCH 3/5] feat: implement GitHub workflow for creating and publishing releases Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) --- .github/workflows/ci.yml | 9 +-- .../workflows/create-and-publish-release.yml | 68 +++++++++++++++++-- ...ease.yml => publish-existing-release.todo} | 0 3 files changed, 65 insertions(+), 12 deletions(-) rename .github/workflows/{publish-existing-release.yml => publish-existing-release.todo} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98c9bc1..abb4ea6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,17 +11,14 @@ jobs: build: # we want to run ubuntu-latest but we'll pin to a specific version so CI is reproducable runs-on: ubuntu-24.04 - strategy: - matrix: - java: [ '17' ] # Java is steps: - uses: actions/checkout@v4 - - name: Set up JDK ${{ matrix.java }} + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: ${{ matrix.java }} + java-version: 17 distribution: 'temurin' - name: Grant execute permission for gradlew @@ -34,5 +31,5 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: test-results-java-${{ matrix.java }} + name: test-results path: build/test-results/test/ diff --git a/.github/workflows/create-and-publish-release.yml b/.github/workflows/create-and-publish-release.yml index 498e238..9488445 100644 --- a/.github/workflows/create-and-publish-release.yml +++ b/.github/workflows/create-and-publish-release.yml @@ -1,10 +1,66 @@ -# This workflow is triggered when a GitHub release is created. -# It can also be run manually to re-publish to Sonatype in case it failed for some reason. -# You can run this workflow by navigating to https://www.github.com/braintrustdata/braintrust-java/actions/workflows/publish-sonatype.yml +# This workflow creates a new release by running CI and then executing the release script. +# It can be run manually with a choice of which semver segment to bump. name: Create And Publish Release on: workflow_dispatch: + inputs: + semver_segment_to_bump: + description: 'Which semver segment to bump' + required: true + default: 'PATCH' + type: choice + options: + - PATCH + - MINOR + - MAJOR + branch: + description: 'Branch to create release from' + required: true + default: 'main' + type: string -# TODO -# - run CI -- abort on failure -# - run $REPO_ROOT/scripts/create-new-release.sh --push=true --branch=main +jobs: + ci: + name: Run CI + # we want to run ubuntu-latest but we'll pin to a specific version so CI is reproducable + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run tests + run: ./gradlew check build + + create-release: + name: Create Release + # we want to run ubuntu-latest but we'll pin to a specific version so CI is reproducable + runs-on: ubuntu-24.04 + needs: ci + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + token: ${{ secrets.RELEASE_TOKEN }} + fetch-depth: 0 + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Run release script + run: | + chmod +x scripts/create-new-release.sh + scripts/create-new-release.sh --push=true --branch=${{ inputs.branch }} --semver-segment-to-bump=${{ inputs.semver_segment_to_bump }} diff --git a/.github/workflows/publish-existing-release.yml b/.github/workflows/publish-existing-release.todo similarity index 100% rename from .github/workflows/publish-existing-release.yml rename to .github/workflows/publish-existing-release.todo From f8f5d5546e0537c1fa6e3937851ee379844d982b Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 2 Oct 2025 08:58:44 -0600 Subject: [PATCH 4/5] feat: implement publish-existing-release workflow with tag validation and GitHub release creation Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) --- .../workflows/publish-existing-release.todo | 11 -- .../workflows/publish-existing-release.yml | 136 ++++++++++++++++++ 2 files changed, 136 insertions(+), 11 deletions(-) delete mode 100644 .github/workflows/publish-existing-release.todo create mode 100644 .github/workflows/publish-existing-release.yml diff --git a/.github/workflows/publish-existing-release.todo b/.github/workflows/publish-existing-release.todo deleted file mode 100644 index 554ec3b..0000000 --- a/.github/workflows/publish-existing-release.todo +++ /dev/null @@ -1,11 +0,0 @@ -# This workflow is triggered when a GitHub release is created. -# It can also be run manually to re-publish to Sonatype in case it failed for some reason. -# You can run this workflow by navigating to https://www.github.com/braintrustdata/braintrust-java/actions/workflows/publish-sonatype.yml -name: Publish Existing Release - -# TODO -# run on main when a new tag is pushed (or a manual run with a valid tag as an input) -# - run CI -- abort on failure -# - run $REPO_ROOT/scripts/create-new-release.sh --push=true --branch=main - -# --generate-notes diff --git a/.github/workflows/publish-existing-release.yml b/.github/workflows/publish-existing-release.yml new file mode 100644 index 0000000..85612dc --- /dev/null +++ b/.github/workflows/publish-existing-release.yml @@ -0,0 +1,136 @@ +# This workflow is triggered when a new tag is pushed to main. +# It can also be run manually to re-publish a release in case it failed for some reason. +name: Publish Existing Release + +on: + push: + tags: + - 'v*' + branches: + - main + workflow_dispatch: + inputs: + tag: + description: 'Tag to publish (e.g., v1.0.0)' + required: true + type: string + +jobs: + validate-and-publish: + name: Validate Tag and Publish Release + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine tag + id: determine-tag + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + TAG_NAME="${{ github.ref_name }}" + else + TAG_NAME="${{ inputs.tag }}" + fi + echo "tag=$TAG_NAME" >> $GITHUB_OUTPUT + echo "Using tag: $TAG_NAME" + + - name: Validate tag format + run: | + TAG="${{ steps.determine-tag.outputs.tag }}" + + # Check if tag starts with 'v' + if [[ ! "$TAG" =~ ^v ]]; then + echo "Error: Tag '$TAG' must start with 'v'" + exit 1 + fi + + # Extract version without 'v' prefix + VERSION="${TAG#v}" + + # Check if version is valid semver (x.y.z) + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Tag '$TAG' is not valid semver format (vx.y.z)" + exit 1 + fi + + # Check that version does not end with -SNAPSHOT + if [[ "$VERSION" =~ -SNAPSHOT$ ]]; then + echo "Error: Tag '$TAG' cannot end with '-SNAPSHOT'" + exit 1 + fi + + echo "Tag '$TAG' is valid" + + - name: Verify tag exists + run: | + TAG="${{ steps.determine-tag.outputs.tag }}" + if ! git tag -l | grep -q "^$TAG$"; then + echo "Error: Tag '$TAG' does not exist" + exit 1 + fi + echo "Tag '$TAG' exists" + + - name: Checkout tag + run: | + git checkout ${{ steps.determine-tag.outputs.tag }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run CI + run: ./gradlew check build + + - name: Build release artifacts + run: ./gradlew publishToMavenLocal -Pversion=${{ steps.determine-tag.outputs.tag }} + + - name: Find built artifacts + id: find-artifacts + run: | + VERSION="${{ steps.determine-tag.outputs.tag }}" + VERSION_NO_V="${VERSION#v}" + + # Find the built JAR files + MAIN_JAR=$(find build/libs -name "*-${VERSION_NO_V}.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | head -1) + SOURCES_JAR=$(find build/libs -name "*-${VERSION_NO_V}-sources.jar" | head -1) + JAVADOC_JAR=$(find build/libs -name "*-${VERSION_NO_V}-javadoc.jar" | head -1) + + echo "main-jar=$MAIN_JAR" >> $GITHUB_OUTPUT + echo "sources-jar=$SOURCES_JAR" >> $GITHUB_OUTPUT + echo "javadoc-jar=$JAVADOC_JAR" >> $GITHUB_OUTPUT + + echo "Found artifacts:" + echo " Main JAR: $MAIN_JAR" + echo " Sources JAR: $SOURCES_JAR" + echo " Javadoc JAR: $JAVADOC_JAR" + + - name: Create GitHub Release + run: | + TAG="${{ steps.determine-tag.outputs.tag }}" + + # Create the release + gh release create "$TAG" \ + --generate-notes \ + --title "Release $TAG" + + # Upload artifacts if they exist + if [[ -n "${{ steps.find-artifacts.outputs.main-jar }}" && -f "${{ steps.find-artifacts.outputs.main-jar }}" ]]; then + gh release upload "$TAG" "${{ steps.find-artifacts.outputs.main-jar }}" + fi + + if [[ -n "${{ steps.find-artifacts.outputs.sources-jar }}" && -f "${{ steps.find-artifacts.outputs.sources-jar }}" ]]; then + gh release upload "$TAG" "${{ steps.find-artifacts.outputs.sources-jar }}" + fi + + if [[ -n "${{ steps.find-artifacts.outputs.javadoc-jar }}" && -f "${{ steps.find-artifacts.outputs.javadoc-jar }}" ]]; then + gh release upload "$TAG" "${{ steps.find-artifacts.outputs.javadoc-jar }}" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a4c8d04f07ab633c292884ac165eda217b00d5ac Mon Sep 17 00:00:00 2001 From: Andrew Kent Date: Thu, 2 Oct 2025 09:07:56 -0600 Subject: [PATCH 5/5] hide docs --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 46f8cfc..18d530f 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,12 @@ The remaining sections document developing the SDK itself. Nothing below is requ - These hooks automatically run common checks for you but CI also runs the same checks before merging to the main branch is allowed - NOTE: this will overwrite existing hooks. Take backups before running -## Releasing the SDK + -TODO: polish this section + -- summary: push the button on github -- bumping a major + + ## Running a local OpenTelemetry collector