Repository navigation
feat: add manual beta releases from any branch #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,303 @@ | ||
| # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json | ||
| # Setup on @langfuse/cli: trusted publisher beta-release.yml with environment | ||
| # npm-publish-beta. Allow source branches and require release reviewers on this | ||
| # environment: dispatch executes workflow code from the selected branch. | ||
| # Run via Actions > Beta release > Run workflow, then select the source branch. | ||
| # Merge this workflow into the default branch to expose the manual run button. | ||
| name: Beta release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| dry_run: | ||
| description: Run all checks and show the version without publishing | ||
| type: boolean | ||
| default: true | ||
|
|
||
| permissions: {} | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| # Serialize beta version allocation and publishing across manual runs. | ||
| concurrency: | ||
| group: npm-beta-publish | ||
| queue: max | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| beta-version: | ||
| name: Select beta version | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| outputs: | ||
| version: ${{ steps.plan.outputs.version }} | ||
| skip: ${{ steps.plan.outputs.skip }} | ||
| steps: | ||
| - name: Checkout selected commit | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
| persist-credentials: false | ||
| - name: Set up Bun | ||
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | ||
| with: | ||
| bun-version: 1.3.14 | ||
| no-cache: true | ||
| - name: Install dependencies | ||
| run: bun ci | ||
| - name: Allocate beta version or recognize a retry | ||
| id: plan | ||
| run: bun scripts/beta-release.ts >> "$GITHUB_OUTPUT" | ||
|
|
||
| preparation: | ||
| name: Prepare beta release | ||
| needs: beta-version | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| outputs: | ||
| version: ${{ needs.beta-version.outputs.version }} | ||
| skip: ${{ needs.beta-version.outputs.skip }} | ||
| dist-tag: ${{ steps.plan.outputs.dist-tag }} | ||
| artifact-id: ${{ steps.upload.outputs.artifact-id }} | ||
| env: | ||
| TAG_NAME: ${{ format('v{0}', needs.beta-version.outputs.version) }} | ||
| IS_PRERELEASE: "true" | ||
| RELEASE_SHA: ${{ github.sha }} | ||
| steps: | ||
| - name: Checkout selected commit | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Bun | ||
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | ||
| with: | ||
| bun-version: 1.3.14 | ||
| # This workflow publishes an artifact; never restore caches into it. | ||
| no-cache: true | ||
|
|
||
| - name: Install dependencies | ||
| run: bun ci | ||
|
|
||
| - name: Set beta package version in the runner | ||
| env: | ||
| BETA_VERSION: ${{ needs.beta-version.outputs.version }} | ||
| run: bun scripts/beta-release.ts --apply | ||
|
|
||
| - name: Verify beta release policy and derive publish plan | ||
| id: plan | ||
| run: | | ||
| set -euo pipefail | ||
| RELEASE_PLAN="$(bun scripts/beta-release.ts --plan)" | ||
| DIST_TAG="$(jq -r '.distTag' <<< "$RELEASE_PLAN")" | ||
| echo "DIST_TAG=$DIST_TAG" >> "$GITHUB_ENV" | ||
| echo "FINAL_PLAN=$RELEASE_PLAN" >> "$GITHUB_ENV" | ||
| echo "dist-tag=$DIST_TAG" >> "$GITHUB_OUTPUT" | ||
| echo "Selected npm dist-tag $DIST_TAG" | ||
| jq '.packages' <<< "$RELEASE_PLAN" | ||
|
|
||
| - name: Typecheck | ||
| run: bun run typecheck | ||
|
|
||
| - name: Run tests | ||
| run: bun test | ||
|
|
||
| - name: Build and fake-call every endpoint in every API snapshot | ||
| run: bun run conformance:all | ||
|
|
||
| - name: Set up Node | ||
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: 24 | ||
| # This workflow publishes an artifact; never restore caches into it. | ||
| package-manager-cache: false | ||
|
|
||
| - name: Verify npm supports trusted publishing (requires npm >= 11.5.1) | ||
| run: | | ||
| set -euo pipefail | ||
| NPM_VERSION="$(npm --version)" | ||
| MINIMUM="11.5.1" | ||
| if [ "$(printf '%s\n' "$MINIMUM" "$NPM_VERSION" | sort -V | head -n1)" != "$MINIMUM" ]; then | ||
| echo "::error::npm $NPM_VERSION is too old for trusted publishing (need >= $MINIMUM)" | ||
| exit 1 | ||
| fi | ||
| echo "npm $NPM_VERSION" | ||
|
|
||
| # Stage both package roots from the one verified build. The canonical | ||
| # package receives README.md; the legacy package receives the migration | ||
| # README under that same filename on npm. | ||
| - name: Prepare canonical and legacy npm packages | ||
| run: | | ||
| set -euo pipefail | ||
| PACKAGE_ROOT="$GITHUB_WORKSPACE/.npm-packages" | ||
| bun scripts/npm-packages.ts "$PACKAGE_ROOT" "$RELEASE_SHA" | ||
| echo "PACKAGE_ROOT=$PACKAGE_ROOT" >> "$GITHUB_ENV" | ||
|
|
||
| # npm publish does not verify tarball completeness; a files-array, | ||
| # README selection, or build-layout regression must fail here. | ||
| - name: Verify both packed tarballs | ||
| run: | | ||
| set -euo pipefail | ||
| for package in canonical legacy; do | ||
| FILES="$(npm pack --dry-run --json --ignore-scripts "$PACKAGE_ROOT/$package" | jq -r '.[0].files[].path')" | ||
| for required in LICENSE bin/langfuse.mjs dist/cli.js dist/contracts/catalog.json README.md; do | ||
| if ! grep -qx "$required" <<< "$FILES"; then | ||
| echo "::error::$required is missing from the $package npm tarball" | ||
| exit 1 | ||
| fi | ||
| done | ||
| done | ||
| test "$(jq -r '.name' "$PACKAGE_ROOT/canonical/package.json")" = "@langfuse/cli" | ||
| test "$(jq -r '.name' "$PACKAGE_ROOT/legacy/package.json")" = "langfuse-cli" | ||
| cmp README.md "$PACKAGE_ROOT/canonical/README.md" | ||
| cmp npm/legacy/README.md "$PACKAGE_ROOT/legacy/README.md" | ||
| diff -qr "$PACKAGE_ROOT/canonical/bin" "$PACKAGE_ROOT/legacy/bin" | ||
| diff -qr "$PACKAGE_ROOT/canonical/dist" "$PACKAGE_ROOT/legacy/dist" | ||
| echo "Both tarballs verified." | ||
|
|
||
| - name: Smoke-test the built CLI under Node and Bun | ||
| run: | | ||
| set -euo pipefail | ||
| node bin/langfuse.mjs --version | ||
| bun bin/langfuse.mjs --help > /dev/null | ||
| echo "Smoke test passed." | ||
|
|
||
| - name: Pack canonical package for publishing | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p "$RUNNER_TEMP/beta-release" | ||
| TARBALL="$(npm pack "$PACKAGE_ROOT/canonical" --ignore-scripts --json --pack-destination "$RUNNER_TEMP/beta-release" | jq -r '.[0].filename')" | ||
| mv "$RUNNER_TEMP/beta-release/$TARBALL" "$RUNNER_TEMP/beta-release/package.tgz" | ||
|
|
||
| - name: Upload prepared package | ||
| id: upload | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: beta-package-${{ github.run_attempt }} | ||
| path: ${{ runner.temp }}/beta-release/package.tgz | ||
| if-no-files-found: error | ||
| retention-days: 7 | ||
|
|
||
| - name: Report release plan | ||
| run: | | ||
| set -euo pipefail | ||
| VERSION="$(jq -r '.version' <<< "$FINAL_PLAN")" | ||
| { | ||
| echo "### Release plan" | ||
| echo "" | ||
| echo "Version: $VERSION" | ||
| echo "" | ||
| echo "npm dist-tag: $DIST_TAG" | ||
| echo "" | ||
| jq -r '.packages[] | "- \(.name): \(.status)"' <<< "$FINAL_PLAN" | ||
| echo "- langfuse-cli: validated, publishing disabled" | ||
| } | tee -a "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| publish: | ||
| name: Publish beta to npm | ||
| needs: preparation | ||
| if: ${{ !inputs.dry_run && needs.preparation.outputs.skip == 'false' }} | ||
| runs-on: ubuntu-latest | ||
| environment: npm-publish-beta | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| env: | ||
| TAG_NAME: ${{ format('v{0}', needs.preparation.outputs.version) }} | ||
| IS_PRERELEASE: "true" | ||
| RELEASE_SHA: ${{ github.sha }} | ||
| DIST_TAG: ${{ needs.preparation.outputs.dist-tag }} | ||
| steps: | ||
| - name: Checkout selected commit | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Bun | ||
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | ||
| with: | ||
| bun-version: 1.3.14 | ||
| # This workflow publishes an artifact; never restore caches into it. | ||
| no-cache: true | ||
|
|
||
| - name: Install dependencies | ||
| run: bun ci | ||
|
|
||
| - name: Set beta package version in the runner | ||
| env: | ||
| BETA_VERSION: ${{ needs.preparation.outputs.version }} | ||
| run: bun scripts/beta-release.ts --apply | ||
|
|
||
| - name: Set up Node (bundles the npm that performs the publish) | ||
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: 24 | ||
| # This workflow publishes an artifact; never restore caches into it. | ||
| package-manager-cache: false | ||
|
|
||
| - name: Verify npm supports trusted publishing (requires npm >= 11.5.1) | ||
| run: | | ||
| set -euo pipefail | ||
| NPM_VERSION="$(npm --version)" | ||
| MINIMUM="11.5.1" | ||
| if [ "$(printf '%s\n' "$MINIMUM" "$NPM_VERSION" | sort -V | head -n1)" != "$MINIMUM" ]; then | ||
| echo "::error::npm $NPM_VERSION is too old for trusted publishing (need >= $MINIMUM)" | ||
| exit 1 | ||
| fi | ||
| echo "npm $NPM_VERSION" | ||
|
|
||
| - name: Download prepared package | ||
| uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 | ||
| with: | ||
| artifact-ids: ${{ needs.preparation.outputs.artifact-id }} | ||
| path: ${{ runner.temp }}/beta-release | ||
|
|
||
| # The early guard ran before ~5 minutes of gates; re-check against the | ||
| # registry state at publish time so changes made outside this workflow | ||
| # cannot slip a dist-tag backwards. | ||
| - name: Re-verify release policy immediately before publish | ||
| run: | | ||
| set -euo pipefail | ||
| FINAL_PLAN="$(bun scripts/beta-release.ts --plan)" | ||
| FINAL_TAG="$(jq -r '.distTag' <<< "$FINAL_PLAN")" | ||
| if [ "$FINAL_TAG" != "$DIST_TAG" ]; then | ||
| echo "::error::dist-tag changed between verification and publish ($DIST_TAG -> $FINAL_TAG)" | ||
| exit 1 | ||
| fi | ||
| echo "FINAL_PLAN=$FINAL_PLAN" >> "$GITHUB_ENV" | ||
| jq '.packages' <<< "$FINAL_PLAN" | ||
|
|
||
| # Publish the package built and checked by preparation without rebuilding it. | ||
| - name: Publish @langfuse/cli to npm | ||
| run: | | ||
| set -euo pipefail | ||
| STATUS="$(jq -r '.packages.canonical.status' <<< "$FINAL_PLAN")" | ||
| VERSION="$(jq -r '.version' <<< "$FINAL_PLAN")" | ||
| if [ "$STATUS" = "already-published" ]; then | ||
| echo "@langfuse/cli@$VERSION was already published from this release; skipping." | ||
| else | ||
| npm publish "$RUNNER_TEMP/beta-release/package.tgz" --ignore-scripts --tag "$DIST_TAG" | ||
| fi | ||
|
|
||
| - name: Verify beta publish reached npm | ||
| run: | | ||
| set -euo pipefail | ||
| EXPECTED_VERSION="$(jq -r '.version' <<< "$FINAL_PLAN")" | ||
| for attempt in 1 2 3 4 5; do | ||
| if PUBLISHED_PLAN="$(bun scripts/beta-release.ts --plan)" && | ||
| [ "$(jq -r '.packages.canonical.status' <<< "$PUBLISHED_PLAN")" = "already-published" ] && | ||
| [ "$(npm view "@langfuse/cli@$DIST_TAG" version)" = "$EXPECTED_VERSION" ]; then | ||
| jq '.packages' <<< "$PUBLISHED_PLAN" | ||
| echo "Beta npm package verified." | ||
| exit 0 | ||
| fi | ||
| sleep "$((attempt * 2))" | ||
| done | ||
| echo "::error::could not verify beta package from release commit $RELEASE_SHA" | ||
| exit 1 | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.