From 47a726b1dd9d01fec0ba4c7486f25d01c899f08d Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:36:00 -0700 Subject: [PATCH] fix(ci): cancel long rust build subprocesses (#3942) --- .github/workflows/pr-ci.yml | 37 ++++++++++++++++++------------------ app/scripts/e2e-web-build.sh | 2 +- scripts/ci-cancel-aware.sh | 29 +++++++++++++++++++++++++--- scripts/test-rust-e2e.sh | 4 ++-- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index ef0ca25d1..35f1cbd31 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -14,6 +14,8 @@ permissions: env: NPM_CONFIG_STORE_DIR: .pnpm-store +# Keep only the newest run per PR/branch. Long Rust/Tauri build commands must +# run through scripts/ci-cancel-aware.sh so cancellation reaches descendants. concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.head_ref || github.ref }} cancel-in-progress: true @@ -56,11 +58,9 @@ jobs: - 'app/vite.config.*' - 'app/tailwind.config.*' - 'app/postcss.config.*' - - 'scripts/ci-cancel-aware.sh' i18n: - '.github/workflows/pr-ci.yml' - 'app/src/**' - - 'scripts/ci-cancel-aware.sh' rust-core: - '.github/workflows/pr-ci.yml' - 'Cargo.toml' @@ -69,6 +69,7 @@ jobs: - 'src/**' - 'tests/**' - 'scripts/ci-cancel-aware.sh' + - 'scripts/test-rust-e2e.sh' - 'scripts/test-rust-with-mock.sh' rust-tauri: - '.github/workflows/pr-ci.yml' @@ -116,7 +117,7 @@ jobs: pnpm-store-${{ runner.os }}- - name: Install dependencies - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -126,17 +127,17 @@ jobs: key: ${{ steps.pnpm-cache.outputs.cache-primary-key }} - name: Type check TypeScript files - run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app compile + run: pnpm --filter openhuman-app compile env: NODE_ENV: test - name: Check Prettier formatting - run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app format:check + run: pnpm --filter openhuman-app format:check env: NODE_ENV: test - name: Run ESLint - run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app lint + run: pnpm --filter openhuman-app lint env: NODE_ENV: test @@ -164,7 +165,7 @@ jobs: pnpm-store-${{ runner.os }}- - name: Install dependencies - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -174,7 +175,7 @@ jobs: key: ${{ steps.pnpm-cache.outputs.cache-primary-key }} - name: Verify i18n coverage (missing / extra / drifted keys across locales) - run: bash scripts/ci-cancel-aware.sh pnpm i18n:check + run: pnpm i18n:check rust-quality: name: Rust Quality (fmt, clippy) @@ -207,7 +208,7 @@ jobs: shared-key: pr-rust-quality - name: Check Rust formatting - run: bash scripts/ci-cancel-aware.sh cargo fmt --all -- --check + run: cargo fmt --all -- --check - name: Run clippy (core crate) run: bash scripts/ci-cancel-aware.sh cargo clippy -p openhuman @@ -258,7 +259,7 @@ jobs: - name: Install dependencies if: steps.playwright-artifact-cache.outputs.cache-hit != 'true' - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -277,7 +278,7 @@ jobs: if: steps.playwright-artifact-cache.outputs.cache-hit != 'true' run: | set -euo pipefail - bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app test:e2e:web:build + pnpm --filter openhuman-app test:e2e:web:build RUST_HOST_TRIPLE="$(rustc -vV | awk '/^host: / { print $2 }')" WEB_CORE_TARGET_DIR="target/e2e-web-${RUST_HOST_TRIPLE}" @@ -335,7 +336,7 @@ jobs: pnpm-store-${{ runner.os }}- - name: Install dependencies - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -345,7 +346,7 @@ jobs: key: ${{ steps.pnpm-cache.outputs.cache-primary-key }} - name: Run Vitest with coverage - run: bash ../scripts/ci-cancel-aware.sh pnpm test:coverage + run: pnpm test:coverage working-directory: app env: NODE_ENV: test @@ -562,7 +563,7 @@ jobs: shared-key: pr-rust-core - name: Install dependencies - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -577,7 +578,7 @@ jobs: touch app/.env - name: Run Rust E2E suite - run: bash scripts/ci-cancel-aware.sh pnpm test:rust:e2e + run: pnpm test:rust:e2e env: RUST_BACKTRACE: "1" @@ -627,7 +628,7 @@ jobs: pnpm-store-${{ runner.os }}- - name: Install JS dependencies (test harness) - run: bash scripts/ci-cancel-aware.sh pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Save pnpm store cache if: always() && steps.pnpm-cache.outputs.cache-hit != 'true' @@ -653,14 +654,14 @@ jobs: chmod +x target/e2e-web-*/debug/openhuman-core - name: Install Playwright Chromium headless shell - run: bash scripts/ci-cancel-aware.sh pnpm --filter openhuman-app exec playwright install chromium-headless-shell + run: pnpm --filter openhuman-app exec playwright install chromium-headless-shell - name: Run Playwright web E2E suite (shard ${{ matrix.shard }}/4) env: OPENHUMAN_WORKSPACE: ${{ runner.temp }}/openhuman-playwright-workspace run: | mkdir -p "$OPENHUMAN_WORKSPACE" - bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-web-session.sh \ + bash app/scripts/e2e-web-session.sh \ --shard=${{ matrix.shard }}/4 - name: Upload Playwright E2E failure artifacts diff --git a/app/scripts/e2e-web-build.sh b/app/scripts/e2e-web-build.sh index c1f26a446..51e4346d3 100755 --- a/app/scripts/e2e-web-build.sh +++ b/app/scripts/e2e-web-build.sh @@ -24,4 +24,4 @@ fi echo "Building web E2E bundle with backend ${VITE_BACKEND_URL}" pnpm run build:web echo "Building standalone openhuman-core for web E2E into ${E2E_WEB_CORE_TARGET_DIR}..." -CARGO_TARGET_DIR="$E2E_WEB_CORE_TARGET_DIR" cargo build --manifest-path "$REPO_ROOT/Cargo.toml" --bin openhuman-core +CARGO_TARGET_DIR="$E2E_WEB_CORE_TARGET_DIR" "$REPO_ROOT/scripts/ci-cancel-aware.sh" cargo build --manifest-path "$REPO_ROOT/Cargo.toml" --bin openhuman-core diff --git a/scripts/ci-cancel-aware.sh b/scripts/ci-cancel-aware.sh index 3d9c9bef1..680b9ac13 100644 --- a/scripts/ci-cancel-aware.sh +++ b/scripts/ci-cancel-aware.sh @@ -10,6 +10,7 @@ fi OS_NAME="$(uname -s 2>/dev/null || echo unknown)" CHILD_PID="" RECEIVED_SIGNAL="" +CHILD_OWNS_PROCESS_GROUP=0 is_windows_shell() { case "$OS_NAME" in @@ -35,6 +36,10 @@ terminate_tree_term() { return fi + if [ "$CHILD_OWNS_PROCESS_GROUP" = "1" ]; then + kill -TERM -- "-$pid" 2>/dev/null || true + fi + local descendants="" descendants="$(collect_descendants_unix "$pid")" if [ -n "$descendants" ]; then @@ -53,6 +58,10 @@ terminate_tree_kill() { return fi + if [ "$CHILD_OWNS_PROCESS_GROUP" = "1" ]; then + kill -KILL -- "-$pid" 2>/dev/null || true + fi + local descendants="" descendants="$(collect_descendants_unix "$pid")" if [ -n "$descendants" ]; then @@ -103,19 +112,33 @@ cleanup() { return "$status" } +start_child() { + if ! is_windows_shell && command -v setsid >/dev/null 2>&1; then + setsid "$@" & + CHILD_OWNS_PROCESS_GROUP=1 + else + "$@" & + CHILD_OWNS_PROCESS_GROUP=0 + fi + CHILD_PID=$! +} + trap 'forward_cancel INT' INT trap 'forward_cancel TERM' TERM trap 'forward_cancel HUP' HUP trap cleanup EXIT echo "[ci-cancel-aware] exec: $(printf '%q ' "$@")" >&2 -"$@" & -CHILD_PID=$! +start_child "$@" set +e wait "$CHILD_PID" status=$? set -e -CHILD_PID="" +# A trapped cancellation can interrupt wait before the child exits. Keep +# CHILD_PID set so the EXIT cleanup can escalate from TERM to KILL. +if ! kill -0 "$CHILD_PID" 2>/dev/null; then + CHILD_PID="" +fi exit "$status" diff --git a/scripts/test-rust-e2e.sh b/scripts/test-rust-e2e.sh index 0bb9a9548..b086796f4 100755 --- a/scripts/test-rust-e2e.sh +++ b/scripts/test-rust-e2e.sh @@ -133,9 +133,9 @@ echo "[rust-e2e] Running ${#SUITES[@]} suite(s) serially." for suite in "${SUITES[@]}"; do if [ "${#EXTRA_ARGS[@]}" -gt 0 ]; then echo "[rust-e2e] cargo test --manifest-path Cargo.toml --test $suite -- ${EXTRA_ARGS[*]}" - cargo test --manifest-path Cargo.toml --test "$suite" -- "${EXTRA_ARGS[@]}" + "$SCRIPT_DIR/ci-cancel-aware.sh" cargo test --manifest-path Cargo.toml --test "$suite" -- "${EXTRA_ARGS[@]}" else echo "[rust-e2e] cargo test --manifest-path Cargo.toml --test $suite" - cargo test --manifest-path Cargo.toml --test "$suite" + "$SCRIPT_DIR/ci-cancel-aware.sh" cargo test --manifest-path Cargo.toml --test "$suite" fi done