Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 125 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ permissions:
contents: read
pull-requests: read
packages: read
actions: read

env:
CARGO_TERM_COLOR: always
Expand Down Expand Up @@ -193,6 +194,11 @@ jobs:
run_test=true
done < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA")

# 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"
echo "run_test=${run_test}" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -374,6 +380,10 @@ jobs:
bash scripts/fix-rollup-native.sh
bash scripts/ci/build-ts-workspace.sh

# 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]
Expand All @@ -383,13 +393,118 @@ 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'

- uses: ./.github/actions/native-binding-cache
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: |
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' }}
11 changes: 11 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand All @@ -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"
7 changes: 6 additions & 1 deletion scripts/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
Loading