Merge pull request #106 from Lythaeon/fix/ts-sdk-windows-publish-shell #11
Workflow file for this run
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 TypeScript SDK | |
| on: | |
| push: | |
| tags: | |
| - "ts-sdk-v*.*.*" | |
| workflow_dispatch: | |
| inputs: | |
| publish: | |
| description: "Publish to npm instead of packing artifacts only" | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| concurrency: | |
| group: release-typescript-sdk-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| NODE_VERSION: "22" | |
| SDK_DIRECTORY: sdks/typescript | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| outputs: | |
| sdk_version: ${{ steps.sdk-version.outputs.value }} | |
| publish_release: ${{ steps.release-mode.outputs.value }} | |
| sdk_already_published: ${{ steps.sdk-presence.outputs.value }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.28.2 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| registry-url: https://registry.npmjs.org | |
| - name: Decide release mode | |
| id: release-mode | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then | |
| echo "value=true" >> "${GITHUB_OUTPUT}" | |
| elif [[ "${{ inputs.publish }}" == "true" ]]; then | |
| echo "value=true" >> "${GITHUB_OUTPUT}" | |
| else | |
| echo "value=false" >> "${GITHUB_OUTPUT}" | |
| fi | |
| - name: Read SDK version | |
| id: sdk-version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| value="$(node -p "require('./${SDK_DIRECTORY}/package.json').version")" | |
| echo "value=${value}" >> "${GITHUB_OUTPUT}" | |
| - name: Verify tagged commit is on main | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git fetch --no-tags origin main | |
| if ! git merge-base --is-ancestor "${GITHUB_SHA}" "FETCH_HEAD"; then | |
| echo "Tag ${GITHUB_REF_NAME} points to commit ${GITHUB_SHA} which is not on origin/main" | |
| exit 1 | |
| fi | |
| - name: Verify tag matches SDK version | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| expected_tag="ts-sdk-v${{ steps.sdk-version.outputs.value }}" | |
| if [[ "${GITHUB_REF_NAME}" != "${expected_tag}" ]]; then | |
| echo "Expected tag ${expected_tag}, got ${GITHUB_REF_NAME}" | |
| exit 1 | |
| fi | |
| - name: Check whether SDK package version already exists on npm | |
| id: sdk-presence | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${{ steps.release-mode.outputs.value }}" != "true" ]]; then | |
| echo "value=false" >> "${GITHUB_OUTPUT}" | |
| exit 0 | |
| fi | |
| package_name="$(node -p "require('./${SDK_DIRECTORY}/package.json').name")" | |
| package_version="${{ steps.sdk-version.outputs.value }}" | |
| if pnpm view "${package_name}@${package_version}" version >/dev/null 2>&1; then | |
| echo "value=true" >> "${GITHUB_OUTPUT}" | |
| echo ">>> ${package_name}@${package_version} already exists on npm; SDK verification/publish steps will skip where possible." | |
| else | |
| echo "value=false" >> "${GITHUB_OUTPUT}" | |
| fi | |
| - name: Install workspace dependencies | |
| if: steps.sdk-presence.outputs.value != 'true' | |
| run: pnpm install --frozen-lockfile | |
| - name: Run TypeScript SDK checks | |
| if: steps.sdk-presence.outputs.value != 'true' | |
| run: pnpm --dir "${SDK_DIRECTORY}" run check | |
| publish-native: | |
| needs: verify | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 60 | |
| continue-on-error: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - package_dir: sdks/typescript/native/linux-x64 | |
| package_name: "@lythaeon-sof/sdk-native-linux-x64" | |
| runner: ubuntu-24.04 | |
| - package_dir: sdks/typescript/native/linux-arm64 | |
| package_name: "@lythaeon-sof/sdk-native-linux-arm64" | |
| runner: ubuntu-24.04-arm | |
| - package_dir: sdks/typescript/native/darwin-x64 | |
| package_name: "@lythaeon-sof/sdk-native-darwin-x64" | |
| runner: macos-15-intel | |
| - package_dir: sdks/typescript/native/darwin-arm64 | |
| package_name: "@lythaeon-sof/sdk-native-darwin-arm64" | |
| runner: macos-15 | |
| - package_dir: sdks/typescript/native/win32-x64 | |
| package_name: "@lythaeon-sof/sdk-native-win32-x64" | |
| runner: windows-2025 | |
| - package_dir: sdks/typescript/native/win32-arm64 | |
| package_name: "@lythaeon-sof/sdk-native-win32-arm64" | |
| runner: windows-11-arm | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.28.2 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| registry-url: https://registry.npmjs.org | |
| - name: Check whether native runtime package version already exists on npm | |
| id: native-package-presence | |
| if: needs.verify.outputs.publish_release == 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| package_name="${{ matrix.package_name }}" | |
| package_version="${{ needs.verify.outputs.sdk_version }}" | |
| if pnpm view "${package_name}@${package_version}" version >/dev/null 2>&1; then | |
| echo "value=true" >> "${GITHUB_OUTPUT}" | |
| echo ">>> ${package_name}@${package_version} already exists on npm; skipping native runtime build/publish." | |
| else | |
| echo "value=false" >> "${GITHUB_OUTPUT}" | |
| fi | |
| - name: Setup Rust CI toolchain and tools | |
| if: needs.verify.outputs.publish_release != 'true' || steps.native-package-presence.outputs.value != 'true' | |
| uses: ./.github/actions/rust-ci-setup | |
| with: | |
| cargo-tools: cargo-make,cargo-nextest | |
| - name: Install workspace dependencies | |
| if: needs.verify.outputs.publish_release != 'true' || steps.native-package-presence.outputs.value != 'true' | |
| run: pnpm install --frozen-lockfile | |
| - name: Publish native runtime package | |
| if: needs.verify.outputs.publish_release == 'true' && steps.native-package-presence.outputs.value != 'true' | |
| working-directory: ${{ matrix.package_dir }} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| shell: bash | |
| run: pnpm publish --access public --no-git-checks | |
| - name: Pack native runtime package | |
| if: needs.verify.outputs.publish_release != 'true' | |
| working-directory: ${{ matrix.package_dir }} | |
| shell: bash | |
| run: pnpm pack --pack-destination ../../../dist/npm | |
| publish-sdk: | |
| if: ${{ !cancelled() && needs.verify.result == 'success' }} | |
| needs: | |
| - verify | |
| - publish-native | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.28.2 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| registry-url: https://registry.npmjs.org | |
| - name: Check whether SDK package version already exists on npm | |
| id: sdk-package-presence | |
| if: needs.verify.outputs.publish_release == 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| package_name="$(node -p "require('./${SDK_DIRECTORY}/package.json').name")" | |
| package_version="${{ needs.verify.outputs.sdk_version }}" | |
| if pnpm view "${package_name}@${package_version}" version >/dev/null 2>&1; then | |
| echo "value=true" >> "${GITHUB_OUTPUT}" | |
| echo ">>> ${package_name}@${package_version} already exists on npm; skipping SDK install/publish." | |
| else | |
| echo "value=false" >> "${GITHUB_OUTPUT}" | |
| fi | |
| - name: Check native publish job conclusions | |
| id: native-publish-status | |
| if: needs.verify.outputs.publish_release == 'true' && steps.sdk-package-presence.outputs.value != 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| jobs_api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" | |
| jobs_json="$(curl --fail --silent --show-error \ | |
| -H "Authorization: Bearer ${GITHUB_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "${jobs_api}")" | |
| cancelled="$(node -e ' | |
| const payload = JSON.parse(process.argv[1]); | |
| const nativeJobs = payload.jobs.filter((job) => job.name.startsWith("publish-native (")); | |
| process.stdout.write(nativeJobs.some((job) => job.conclusion === "cancelled") ? "true" : "false"); | |
| ' "${jobs_json}")" | |
| echo "cancelled=${cancelled}" >> "${GITHUB_OUTPUT}" | |
| if [[ "${cancelled}" == "true" ]]; then | |
| echo "::warning::Skipping SDK publish because one or more native publish jobs were cancelled." | |
| fi | |
| - name: Install workspace dependencies | |
| if: needs.verify.outputs.publish_release != 'true' || (steps.sdk-package-presence.outputs.value != 'true' && steps.native-publish-status.outputs.cancelled != 'true') | |
| run: pnpm install --frozen-lockfile | |
| - name: Wait for native packages to index on npm | |
| if: needs.verify.outputs.publish_release == 'true' && steps.sdk-package-presence.outputs.value != 'true' && steps.native-publish-status.outputs.cancelled != 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| trap 'echo "::warning::Cancellation received; stopping SDK publish wait."; exit 130' INT TERM | |
| version="${{ needs.verify.outputs.sdk_version }}" | |
| packages="$(node -p "JSON.stringify(Object.keys(require('./${SDK_DIRECTORY}/package.json').optionalDependencies).sort())")" | |
| missing_packages=() | |
| indexed_packages=() | |
| native_jobs_cancelled() { | |
| local jobs_api | |
| local jobs_json | |
| jobs_api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" | |
| jobs_json="$(curl --fail --silent --show-error \ | |
| -H "Authorization: Bearer ${GITHUB_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "${jobs_api}")" | |
| node -e ' | |
| const payload = JSON.parse(process.argv[1]); | |
| const nativeJobs = payload.jobs.filter((job) => job.name.startsWith("publish-native (")); | |
| process.exit(nativeJobs.some((job) => job.conclusion === "cancelled") ? 0 : 1); | |
| ' "${jobs_json}" | |
| } | |
| wait_until_indexed() { | |
| local pkg="$1" | |
| local attempt | |
| for attempt in $(seq 1 30); do | |
| if native_jobs_cancelled; then | |
| echo "::warning::Stopping SDK publish wait because one or more native publish jobs were cancelled." | |
| return 2 | |
| fi | |
| if pnpm view "${pkg}@${version}" version >/dev/null 2>&1; then | |
| echo ">>> ${pkg}@${version} is visible on npm" | |
| return 0 | |
| fi | |
| echo ">>> Waiting for ${pkg}@${version} to appear on npm (${attempt}/30)..." | |
| sleep 10 | |
| done | |
| echo ">>> ${pkg}@${version} did not appear on npm in time" | |
| return 1 | |
| } | |
| mapfile -t native_packages < <(node -e "for (const pkg of ${packages}) console.log(pkg)") | |
| for package_name in "${native_packages[@]}"; do | |
| if wait_until_indexed "${package_name}"; then | |
| indexed_packages+=("${package_name}") | |
| elif [[ $? -eq 2 ]]; then | |
| exit 0 | |
| else | |
| missing_packages+=("${package_name}") | |
| fi | |
| done | |
| if ((${#indexed_packages[@]} > 0)); then | |
| printf 'Indexed native packages: %s\n' "${indexed_packages[*]}" | |
| fi | |
| if ((${#missing_packages[@]} > 0)); then | |
| printf 'Missing native packages: %s\n' "${missing_packages[*]}" | |
| echo "::warning::Proceeding with SDK publish even though some native runtime packages are still missing or failed to publish." | |
| fi | |
| - name: Publish TypeScript SDK | |
| if: needs.verify.outputs.publish_release == 'true' && steps.sdk-package-presence.outputs.value != 'true' && steps.native-publish-status.outputs.cancelled != 'true' | |
| working-directory: ${{ env.SDK_DIRECTORY }} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| shell: bash | |
| run: pnpm publish --access public --no-git-checks | |
| - name: Pack TypeScript SDK | |
| if: needs.verify.outputs.publish_release != 'true' | |
| working-directory: ${{ env.SDK_DIRECTORY }} | |
| shell: bash | |
| run: pnpm pack --pack-destination ../dist/npm |