fix(dropdown): align overflow and shortcut behavior with the public c… #1815
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: ci | |
| on: | |
| pull_request: | |
| push: | |
| permissions: | |
| contents: read | |
| # Cancel previous runs on the same branch/PR — the single biggest win for | |
| # unblocking queued PRs when authors force-push or push fixups. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| dependency-review: | |
| if: github.event_name == 'pull_request' | |
| name: dependency review | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Detect dependency review support | |
| id: support | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| REPOSITORY: ${{ github.repository }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euo pipefail | |
| response_path="$(mktemp)" | |
| headers_path="$(mktemp)" | |
| status_code="$( | |
| curl -sS \ | |
| -D "${headers_path}" \ | |
| -H "Authorization: Bearer ${GITHUB_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| -o "${response_path}" \ | |
| -w "%{http_code}" \ | |
| "https://api.github.com/repos/${REPOSITORY}/dependency-graph/compare/${BASE_SHA}...${HEAD_SHA}" | |
| )" | |
| message="$(jq -r '.message // ""' "${response_path}" 2>/dev/null || true)" | |
| rate_limit_remaining="$(awk 'tolower($1)=="x-ratelimit-remaining:" { print $2 }' "${headers_path}" | tr -d '\r')" | |
| retry_after="$(awk 'tolower($1)=="retry-after:" { print $2 }' "${headers_path}" | tr -d '\r')" | |
| skip_dependency_review() { | |
| local reason="$1" | |
| echo "supported=false" >> "${GITHUB_OUTPUT}" | |
| echo "::warning::Dependency review skipped because ${reason}. Enable Dependency graph / Advanced Security for this repository and event context to enforce this gate." | |
| { | |
| echo "### Dependency review skipped" | |
| echo | |
| echo "${reason}." | |
| echo | |
| echo "- HTTP status: ${status_code}" | |
| echo "- API message: ${message:-unknown}" | |
| if [[ -n "${rate_limit_remaining}" ]]; then | |
| echo "- X-RateLimit-Remaining: ${rate_limit_remaining}" | |
| fi | |
| if [[ -n "${retry_after}" ]]; then | |
| echo "- Retry-After: ${retry_after}" | |
| fi | |
| echo "- Action: enable Dependency graph / Advanced Security for this repository and event context to enforce this gate" | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| } | |
| case "${status_code}" in | |
| 200) | |
| echo "supported=true" >> "${GITHUB_OUTPUT}" | |
| ;; | |
| 404) | |
| skip_dependency_review "GitHub dependency review support is unavailable for this repository or event context" | |
| ;; | |
| 403) | |
| if [[ "${rate_limit_remaining:-}" == "0" || -n "${retry_after}" ]]; then | |
| echo "Dependency review preflight hit GitHub API rate limiting" >&2 | |
| echo "X-RateLimit-Remaining: ${rate_limit_remaining:-unknown}" >&2 | |
| echo "Retry-After: ${retry_after:-none}" >&2 | |
| cat "${response_path}" >&2 | |
| exit 1 | |
| fi | |
| if echo "${message}" | grep -qiE '(^forbidden$|dependency graph|advanced security|repository is a fork|disabled|not enabled|unavailable)'; then | |
| skip_dependency_review "GitHub dependency review returned HTTP 403 without rate-limit signals for this repository or event context" | |
| else | |
| echo "Dependency review preflight failed with HTTP 403: ${message:-unknown}" >&2 | |
| cat "${response_path}" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "Dependency review preflight failed with HTTP ${status_code}" >&2 | |
| cat "${response_path}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| - uses: actions/checkout@v4 | |
| if: steps.support.outputs.supported == 'true' | |
| with: | |
| submodules: false | |
| - name: Review dependency changes | |
| if: steps.support.outputs.supported == 'true' | |
| uses: actions/dependency-review-action@v4 | |
| # Fast gate: run the repo's blocking static analysis and quality policy once | |
| # on Linux/Node 22 before the slower matrix and smoke jobs fan out. | |
| quality-gates: | |
| name: quality gates | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - name: Install repo utilities | |
| run: | | |
| if ! command -v rg >/dev/null 2>&1; then | |
| sudo apt-get update | |
| sudo apt-get install -y ripgrep | |
| fi | |
| - name: Install | |
| run: npm ci | |
| - name: Drawlist codegen guardrail | |
| run: npm run codegen:check | |
| - name: Lint | |
| run: npm run lint | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Build | |
| run: npm run build | |
| - name: Full TS project graph | |
| run: npm run typecheck:ci | |
| - name: Strict publishable source TS pass | |
| run: npm run typecheck:strict | |
| - name: Check core portability | |
| run: npm run check:core-portability | |
| - name: Check native vendor integrity | |
| run: npm run check:native-vendor | |
| - name: Check Unicode pins (submodule sync) | |
| run: npm run check:unicode | |
| - name: Repo guardrails | |
| run: npm run quality:guardrails | |
| - name: Dependency graph guardrail | |
| run: npm run quality:deps | |
| - name: API surface guardrail | |
| run: npm run quality:api | |
| - name: Dead code advisory | |
| id: quality_deadcode | |
| continue-on-error: true | |
| run: npm run quality:deadcode | |
| - name: Dead code advisory summary | |
| if: ${{ steps.quality_deadcode.outcome != 'success' }} | |
| run: echo "::warning::Knip remains advisory in this PR. Review the dead-code report separately from the blocking quality gates." | |
| template-smoke: | |
| name: create-rezi smoke | |
| needs: [quality-gates] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - name: Install | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| - name: Smoke scaffolded templates | |
| run: npm run smoke:create-rezi-templates | |
| # Compute matrix: 5 runners on PRs (all Node versions on Linux + latest on | |
| # macOS/Windows), full 3×3 on push to main. | |
| matrix-config: | |
| name: configure matrix | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.set.outputs.matrix }} | |
| steps: | |
| - id: set | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| echo 'matrix={"include":[{"os":"ubuntu-latest","node-version":"18"},{"os":"ubuntu-latest","node-version":"20"},{"os":"ubuntu-latest","node-version":"22"},{"os":"macos-latest","node-version":"22"},{"os":"windows-latest","node-version":"22"}]}' >> "$GITHUB_OUTPUT" | |
| else | |
| echo 'matrix={"os":["ubuntu-latest","macos-latest","windows-latest"],"node-version":["18","20","22"]}' >> "$GITHUB_OUTPUT" | |
| fi | |
| ci: | |
| needs: [quality-gates, template-smoke, matrix-config] | |
| name: node ${{ matrix.node-version }} / ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.matrix-config.outputs.matrix) }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: npm | |
| - name: Install | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| - name: Tests | |
| run: npm run test | |
| - name: Repro replay fixtures (headless, explicit gate) | |
| if: runner.os == 'Linux' | |
| run: | | |
| CONCURRENCY=$(node -p "parseInt(process.versions.node)>=19?'--test-concurrency=1':''") | |
| node --test $CONCURRENCY packages/core/dist/repro/__tests__/replay.harness.test.js | |
| - name: Setup Rust (stable) | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Build native addon | |
| run: npm run build:native | |
| - name: Terminal e2e (Linux full) | |
| if: runner.os == 'Linux' | |
| run: npm run test:e2e | |
| - name: Terminal e2e (Linux reduced) | |
| if: runner.os == 'Linux' | |
| run: npm run test:e2e:reduced | |
| - name: Terminal e2e equivalent (non-Linux reduced) | |
| if: runner.os != 'Linux' | |
| run: npm run test:e2e:reduced | |
| - name: Native smoke + worker integration (Unix PTY) | |
| if: runner.os != 'Windows' | |
| run: | | |
| python3 - <<'PY' | |
| import os | |
| import pty | |
| import sys | |
| status = pty.spawn(["bash", "-lc", "npm run test:native:smoke"]) | |
| if os.WIFEXITED(status): | |
| sys.exit(os.WEXITSTATUS(status)) | |
| if os.WIFSIGNALED(status): | |
| sys.exit(128 + os.WTERMSIG(status)) | |
| sys.exit(1) | |
| PY | |
| - name: Native smoke + worker integration (Windows) | |
| if: runner.os == 'Windows' | |
| run: npm run test:native:smoke | |
| bun: | |
| needs: [quality-gates] | |
| name: bun / ubuntu-latest | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.9" | |
| - name: Install | |
| run: bun install | |
| - name: Build | |
| run: bun run build | |
| - name: Tests | |
| run: bun run test | |
| - name: Terminal e2e (reduced) | |
| run: bun run test:e2e:reduced | |
| perf-regression: | |
| name: perf regression gate | |
| if: github.event_name == 'pull_request' | |
| needs: [quality-gates] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout (with submodules) | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - name: Install | |
| run: npm ci | |
| - name: Build packages | |
| run: npm run build | |
| - name: Build benchmark package | |
| run: npx tsc -p packages/bench/tsconfig.json | |
| - id: perf_bench | |
| name: Run reduced benchmark suite (non-blocking) | |
| continue-on-error: true | |
| run: npm run bench:ci -- --output-dir .artifacts/bench/ci | |
| - id: perf_compare | |
| name: Compare reduced benchmark results against baseline (non-blocking) | |
| if: ${{ always() }} | |
| continue-on-error: true | |
| run: | | |
| npm run bench:ci:compare -- \ | |
| --baseline benchmarks/ci-baseline/results.json \ | |
| --current .artifacts/bench/ci/results.json \ | |
| --config benchmarks/ci-baseline/config.json \ | |
| --report-json .artifacts/bench/ci/compare.json \ | |
| --report-md .artifacts/bench/ci/compare.md | |
| - name: Perf regression summary (non-blocking) | |
| if: ${{ always() }} | |
| run: | | |
| echo "bench outcome: ${{ steps.perf_bench.outcome }}" | |
| echo "compare outcome: ${{ steps.perf_compare.outcome }}" | |
| if [ "${{ steps.perf_bench.outcome }}" != "success" ] || [ "${{ steps.perf_compare.outcome }}" != "success" ]; then | |
| echo "::warning::Perf regression gate is non-blocking for CI stability. Review perf-regression artifacts." | |
| fi | |
| - name: Upload perf regression artifacts | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: perf-regression-artifacts | |
| if-no-files-found: warn | |
| path: | | |
| .artifacts/bench/ci/results.json | |
| .artifacts/bench/ci/results.md | |
| .artifacts/bench/ci/compare.json | |
| .artifacts/bench/ci/compare.md | |
| .artifacts/bench/ci/manifest.json | |
| .artifacts/bench/ci/profile.json |