diff --git a/.github/workflows/create-and-publish-release.yml b/.github/workflows/create-and-publish-release.yml deleted file mode 100644 index 204333d..0000000 --- a/.github/workflows/create-and-publish-release.yml +++ /dev/null @@ -1,58 +0,0 @@ -# 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 - -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 - - - 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: - 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=false --branch=${{ github.ref_name }} --semver-segment-to-bump=${{ inputs.semver_segment_to_bump }} diff --git a/.github/workflows/publish-existing-release.yml b/.github/workflows/publish-release-from-tag.yml similarity index 90% rename from .github/workflows/publish-existing-release.yml rename to .github/workflows/publish-release-from-tag.yml index a5a22b9..00e9010 100644 --- a/.github/workflows/publish-existing-release.yml +++ b/.github/workflows/publish-release-from-tag.yml @@ -1,6 +1,6 @@ # 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 +name: Publish Release From Tag on: push: @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - + - name: Determine tag id: determine-tag run: | @@ -33,34 +33,34 @@ jobs: 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 }}" @@ -69,64 +69,64 @@ jobs: 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 - + run: ./gradlew check + - name: Build release artifacts - run: ./gradlew publishToMavenLocal -Pversion=${{ steps.determine-tag.outputs.tag }} - + run: ./gradlew build 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 "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 diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 13d87a3..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,117 +0,0 @@ -# Braintrust SDK Implementation Notes - -This file contains important implementation details and quirks specific to the Braintrust API that should be considered when working on this SDK. - -## API Quirks - -### 1. Authorization Header Error Messages -**Symptom**: Getting error "Invalid key=value pair (missing equal-sign) in Authorization header" -**Cause**: This error is misleading - it usually means you're hitting a URL path that doesn't exist (404) -**Solution**: Verify the API endpoint path is correct - -**Important**: This is a Braintrust API quirk where 404 errors are returned as 403 errors with this specific message - -### 2. OTLP Endpoint Path -- Correct path: `/otel/v1/traces` (NOT `/otlp/v1/traces`) -- The Java OTLP exporter requires the full path in setEndpoint() -- Example: `setEndpoint(config.apiUrl() + "/otel/v1/traces")` - -### 3. x-bt-parent Header (CRITICAL) -- **Required**: The `x-bt-parent` HTTP header must be set for traces to appear in experiments -- Format: `experiment_id:` or `project_id:` or `project_name:` -- This header tells Braintrust which experiment/project the traces belong to -- Without this header, traces will be created but won't show up in the experiment view -- Implemented via custom BraintrustSpanExporter that dynamically sets the header - -### 4. API Key Format -- Standard Bearer token format works: `Authorization: Bearer ` -- API keys work for both production (api.braintrust.dev) and staging (staging-api.braintrust.dev) - -### 5. Transport Protocol -- Braintrust supports HTTP only (NOT gRPC) -- Both protobuf and JSON formats work over HTTP -- Use `OtlpHttpSpanExporter` not `OtlpGrpcSpanExporter` - -### 6. REST API Endpoints (IMPORTANT) -The API uses **singular** resource names with `/v1/` prefix: -- Projects: `/v1/project` (NOT `/v1/projects` or `/projects`) -- Experiments: `/v1/experiment` (NOT `/v1/experiments` or `/experiments`) -- Datasets: `/v1/dataset` (NOT `/v1/datasets` or `/datasets`) -- Dataset operations: `/v1/dataset/{id}/insert`, `/v1/dataset/{id}/fetch` - -**Note**: Using the wrong endpoint (e.g., `/projects` instead of `/v1/project`) will return a 403 error with the misleading "Invalid key=value pair" message. - -## SDK Design Decisions - -### 1. Mimic Go SDK -The goal is to match the Go SDK's developer experience: -- `RegisterProject()` and `RegisterExperiment()` functions -- Similar API patterns and naming conventions -- Automatic project/experiment creation helpers - -### 2. Evaluation Trace Structure (IMPORTANT) -Each evaluation case must be its own root trace with nested child spans: -- **Root span**: Named "eval" with `type: "eval"` attribute -- **Child spans**: - - "task" span (with `type: "task"`) for executing the evaluation function - - "score" span (with `type: "score"`) for calculating scores -- **Attributes on eval span**: - - `input`: The actual input value (e.g., the question for MathProblem) - - `output`: The actual output value (e.g., the result for Answer) - - `expected`: The expected value if available - - `score.`: Score values (e.g., `score.accuracy`, `score.speed`) -- This nested structure matches how the Go SDK and JavaScript SDK work - -### 3. OpenAI SDK -- Must use the official OpenAI Java SDK: https://github.com/openai/openai-java -- NOT the community versions (e.g., TheoKanning) - -### 4. Model Selection -- Default to using GPT-4o in examples (not GPT-3.5-turbo) - -## Common Issues and Solutions - -### HTTP 403 Errors -1. First check if the endpoint path exists (403 can mean 404 due to API quirk) -2. Verify API key is set correctly -3. Check if hitting staging vs production endpoints - -### HTTP 400 Errors -- Usually means the request format is correct but data is invalid -- Check OTLP data format matches expectations -- Verify all required fields are present - -### Jackson Deserialization Errors -- The API may return additional fields not in our model classes -- Fixed by setting `FAIL_ON_UNKNOWN_PROPERTIES` to false in ObjectMapper -- Project response includes: id, name, org_id, created_at, updated_at, user_id, settings, etc. -- Experiment response includes: id, project_id, name, description, created_at, updated_at, etc. - -## Testing Commands - -```bash -# Run with debugging -export BRAINTRUST_API_KEY='your-key' -export BRAINTRUST_API_URL='https://staging-api.braintrust.dev' # for testing -./gradlew :examples:runGoStyleExperiment - -# Debug authorization issues -./gradlew :examples:runDebugAuth -``` - -## File Structure -- Java SDK core: `/src/main/java/dev/braintrust/` -- Examples: `/examples/src/main/java/dev/braintrust/examples/` -- Scala module: `/braintrust-scala/` - -## Build and Format -```bash -# Format code -./gradlew spotlessApply - -# Run tests -./gradlew test - -# Build everything -./gradlew build -``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4f2c907..a5d3047 100644 --- a/build.gradle +++ b/build.gradle @@ -6,10 +6,86 @@ plugins { id("io.freefair.lombok") version "9.0.0-rc2" } -// use braintrust.properties as the single source of truth for sdk version -def braintrustProps = new Properties() -file('src/main/resources/braintrust.properties').withInputStream { braintrustProps.load(it) } -version = braintrustProps.getProperty('sdk.version') +// Generate braintrust.properties at build time with smart versioning +task generateBraintrustProperties { + description = 'Generate braintrust.properties with smart git-based versioning' + group = 'build' + + def outputDir = file("$buildDir/generated/resources") + def outputFile = file("$outputDir/braintrust.properties") + + outputs.file outputFile + outputs.upToDateWhen { false } // Always regenerate to get latest version + + doLast { + outputDir.mkdirs() + + def version = generateVersion() + + outputFile.text = "sdk.version=${version}\n" + + logger.info("Generated braintrust.properties with sdk.version=${version}") + } +} + +def generateVersion() { + // Check if workspace is clean + def gitStatusProcess = ['git', 'status', '--porcelain'].execute() + gitStatusProcess.waitFor() + def gitStatus = gitStatusProcess.text.trim() + def isDirty = !gitStatus.isEmpty() + + // Get current commit SHA (short version) + def gitShaProcess = ['git', 'rev-parse', '--short', 'HEAD'].execute() + gitShaProcess.waitFor() + def gitSha = gitShaProcess.text.trim() + + // Check if we're currently on a tag + def currentTag = null + try { + def currentTagProcess = ['git', 'describe', '--exact-match', '--tags', 'HEAD'].execute() + currentTagProcess.waitFor() + if (currentTagProcess.exitValue() == 0) { + currentTag = currentTagProcess.text.trim() + } + } catch (Exception e) { + // Not on a tag, that's fine + } + + def version + if (currentTag != null) { + // We're on a tag, use the tag name + version = currentTag + } else { + // Not on a tag, find the most recent tag + def mostRecentTag = null + try { + def mostRecentTagProcess = ['git', 'describe', '--tags', '--abbrev=0'].execute() + mostRecentTagProcess.waitFor() + if (mostRecentTagProcess.exitValue() == 0) { + mostRecentTag = mostRecentTagProcess.text.trim() + } + } catch (Exception e) { + // No tags found, use just the SHA + mostRecentTag = null + } + + if (mostRecentTag != null) { + version = "${mostRecentTag}-${gitSha}" + } else { + version = gitSha + } + } + + // Append -DIRTY if workspace is not clean + if (isDirty) { + version = "${version}-DIRTY" + } + + return version +} + +version = generateVersion() // we could cache but not worth the hassle group = 'dev.braintrust' java { @@ -60,6 +136,7 @@ dependencies { } test { + dependsOn generateBraintrustProperties useJUnitPlatform() testLogging { events "passed", "skipped", "failed" @@ -67,7 +144,11 @@ test { } environment 'TEST_VAR1', 'fromenv1' environment 'TEST_VAR2', 'fromenv2' - environment 'GRADLE_SDK_VERSION', version + + doFirst { + // Set the version at execution time, after properties are generated + environment 'GRADLE_SDK_VERSION', version + } } // Enable preview features for pattern matching, etc. @@ -124,6 +205,20 @@ publishing { } +// Include generated resources in the JAR +sourceSets { + main { + resources { + srcDir "$buildDir/generated/resources" + } + } +} + +// Make sure properties are generated before compilation and packaging +compileJava.dependsOn generateBraintrustProperties +processResources.dependsOn generateBraintrustProperties +jar.dependsOn generateBraintrustProperties + // Configure Spotless for code formatting spotless { java { diff --git a/scripts/create-new-release.sh b/scripts/create-new-release.sh deleted file mode 100755 index 8c81029..0000000 --- a/scripts/create-new-release.sh +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env bash - -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!" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..f541389 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +set -euo pipefail + + +# Usage function +usage() { + echo "Usage: ./scripts/release.sh [--dry-run] [--skip-push]" +} + +# Parse arguments +VERSION="" +DRY_RUN=false +SKIP_PUSH=false + +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + --skip-push) + SKIP_PUSH=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + if [[ -z "$VERSION" ]]; then + VERSION="$1" + else + echo "Error: Unknown argument: $1" >&2 + usage + exit 1 + fi + shift + ;; + esac +done + +if [[ -z "$VERSION" ]]; then + echo "Error: Version is required" >&2 + usage + exit 1 +fi + +# Validate version format (basic semver check) +if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then + echo "Error: Version must follow semantic versioning format (e.g., v1.2.3 or v1.2.3-beta.1)" >&2 + exit 1 +fi + +if ! git diff-index --quiet HEAD --; then + echo "Error: Working directory is not clean." >&2 + git status --porcelain + exit 1 +fi + +# Check if local branch is in sync with remote +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +git fetch origin "$CURRENT_BRANCH" > /dev/null 2>&1 || { + echo "Error: Failed to fetch remote branch '$CURRENT_BRANCH'" >&2 + exit 1 +} + +LOCAL_COMMIT=$(git rev-parse HEAD) +REMOTE_COMMIT=$(git rev-parse "origin/$CURRENT_BRANCH" 2>/dev/null || echo "") + +if [[ -z "$REMOTE_COMMIT" ]]; then + echo "Error: Remote branch 'origin/$CURRENT_BRANCH' does not exist" >&2 + exit 1 +fi + +if [[ "$LOCAL_COMMIT" != "$REMOTE_COMMIT" ]]; then + echo "Error: Local branch '$CURRENT_BRANCH' is not in sync with remote 'origin/$CURRENT_BRANCH'" >&2 + echo "Local: $LOCAL_COMMIT" + echo "Remote: $REMOTE_COMMIT" + echo "Please pull or push to sync before releasing." + exit 1 +fi + +if git tag --list | grep -q "^$VERSION$"; then + echo "Error: Version '$VERSION' already exists locally" >&2 + exit 1 +fi + +# Check remote tags +git fetch --tags > /dev/null 2>&1 || true +if git ls-remote --tags origin | grep -q "refs/tags/$VERSION$"; then + echo "Error: Version '$VERSION' already exists on remote" >&2 + exit 1 +fi + +# Show release information +COMMIT=$(git rev-parse HEAD) +SHORT_COMMIT=$(git rev-parse --short HEAD) +REPO_URL=$(git config --get remote.origin.url | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//') +LAST_TAG=$(git tag --sort=-version:refname | grep -v -- '-rc' | head -n 1 2>/dev/null || echo "") + +echo "================================================" +echo " Java SDK Release" +echo "================================================" +printf "%-13s %s\n" "version:" "$VERSION" +printf "%-13s %s\n" "commit:" "$SHORT_COMMIT" +printf "%-13s %s\n" "code:" "$REPO_URL/commit/$COMMIT" +if [[ -n "$LAST_TAG" ]]; then + printf "%-13s %s\n" "changeset:" "$REPO_URL/compare/$LAST_TAG...$COMMIT" +else + printf "%-13s %s\n" "changeset:" "$REPO_URL/commits/$COMMIT" +fi +echo "" + +# Confirmation prompt (skip in dry-run) +if [[ "$DRY_RUN" == true ]]; then + echo "dry-run was requested. Bailing" + exit 0 +fi + +read -p "Are you ready to release version $VERSION? Type 'YOLO' to continue: " -r +echo "" +if [[ "$REPLY" != "YOLO" ]]; then + echo "Release aborted" + exit 0 +fi + +if ! ./gradlew check; then + echo "Error: ./gradlew check" >&2 + exit 1 +fi + +git tag -a "$VERSION" -m "Release $VERSION" +if [[ "$SKIP_PUSH" == true ]]; then + echo "skip-push was requested. tag is created locally but not pushed. Do what you will." + exit 0 +fi +git push origin "$VERSION" + +echo "================================================" +echo " Release Complete!" +echo "================================================" +echo "Version $VERSION has been created and pushed to origin." +echo "" +echo "View changelog: $REPO_URL/releases/tag/$VERSION" diff --git a/src/main/java/dev/braintrust/trace/BraintrustTracing.java b/src/main/java/dev/braintrust/trace/BraintrustTracing.java index 5e46c69..9f636a6 100644 --- a/src/main/java/dev/braintrust/trace/BraintrustTracing.java +++ b/src/main/java/dev/braintrust/trace/BraintrustTracing.java @@ -17,8 +17,6 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; @@ -31,6 +29,7 @@ */ @Slf4j public final class BraintrustTracing { + static final String OTEL_SERVICE_NAME = "braintrust-app"; static final String INSTRUMENTATION_NAME = "braintrust-java"; static final String INSTRUMENTATION_VERSION = loadVersionFromProperties(); @@ -85,21 +84,21 @@ public static void enable( @Nonnull SdkTracerProviderBuilder tracerProviderBuilder, @Nonnull SdkLoggerProviderBuilder loggerProviderBuilder, @Nonnull SdkMeterProviderBuilder meterProviderBuilder) { - // NOTE: these options are hardcoded for now. We'll move them into braintrust config - // as-needed - final Map resourceAttributes = new HashMap<>(); - final String serviceName = "braintrust-app"; final Duration exportInterval = Duration.ofSeconds(5); final int maxQueueSize = 2048; final int maxExportBatchSize = 512; - log.info("Initializing Braintrust OpenTelemetry with service={}", serviceName); + log.info( + "Initializing Braintrust OpenTelemetry with service={}, instrumentation-name={}," + + " instrumentation-version={}", + OTEL_SERVICE_NAME, + INSTRUMENTATION_NAME, + INSTRUMENTATION_VERSION); // Create resource first so BraintrustSpanProcessor can access service.name var resourceBuilder = Resource.getDefault().toBuilder() - .put(ResourceAttributes.SERVICE_NAME, serviceName) + .put(ResourceAttributes.SERVICE_NAME, OTEL_SERVICE_NAME) .put(ResourceAttributes.SERVICE_VERSION, INSTRUMENTATION_VERSION); - resourceAttributes.forEach(resourceBuilder::put); var resource = resourceBuilder.build(); // spans diff --git a/src/main/resources/braintrust.properties b/src/main/resources/braintrust.properties deleted file mode 100644 index 703f6d7..0000000 --- a/src/main/resources/braintrust.properties +++ /dev/null @@ -1 +0,0 @@ -sdk.version=0.0.1-SNAPSHOT \ No newline at end of file