Skip to content

feat(bulldozer-js): opt-in IO delay for the in-memory low-level backend #13191

feat(bulldozer-js): opt-in IO delay for the in-memory low-level backend

feat(bulldozer-js): opt-in IO delay for the in-memory low-level backend #13191

Workflow file for this run

name: "Run setup tests"
on:
push:
branches:
- main
- dev
pull_request:
# A concurrency group only ever runs one run at a time, so the sha is part of the group on main/dev:
# otherwise consecutive dev pushes queue behind each other (and GitHub cancels the older queued run),
# so one slow or hung run silently skips CI for every commit after it. PRs and feature branches stay
# grouped per ref, where cancelling superseded runs is what we want.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && format('-{0}', github.sha) || '' }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
env:
SHELL: /usr/bin/bash
jobs:
wait-for-fast-fail:
name: wait-for-fast-fail
if: ${{ (github.head_ref || github.ref_name) == 'dev' }}
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: ./.github/actions/wait-for-check
with:
workflow-file: fast-fail-tests.yaml
github-token: ${{ secrets.GITHUB_TOKEN }}
setup-tests:
timeout-minutes: 90
needs: wait-for-fast-fail
# `success()` is required because the custom `if` otherwise overrides the
# implicit success() gate from `needs`, letting this run even if the gate failed.
if: ${{ (github.head_ref || github.ref_name) == 'dev' && success() }}
runs-on: ubicloud-standard-16
env:
HEXCLAVE_EXTERNAL_DB_SYNC_MAX_DURATION_MS: "20000"
HEXCLAVE_EXTERNAL_DB_SYNC_DIRECT: "false"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup Node.js v22
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
- name: Install packages
run: pnpm install
- run: pnpm run build:packages
- run: pnpm run codegen
- run: pnpm run start-deps
- name: Start development services with persistent logs
env:
# Package/codegen watchers still run, but CI keeps the already-built backend process stable.
STACK_BACKEND_DEV_DISABLE_WATCH: "true"
run: |
dev_log="$RUNNER_TEMP/hexclave-dev.untracked.log"
(
set +e
pnpm run dev >"$dev_log" 2>&1
echo "$?" > "$RUNNER_TEMP/hexclave-dev-exit-code.untracked.txt"
) &
pnpm exec wait-on --timeout 120000 http://localhost:8102 tcp:localhost:8146
# The readiness gate only proves that 8102 and 8146 accept connections;
# both services normally listen within seconds. build-packages:watch then
# performs a full non-watch tsdown pass even though build:packages already
# ran, rewriting packages/*/dist (about 8.3k of 8.4k files in our
# reproduction, including about 270 content changes). Watch-based dev
# tasks such as bulldozer-js, which imports @hexclave/shared from dist,
# restart when those files land and briefly take their ports down. That
# caused the failures in run 31347402547; the observed bulldozer outage
# was about 1.7 seconds and happened tens of seconds after this gate.
# This is a mitigation, not a guarantee: a slow runner may still be
# rebuilding at 60 seconds, and codegen watchers can write files later.
# We keep the watchers enabled so this job still exercises pnpm run dev,
# and instead allow the initial rebuild and restarts to settle first.
- name: Allow development watchers to settle
run: sleep 60
# The cause of missing per-task output in some historical runs is still
# unexplained. Snapshot the log before tests so a future run shows
# whether the output was absent before testing began or disappeared
# later.
- name: Snapshot development service diagnostics before tests
run: |
dev_log="$RUNNER_TEMP/hexclave-dev.untracked.log"
if [[ -f "$dev_log" ]]; then
echo "Development service log size: $(wc -c < "$dev_log") bytes"
echo "Development service log lines: $(wc -l < "$dev_log")"
echo "Development service log excerpt (last 200 lines):"
tail -n 200 "$dev_log"
else
echo "Development service log does not exist yet"
fi
- name: Run tests (first attempt)
id: run-tests-first-attempt
continue-on-error: ${{ (github.head_ref || github.ref_name) != 'main' && (github.head_ref || github.ref_name) != 'dev' }}
run: pnpm run test run --reporter=verbose
# These tests are often flakey, as a temporary measure we retry them once.
- name: Run tests (retry once on non-dev/main branches)
if: ${{ (github.head_ref || github.ref_name) != 'main' && (github.head_ref || github.ref_name) != 'dev' && steps.run-tests-first-attempt.outcome == 'failure' }}
run: pnpm run test run --reporter=verbose
- name: Print development service diagnostics
# Keep the stream log visible even when a retry turns a transient test
# failure into a green job; that is when the first failure is easiest
# to diagnose. The log size and line count distinguish a genuinely
# sparse log from output that was only truncated in this step.
if: ${{ always() }}
run: |
if [[ -f "$RUNNER_TEMP/hexclave-dev-exit-code.untracked.txt" ]]; then
echo "Development service command exited with code $(<"$RUNNER_TEMP/hexclave-dev-exit-code.untracked.txt")"
else
echo "Development service command is still running; a child service may have exited"
fi
echo "Development service log size: $(wc -c < "$RUNNER_TEMP/hexclave-dev.untracked.log") bytes"
echo "Development service log lines: $(wc -l < "$RUNNER_TEMP/hexclave-dev.untracked.log")"
tail -n 2000 "$RUNNER_TEMP/hexclave-dev.untracked.log"
- name: Upload development service logs
if: ${{ always() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: setup-tests-development-services
path: ${{ runner.temp }}/hexclave-dev.untracked.log
if-no-files-found: error