Skip to content

ci: make path-filtered required gates always report (unblock stuck PR… #68

ci: make path-filtered required gates always report (unblock stuck PR…

ci: make path-filtered required gates always report (unblock stuck PR… #68

Workflow file for this run

# 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."