chore(repo): release prep 0.9.8 (#240) #67
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 | |
| # Builds all targets, runs tests, publishes to npm, and creates a GitHub Release. | |
| # Trigger by pushing a tag: git tag release/0.1.0 && git push origin refs/tags/release/0.1.0 | |
| # Prep PRs use branch release-prep/X.Y.Z (see scripts/RELEASE.md) — not the same ref as the tag. | |
| # | |
| # Native reuse: prefer a validated native-main-bundle from a successful Build & Test (main) | |
| # run (exact SHA, then matching aggregate fingerprint). Missing/invalid → rebuild matrix. | |
| # Integration tests skip only when this exact SHA already passed main. | |
| on: | |
| push: | |
| tags: ['release/*'] | |
| permissions: | |
| contents: write | |
| packages: read | |
| id-token: write | |
| actions: write | |
| pull-requests: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: 0 | |
| CI_IMAGE: ghcr.io/${{ github.repository }}/ci-build:latest | |
| CI_IMAGE_ALPINE: ghcr.io/${{ github.repository }}/ci-build-alpine:latest | |
| CMAKE_POLICY_VERSION_MINIMUM: '3.5' | |
| OPUS_STATIC: '1' | |
| NATIVE_TOOL_MODE: declared | |
| jobs: | |
| validate-package-lock: | |
| name: Package-lock optional bindings | |
| runs-on: self-hosted | |
| steps: | |
| - name: Prepare workspace | |
| run: | | |
| if [[ -n "${RUNNER_WORKSPACE:-}" && -d "${RUNNER_WORKSPACE}" ]]; then | |
| docker run --rm \ | |
| -v "${RUNNER_WORKSPACE}:${RUNNER_WORKSPACE}" \ | |
| alpine:3.20 \ | |
| chown -R "$(id -u):$(id -g)" "${RUNNER_WORKSPACE}" || true | |
| fi | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - uses: ./.github/actions/validate-package-lock | |
| quality: | |
| name: Typecheck & lint | |
| needs: validate-package-lock | |
| runs-on: self-hosted | |
| steps: | |
| - name: Prepare workspace | |
| run: | | |
| if [[ -n "${RUNNER_WORKSPACE:-}" && -d "${RUNNER_WORKSPACE}" ]]; then | |
| docker run --rm \ | |
| -v "${RUNNER_WORKSPACE}:${RUNNER_WORKSPACE}" \ | |
| alpine:3.20 \ | |
| chown -R "$(id -u):$(id -g)" "${RUNNER_WORKSPACE}" || true | |
| fi | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - run: bash scripts/ci/run-pr-quality.sh | |
| plan: | |
| name: Plan native builds | |
| needs: [validate-package-lock, quality] | |
| runs-on: self-hosted | |
| outputs: | |
| linux_x64_matrix: ${{ steps.matrices.outputs.linux_x64_matrix }} | |
| build_linux_arm64: ${{ steps.matrices.outputs.build_linux_arm64 }} | |
| build_linux_musl: ${{ steps.matrices.outputs.build_linux_musl }} | |
| host_matrix: ${{ steps.matrices.outputs.host_matrix }} | |
| cached_linux_x64: ${{ steps.matrices.outputs.cached_linux_x64 }} | |
| cached_linux_arm64: ${{ steps.matrices.outputs.cached_linux_arm64 }} | |
| cached_host: ${{ steps.matrices.outputs.cached_host }} | |
| all_cached: ${{ steps.matrices.outputs.all_cached }} | |
| main_validated: ${{ steps.main.outputs.main_validated }} | |
| bundle_reused: ${{ steps.resolve.outputs.bundle_reused }} | |
| bundle_run_id: ${{ steps.resolve.outputs.run_id }} | |
| bundle_exact_sha: ${{ steps.resolve.outputs.exact_sha }} | |
| aggregate_digest: ${{ steps.resolve.outputs.aggregate_digest }} | |
| fallback_reason: ${{ steps.resolve.outputs.fallback_reason }} | |
| steps: | |
| - name: Prepare workspace | |
| run: | | |
| if [[ -n "${RUNNER_WORKSPACE:-}" && -d "${RUNNER_WORKSPACE}" ]]; then | |
| docker run --rm \ | |
| -v "${RUNNER_WORKSPACE}:${RUNNER_WORKSPACE}" \ | |
| alpine:3.20 \ | |
| chown -R "$(id -u):$(id -g)" "${RUNNER_WORKSPACE}" || true | |
| fi | |
| - uses: actions/checkout@v4 | |
| # Bundle resolution computes the current contract with `cargo metadata` | |
| # inside CI_IMAGE before the fallback planner composite action runs. | |
| - name: Resolve native-main-bundle from successful main | |
| id: resolve | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| NATIVE_TOOL_MODE: declared | |
| CARGO_VIA_CI_IMAGE: '1' | |
| run: bash scripts/ci/resolve-native-main-bundle.sh --profile release | |
| - name: Plan per-target cache rebuilds (fallback / accelerator) | |
| id: plan | |
| if: steps.resolve.outputs.bundle_reused != 'true' | |
| uses: ./.github/actions/plan-native-builds | |
| with: | |
| check_main_success: 'false' | |
| - name: Check main CI success (exact SHA — test skip gate) | |
| id: main | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash scripts/ci/check-main-ci-success.sh | |
| # Pass JSON via env — never echo "${{ … }}" with nested quotes (bash strips them | |
| # and fromJson fails, so build-host / stage-linux-x64 never start). | |
| - name: Export matrices | |
| id: matrices | |
| shell: bash | |
| env: | |
| BUNDLE_REUSED: ${{ steps.resolve.outputs.bundle_reused }} | |
| LINUX_X64_MATRIX: ${{ steps.plan.outputs.linux_x64_matrix }} | |
| BUILD_LINUX_ARM64: ${{ steps.plan.outputs.build_linux_arm64 }} | |
| BUILD_LINUX_MUSL: ${{ steps.plan.outputs.build_linux_musl }} | |
| HOST_MATRIX: ${{ steps.plan.outputs.host_matrix }} | |
| CACHED_LINUX_X64: ${{ steps.plan.outputs.cached_linux_x64 }} | |
| CACHED_LINUX_ARM64: ${{ steps.plan.outputs.cached_linux_arm64 }} | |
| CACHED_HOST: ${{ steps.plan.outputs.cached_host }} | |
| ALL_CACHED_PLAN: ${{ steps.plan.outputs.all_cached }} | |
| CACHED_TARGETS: ${{ steps.plan.outputs.cached_targets }} | |
| REBUILT_TARGETS: ${{ steps.plan.outputs.rebuilt_targets }} | |
| AGGREGATE_DIGEST: ${{ steps.resolve.outputs.aggregate_digest }} | |
| PREFERENCE: ${{ steps.resolve.outputs.preference }} | |
| PRODUCER_SHA: ${{ steps.resolve.outputs.head_sha }} | |
| PRODUCER_RUN_ID: ${{ steps.resolve.outputs.run_id }} | |
| FALLBACK_REASON: ${{ steps.resolve.outputs.fallback_reason }} | |
| MAIN_VALIDATED: ${{ steps.main.outputs.main_validated }} | |
| run: | | |
| set -euo pipefail | |
| write_json_out() { | |
| local key="$1" | |
| local value="$2" | |
| echo "${key}<<EOF" >> "$GITHUB_OUTPUT" | |
| printf '%s\n' "${value}" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| } | |
| if [[ "${BUNDLE_REUSED}" == "true" ]]; then | |
| echo "linux_x64_matrix=[]" >> "$GITHUB_OUTPUT" | |
| echo "build_linux_arm64=false" >> "$GITHUB_OUTPUT" | |
| echo "build_linux_musl=false" >> "$GITHUB_OUTPUT" | |
| echo "host_matrix=[]" >> "$GITHUB_OUTPUT" | |
| echo "cached_linux_x64=[]" >> "$GITHUB_OUTPUT" | |
| echo "cached_linux_arm64=false" >> "$GITHUB_OUTPUT" | |
| echo "cached_host=[]" >> "$GITHUB_OUTPUT" | |
| echo "all_cached=true" >> "$GITHUB_OUTPUT" | |
| else | |
| write_json_out linux_x64_matrix "${LINUX_X64_MATRIX:-[]}" | |
| echo "build_linux_arm64=${BUILD_LINUX_ARM64:-false}" >> "$GITHUB_OUTPUT" | |
| echo "build_linux_musl=${BUILD_LINUX_MUSL:-false}" >> "$GITHUB_OUTPUT" | |
| write_json_out host_matrix "${HOST_MATRIX:-[]}" | |
| write_json_out cached_linux_x64 "${CACHED_LINUX_X64:-[]}" | |
| echo "cached_linux_arm64=${CACHED_LINUX_ARM64:-false}" >> "$GITHUB_OUTPUT" | |
| write_json_out cached_host "${CACHED_HOST:-[]}" | |
| echo "all_cached=${ALL_CACHED_PLAN:-false}" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "${BUNDLE_REUSED}" == "true" ]]; then | |
| all_cached_sum=true | |
| cached_sum='["*bundle*"]' | |
| rebuilt_sum='[]' | |
| else | |
| all_cached_sum="${ALL_CACHED_PLAN:-false}" | |
| cached_sum="${CACHED_TARGETS:-[]}" | |
| rebuilt_sum="${REBUILT_TARGETS:-[]}" | |
| fi | |
| SUMMARY_TITLE="Release native plan" \ | |
| AGGREGATE_DIGEST="${AGGREGATE_DIGEST}" \ | |
| ALL_CACHED="$all_cached_sum" \ | |
| CACHED_TARGETS_JSON="$cached_sum" \ | |
| REBUILT_TARGETS_JSON="$rebuilt_sum" \ | |
| BUNDLE_REUSED="${BUNDLE_REUSED}" \ | |
| PREFERENCE="${PREFERENCE}" \ | |
| PRODUCER_SHA="${PRODUCER_SHA}" \ | |
| PRODUCER_RUN_ID="${PRODUCER_RUN_ID}" \ | |
| FALLBACK_REASON="${FALLBACK_REASON}" \ | |
| MAIN_VALIDATED="${MAIN_VALIDATED}" \ | |
| bash scripts/ci/write-native-ci-summary.sh | |
| reuse-bundle: | |
| name: Stage reused native-main-bundle | |
| needs: [quality, plan] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| needs.plan.outputs.bundle_reused == 'true' | |
| runs-on: self-hosted | |
| steps: | |
| - name: Prepare workspace | |
| run: | | |
| if [[ -n "${RUNNER_WORKSPACE:-}" && -d "${RUNNER_WORKSPACE}" ]]; then | |
| docker run --rm \ | |
| -v "${RUNNER_WORKSPACE}:${RUNNER_WORKSPACE}" \ | |
| alpine:3.20 \ | |
| chown -R "$(id -u):$(id -g)" "${RUNNER_WORKSPACE}" || true | |
| fi | |
| - uses: actions/checkout@v4 | |
| - uses: ./.github/actions/ci-restore-workspace-ownership | |
| # Validation recomputes the current six-target native contract via CI_IMAGE cargo. | |
| - name: Download native-main-bundle from producer run | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: native-main-bundle | |
| path: native-main-bundle | |
| github-token: ${{ github.token }} | |
| run-id: ${{ needs.plan.outputs.bundle_run_id }} | |
| - name: Validate bundle against current contract | |
| shell: bash | |
| env: | |
| NATIVE_TOOL_MODE: declared | |
| CARGO_VIA_CI_IMAGE: '1' | |
| run: | | |
| set -euo pipefail | |
| bash scripts/ci/native-artifact-bundle.sh validate \ | |
| --bundle native-main-bundle \ | |
| --profile release \ | |
| --expect-aggregate "${{ needs.plan.outputs.aggregate_digest }}" | |
| - name: Stage bindings-* for current run | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ci-native-out | |
| python3 scripts/ci/native_build_contract.py stage-bundle \ | |
| --bundle native-main-bundle \ | |
| --output ci-native-out | |
| - name: Upload staged binding artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # upload-artifact@v4 does not support multiple names in one step; | |
| # upload each bindings-* directory via a small loop job alternative: | |
| # use a single archive artifact then expand in publish — but publish | |
| # expects bindings-* names. Use matrix via bash + multiple uploads. | |
| name: reused-native-staging | |
| path: ci-native-out/ | |
| if-no-files-found: error | |
| # Re-upload each target under the canonical bindings-<triple> name for publish/napi. | |
| - name: Upload per-target bindings artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # actions/upload-artifact cannot loop natively; use gh api? Prefer | |
| # @actions/upload-artifact via a node script is heavy. Use the | |
| # undocumented pattern: one artifact per dir with a composite approach. | |
| echo "Per-target uploads follow" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-x86_64-unknown-linux-gnu | |
| path: ci-native-out/bindings-x86_64-unknown-linux-gnu/ | |
| if-no-files-found: error | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-x86_64-unknown-linux-musl | |
| path: ci-native-out/bindings-x86_64-unknown-linux-musl/ | |
| if-no-files-found: error | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-aarch64-unknown-linux-gnu | |
| path: ci-native-out/bindings-aarch64-unknown-linux-gnu/ | |
| if-no-files-found: error | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-x86_64-apple-darwin | |
| path: ci-native-out/bindings-x86_64-apple-darwin/ | |
| if-no-files-found: error | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-aarch64-apple-darwin | |
| path: ci-native-out/bindings-aarch64-apple-darwin/ | |
| if-no-files-found: error | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-x86_64-pc-windows-msvc | |
| path: ci-native-out/bindings-x86_64-pc-windows-msvc/ | |
| if-no-files-found: error | |
| build-linux: | |
| needs: [quality, plan] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| needs.plan.outputs.bundle_reused != 'true' && | |
| needs.plan.outputs.all_cached != 'true' && | |
| ( | |
| needs.plan.outputs.linux_x64_matrix != '[]' || | |
| needs.plan.outputs.build_linux_arm64 == 'true' || | |
| needs.plan.outputs.build_linux_musl == 'true' | |
| ) | |
| uses: ./.github/workflows/reusable-build-linux.yml | |
| with: | |
| cache_prefix: v3-release-otel | |
| linux_x64_matrix: ${{ needs.plan.outputs.linux_x64_matrix }} | |
| build_linux_arm64: ${{ needs.plan.outputs.build_linux_arm64 == 'true' }} | |
| build_linux_musl: ${{ needs.plan.outputs.build_linux_musl == 'true' }} | |
| secrets: inherit | |
| build-host: | |
| needs: [quality, plan] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| needs.plan.outputs.bundle_reused != 'true' && | |
| needs.plan.outputs.all_cached != 'true' && | |
| needs.plan.outputs.host_matrix != '[]' | |
| uses: ./.github/workflows/reusable-build-host.yml | |
| with: | |
| cache_prefix: v3-release-otel | |
| host_matrix: ${{ needs.plan.outputs.host_matrix }} | |
| secrets: inherit | |
| stage-cached: | |
| needs: [quality, plan] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| needs.plan.outputs.bundle_reused != 'true' && | |
| ( | |
| needs.plan.outputs.cached_linux_x64 != '[]' || | |
| needs.plan.outputs.cached_linux_arm64 == 'true' || | |
| needs.plan.outputs.cached_host != '[]' | |
| ) | |
| uses: ./.github/workflows/reusable-stage-cached-bindings.yml | |
| with: | |
| cached_linux_x64: ${{ needs.plan.outputs.cached_linux_x64 }} | |
| cached_linux_arm64: ${{ needs.plan.outputs.cached_linux_arm64 == 'true' }} | |
| cached_host: ${{ needs.plan.outputs.cached_host }} | |
| secrets: inherit | |
| test: | |
| needs: [quality, plan, build-linux, build-host, stage-cached, reuse-bundle] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| needs.plan.outputs.main_validated != 'true' && | |
| (needs.build-linux.result == 'success' || needs.build-linux.result == 'skipped') && | |
| (needs.build-host.result == 'success' || needs.build-host.result == 'skipped') && | |
| (needs.stage-cached.result == 'success' || needs.stage-cached.result == 'skipped') && | |
| (needs.reuse-bundle.result == 'success' || needs.reuse-bundle.result == 'skipped') | |
| uses: ./.github/workflows/reusable-test.yml | |
| with: | |
| test_script: scripts/ci/run-pr-integration.sh | |
| quality_result: ${{ needs.quality.result }} | |
| binding_artifact: bindings-x86_64-unknown-linux-gnu | |
| binding_profile: release | |
| require_binding_artifact: 'true' | |
| needs_native_binding: 'true' | |
| needs_ts_dist: 'true' | |
| secrets: inherit | |
| publish: | |
| name: Publish release | |
| needs: [quality, plan, build-linux, build-host, stage-cached, reuse-bundle, test] | |
| if: | | |
| !cancelled() && | |
| needs.quality.result == 'success' && | |
| needs.plan.result == 'success' && | |
| (needs.test.result == 'success' || needs.test.result == 'skipped') && | |
| (needs.build-linux.result == 'success' || needs.build-linux.result == 'skipped') && | |
| (needs.build-host.result == 'success' || needs.build-host.result == 'skipped') && | |
| (needs.stage-cached.result == 'success' || needs.stage-cached.result == 'skipped') && | |
| (needs.reuse-bundle.result == 'success' || needs.reuse-bundle.result == 'skipped') | |
| runs-on: self-hosted | |
| container: | |
| image: ghcr.io/${{ github.repository }}/ci-build:latest | |
| credentials: | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Mark workspace safe for git | |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| - name: Resolve release version from tag | |
| id: version | |
| shell: bash | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#release/}" | |
| if [ -z "$VERSION" ]; then | |
| echo "Could not parse version from tag: $GITHUB_REF_NAME" >&2 | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Publishing release version: $VERSION" | |
| - run: npm ci --ignore-scripts | |
| - name: Download all build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ci-artifacts | |
| pattern: bindings-* | |
| merge-multiple: false | |
| - name: Validate staged binding manifests | |
| shell: bash | |
| env: | |
| NATIVE_TOOL_MODE: declared | |
| run: | | |
| set -euo pipefail | |
| missing=0 | |
| while IFS= read -r target; do | |
| dir="ci-artifacts/bindings-${target}" | |
| if [[ ! -d "$dir" ]]; then | |
| echo "Missing current-run artifact dir: $dir" >&2 | |
| missing=1 | |
| continue | |
| fi | |
| shopt -s nullglob | |
| nodes=("$dir"/*.node) | |
| if [[ ${#nodes[@]} -eq 0 ]]; then | |
| echo "Missing .node in $dir" >&2 | |
| missing=1 | |
| continue | |
| fi | |
| if [[ -f "$dir/manifest.json" ]]; then | |
| mkdir -p packages/bindings/native-manifests | |
| cp "$dir"/*.node packages/bindings/ | |
| cp "$dir/manifest.json" "packages/bindings/native-manifests/${target}.json" | |
| bash scripts/ci/native-artifact-manifest.sh validate \ | |
| --manifest "packages/bindings/native-manifests/${target}.json" || missing=1 | |
| else | |
| echo "::warning::No manifest.json in $dir — proceeding with .node only" | |
| fi | |
| done < <(bash scripts/ci/list-release-targets.sh) | |
| if [[ "$missing" -ne 0 ]]; then | |
| echo "Publish blocked: incomplete/invalid current-run native artifacts" >&2 | |
| exit 1 | |
| fi | |
| - name: Stage binding artifacts | |
| shell: bash | |
| working-directory: packages/bindings | |
| run: | | |
| # napi artifacts crawls recursively and matches *.node basenames. | |
| # Copy only .node files — leave provenance manifest.json out of the tree. | |
| mkdir -p artifacts | |
| shopt -s nullglob | |
| for dir in ../../ci-artifacts/bindings-*; do | |
| dest="artifacts/$(basename "$dir")" | |
| mkdir -p "$dest" | |
| cp "$dir"/*.node "$dest/" | |
| done | |
| npx napi artifacts --dir artifacts | |
| - name: Set workspace package versions | |
| shell: bash | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| cd packages/bindings | |
| npm version "$VERSION" --no-git-tag-version --allow-same-version | |
| npx napi version | |
| cd ../.. | |
| bash scripts/ci/set-release-deps.sh "$VERSION" | |
| - name: Build TypeScript packages | |
| run: bash scripts/ci/build-ts-workspace.sh | |
| - name: Verify binding packages are staged | |
| shell: bash | |
| working-directory: packages/bindings | |
| run: | | |
| set -euo pipefail | |
| for dir in npm/*/; do | |
| shopt -s nullglob | |
| nodes=("$dir"*.node) | |
| if [[ ${#nodes[@]} -eq 0 ]]; then | |
| echo "Missing .node in $dir (napi artifacts should have copied it)" >&2 | |
| exit 1 | |
| fi | |
| echo " ok: $(basename "$dir") (${nodes[0]##*/})" | |
| done | |
| - name: Configure npm registry auth | |
| shell: bash | |
| run: | | |
| echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "$HOME/.npmrc" | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish npm packages | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.version.outputs.version }}" | |
| BINDINGS="packages/bindings" | |
| PUBLISH=(bash scripts/ci/publish-npm-if-needed.sh) | |
| echo "==> Platform binding packages" | |
| for dir in "${BINDINGS}"/npm/*/; do | |
| pkg="$(node -p "require('./${dir}package.json').name")" | |
| shopt -s nullglob | |
| nodes=("$dir"*.node) | |
| if [[ ${#nodes[@]} -eq 0 ]]; then | |
| echo "Missing .node in $dir" >&2 | |
| exit 1 | |
| fi | |
| "${PUBLISH[@]}" "$dir" "$pkg" "$VERSION" | |
| done | |
| echo "==> @node-webrtc-rust/bindings" | |
| "${PUBLISH[@]}" "$BINDINGS/" "@node-webrtc-rust/bindings" "$VERSION" --ignore-scripts | |
| echo "==> @node-webrtc-rust/signaling" | |
| "${PUBLISH[@]}" "packages/signaling/" "@node-webrtc-rust/signaling" "$VERSION" --ignore-scripts --omit=dev | |
| echo "==> @node-webrtc-rust/sdk" | |
| "${PUBLISH[@]}" "packages/sdk/" "@node-webrtc-rust/sdk" "$VERSION" --ignore-scripts | |
| echo "==> @node-webrtc-rust/helpers" | |
| "${PUBLISH[@]}" "packages/helpers/" "@node-webrtc-rust/helpers" "$VERSION" --ignore-scripts | |
| echo "==> Published @node-webrtc-rust/*@${VERSION} to npm" | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Prepare GitHub release notes from CHANGELOG | |
| id: release_notes | |
| shell: bash | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| BODY_FILE="${RUNNER_TEMP}/release-notes.md" | |
| bash scripts/changelog-release-body.sh "$VERSION" > "$BODY_FILE" | |
| echo "body_file=$BODY_FILE" >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Release ${{ steps.version.outputs.version }} | |
| body_path: ${{ steps.release_notes.outputs.body_file }} | |
| generate_release_notes: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| sync-main-package-lock: | |
| name: Sync main package-lock (PR) | |
| needs: publish | |
| if: ${{ !cancelled() && needs.publish.result == 'success' }} | |
| runs-on: self-hosted | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Prepare workspace | |
| run: | | |
| if [[ -n "${RUNNER_WORKSPACE:-}" && -d "${RUNNER_WORKSPACE}" ]]; then | |
| docker run --rm \ | |
| -v "${RUNNER_WORKSPACE}:${RUNNER_WORKSPACE}" \ | |
| alpine:3.20 \ | |
| chown -R "$(id -u):$(id -g)" "${RUNNER_WORKSPACE}" || true | |
| fi | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Resolve release version from tag | |
| id: version | |
| shell: bash | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#release/}" | |
| if [ -z "$VERSION" ]; then | |
| echo "Could not parse version from tag: $GITHUB_REF_NAME" >&2 | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Syncing main after release $VERSION" | |
| - name: Sync workspace versions and package-lock from npm | |
| run: bash scripts/ci/post-release-sync-main-package-lock.sh "${{ steps.version.outputs.version }}" | |
| - name: Open PR to main | |
| id: open_pr | |
| uses: peter-evans/create-pull-request@v7 | |
| with: | |
| token: ${{ secrets.REPO_SYNC_PAT != '' && secrets.REPO_SYNC_PAT || github.token }} | |
| commit-message: 'chore(ci): sync package-lock after release ${{ steps.version.outputs.version }}' | |
| title: 'chore(ci): sync package-lock after release ${{ steps.version.outputs.version }}' | |
| body: | | |
| Automated follow-up to tag `${{ github.ref_name }}` after npm publish. | |
| - Workspace `@node-webrtc-rust/*` versions aligned to `${{ steps.version.outputs.version }}` | |
| - `package-lock.json` optional platform binding entries refreshed from the npm registry | |
| **Merge promptly** so `main` stays green for `npm ci` (see `validate-package-lock` CI job). | |
| Re-run locally if needed: | |
| ```bash | |
| bash scripts/ci/post-release-sync-main-package-lock.sh ${{ steps.version.outputs.version }} | |
| ``` | |
| branch: chore/post-release-package-lock-${{ steps.version.outputs.version }} | |
| base: main | |
| delete-branch: true | |
| labels: dependencies,automation | |
| - name: Manual PR fallback (when GITHUB_TOKEN cannot open PRs) | |
| if: failure() && steps.open_pr.outcome == 'failure' | |
| shell: bash | |
| run: | | |
| BRANCH="chore/post-release-package-lock-${{ steps.version.outputs.version }}" | |
| echo "::warning::Automated PR creation failed (org may block GITHUB_TOKEN from opening PRs)." | |
| echo "Fix: repo Settings → Actions → allow Actions to create PRs, or add secret REPO_SYNC_PAT (classic PAT, repo scope)." | |
| echo "Open manually: https://github.com/${{ github.repository }}/compare/main...${BRANCH}?expand=1" |