From 11b6c6619723ead2b20d73f08a7ebc5f984a9904 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Thu, 4 Jun 2026 22:34:46 +0000 Subject: [PATCH 1/4] =?UTF-8?q?ci(sea):=20add=20Kernel=20E2E=20workflow=20?= =?UTF-8?q?=E2=80=94=20build=20napi=20binding=20from=20KERNEL=5FREV,=20run?= =?UTF-8?q?=20tests/e2e/sea=20on=20a=20live=20warehouse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings the SEA-backend end-to-end gate onto main (it previously lived only in the stacked sea-kernel-rev-pin branch). Mirrors the Python connector's kernel-e2e.yml. What it does: - Reads the pinned kernel SHA from KERNEL_REV, checks the kernel out via a GitHub App token (private repo), runs `npm run build:native` to compile the napi binding into native/sea/ — so the binary matches KERNEL_REV exactly. - Asserts the committed native/sea/index.{d.ts,js} contract matches the rebuilt binding (catches drift between the pin and the committed router/types). - Smoke-checks the binding loads, then runs `tests/e2e/sea/**` against the azure-prod warehouse (maps the secrets onto the DATABRICKS_PECOTESTING_* vars the SEA suite gates on). Gate semantics (same pattern as the Thrift integration + Python kernel-e2e): - Plain PR events post a synthetic-success "Kernel E2E" check so PRs that don't touch SEA aren't blocked; the real suite runs in the merge queue. - The `kernel-e2e` label triggers a preview run on the PR and is auto-removed on push (a labelled run can't be re-triggered with unreviewed code). - merge_group runs the real gate when SEA-relevant files changed, auto-passes otherwise. All referenced pieces already exist on main: the setup-jfrog composite (npm + the inline cargo proxy config), KERNEL_REV, the build:native script, the committed native/sea contract, and tests/e2e/sea/. One-time admin setup required before this gates anything (documented in the workflow header): the `kernel-e2e` label, the INTEGRATION_TEST_APP_ID / INTEGRATION_TEST_PRIVATE_KEY GitHub App (allowlisted for databricks-sql-kernel), and the azure-prod environment exposing DATABRICKS_HOST / TEST_PECO_WAREHOUSE_HTTP_PATH / DATABRICKS_TOKEN. Co-authored-by: Isaac Signed-off-by: Madhavendra Rathore --- .github/workflows/kernel-e2e.yml | 377 +++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 .github/workflows/kernel-e2e.yml diff --git a/.github/workflows/kernel-e2e.yml b/.github/workflows/kernel-e2e.yml new file mode 100644 index 00000000..11f05c1f --- /dev/null +++ b/.github/workflows/kernel-e2e.yml @@ -0,0 +1,377 @@ +name: Kernel E2E Tests + +# Runs the SEA backend e2e suite (tests/e2e/sea/**) against a real +# Databricks warehouse with a freshly-built napi-rs kernel binding. +# +# The kernel is a private repo with no published binary artifact. We pin +# a kernel SHA in the `KERNEL_REV` file at the repo root, check the kernel +# out via a GitHub App token, and run `npm run build:native` to compile +# the napi binding into native/sea/ in the same checkout the tests run +# against. Bumping `KERNEL_REV` is the ONLY way to pick up a new kernel +# version — this keeps the driver <-> kernel pair bisectable, so a driver +# change and the kernel revision it depends on always land together. +# +# Why this exists: the committed native/sea/index.d.ts + index.js are the +# TypeScript declarations and the napi-rs platform router; the actual +# `.node` binary is gitignored (large, per-platform) and is NOT in the +# repo. The standard `main.yml` e2e job has no binary, so its SEA suite +# skips (it gates on DATABRICKS_PECOTESTING_* secrets it doesn't set). +# This workflow is what actually exercises the SEA path end-to-end against +# a known kernel revision. +# +# Gate semantics: +# - Plain PR events post a synthetic-success check so the required +# "Kernel E2E" check doesn't block PRs that don't touch the SEA path. +# Real tests run in the merge queue. +# - `kernel-e2e` label triggers a preview run on the PR; the label is +# auto-removed on `synchronize` for the same security reason. +# - merge_group fires the real gate — runs when SEA-relevant files +# changed, auto-passes otherwise. +# +# Required external setup (one-time, by a repo admin): +# 1. `kernel-e2e` label exists in this repo. +# 2. `INTEGRATION_TEST_APP_ID` / `INTEGRATION_TEST_PRIVATE_KEY` secrets +# exist and the GitHub App's repo allowlist includes +# `databricks/databricks-sql-kernel`. +# 3. `KERNEL_REV` at the repo root contains a 40-char kernel commit SHA. +# 4. `azure-prod` environment exposes DATABRICKS_HOST / +# TEST_PECO_WAREHOUSE_HTTP_PATH / DATABRICKS_TOKEN. + +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + merge_group: + +permissions: + contents: read + id-token: write + +concurrency: + group: kernel-e2e-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + # ─────────────────────────────────────────────────────────────── + # Security: auto-remove `kernel-e2e` label on new commits so a + # labelled preview run can't be re-triggered with unreviewed code. + # ─────────────────────────────────────────────────────────────── + strip-label: + if: github.event_name == 'pull_request' && github.event.action == 'synchronize' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Remove kernel-e2e label + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + github-token: ${{ github.token }} + script: | + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: 'kernel-e2e', + }); + } catch (error) { + if (error.status !== 404) throw error; + } + + # ─────────────────────────────────────────────────────────────── + # Synthetic success on every non-label PR event so the required + # "Kernel E2E" check doesn't permablock PRs that don't touch SEA + # code. Real run happens in the merge queue (or via explicit label). + # ─────────────────────────────────────────────────────────────── + skip-kernel-e2e-pr: + if: github.event_name == 'pull_request' && github.event.action != 'labeled' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + permissions: + checks: write + steps: + - name: Post synthetic-success check + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + github-token: ${{ github.token }} + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Kernel E2E', + head_sha: context.payload.pull_request.head.sha, + status: 'completed', + conclusion: 'success', + completed_at: new Date().toISOString(), + output: { + title: 'Skipped on PR — runs in merge queue', + summary: 'Kernel E2E is skipped on PRs and runs as a required gate in the merge queue. Add the `kernel-e2e` label to preview on this PR.' + } + }); + + # ─────────────────────────────────────────────────────────────── + # Detect whether SEA-relevant files changed. Used by both the + # labelled-PR path and the merge-queue path to decide between + # "really run the suite" and "auto-pass the check". + # ─────────────────────────────────────────────────────────────── + detect-changes: + if: | + github.event_name == 'merge_group' || + (github.event_name == 'pull_request' && + github.event.action == 'labeled' && + contains(github.event.pull_request.labels.*.name, 'kernel-e2e')) + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + outputs: + run_tests: ${{ steps.changed.outputs.run_tests }} + head_sha: ${{ steps.refs.outputs.head_sha }} + steps: + - name: Resolve head SHA + id: refs + env: + MERGE_QUEUE_REF: ${{ github.event.merge_group.head_ref }} + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + if (context.eventName === 'pull_request') { + core.setOutput('head_sha', context.payload.pull_request.head.sha); + return; + } + core.setOutput('head_sha', context.payload.merge_group.head_sha); + + - name: Check out repo at head SHA + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ steps.refs.outputs.head_sha }} + fetch-depth: 0 + + - name: Detect SEA-relevant changes + id: changed + env: + HEAD_SHA: ${{ steps.refs.outputs.head_sha }} + BASE_SHA: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_sha || github.event.pull_request.base.sha }} + run: | + CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA") + echo "Changed files:" + echo "$CHANGED" + # Run when the SEA driver layer, the napi binding contract, SEA + # e2e tests, this workflow, the kernel revision pin, or core deps + # move. + if echo "$CHANGED" | grep -qE "^(lib/sea/|native/sea/|tests/e2e/sea/|tests/unit/sea/|\.github/workflows/kernel-e2e\.yml|KERNEL_REV|package\.json|package-lock\.json)"; then + echo "run_tests=true" >> "$GITHUB_OUTPUT" + else + echo "run_tests=false" >> "$GITHUB_OUTPUT" + fi + + # ─────────────────────────────────────────────────────────────── + # Real test job. Builds the napi binding from the pinned kernel SHA + # and runs the SEA e2e suite against the dogfood warehouse. + # ─────────────────────────────────────────────────────────────── + run-kernel-e2e: + needs: detect-changes + if: needs.detect-changes.outputs.run_tests == 'true' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + environment: azure-prod + permissions: + contents: read + checks: write + id-token: write + env: + # SEA e2e tests gate on the DATABRICKS_PECOTESTING_* vars; map the + # warehouse secrets onto them so the suite actually runs (it skips + # when they are absent). + DATABRICKS_PECOTESTING_SERVER_HOSTNAME: ${{ secrets.DATABRICKS_HOST }} + DATABRICKS_PECOTESTING_HTTP_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }} + DATABRICKS_PECOTESTING_TOKEN_PERSONAL: ${{ secrets.DATABRICKS_TOKEN }} + steps: + - name: Check out driver + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ needs.detect-changes.outputs.head_sha }} + + - name: Read pinned kernel SHA + id: kernel-rev + run: | + if [[ ! -f KERNEL_REV ]]; then + echo "::error::KERNEL_REV file missing" + exit 1 + fi + REV=$(tr -d '[:space:]' < KERNEL_REV) + if [[ ! "$REV" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::KERNEL_REV must be a 40-char commit SHA, got: $REV" + exit 1 + fi + echo "rev=$REV" >> "$GITHUB_OUTPUT" + echo "Pinned kernel SHA: $REV" + + - name: Generate GitHub App token (kernel repo read access) + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }} + private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }} + owner: databricks + repositories: databricks-sql-kernel + + - name: Check out kernel at pinned SHA + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + repository: databricks/databricks-sql-kernel + ref: ${{ steps.kernel-rev.outputs.rev }} + token: ${{ steps.app-token.outputs.token }} + path: databricks-sql-kernel + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 20 + + - name: Set up Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # v1.15.2 + with: + cache: false + + - name: Cache cargo build artifacts (keyed on kernel SHA) + uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0 + with: + workspaces: databricks-sql-kernel + key: kernel-${{ steps.kernel-rev.outputs.rev }} + + - name: Set up JFrog (npm registry proxy) + uses: ./.github/actions/setup-jfrog + + - name: Configure Cargo for JFrog proxy + shell: bash + # databricks-protected-runner-group blocks direct egress to + # index.crates.io, so cargo must route through JFrog's + # db-cargo-remote proxy. Reuses the JFrog token setup-jfrog + # exported into the environment. + run: | + set -euo pipefail + mkdir -p ~/.cargo + cat > ~/.cargo/config.toml << 'EOF' + [source.crates-io] + replace-with = "jfrog" + [source.jfrog] + registry = "sparse+https://databricks.jfrog.io/artifactory/api/cargo/db-cargo-remote/index/" + [registries.jfrog] + index = "sparse+https://databricks.jfrog.io/artifactory/api/cargo/db-cargo-remote/index/" + credential-provider = ["cargo:token"] + EOF + cat > ~/.cargo/credentials.toml << EOF + [registries.jfrog] + token = "Bearer ${JFROG_ACCESS_TOKEN}" + EOF + echo "CARGO_REGISTRIES_JFROG_TOKEN=Bearer ${JFROG_ACCESS_TOKEN}" >> "$GITHUB_ENV" + + - name: Install driver deps + run: npm ci + + - name: Build napi binding from pinned kernel + # build:native cd's into ${DATABRICKS_SQL_KERNEL_REPO}/napi, runs the + # napi-rs build, and copies index.* into native/sea/. Pointing it at + # the SHA-pinned kernel checkout is what makes the binary match + # KERNEL_REV exactly. + env: + DATABRICKS_SQL_KERNEL_REPO: ${{ github.workspace }}/databricks-sql-kernel + run: npm run build:native + + - name: Assert committed binding matches KERNEL_REV + # The committed native/sea/index.d.ts + index.js are the consumer-facing + # type contract + platform router; they MUST correspond to the pinned + # kernel. build:native just regenerated them from the KERNEL_REV + # checkout, so any diff means the committed contract drifted from the + # pin — fail loudly and tell the author to commit the regenerated files. + # (The .node binaries are gitignored, so git diff only sees the contract.) + run: | + if ! git diff --exit-code -- native/sea/index.d.ts native/sea/index.js; then + echo "::error::native/sea/index.d.ts / index.js are out of sync with KERNEL_REV ($(tr -d '[:space:]' < KERNEL_REV)). Run 'npm run build:native' against that kernel SHA and commit native/sea/index.*." + exit 1 + fi + echo "Committed binding matches KERNEL_REV." + + - name: Smoke-check binding loads + run: node -e "const b=require('./native/sea'); if(typeof b.version!=='function'){throw new Error('napi binding failed to load')} console.log('kernel binding ok:', b.version())" + + - name: Run SEA e2e tests + # Invoke mocha directly rather than via `npm run e2e -- `: routing a + # glob through the npm-script's inner shell mangles `**` and silently + # resolves to ZERO files (a false pass). mocha expands the quoted glob + # itself, reliably matching every tests/e2e/sea file. + run: NODE_OPTIONS="--max-old-space-size=4096" npx mocha --config tests/e2e/.mocharc.js "tests/e2e/sea/**/*.test.ts" + + - name: Post Kernel E2E check (success) + if: success() + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + github-token: ${{ github.token }} + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Kernel E2E', + head_sha: '${{ needs.detect-changes.outputs.head_sha }}', + status: 'completed', + conclusion: 'success', + completed_at: new Date().toISOString(), + output: { + title: 'Kernel E2E passed', + summary: 'tests/e2e/sea ran green against the pinned kernel SHA.' + } + }); + + - name: Post Kernel E2E check (failure) + if: failure() + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + github-token: ${{ github.token }} + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Kernel E2E', + head_sha: '${{ needs.detect-changes.outputs.head_sha }}', + status: 'completed', + conclusion: 'failure', + completed_at: new Date().toISOString(), + output: { + title: 'Kernel E2E failed', + summary: 'See workflow logs for details.' + } + }); + + # ─────────────────────────────────────────────────────────────── + # Auto-pass the Kernel E2E check in the merge queue when no SEA- + # relevant files changed. + # ─────────────────────────────────────────────────────────────── + auto-pass-merge-queue: + needs: detect-changes + if: github.event_name == 'merge_group' && needs.detect-changes.outputs.run_tests != 'true' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + permissions: + checks: write + steps: + - name: Auto-pass + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + github-token: ${{ github.token }} + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Kernel E2E', + head_sha: '${{ github.event.merge_group.head_sha }}', + status: 'completed', + conclusion: 'success', + completed_at: new Date().toISOString(), + output: { + title: 'Skipped — no SEA-relevant changes', + summary: 'No files under lib/sea/, native/sea/, tests/e2e/sea/, tests/unit/sea/, KERNEL_REV, package.json, or package-lock.json changed.' + } + }); From 3ade6d99d710139cabae17d4917dfa6e21057299 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Thu, 4 Jun 2026 22:44:30 +0000 Subject: [PATCH 2/4] ci(sea): inject E2E_* vars so tests/e2e/sea actually runs (config.ts process.exit fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/e2e/sea suite reads creds from two sources: most tests read DATABRICKS_PECOTESTING_* directly (already injected), but e2e-smoke.test.ts imports tests/e2e/utils/config.ts, which process.exit(1)s at module load if any E2E_* var is unset — aborting the whole mocha run before it starts. Mirror main.yml's e2e-test job mapping (E2E_HOST/PATH/ACCESS_TOKEN + CATALOG/SCHEMA/ VOLUME/TABLE_SUFFIX) so the suite loads and runs. Co-authored-by: Isaac Signed-off-by: Madhavendra Rathore --- .github/workflows/kernel-e2e.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/kernel-e2e.yml b/.github/workflows/kernel-e2e.yml index 11f05c1f..62a07eb0 100644 --- a/.github/workflows/kernel-e2e.yml +++ b/.github/workflows/kernel-e2e.yml @@ -182,12 +182,25 @@ jobs: checks: write id-token: write env: - # SEA e2e tests gate on the DATABRICKS_PECOTESTING_* vars; map the - # warehouse secrets onto them so the suite actually runs (it skips - # when they are absent). + # The tests/e2e/sea suite reads creds from TWO sources, so we set both: + # + # 1. Most SEA tests (execution, results, interval-*) read the + # DATABRICKS_PECOTESTING_* vars directly and skip when absent. DATABRICKS_PECOTESTING_SERVER_HOSTNAME: ${{ secrets.DATABRICKS_HOST }} DATABRICKS_PECOTESTING_HTTP_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }} DATABRICKS_PECOTESTING_TOKEN_PERSONAL: ${{ secrets.DATABRICKS_TOKEN }} + # + # 2. `e2e-smoke.test.ts` imports tests/e2e/utils/config.ts, which + # `process.exit(1)`s at module load if ANY E2E_* var is unset — which + # would abort the whole `tests/e2e/sea/**` mocha run before it starts. + # Mirror main.yml's e2e-test job mapping so the suite loads + runs. + E2E_HOST: ${{ secrets.DATABRICKS_HOST }} + E2E_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }} + E2E_ACCESS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }} + E2E_CATALOG: peco + E2E_SCHEMA: default + E2E_VOLUME: e2etests + E2E_TABLE_SUFFIX: ${{ github.sha }} steps: - name: Check out driver uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 From 45b79c102d5f955ff2352b401f7470a3ce433845 Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Thu, 4 Jun 2026 23:49:52 +0000 Subject: [PATCH 3/4] ci(sea): cache ~/.npm in kernel-e2e (mirror main.yml) so npm ci doesn't hit unreachable dev proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two package-lock entries (flatbuffers etc.) resolve to npm-proxy.dev.databricks.com, which the protected runner can't reach (ECONNRESET). main.yml's e2e job only succeeds because it caches ~/.npm — those tarballs load from the warm cache, never fetched. kernel-e2e had no such cache, so npm ci failed deterministically. Add the identical actions/cache step (same key) before npm ci. The driver has a single root package-lock.json and the kernel checkout adds none, so hashFiles('**/package-lock.json') matches main.yml's key exactly → restores main's warmed cache. Confirmed end-to-end up to this point: App token + private-kernel checkout + JFrog now all pass; npm ci was the only remaining failure. Co-authored-by: Isaac Signed-off-by: Madhavendra Rathore --- .github/workflows/kernel-e2e.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/kernel-e2e.yml b/.github/workflows/kernel-e2e.yml index 62a07eb0..15902e80 100644 --- a/.github/workflows/kernel-e2e.yml +++ b/.github/workflows/kernel-e2e.yml @@ -281,6 +281,24 @@ jobs: EOF echo "CARGO_REGISTRIES_JFROG_TOKEN=Bearer ${JFROG_ACCESS_TOKEN}" >> "$GITHUB_ENV" + # Restore the same `~/.npm` cache main.yml's e2e job warms. A couple of + # lockfile entries resolve to `npm-proxy.dev.databricks.com` (a dev proxy + # the protected runner can't reach); they only ever load from this cache. + # The key matches main.yml exactly (driver has a single root + # package-lock.json; the kernel checkout adds none), so this restores + # main's warmed cache and `npm ci` never hits the unreachable proxy. + - name: Cache node modules + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Install driver deps run: npm ci From 30b7aee017a22621abaa1d9ffd3f5481dccee33a Mon Sep 17 00:00:00 2001 From: Madhavendra Rathore Date: Fri, 5 Jun 2026 00:02:44 +0000 Subject: [PATCH 4/4] ci(sea): run SEA e2e under nyc so ts-node/register loads .ts (fix ESM-loader race) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare `mocha` has no ts-node registration, so it loaded the .ts specs via the ESM dynamic-import path and crashed with ERR_INTERNAL_ASSERTION (... not yet fully loaded ... Promise.all) before any test ran. main.yml's e2e job avoids this by running `nyc mocha` — nyc.config.js's `require: ['ts-node/register']` installs the CommonJS ts-node hook, loading specs synchronously. Mirror that: invoke `nyc ... mocha --config tests/e2e/.mocharc.js `, keeping the glob at argv index 4 (the mocharc derives spec from process.argv.slice(4)). Everything else in the pipeline is now confirmed green: App-token + private-kernel checkout, ~/.npm cache, npm ci, build:native, the KERNEL_REV contract-drift assert, and the binding smoke-check. This was the last failing step. Co-authored-by: Isaac Signed-off-by: Madhavendra Rathore --- .github/workflows/kernel-e2e.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/kernel-e2e.yml b/.github/workflows/kernel-e2e.yml index 15902e80..b48fdf1b 100644 --- a/.github/workflows/kernel-e2e.yml +++ b/.github/workflows/kernel-e2e.yml @@ -329,11 +329,18 @@ jobs: run: node -e "const b=require('./native/sea'); if(typeof b.version!=='function'){throw new Error('napi binding failed to load')} console.log('kernel binding ok:', b.version())" - name: Run SEA e2e tests - # Invoke mocha directly rather than via `npm run e2e -- `: routing a - # glob through the npm-script's inner shell mangles `**` and silently - # resolves to ZERO files (a false pass). mocha expands the quoted glob - # itself, reliably matching every tests/e2e/sea file. - run: NODE_OPTIONS="--max-old-space-size=4096" npx mocha --config tests/e2e/.mocharc.js "tests/e2e/sea/**/*.test.ts" + # Run through `nyc` (not bare `mocha`) so the `require: ['ts-node/register']` + # in nyc.config.js loads the .ts specs via the CommonJS ts-node hook — + # exactly how main.yml's e2e job runs them. Bare `mocha` has no ts-node + # registration, so it falls back to the ESM dynamic-import loader and + # crashes with `ERR_INTERNAL_ASSERTION: ... not yet fully loaded ... + # Promise.all` before any test runs. + # + # The glob is passed as the trailing positional and MUST stay at argv + # index 4 (`mocha --config `): tests/e2e/.mocharc.js derives + # `spec` from `process.argv.slice(4)`. We invoke mocha directly (not via + # `npm run e2e -- `, whose inner shell mangles `**` to zero files). + run: NODE_OPTIONS="--max-old-space-size=4096" npx nyc --report-dir coverage_kernel_e2e mocha --config tests/e2e/.mocharc.js "tests/e2e/sea/**/*.test.ts" - name: Post Kernel E2E check (success) if: success()