From 1167846b03ef9ecedb381b8df02ac74b5e138309 Mon Sep 17 00:00:00 2001 From: Wolfvin Date: Fri, 17 Jul 2026 13:20:11 +0700 Subject: [PATCH] fix(ci): cap every job at 30m and name hanging tests (refs #303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeLens CI has not gone green in 60+ runs. Every run is marked "cancelled", but nobody cancels them: run 29502899082 ran from 13:36:17Z to 19:36:35Z — 6h00m18s, GitHub's hard job ceiling. The "Run test suite" step hangs on Linux while the same suite finishes in 160s locally, so each push burned 6h of Actions minutes across CI + Quality Gate (4 python matrix) + SARIF, and the merge gate has been fiction since at least July 14. No workflow had timeout-minutes at all. That is what made this durable: logs are unavailable while a job is in_progress, so a hang that only ends at the 6h kill can never be read. The problem locked away the evidence needed to diagnose it. Two changes, both diagnostics rather than a cure: - timeout-minutes: 30 on all 14 jobs across 8 workflows — a hang now dies in minutes with a readable log instead of burning 6h in silence. - pytest-timeout at --timeout=120 --timeout-method=thread on the test job — the suite is ~160s in full, so any single test past 120s is stuck, not slow. Verified locally: the timeout fires and prints the offending test's name, file and line. This does NOT fix the hang. #303 stays open until the root cause is known; the cap only makes it visible and cheap to investigate. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/codelens-benchmark.yml | 1 + .github/workflows/codelens-ci.yml | 11 ++++++++++- .github/workflows/codelens-quality-gate.yml | 2 ++ .github/workflows/codelens-sarif.yml | 1 + .github/workflows/design-doc-check.yml | 1 + .github/workflows/docker-publish.yml | 1 + .github/workflows/publish-pypi.yml | 3 +++ .github/workflows/refresh-vuln-db.yml | 1 + 8 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codelens-benchmark.yml b/.github/workflows/codelens-benchmark.yml index 40f9a170..917606f2 100644 --- a/.github/workflows/codelens-benchmark.yml +++ b/.github/workflows/codelens-benchmark.yml @@ -8,6 +8,7 @@ on: jobs: benchmark: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - name: Set up Python diff --git a/.github/workflows/codelens-ci.yml b/.github/workflows/codelens-ci.yml index 5cffd263..e97c6e08 100644 --- a/.github/workflows/codelens-ci.yml +++ b/.github/workflows/codelens-ci.yml @@ -16,6 +16,7 @@ jobs: test: name: Test Suite runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Checkout repository @@ -37,20 +38,26 @@ jobs: # and causes a native use-after-free SIGSEGV in tree_sitter_python._binding # even on shallow files (issue #235). python_parser.py's keep_alive # mitigation is insufficient for 0.26; 0.25.x is known-safe. - pip install "tree-sitter>=0.21.0,<0.26" pyyaml pytest pytest-cov pygls lsprotocol + # pytest-timeout: names the hanging test instead of letting the job + # burn the 6h ceiling in silence (issue #303). + pip install "tree-sitter>=0.21.0,<0.26" pyyaml pytest pytest-cov pytest-timeout pygls lsprotocol # Install optional grammar packages for full parser coverage pip install tree-sitter-python tree-sitter-javascript tree-sitter-html tree-sitter-css || true pip install tree-sitter-typescript tree-sitter-rust || true - name: Run test suite run: | + # --timeout: the whole suite runs in ~160s locally, so any single + # test past 120s is stuck, not slow. `thread` method dumps its stack. cd scripts && python -m pytest ../tests/ -v --tb=short \ + --timeout=120 --timeout-method=thread \ --ignore=../tests/test_integration.py # ─── Benchmark ───────────────────────────────────────────────── benchmark: name: Quick Benchmark runs-on: ubuntu-latest + timeout-minutes: 30 needs: test steps: @@ -81,6 +88,7 @@ jobs: self-check: name: CodeLens Self-Check runs-on: ubuntu-latest + timeout-minutes: 30 needs: test steps: @@ -125,6 +133,7 @@ jobs: upload-sarif: name: Upload SARIF to Code Scanning runs-on: ubuntu-latest + timeout-minutes: 30 needs: self-check if: github.event_name == 'push' && github.ref == 'refs/heads/main' diff --git a/.github/workflows/codelens-quality-gate.yml b/.github/workflows/codelens-quality-gate.yml index 8d2c1a8b..4c222641 100644 --- a/.github/workflows/codelens-quality-gate.yml +++ b/.github/workflows/codelens-quality-gate.yml @@ -9,6 +9,7 @@ on: jobs: quality-gate: runs-on: ubuntu-latest + timeout-minutes: 30 strategy: matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] @@ -47,6 +48,7 @@ jobs: benchmark: runs-on: ubuntu-latest + timeout-minutes: 30 needs: quality-gate if: github.event_name == 'push' && github.ref == 'refs/heads/main' diff --git a/.github/workflows/codelens-sarif.yml b/.github/workflows/codelens-sarif.yml index cb6103cb..860c9eb0 100644 --- a/.github/workflows/codelens-sarif.yml +++ b/.github/workflows/codelens-sarif.yml @@ -16,6 +16,7 @@ jobs: sarif: name: Generate SARIF for Code Scanning runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Checkout repository diff --git a/.github/workflows/design-doc-check.yml b/.github/workflows/design-doc-check.yml index 0ec6a5bd..46616b5d 100644 --- a/.github/workflows/design-doc-check.yml +++ b/.github/workflows/design-doc-check.yml @@ -32,6 +32,7 @@ jobs: check: name: Design Doc Required runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Checkout repository diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c46e2ce4..5472f177 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -49,6 +49,7 @@ jobs: build-and-push: name: Build & Push ${{ matrix.variant }} runs-on: ubuntu-latest + timeout-minutes: 30 if: github.repository == 'Wolfvin/CodeLens' strategy: fail-fast: false diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index d87b0a74..896067a9 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -18,6 +18,7 @@ jobs: build: name: Build sdist + wheel runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Checkout repository @@ -56,6 +57,7 @@ jobs: publish-pypi: name: Publish to PyPI runs-on: ubuntu-latest + timeout-minutes: 30 needs: build environment: pypi # Requires manual approval for first-time publishes @@ -75,6 +77,7 @@ jobs: publish-testpypi: name: Publish to TestPyPI (on manual trigger) runs-on: ubuntu-latest + timeout-minutes: 30 needs: build if: github.event_name == 'workflow_dispatch' environment: testpypi diff --git a/.github/workflows/refresh-vuln-db.yml b/.github/workflows/refresh-vuln-db.yml index aae313d3..d430a356 100644 --- a/.github/workflows/refresh-vuln-db.yml +++ b/.github/workflows/refresh-vuln-db.yml @@ -32,6 +32,7 @@ permissions: jobs: refresh-vuln-db: runs-on: ubuntu-latest + timeout-minutes: 30 # Only run on the main repo (not forks). Schedule triggers on forks # are disabled by GitHub anyway, but this guards workflow_dispatch. if: github.repository == 'Wolfvin/CodeLens'