From 19d4a56bcf1ecd66b4dce9eeb378878bf5d8aa63 Mon Sep 17 00:00:00 2001 From: akirilyuk Date: Mon, 15 Jun 2026 15:12:29 +0700 Subject: [PATCH 1/3] fix(ci): unblock PR Test job and release sync PR creation PR Test failed in ~12s because build.yml lacked actions:read (artifact download from compile-native) and TS-only PRs skipped native compile while integration tests still required a .node. Also document REPO_SYNC_PAT / repo Actions setting for post-release lockfile PRs when GITHUB_TOKEN cannot create pull requests. Co-authored-by: Cursor --- .github/workflows/build.yml | 8 ++++++++ .github/workflows/release.yml | 11 +++++++++++ scripts/RELEASE.md | 7 ++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9eca78..9b386c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,7 @@ permissions: contents: read pull-requests: read packages: read + actions: read env: CARGO_TERM_COLOR: always @@ -193,6 +194,13 @@ jobs: run_test=true done < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA") + # Integration tests always need a .node in the workspace. TS-only PRs do not + # touch Rust paths but still run Test — compile must run so the job uploads + # bindings-x86_64-unknown-linux-gnu for reusable-test to download. + if [[ "$run_test" == true ]]; then + run_compile=true + fi + echo "run_compile=${run_compile}" >> "$GITHUB_OUTPUT" echo "run_build_ts=${run_build_ts}" >> "$GITHUB_OUTPUT" echo "run_test=${run_test}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ae887e..32c00cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -383,8 +383,10 @@ jobs: 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: | @@ -404,3 +406,12 @@ jobs: 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" diff --git a/scripts/RELEASE.md b/scripts/RELEASE.md index b26f8b5..305799c 100644 --- a/scripts/RELEASE.md +++ b/scripts/RELEASE.md @@ -187,7 +187,12 @@ Job **`Sync main package-lock (PR)`** in [`release.yml`](../.github/workflows/re 4. Opens a PR via [`peter-evans/create-pull-request`](https://github.com/peter-evans/create-pull-request) — branch `chore/post-release-package-lock-X.Y.Z`, labels `dependencies`, `automation`. 5. Skips opening a PR if there is no diff. -Requires workflow permission **`pull-requests: write`** and a `GITHUB_TOKEN` that can push branches. If branch protection blocks the bot, create the PR manually from the branch the job pushed (or re-run the sync script locally and open the PR yourself). +Requires workflow permission **`pull-requests: write`**. If the org disables **“Allow GitHub Actions to create and approve pull requests”**, `GITHUB_TOKEN` cannot open the PR (the branch may still be pushed). Fix one of: + +1. **Repo Settings → Actions → General** — enable **Allow GitHub Actions to create and approve pull requests**, or +2. Add repo secret **`REPO_SYNC_PAT`** (classic PAT with `repo` scope) — `release.yml` uses it for `create-pull-request`. + +If automation fails, open the PR manually from branch `chore/post-release-package-lock-X.Y.Z` (compare link is printed in the failed workflow log). ### `SKIP_LOCK_REFRESH` From bdb41fb1a8ba6fd69615eb2d250170cc1f587509 Mon Sep 17 00:00:00 2001 From: akirilyuk Date: Mon, 15 Jun 2026 15:27:11 +0700 Subject: [PATCH 2/3] fix(ci): inline PR integration tests for artifact download Reusable workflow_call jobs cannot download artifacts uploaded by caller jobs in build.yml. Inline reusable-test steps so compile-native bindings are available in the same workflow run. Co-authored-by: Cursor --- .github/workflows/build.yml | 105 ++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b386c6..ab4d8ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -382,6 +382,9 @@ jobs: bash scripts/fix-rollup-native.sh bash scripts/ci/build-ts-workspace.sh + # Inline integration tests (not reusable-test.yml): GHA cannot download caller + # workflow artifacts from workflow_call jobs — compile-native uploads here, Test + # consumes in the same run. See development/node-webrtc-rust/followups/2026-06-02-ci-release-branch-and-flat-pr-checks.md test: name: Test needs: [changes, validate-package-lock, quality, compile-native, build-ts] @@ -391,13 +394,95 @@ jobs: needs.validate-package-lock.result == 'success' && needs.compile-native.result != 'failure' && needs.build-ts.result != 'failure' - uses: ./.github/workflows/reusable-test.yml - with: - test_script: scripts/ci/run-pr-integration.sh - skip: ${{ needs.changes.outputs.skip_test }} - quality_result: ${{ needs.quality.result }} - binding_artifact: ${{ needs.compile-native.outputs.ran_compile == 'true' && 'bindings-x86_64-unknown-linux-gnu' || '' }} - require_binding_artifact: ${{ needs.compile-native.outputs.ran_compile == 'true' && 'true' || 'false' }} - needs_native_binding: ${{ needs.changes.outputs.needs_native_for_test }} - needs_ts_dist: ${{ needs.changes.outputs.needs_ts_for_test }} - secrets: inherit + runs-on: self-hosted + env: + TURN_AVAILABLE: '1' + CI_IMAGE: ghcr.io/${{ github.repository }}/ci-build:latest + steps: + - name: Resolve integration test gates + id: gate + env: + SKIP: ${{ needs.changes.outputs.skip_test }} + NEEDS_NATIVE: ${{ needs.changes.outputs.needs_native_for_test }} + NEEDS_TS: ${{ needs.changes.outputs.needs_ts_for_test }} + run: | + is_true() { + case "${1,,}" in + true|1|yes) return 0 ;; + *) return 1 ;; + esac + } + if is_true "$SKIP"; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "::notice::Skipping integration tests — no source code changes in this PR." + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + if is_true "$NEEDS_NATIVE"; then + echo "needs_native=true" >> "$GITHUB_OUTPUT" + else + echo "needs_native=false" >> "$GITHUB_OUTPUT" + fi + if is_true "$NEEDS_TS"; then + echo "needs_ts=true" >> "$GITHUB_OUTPUT" + else + echo "needs_ts=false" >> "$GITHUB_OUTPUT" + fi + + - name: Require quality passed before integration tests + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' && needs.quality.result != 'success' + run: | + echo "Typecheck & lint must pass before integration tests (quality=${{ needs.quality.result }})." >&2 + exit 1 + + - name: Prepare workspace + if: steps.gate.outputs.skip != 'true' + 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 + if: steps.gate.outputs.skip != 'true' + + - name: Download native binding artifact + id: binding-artifact + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' && needs.compile-native.outputs.ran_compile == 'true' + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-unknown-linux-gnu + path: packages/bindings + + - uses: ./.github/actions/native-binding-cache + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' && (needs.compile-native.outputs.ran_compile != 'true' || steps.binding-artifact.outcome == 'failure') + with: + target: x86_64-unknown-linux-gnu + profile: debug + + - name: Verify native binding in workspace + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' + run: | + shopt -s nullglob + files=(packages/bindings/*.node) + if [[ ${#files[@]} -eq 0 ]]; then + echo "No .node in packages/bindings after artifact/cache restore." >&2 + exit 1 + fi + echo "Native binding ready: $(basename "${files[0]}")" + + - uses: ./.github/actions/ci-cache-ts-dist + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_ts == 'true' + id: ts-cache + with: + profile: release + + - uses: ./.github/actions/ci-run-integration-tests + if: steps.gate.outputs.skip != 'true' + with: + test_script: scripts/ci/run-pr-integration.sh + ci_image: ${{ env.CI_IMAGE }} + github_token: ${{ secrets.GITHUB_TOKEN }} + ts_dist_cache_hit: ${{ steps.ts-cache.outputs.cache-hit || 'false' }} From 8d182b9bb4c9611b252f12c015b9f54d4830e530 Mon Sep 17 00:00:00 2001 From: akirilyuk Date: Mon, 15 Jun 2026 15:35:14 +0700 Subject: [PATCH 3/3] fix(ci): build native binding in PR Test job on cache miss Container compile-native artifacts are not reliably available to the host Test job on self-hosted runners. Restore GHA cache first, then compile inside ci-build via Docker when packages/bindings has no .node. Co-authored-by: Cursor --- .github/workflows/build.yml | 58 +++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab4d8ac..b34c96b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,12 +194,10 @@ jobs: run_test=true done < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA") - # Integration tests always need a .node in the workspace. TS-only PRs do not - # touch Rust paths but still run Test — compile must run so the job uploads - # bindings-x86_64-unknown-linux-gnu for reusable-test to download. - if [[ "$run_test" == true ]]; then - run_compile=true - fi + # Integration tests need a .node in the Test job workspace. Do not force + # compile-native here — compile runs in a container and artifact download + # to the host Test job is unreliable on self-hosted; Test restores cache + # and builds in CI image when needed (see test job below). echo "run_compile=${run_compile}" >> "$GITHUB_OUTPUT" echo "run_build_ts=${run_build_ts}" >> "$GITHUB_OUTPUT" @@ -382,9 +380,10 @@ jobs: bash scripts/fix-rollup-native.sh bash scripts/ci/build-ts-workspace.sh - # Inline integration tests (not reusable-test.yml): GHA cannot download caller - # workflow artifacts from workflow_call jobs — compile-native uploads here, Test - # consumes in the same run. See development/node-webrtc-rust/followups/2026-06-02-ci-release-branch-and-flat-pr-checks.md + # Inline integration tests (not reusable-test.yml): workflow_call cannot download + # caller artifacts; compile-native runs in a container while Test runs on the + # self-hosted host — artifact handoff is unreliable. Test restores GHA cache and + # compiles in CI image when needed. test: name: Test needs: [changes, validate-package-lock, quality, compile-native, build-ts] @@ -448,20 +447,43 @@ jobs: - uses: actions/checkout@v4 if: steps.gate.outputs.skip != 'true' - - name: Download native binding artifact - id: binding-artifact - if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' && needs.compile-native.outputs.ran_compile == 'true' - uses: actions/download-artifact@v4 - with: - name: bindings-x86_64-unknown-linux-gnu - path: packages/bindings - - uses: ./.github/actions/native-binding-cache - if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' && (needs.compile-native.outputs.ran_compile != 'true' || steps.binding-artifact.outcome == 'failure') + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' + id: native-cache with: target: x86_64-unknown-linux-gnu profile: debug + - name: Build native binding in CI image when cache miss + if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' + run: | + shopt -s nullglob + files=(packages/bindings/*.node) + if [[ ${#files[@]} -gt 0 ]]; then + echo "Native binding already present: $(basename "${files[0]}")" + exit 0 + fi + echo "No cached .node — compiling in ${{ env.CI_IMAGE }}" + docker run --rm \ + -v "${GITHUB_WORKSPACE}:/workspace" \ + -w /workspace \ + -e CMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -e OPUS_STATIC=1 \ + "${{ env.CI_IMAGE }}" \ + bash -c ' + set -euo pipefail + cd packages/bindings + npm ci --ignore-scripts + npx napi build --target x86_64-unknown-linux-gnu + npm run copy:local-node + ' + files=(packages/bindings/*.node) + if [[ ${#files[@]} -eq 0 ]]; then + echo "Native build in CI image did not produce a .node file." >&2 + exit 1 + fi + echo "Built native binding: $(basename "${files[0]}")" + - name: Verify native binding in workspace if: steps.gate.outputs.skip != 'true' && steps.gate.outputs.needs_native == 'true' run: |