busbar-sf-agentscript 0.0.2

AgentScript parser, graph analysis, and LSP for Salesforce Agentforce
Documentation
name: Release

on:
  push:
    tags:
      - "v[0-9]+.[0-9]+.[0-9]+"        # stable:     v0.1.0
      - "v[0-9]+.[0-9]+.[0-9]+-*"      # pre-release: v0.1.0-beta.3
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release (e.g. v0.0.1 or v0.0.1-beta.3)"
        required: false

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

permissions:
  contents: write
  id-token: write

env:
  RUST_BACKTRACE: 1
  CARGO_TERM_COLOR: always

jobs:
  validate:
    name: Validate release tag
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      is_prerelease: ${{ steps.version.outputs.is_prerelease }}
      npm_tag: ${{ steps.version.outputs.npm_tag }}
    steps:
      - uses: actions/checkout@v4
      - name: Extract version
        id: version
        run: |
          TAG="${{ github.ref_name }}"
          if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.tag }}" ]]; then
            TAG="${{ github.event.inputs.tag }}"
          fi
          VERSION="${TAG#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          if [[ "$VERSION" == *-* ]]; then
            echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
            NPM_TAG="${VERSION#*-}"   # e.g. beta.3
            NPM_TAG="${NPM_TAG%%.*}"  # e.g. beta
            echo "npm_tag=$NPM_TAG" >> "$GITHUB_OUTPUT"
          else
            echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
            echo "npm_tag=latest" >> "$GITHUB_OUTPUT"
          fi
          echo "Releasing version $VERSION (npm tag: ${NPM_TAG:-latest})"

  publish-crates:
    name: Publish crates to crates.io
    runs-on: ubuntu-latest
    needs: [validate]
    if: needs.validate.outputs.is_prerelease == 'false'
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Publish busbar-sf-agentscript
        run: cargo publish -p busbar-sf-agentscript
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
      - name: Wait for index propagation
        run: sleep 20
      - name: Publish busbar-sf-agentscript-lsp
        run: cargo publish -p busbar-sf-agentscript-lsp
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  build-lsp:
    name: Build LSP binary / ${{ matrix.target }}
    runs-on: ${{ matrix.runner }}
    needs: [validate]
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            runner: ubuntu-latest
          - target: aarch64-apple-darwin
            runner: macos-latest
          - target: x86_64-pc-windows-msvc
            runner: windows-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}
      - name: Build
        run: cargo build -p busbar-sf-agentscript-lsp --release --target ${{ matrix.target }}
      - name: Rename binary (Unix)
        if: matrix.target != 'x86_64-pc-windows-msvc'
        run: |
          cp target/${{ matrix.target }}/release/busbar-sf-agentscript-lsp \
             busbar-sf-agentscript-lsp-${{ matrix.target }}
      - name: Rename binary (Windows)
        if: matrix.target == 'x86_64-pc-windows-msvc'
        shell: pwsh
        run: |
          Copy-Item "target/${{ matrix.target }}/release/busbar-sf-agentscript-lsp.exe" `
                    "busbar-sf-agentscript-lsp-${{ matrix.target }}.exe"
      - name: Upload LSP binary artifact (Unix)
        if: matrix.target != 'x86_64-pc-windows-msvc'
        uses: actions/upload-artifact@v4
        with:
          name: lsp-${{ matrix.target }}
          path: busbar-sf-agentscript-lsp-${{ matrix.target }}
          if-no-files-found: error
      - name: Upload LSP binary artifact (Windows)
        if: matrix.target == 'x86_64-pc-windows-msvc'
        uses: actions/upload-artifact@v4
        with:
          name: lsp-${{ matrix.target }}
          path: busbar-sf-agentscript-lsp-${{ matrix.target }}.exe
          if-no-files-found: error

  build-zed-extension:
    name: Build Zed extension
    runs-on: ubuntu-latest
    needs: [validate]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-wasip2
      - uses: Swatinem/rust-cache@v2
        with:
          key: zed-wasm32-wasip2
          workspaces: zed-extension
      - name: Build Zed extension
        run: cargo build --release --manifest-path zed-extension/Cargo.toml --target wasm32-wasip2
      - name: Copy extension.wasm
        run: |
          cp zed-extension/target/wasm32-wasip2/release/busbar_sf_agentscript_zed.wasm \
             zed-extension/extension.wasm
      - uses: actions/upload-artifact@v4
        with:
          name: zed-extension
          path: |
            zed-extension/extension.wasm
            zed-extension/extension.toml
          if-no-files-found: error

  build-wasm:
    name: Build WASM
    runs-on: ubuntu-latest
    needs: [validate]
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown
      - uses: Swatinem/rust-cache@v2
      - name: Install wasm-bindgen-cli
        run: |
          VERSION=$(cargo metadata --format-version 1 | jq -r '.packages[] | select(.name == "wasm-bindgen") | .version')
          cargo install wasm-bindgen-cli --version "$VERSION" --locked
      - name: Build WASM package
        run: |
          cargo build --lib --release --target wasm32-unknown-unknown --features wasm,graph
          mkdir -p dist/wasm
          wasm-bindgen --target web --out-dir dist/wasm \
            target/wasm32-unknown-unknown/release/busbar_sf_agentscript.wasm
          node -e "
            const fs = require('fs');
            fs.writeFileSync('dist/wasm/package.json', JSON.stringify({
              name: '@muselab/busbar-sf-agentscript',
              version: '${{ needs.validate.outputs.version }}',
              files: ['busbar_sf_agentscript_bg.wasm','busbar_sf_agentscript.js','busbar_sf_agentscript.d.ts','busbar_sf_agentscript_bg.wasm.d.ts'],
              main: 'busbar_sf_agentscript.js',
              types: 'busbar_sf_agentscript.d.ts',
              sideEffects: false
            }, null, 2));
          "
      - uses: actions/upload-artifact@v4
        with:
          name: wasm
          path: dist/wasm/
          if-no-files-found: error

  package-vscode:
    name: Package VS Code extension / ${{ matrix.vsce_target }}
    runs-on: ubuntu-latest
    needs: [validate, build-lsp]
    strategy:
      fail-fast: false
      matrix:
        include:
          - vsce_target: linux-x64
            lsp_target: x86_64-unknown-linux-gnu
            lsp_ext: ""
          - vsce_target: darwin-arm64
            lsp_target: aarch64-apple-darwin
            lsp_ext: ""
          - vsce_target: win32-x64
            lsp_target: x86_64-pc-windows-msvc
            lsp_ext: ".exe"
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
          cache-dependency-path: packages/package-lock.json
      - name: Download LSP binary
        uses: actions/download-artifact@v4
        with:
          name: lsp-${{ matrix.lsp_target }}
          path: packages/bin/
      - name: Make LSP binary executable
        if: matrix.lsp_ext == ''
        run: chmod +x packages/bin/busbar-sf-agentscript-lsp-${{ matrix.lsp_target }}
      - name: Rename LSP binary to expected name
        run: |
          mv packages/bin/busbar-sf-agentscript-lsp-${{ matrix.lsp_target }}${{ matrix.lsp_ext }} \
             packages/bin/busbar-sf-agentscript-lsp${{ matrix.lsp_ext }}
      - name: Install dependencies
        working-directory: packages/
        run: npm ci
      - name: Compile extension
        working-directory: packages/
        run: npm run compile
      - name: Package VSIX
        working-directory: packages/
        run: |
          npx vsce package --target ${{ matrix.vsce_target }} \
            --out vscode-agentscript-${{ needs.validate.outputs.version }}-${{ matrix.vsce_target }}.vsix
      - name: Upload VSIX artifact
        uses: actions/upload-artifact@v4
        with:
          name: vsix-${{ matrix.vsce_target }}
          path: packages/vscode-agentscript-${{ needs.validate.outputs.version }}-${{ matrix.vsce_target }}.vsix
          if-no-files-found: error
      - name: Publish to VS Code Marketplace
        working-directory: packages/
        run: npx vsce publish --target ${{ matrix.vsce_target }} --pat ${{ secrets.VS_CODE_MARKETPLACE_TOKEN }}

  publish-vscode-marketplace:
    name: VS Code Marketplace publish complete
    runs-on: ubuntu-latest
    needs: [package-vscode]
    steps:
      - run: echo "All VS Code platform packages published"

  publish-tree-sitter-npm:
    name: Publish tree-sitter-agentscript to npm
    runs-on: ubuntu-latest
    needs: [validate]
    defaults:
      run:
        working-directory: tree-sitter-agentscript/
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"
          cache: npm
          cache-dependency-path: tree-sitter-agentscript/package-lock.json
      - run: npm ci
      - run: npm run build
      - run: npm publish --access public --tag ${{ needs.validate.outputs.npm_tag }}

  publish-wasm-npm:
    name: Publish WASM to npm
    runs-on: ubuntu-latest
    needs: [validate, build-wasm]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"
      - name: Download WASM artifact
        uses: actions/download-artifact@v4
        with:
          name: wasm
          path: dist/wasm/
      - name: Publish to npm
        working-directory: dist/wasm/
        run: npm publish --access public --tag ${{ needs.validate.outputs.npm_tag }}

  publish-sf-plugin-npm:
    name: Publish SF plugin to npm
    runs-on: ubuntu-latest
    needs: [validate, publish-wasm-npm]
    defaults:
      run:
        working-directory: plugin-agency/
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"
      - name: Wait for WASM package to be available on npm
        run: |
          VERSION="${{ needs.validate.outputs.version }}"
          timeout=120
          interval=5
          elapsed=0
          until npm view @muselab/busbar-sf-agentscript@$VERSION version 2>/dev/null; do
            if [ $elapsed -ge $timeout ]; then
              echo "Timed out waiting for @muselab/busbar-sf-agentscript@$VERSION on npm"
              exit 1
            fi
            echo "Waiting for @muselab/busbar-sf-agentscript@$VERSION... (${elapsed}s / ${timeout}s)"
            sleep $interval
            elapsed=$((elapsed + interval))
          done
          echo "@muselab/busbar-sf-agentscript@$VERSION is available"
      - name: Update WASM dependency to published npm version
        run: |
          node -e "
            const fs = require('fs');
            const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
            pkg.dependencies['@muselab/busbar-sf-agentscript'] = '${{ needs.validate.outputs.version }}';
            fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
          "
      - name: Install dependencies
        run: npm install
      - name: Build
        run: npm run build
      - name: Publish to npm
        run: npm publish --access public --tag ${{ needs.validate.outputs.npm_tag }}

  github-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs:
      - validate
      - build-lsp
      - build-wasm
      - build-zed-extension
      - package-vscode
      - publish-tree-sitter-npm
      - publish-wasm-npm
      - publish-sf-plugin-npm
    steps:
      - uses: actions/checkout@v4
      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: release-artifacts/
      - name: Flatten artifact directories
        run: |
          mkdir -p release-upload
          find release-artifacts/ -type f \
            \( -name "*.vsix" -o -name "busbar-sf-agentscript-lsp-*" \) \
            -exec cp {} release-upload/ \;
          # WASM artifact as zip bundle
          cd release-artifacts/wasm && zip -r ../../release-upload/wasm.zip . && cd ../..
          # Zed extension artifact
          cd release-artifacts/zed-extension && zip -r ../../release-upload/zed-extension.zip . && cd ../..

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.ref_name }}
          generate_release_notes: true
          files: release-upload/*