Release JS/TS SDK #98
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version bump type" | |
| required: true | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| - prerelease | |
| prerelease_type: | |
| description: 'Pre-release type (only used if version is "prerelease")' | |
| type: choice | |
| default: "" | |
| options: | |
| - "" | |
| - alpha | |
| - beta | |
| - rc | |
| dry_run: | |
| description: "Dry run (skip publish and git push)" | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| id-token: write # Required for npm provenance via OIDC | |
| pull-requests: write | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify branch | |
| run: | | |
| if [ "${{ github.ref }}" != "refs/heads/main" ]; then | |
| echo "❌ Error: Releases can only be triggered from main branch" | |
| echo "Current ref: ${{ github.ref }}" | |
| exit 1 | |
| fi | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v3 | |
| with: | |
| version: 9.15.0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Configure Git | |
| run: | | |
| git config user.name "langfuse-bot" | |
| git config user.email "langfuse-bot@langfuse.com" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Verify lockfile integrity | |
| run: | | |
| echo "Verifying pnpm lockfile has not been tampered with..." | |
| git diff --exit-code pnpm-lock.yaml || { | |
| echo "❌ Error: pnpm-lock.yaml was modified during install" | |
| exit 1 | |
| } | |
| - name: Determine release parameters | |
| id: release-params | |
| run: | | |
| if [ "${{ inputs.version }}" = "prerelease" ]; then | |
| if [ -z "${{ inputs.prerelease_type }}" ]; then | |
| echo "❌ Error: prerelease_type must be specified when version is 'prerelease'" | |
| exit 1 | |
| fi | |
| echo "prerelease_flag=--preRelease=${{ inputs.prerelease_type }}" >> $GITHUB_OUTPUT | |
| echo "tag=${{ inputs.prerelease_type }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "prerelease_flag=${{ inputs.version }}" >> $GITHUB_OUTPUT | |
| echo "tag=latest" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run release-it (version bump and git operations) | |
| if: inputs.dry_run == false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| pnpm exec release-it ${{ steps.release-params.outputs.prerelease_flag }} --ci --config .release-it.ci.json | |
| - name: Run release-it (dry run) | |
| if: inputs.dry_run == true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "🧪 Running in DRY RUN mode - no changes will be pushed" | |
| pnpm exec release-it ${{ steps.release-params.outputs.prerelease_flag }} --ci --config .release-it.ci.json --dry-run | |
| - name: Get version | |
| id: version | |
| run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT | |
| - name: Build all packages | |
| run: pnpm build | |
| - name: Verify build artifacts | |
| run: | | |
| echo "Verifying all packages have dist folders..." | |
| for pkg in packages/*/package.json; do | |
| pkg_dir=$(dirname $pkg) | |
| pkg_name=$(basename $pkg_dir) | |
| if [ ! -d "$pkg_dir/dist" ]; then | |
| echo "❌ Error: Missing dist folder for $pkg_name" | |
| exit 1 | |
| fi | |
| echo "✅ $pkg_name: dist folder exists" | |
| done | |
| echo "Checking for suspicious files in dist folders..." | |
| if find packages/*/dist -type f \( -name "*.sh" -o -name "*.exe" -o -name "*.bat" -o -name "*.cmd" \) | grep -q .; then | |
| echo "❌ Error: Found suspicious executable files in dist folders" | |
| find packages/*/dist -type f \( -name "*.sh" -o -name "*.exe" -o -name "*.bat" -o -name "*.cmd" \) | |
| exit 1 | |
| fi | |
| echo "✅ No suspicious files found" | |
| echo "Verifying package sizes are reasonable..." | |
| for dist_dir in packages/*/dist; do | |
| size=$(du -sh "$dist_dir" | cut -f1) | |
| echo " $(basename $(dirname $dist_dir)): $size" | |
| done | |
| - name: Publish to npm with provenance | |
| id: npm_publish | |
| if: inputs.dry_run == false | |
| run: | | |
| echo "Publishing packages to npm with provenance attestations..." | |
| pnpm -r publish --access public --no-git-checks --tag ${{ steps.release-params.outputs.tag }} --provenance | |
| # NOTE: No NODE_AUTH_TOKEN needed - npm CLI auto-detects OIDC environment for Trusted Publishing | |
| - name: Dry run - Skip npm publish | |
| if: inputs.dry_run == true | |
| run: | | |
| echo "🧪 DRY RUN: Would publish packages with tag '${{ steps.release-params.outputs.tag }}'" | |
| echo "Packages that would be published:" | |
| pnpm -r exec pwd | |
| - name: Create release artifacts archive | |
| if: inputs.dry_run == false | |
| run: | | |
| mkdir -p release-artifacts | |
| for pkg in packages/*/dist; do | |
| pkg_name=$(basename $(dirname $pkg)) | |
| echo "Creating archive for $pkg_name..." | |
| tar -czf release-artifacts/${pkg_name}-${{ steps.version.outputs.version }}.tar.gz -C $(dirname $pkg) dist | |
| done | |
| echo "Build artifacts:" | |
| ls -lh release-artifacts/ | |
| - name: Upload release artifacts to GitHub Release | |
| if: inputs.dry_run == false | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ steps.version.outputs.version }} | |
| files: release-artifacts/*.tar.gz | |
| fail_on_unmatched_files: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Notify Slack on success | |
| if: success() && inputs.dry_run == false | |
| uses: slackapi/slack-github-action@v1.26.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "✅ Langfuse JS/TS SDK v${{ steps.version.outputs.version }} published to npm", | |
| "blocks": [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "✅ Langfuse JS/TS SDK Released", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Version:*\n`v${{ steps.version.outputs.version }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*npm Tag:*\n`${{ steps.release-params.outputs.tag }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Released by:*\n${{ github.actor }}" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Packages:*\n6 packages published" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "divider" | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "*📦 Published Packages:*\n• `@langfuse/client`\n• `@langfuse/core`\n• `@langfuse/tracing`\n• `@langfuse/otel`\n• `@langfuse/openai`\n• `@langfuse/langchain`" | |
| } | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "📋 View Release Notes", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }}", | |
| "style": "primary" | |
| }, | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "📦 View on npm", | |
| "emoji": true | |
| }, | |
| "url": "https://www.npmjs.com/package/@langfuse/client/v/${{ steps.version.outputs.version }}" | |
| }, | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "🔧 View Workflow", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "🔒 Published with provenance attestations • 🤖 Automated release" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_RELEASES }} | |
| SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK | |
| - name: Notify Slack on dry run success | |
| if: success() && inputs.dry_run == true | |
| uses: slackapi/slack-github-action@v1.26.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "🧪 Langfuse JS/TS SDK dry run completed for v${{ steps.version.outputs.version }}", | |
| "blocks": [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "🧪 Langfuse JS/TS SDK Dry Run", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "Release dry run completed successfully. No packages were published." | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Version:*\n`v${{ steps.version.outputs.version }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*npm Tag:*\n`${{ steps.release-params.outputs.tag }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Triggered by:*\n${{ github.actor }}" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Mode:*\nDry run (testing only)" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "🔧 View Workflow", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "style": "primary" | |
| }, | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "📁 View Repository", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "ℹ️ This was a test run. To publish, re-run without dry run mode." | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ENGINEERING }} | |
| SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK | |
| - name: Notify Slack on failure | |
| if: failure() | |
| uses: slackapi/slack-github-action@v1.26.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "❌ Langfuse JS/TS SDK release workflow failed", | |
| "blocks": [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "❌ Langfuse JS/TS SDK Release Failed", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "⚠️ The release workflow encountered an error and did not complete successfully." | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Requested Version:*\n`${{ inputs.version }}`" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Pre-release Type:*\n${{ inputs.prerelease_type || 'N/A' }}" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Triggered by:*\n${{ github.actor }}" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": "*Dry Run:*\n${{ inputs.dry_run }}" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "divider" | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "*🔍 Next Steps:*\n• Check the workflow logs for error details\n• Verify all prerequisites are met\n• Check npm Trusted Publishing configuration\n• Verify Slack webhook secrets are set" | |
| } | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "🔧 View Workflow Logs", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "style": "danger" | |
| }, | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": "📖 View Documentation", | |
| "emoji": true | |
| }, | |
| "url": "${{ github.server_url }}/${{ github.repository }}/blob/main/CONTRIBUTING.md" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": "🚨 Action required • Check workflow logs for details" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ENGINEERING }} | |
| SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK | |
| - name: Rollback notification on partial failure | |
| if: failure() && steps.npm_publish.outcome == 'success' | |
| run: | | |
| echo "⚠️ CRITICAL: npm publish succeeded but subsequent steps failed" | |
| echo "Published version: v${{ steps.version.outputs.version }}" | |
| echo "Manual intervention may be required" | |
| echo "" | |
| echo "Options:" | |
| echo "1. Re-run the workflow to complete GitHub release creation" | |
| echo "2. Manually create GitHub release for tag v${{ steps.version.outputs.version }}" | |
| echo "3. If necessary, deprecate the npm version: npm deprecate @langfuse/package@${{ steps.version.outputs.version }}" |