ci: make path-filtered required gates always report (unblock stuck PR… #68
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
| # SPDX-License-Identifier: MPL-2.0 | |
| # lsp-dap-bsp.yml — Dedicated CI for Language Server, Debug Adapter, and Build Server cartridges | |
| # Validates ABI specs, FFI builds, adapter compilation, and panel manifests | |
| # for the three protocol cartridges (lsp-mcp, dap-mcp, bsp-mcp). | |
| name: LSP/DAP/BSP CI | |
| # Was workflow-level path-filtered: required checks that never ran on PRs | |
| # touching none of its paths, leaving them "Expected" and the PR blocked. | |
| # Now always runs; the `changes` job gates the heavy jobs, which when skipped | |
| # report SUCCESS to required checks. | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: read-all | |
| concurrency: | |
| group: lsp-dap-bsp-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| name: Detect relevant changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| run: ${{ steps.detect.outputs.run }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - id: detect | |
| env: | |
| EVENT: ${{ github.event_name }} | |
| BASE: ${{ github.base_ref }} | |
| BEFORE: ${{ github.event.before }} | |
| run: | | |
| set -uo pipefail | |
| run=true | |
| if [ "$EVENT" = pull_request ]; then | |
| git fetch --no-tags --depth=200 origin "$BASE" 2>/dev/null \ | |
| && changed=$(git diff --name-only "origin/${BASE}...HEAD" 2>/dev/null) \ | |
| && { printf '%s\n' "$changed" | grep -qE '^cartridges/(lsp|dap|bsp)-mcp/|^src/abi/Boj/Protocol\.idr$' && run=true || run=false; } | |
| elif [ "$EVENT" = push ] && [ -n "$BEFORE" ] && [ "$BEFORE" != 0000000000000000000000000000000000000000 ]; then | |
| changed=$(git diff --name-only "${BEFORE}...${GITHUB_SHA}" 2>/dev/null) \ | |
| && { printf '%s\n' "$changed" | grep -qE '^cartridges/(lsp|dap|bsp)-mcp/|^src/abi/Boj/Protocol\.idr$' && run=true || run=false; } | |
| fi | |
| printf 'run=%s\n' "$run" >> "$GITHUB_OUTPUT" | |
| echo "relevant=$run; changed files:"; printf '%s\n' "${changed:-<none computed>}" | |
| abi-check: | |
| name: ABI Specification Check (Idris2) | |
| needs: changes | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Validate ABI modules exist | |
| run: | | |
| echo "=== Checking LSP/DAP/BSP ABI modules ===" | |
| for cart in lsp-mcp dap-mcp bsp-mcp; do | |
| echo "--- $cart ---" | |
| abi_dir="cartridges/$cart/abi" | |
| if [ ! -d "$abi_dir" ]; then | |
| echo "ERROR: $abi_dir directory missing" | |
| exit 1 | |
| fi | |
| idr_count=$(find "$abi_dir" -name '*.idr' | wc -l) | |
| echo " Found $idr_count .idr files" | |
| if [ "$idr_count" -eq 0 ]; then | |
| echo "ERROR: No .idr files in $abi_dir" | |
| exit 1 | |
| fi | |
| # Scan ONLY .idr sources (not README/docs) for axioms in leaf | |
| # cartridge proofs. Previously a recursive grep matched the abi/ | |
| # README's own "Zero believe_me..." prose — a false positive that | |
| # red-flagged every run. The full idris2 --check of every ABI is | |
| # proofs.yml's job (this is just a fast leaf-axiom guard). | |
| if grep -rn --include='*.idr' 'believe_me\|assert_total\|sorry' "$abi_dir" 2>/dev/null; then | |
| echo "ERROR: Banned axiom found in $abi_dir/*.idr (leaf proofs must be axiom-free)" | |
| exit 1 | |
| fi | |
| echo " No banned patterns — OK" | |
| done | |
| ffi-build: | |
| name: FFI Build & Test (Zig) | |
| needs: changes | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # The old step curled ziglang.org/builds/...0.15.2... — that path is the | |
| # nightly dir, not release downloads, so it returned an error page and | |
| # `tar` died ("File format not recognized"). Use the pinned action + | |
| # .tool-versions' canonical 0.15.1, matching e2e.yml. | |
| - name: Install Zig | |
| uses: goto-bus-stop/setup-zig@9566bb3e8749893055694249726756f25e099b30 # v2 | |
| with: | |
| version: 0.15.1 | |
| - name: Build LSP/DAP/BSP cartridge FFI | |
| run: | | |
| for cart in lsp-mcp dap-mcp bsp-mcp; do | |
| echo "=== Building $cart FFI ===" | |
| ffi_dir="cartridges/$cart/ffi" | |
| if [ ! -f "$ffi_dir/build.zig" ]; then | |
| echo "ERROR: $ffi_dir/build.zig missing" | |
| exit 1 | |
| fi | |
| cd "$ffi_dir" | |
| zig build 2>&1 || { echo "ERROR: Zig build failed for $cart"; exit 1; } | |
| echo " Build succeeded" | |
| # Check .so output exists | |
| so_file=$(find zig-out/lib/ -name '*.so' 2>/dev/null | head -1) | |
| if [ -n "$so_file" ]; then | |
| echo " Shared library: $so_file" | |
| else | |
| echo " WARNING: No .so output (may be static-only)" | |
| fi | |
| cd "$GITHUB_WORKSPACE" | |
| done | |
| - name: Run FFI tests | |
| run: | | |
| for cart in lsp-mcp dap-mcp bsp-mcp; do | |
| echo "=== Testing $cart FFI ===" | |
| cd "cartridges/$cart/ffi" | |
| zig build test 2>&1 || { echo "ERROR: Tests failed for $cart"; exit 1; } | |
| echo " Tests passed" | |
| cd "$GITHUB_WORKSPACE" | |
| done | |
| panel-validation: | |
| name: Panel Manifest Validation | |
| needs: changes | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # jq (pre-installed on ubuntu-latest) — the repo's no-Python policy | |
| # (dogfood-gate) bans the previous json.tool/json.load approach. | |
| # Same checks: valid JSON + required fields + panel count. | |
| - name: Validate LSP/DAP/BSP panel manifests | |
| run: | | |
| for cart in lsp-mcp dap-mcp bsp-mcp; do | |
| echo "=== Validating $cart panel manifest ===" | |
| manifest="cartridges/$cart/panels/manifest.json" | |
| if [ ! -f "$manifest" ]; then | |
| echo "ERROR: $manifest missing" | |
| exit 1 | |
| fi | |
| # Validate JSON | |
| jq empty "$manifest" 2>/dev/null || { echo "ERROR: Invalid JSON in $manifest"; exit 1; } | |
| # Check required fields | |
| for field in cartridge domain version panels; do | |
| if ! jq -e --arg f "$field" 'has($f)' "$manifest" >/dev/null 2>&1; then | |
| echo "ERROR: Missing field '$field' in $manifest" | |
| exit 1 | |
| fi | |
| done | |
| # Count panels | |
| panel_count=$(jq '.panels | length' "$manifest") | |
| echo " Valid JSON, $panel_count panels defined" | |
| done | |
| completeness: | |
| name: Cartridge Completeness Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes, abi-check, ffi-build, panel-validation] | |
| if: needs.changes.outputs.run == 'true' | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Verify triadic structure | |
| run: | | |
| echo "=== LSP/DAP/BSP Triadic Structure Audit ===" | |
| all_pass=true | |
| for cart in lsp-mcp dap-mcp bsp-mcp; do | |
| echo "--- $cart ---" | |
| layers=("abi" "ffi" "adapter" "panels") | |
| for layer in "${layers[@]}"; do | |
| dir="cartridges/$cart/$layer" | |
| if [ -d "$dir" ]; then | |
| echo " ✓ $layer/" | |
| else | |
| echo " ✗ $layer/ MISSING" | |
| all_pass=false | |
| fi | |
| done | |
| done | |
| if [ "$all_pass" != "true" ]; then | |
| echo "ERROR: Incomplete triadic structure" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "All 3 protocol cartridges have complete ABI+FFI+Adapter+Panel stacks." |