From 339d0c9beb32be3fa889aefe68ad68fe524d527b Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Mar 2026 10:16:23 +0000 Subject: [PATCH 1/2] fix: simplify CI and integration test timeout --- .github/workflows/ci.yml | 131 ++++++++++++++++++-- templates/default/tests/integration.spec.ts | 59 +++++++-- tests/integration/fns.sh | 6 + 3 files changed, 176 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf67ce45ed..73955dd8b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,6 @@ on: branches: - main - dev - push: - branches: - - main - - dev env: SUPPORT_DOCKERFILE_PATH: crates/support/Dockerfile CIPHERNODE_DOCKERFILE_PATH: crates/Dockerfile @@ -33,7 +29,93 @@ permissions: packages: write jobs: + detect_changes: + runs-on: 'ubuntu-latest' + outputs: + rust_tests: ${{ steps.jobs.outputs.rust_tests }} + ciphernode_e2e: ${{ steps.jobs.outputs.ciphernode_e2e }} + crisp: ${{ steps.jobs.outputs.crisp }} + templates: ${{ steps.jobs.outputs.templates }} + zk: ${{ steps.jobs.outputs.zk }} + contracts: ${{ steps.jobs.outputs.contracts }} + docker_support: ${{ steps.jobs.outputs.docker_support }} + docker_ciphernode: ${{ steps.jobs.outputs.docker_ciphernode }} + net: ${{ steps.jobs.outputs.net }} + init: ${{ steps.jobs.outputs.init }} + build_sdk: ${{ steps.jobs.outputs.build_sdk }} + build_crisp_sdk: ${{ steps.jobs.outputs.build_crisp_sdk }} + build_e3_support_dev: ${{ steps.jobs.outputs.build_e3_support_dev }} + build_circuits: ${{ steps.jobs.outputs.build_circuits }} + integration_prebuild: ${{ steps.jobs.outputs.integration_prebuild }} + zk_prover_integration: ${{ steps.jobs.outputs.zk_prover_integration }} + steps: + - uses: actions/checkout@v6 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + rust: + - 'crates/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + contracts: + - 'packages/enclave-contracts/**' + circuits: + - 'circuits/**' + - 'scripts/*-circuits.sh' + crisp: + - 'examples/CRISP/**' + templates: + - 'templates/**' + sdk: + - 'packages/enclave-sdk/**' + - 'packages/enclave-react/**' + - 'packages/enclave-config/**' + - 'crates/wasm/**' + integration_tests: + - 'tests/integration/**' + docker: + - 'crates/Dockerfile' + - 'crates/support/Dockerfile' + - 'crates/support/**' + ci: + - '.github/**' + - name: Compute job filters + id: jobs + run: | + RUST="${{ steps.filter.outputs.rust }}" + CONTRACTS="${{ steps.filter.outputs.contracts }}" + CIRCUITS="${{ steps.filter.outputs.circuits }}" + CRISP="${{ steps.filter.outputs.crisp }}" + TEMPLATES="${{ steps.filter.outputs.templates }}" + SDK="${{ steps.filter.outputs.sdk }}" + INTEGRATION="${{ steps.filter.outputs.integration_tests }}" + DOCKER="${{ steps.filter.outputs.docker }}" + CI="${{ steps.filter.outputs.ci }}" + FORCE="${{ github.event_name == 'workflow_dispatch' }}" + + any() { for v in "$@"; do [ "$v" = "true" ] && echo "true" && return; done; echo "false"; } + + echo "rust_tests=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $CI)" >> $GITHUB_OUTPUT + echo "build_sdk=$(any "$FORCE" "$RUST" "$CONTRACTS" "$SDK" "$INTEGRATION" "$CIRCUITS" "$CI" "$TEMPLATES")" >> $GITHUB_OUTPUT + echo "crisp=$(any $FORCE $CRISP $CONTRACTS $CIRCUITS $RUST $CI)" >> $GITHUB_OUTPUT + echo "templates=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT + echo "zk=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT + echo "contracts=$(any $FORCE $CONTRACTS $CI)" >> $GITHUB_OUTPUT + echo "docker_support=$(any $FORCE $DOCKER $CI)" >> $GITHUB_OUTPUT + echo "docker_ciphernode=$(any $FORCE $RUST $CONTRACTS $DOCKER $CI)" >> $GITHUB_OUTPUT + echo "net=$(any $FORCE $INTEGRATION $CI)" >> $GITHUB_OUTPUT + echo "init=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $CI)" >> $GITHUB_OUTPUT + echo "build_sdk=$(any $FORCE $RUST $CONTRACTS $SDK $CI $TEMPLATES)" >> $GITHUB_OUTPUT + echo "build_e3_support_dev=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT + echo "build_circuits=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT + echo "integration_prebuild=$(any "$FORCE" "$RUST" "$CONTRACTS" "$CIRCUITS" "$INTEGRATION" "$CI")" >> $GITHUB_OUTPUT + echo "zk_prover_integration=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT + rust_tests: + needs: [detect_changes] + if: needs.detect_changes.outputs.rust_tests == 'true' runs-on: group: enclave-ci labels: [enclave-ci-runner] @@ -81,6 +163,8 @@ jobs: run: 'cargo test --test integration -- --nocapture' zk_prover_integration: + needs: [detect_changes] + if: needs.detect_changes.outputs.zk_prover_integration == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -110,6 +194,8 @@ jobs: run: 'cargo test -p e3-zk-prover --features integration-tests --test integration_tests -- --nocapture' build_e3_support_risc0: + needs: [detect_changes] + if: needs.detect_changes.outputs.docker_support == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -147,6 +233,8 @@ jobs: type=gha,mode=max,scope=e3-support build_ciphernode_image: + needs: [detect_changes] + if: needs.detect_changes.outputs.docker_ciphernode == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -187,6 +275,8 @@ jobs: type=gha,mode=max,scope=ciphernode test_contracts: + needs: [detect_changes] + if: needs.detect_changes.outputs.contracts == 'true' runs-on: 'ubuntu-latest' steps: - name: 'Check out the repo' @@ -222,6 +312,8 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY test_net: + needs: [detect_changes] + if: needs.detect_changes.outputs.net == 'true' runs-on: 'ubuntu-latest' steps: - name: 'Check out the repo' @@ -235,6 +327,9 @@ jobs: echo "✅ Passed" >> $GITHUB_STEP_SUMMARY integration_prebuild: + needs: [detect_changes] + # TODO: re-enable with ciphernode_integration_test + if: false && needs.detect_changes.outputs.integration_prebuild == 'true' runs-on: 'ubuntu-latest' steps: - name: 'Check out the repo' @@ -291,7 +386,9 @@ jobs: if-no-files-found: error ciphernode_integration_test: - needs: [integration_prebuild, build_enclave_cli, build_sdk] + needs: [detect_changes, integration_prebuild, build_enclave_cli, build_sdk] + # TODO: discuss if we need to re-enable — template_integration covers the same flow + if: false && needs.detect_changes.outputs.ciphernode_e2e == 'true' runs-on: group: enclave-ci labels: [enclave-ci-runner] @@ -355,6 +452,7 @@ jobs: echo "## Test results for ${{ matrix.test-suite }}" >> $GITHUB_STEP_SUMMARY echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + # Always runs — shared artifact dependency for many downstream jobs build_enclave_cli: runs-on: 'ubuntu-latest' steps: @@ -387,8 +485,9 @@ jobs: retention-days: 1 crisp_unit: + needs: [detect_changes, build_crisp_sdk] + if: needs.detect_changes.outputs.crisp == 'true' runs-on: 'ubuntu-latest' - needs: [build_crisp_sdk] steps: - uses: actions/checkout@v6 with: @@ -460,10 +559,11 @@ jobs: run: 'pnpm test:contracts' crisp_e2e: + needs: [detect_changes, build_enclave_cli, build_crisp_sdk] + if: needs.detect_changes.outputs.crisp == 'true' runs-on: group: enclave-ci labels: [enclave-ci-runner] - needs: [build_enclave_cli, build_crisp_sdk] steps: - uses: actions/checkout@v6 with: @@ -573,6 +673,8 @@ jobs: retention-days: 30 build_circuits: + needs: [detect_changes] + if: needs.detect_changes.outputs.build_circuits == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -626,8 +728,9 @@ jobs: if-no-files-found: error zk_prover_e2e: + needs: [detect_changes, build_circuits] + if: needs.detect_changes.outputs.zk == 'true' runs-on: 'ubuntu-latest' - needs: [build_circuits] steps: - uses: actions/checkout@v6 @@ -689,6 +792,8 @@ jobs: run: cargo test -p e3-zk-prover --test local_e2e_tests -- --nocapture build_e3_support_dev: + needs: [detect_changes] + if: needs.detect_changes.outputs.build_e3_support_dev == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -718,6 +823,8 @@ jobs: if-no-files-found: error build_sdk: + needs: [detect_changes] + if: needs.detect_changes.outputs.build_sdk == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -774,6 +881,8 @@ jobs: if-no-files-found: warn build_crisp_sdk: + needs: [detect_changes] + if: needs.detect_changes.outputs.crisp == 'true' runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6 @@ -821,10 +930,11 @@ jobs: if-no-files-found: warn template_integration: + needs: [detect_changes, build_enclave_cli, build_e3_support_dev, build_sdk] + if: needs.detect_changes.outputs.templates == 'true' runs-on: group: enclave-ci labels: [enclave-ci-runner] - needs: [build_enclave_cli, build_e3_support_dev, build_sdk] steps: - uses: actions/checkout@v6 with: @@ -895,8 +1005,9 @@ jobs: pnpm test:integration test_enclave_init: + needs: [detect_changes, build_enclave_cli, build_e3_support_dev] + if: needs.detect_changes.outputs.init == 'true' runs-on: 'ubuntu-latest' - needs: [build_enclave_cli, build_e3_support_dev] steps: - name: Install pnpm uses: pnpm/action-setup@v4 diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index b44f08aded..39b22fadbd 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -58,11 +58,43 @@ type E3StateOutputPublished = E3Shared & { type E3State = E3StateRequested | E3StatePublished | E3StateOutputPublished async function setupEventListeners(sdk: EnclaveSDK, store: Map) { - async function waitForEvent(type: T, trigger?: () => Promise): Promise> { + async function waitForEvent( + type: T, + trigger?: () => Promise, + timeoutMs?: number, + ): Promise> { return new Promise((resolve, reject) => { - sdk.once(type, resolve).catch(reject) + let settled = false + let timer: ReturnType | undefined + + const handler = (event: EnclaveEvent) => { + if (settled) return + settled = true + if (timer !== undefined) clearTimeout(timer) + sdk.off(type, handler) + resolve(event) + } + + const fail = (err: unknown) => { + if (settled) return + settled = true + if (timer !== undefined) clearTimeout(timer) + sdk.off(type, handler) + reject(err) + } + + // Use onEnclaveEvent so `handler` is the actual registered reference + // (sdk.once wraps in an internal closure, making sdk.off unable to remove it) + sdk.onEnclaveEvent(type, handler).catch(fail) + + if (timeoutMs !== undefined) { + timer = setTimeout(() => { + fail(new Error(`Timed out waiting for event: ${type} after ${timeoutMs}ms`)) + }, timeoutMs) + } + if (trigger) { - trigger().catch(reject) + trigger().catch(fail) } }) } @@ -102,6 +134,7 @@ async function setupEventListeners(sdk: EnclaveSDK, store: Map) await sdk.onEnclaveEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, (event) => { const id = event.data.e3Id + const state = store.get(id) if (!state) { @@ -156,7 +189,7 @@ describe('Integration', () => { const { waitForEvent } = await setupEventListeners(sdk, store) const threshold: [number, number] = [DEFAULT_E3_CONFIG.threshold_min, DEFAULT_E3_CONFIG.threshold_max] - const duration = 250 + const duration = 600 const inputWindow = await calculateInputWindow(publicClient, duration) const thresholdBfvParams = await sdk.getThresholdBfvParamsSet() const e3ProgramParams = encodeBfvParams(thresholdBfvParams) @@ -189,10 +222,16 @@ describe('Integration', () => { await new Promise((resolve) => setTimeout(resolve, 1000)) // REQUEST phase - await waitForEvent(EnclaveEventType.E3_REQUESTED, async () => { - console.log('Requested E3...') - await sdk.requestE3(requestParams) - }) + const timeoutMs = duration * 1000 + + await waitForEvent( + EnclaveEventType.E3_REQUESTED, + async () => { + console.log('Requested E3...') + await sdk.requestE3(requestParams) + }, + timeoutMs, + ) state = store.get(0n) assert(state, 'store should have E3State but it was falsey') @@ -205,7 +244,7 @@ describe('Integration', () => { assert.strictEqual(stageAfterRequest, E3Stage.Requested, 'E3 stage should be Requested after requestE3') // Ciphernodes will publish a public key within the COMMITTEE_PUBLISHED event - event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED) + event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED, undefined, timeoutMs) const publicKeyBytes = hexToBytes(event.data.publicKey as `0x${string}`) @@ -244,7 +283,7 @@ describe('Integration', () => { ) await sdk.waitForTransaction(txHash) - const plaintextEvent = await waitForEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED) + const plaintextEvent = await waitForEvent(EnclaveEventType.PLAINTEXT_OUTPUT_PUBLISHED, undefined, timeoutMs) const result = decodePlaintextOutput(plaintextEvent.data.plaintextOutput) assert(result !== null, 'Failed to decode plaintext output') diff --git a/tests/integration/fns.sh b/tests/integration/fns.sh index 1a09dd3182..ab647c47bf 100644 --- a/tests/integration/fns.sh +++ b/tests/integration/fns.sh @@ -65,7 +65,13 @@ strip_ansi() { waiton() { local file_path="$1" + local timeout="${2:-600}" # default 10 minutes + local start_time=$(date +%s) until [ -f "$file_path" ]; do + if [ $(($(date +%s) - start_time)) -ge $timeout ]; then + echo "Timeout after ${timeout}s waiting for: $file_path" >&2 + return 1 + fi sleep 1 done } From fb30e3ef29a0da830585c4678cdaf15b092bd20a Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:17:26 +0000 Subject: [PATCH 2/2] chore: cleanup --- .github/workflows/ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73955dd8b8..8f7716655f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,6 @@ jobs: net: ${{ steps.jobs.outputs.net }} init: ${{ steps.jobs.outputs.init }} build_sdk: ${{ steps.jobs.outputs.build_sdk }} - build_crisp_sdk: ${{ steps.jobs.outputs.build_crisp_sdk }} build_e3_support_dev: ${{ steps.jobs.outputs.build_e3_support_dev }} build_circuits: ${{ steps.jobs.outputs.build_circuits }} integration_prebuild: ${{ steps.jobs.outputs.integration_prebuild }} @@ -98,7 +97,8 @@ jobs: any() { for v in "$@"; do [ "$v" = "true" ] && echo "true" && return; done; echo "false"; } echo "rust_tests=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $CI)" >> $GITHUB_OUTPUT - echo "build_sdk=$(any "$FORCE" "$RUST" "$CONTRACTS" "$SDK" "$INTEGRATION" "$CIRCUITS" "$CI" "$TEMPLATES")" >> $GITHUB_OUTPUT + echo "ciphernode_e2e=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $INTEGRATION $CI)" >> $GITHUB_OUTPUT + echo "build_sdk=$(any $FORCE $RUST $CONTRACTS $SDK $INTEGRATION $CIRCUITS $CI $TEMPLATES)" >> $GITHUB_OUTPUT echo "crisp=$(any $FORCE $CRISP $CONTRACTS $CIRCUITS $RUST $CI)" >> $GITHUB_OUTPUT echo "templates=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT echo "zk=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT @@ -107,10 +107,9 @@ jobs: echo "docker_ciphernode=$(any $FORCE $RUST $CONTRACTS $DOCKER $CI)" >> $GITHUB_OUTPUT echo "net=$(any $FORCE $INTEGRATION $CI)" >> $GITHUB_OUTPUT echo "init=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $CI)" >> $GITHUB_OUTPUT - echo "build_sdk=$(any $FORCE $RUST $CONTRACTS $SDK $CI $TEMPLATES)" >> $GITHUB_OUTPUT echo "build_e3_support_dev=$(any $FORCE $TEMPLATES $RUST $CONTRACTS $SDK $CI)" >> $GITHUB_OUTPUT echo "build_circuits=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT - echo "integration_prebuild=$(any "$FORCE" "$RUST" "$CONTRACTS" "$CIRCUITS" "$INTEGRATION" "$CI")" >> $GITHUB_OUTPUT + echo "integration_prebuild=$(any $FORCE $RUST $CONTRACTS $CIRCUITS $INTEGRATION $CI)" >> $GITHUB_OUTPUT echo "zk_prover_integration=$(any $FORCE $RUST $CIRCUITS $CI)" >> $GITHUB_OUTPUT rust_tests: @@ -328,8 +327,7 @@ jobs: integration_prebuild: needs: [detect_changes] - # TODO: re-enable with ciphernode_integration_test - if: false && needs.detect_changes.outputs.integration_prebuild == 'true' + if: needs.detect_changes.outputs.integration_prebuild == 'true' runs-on: 'ubuntu-latest' steps: - name: 'Check out the repo' @@ -387,14 +385,14 @@ jobs: ciphernode_integration_test: needs: [detect_changes, integration_prebuild, build_enclave_cli, build_sdk] - # TODO: discuss if we need to re-enable — template_integration covers the same flow - if: false && needs.detect_changes.outputs.ciphernode_e2e == 'true' + if: needs.detect_changes.outputs.ciphernode_e2e == 'true' runs-on: group: enclave-ci labels: [enclave-ci-runner] strategy: matrix: - test-suite: [base, persist] + # TODO removed base test for now + test-suite: [persist] fail-fast: false steps: - name: 'Check out the repo'