@@ -3,8 +3,28 @@ name: CI
33on :
44 push :
55 branches : [ main, mvp-0.0.1-snapshot ]
6+ paths :
7+ - ' modules/**'
8+ - ' build.sbt'
9+ - ' project/**'
10+ - ' .github/workflows/ci.yml'
11+ - ' !docs/**'
12+ - ' !**/*.md'
613 pull_request :
714 branches : [ main, mvp-0.0.1-snapshot ]
15+ paths :
16+ - ' modules/**'
17+ - ' build.sbt'
18+ - ' project/**'
19+ - ' .github/workflows/ci.yml'
20+ workflow_dispatch :
21+
22+ permissions :
23+ contents : read
24+
25+ concurrency :
26+ group : ${{ github.workflow }}-${{ github.ref }}
27+ cancel-in-progress : true
828
929env :
1030 JAVA_OPTS : -Xmx4G -XX:+UseG1GC
@@ -39,52 +59,34 @@ jobs:
3959 modules/engines-flink/target
4060 name : ${{ matrix.module }}
4161 runs-on : ubuntu-22.04
62+ if : ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
4263 steps :
4364 - name : Checkout
4465 uses : actions/checkout@v4
4566
46- - name : Set up JDK 21
67+ - name : Set up JDK 17 (with SBT cache)
4768 uses : actions/setup-java@v4
4869 with :
4970 distribution : temurin
50- java-version : ' 21'
51-
52- - name : Cache SBT and dependencies
53- uses : actions/cache@v4
54- with :
55- path : |
56- ~/.ivy2/cache
57- ~/.cache/coursier
58- ~/.sbt
59- ${{ matrix.paths }}
60- key : ${{ runner.os }}-sbt-${{ matrix.module }}-${{ hashFiles('**/build.sbt', '**/project/**/*.sbt', format('modules/{0}/**', matrix.module)) }}
61- restore-keys : |
62- ${{ runner.os }}-sbt-${{ matrix.module }}-
71+ java-version : ' 17'
72+ cache : ' sbt'
6373
6474 - name : Build module
6575 run : sbt -batch ${{ matrix.commands }}
6676
6777 refactor-lint :
6878 name : Refactor lint & Unsafe cast gate
6979 runs-on : ubuntu-22.04
80+ if : ${{ !github.event.pull_request.draft }}
7081 steps :
7182 - name : Checkout
7283 uses : actions/checkout@v4
73- - name : Set up JDK 21
84+ - name : Set up JDK 17 (with SBT cache)
7485 uses : actions/setup-java@v4
7586 with :
7687 distribution : temurin
77- java-version : ' 21'
78- - name : Cache Ivy and Coursier
79- uses : actions/cache@v4
80- with :
81- path : |
82- ~/.ivy2/cache
83- ~/.cache/coursier
84- ~/.sbt
85- key : ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt', '**/project/**/*.sbt', '**/.scalafix.conf') }}
86- restore-keys : |
87- ${{ runner.os }}-sbt-
88+ java-version : ' 17'
89+ cache : ' sbt'
8890 - name : Scalafix check (no rewrites)
8991 continue-on-error : true
9092 run : sbt -batch "scalafixAll --check"
9799 # Whitelist files where reflective casts are intentionally localized
98100 WHITELIST=(
99101 "modules/engines-spark/src/main/scala/com/flowforge/engines/spark/SparkDataAlgebra.scala"
100- "modules/core/src/main/scala/com/flowforge/core/contracts/internal/SchemaConformsMacros .scala"
102+ "modules/core/src/main/scala/com/flowforge/core/contracts/internal/ContractMacros .scala"
101103 )
102104
103105 is_whitelisted() {
@@ -127,6 +129,61 @@ jobs:
127129 fi
128130 echo "✅ PASS: No unsafe asInstanceOf/Any found outside whitelist"
129131
132+ doc-lint :
133+ name : Scaladoc coverage (report → enforce later)
134+ runs-on : ubuntu-22.04
135+ steps :
136+ - name : Checkout
137+ uses : actions/checkout@v4
138+ - name : Install ripgrep
139+ run : sudo apt-get update && sudo apt-get install -y ripgrep
140+ - name : Scan public packages for missing Scaladoc
141+ shell : bash
142+ run : |
143+ set -euo pipefail
144+ ROOTS=(
145+ modules/core/src/main/scala/com/flowforge/core
146+ modules/core/src/main/scala/com/flowforge/framework
147+ modules/core/src/main/scala/com/flowforge/core/contracts
148+ modules/core/src/main/scala/com/flowforge/core/algebra
149+ modules/engines-spark/src/main/scala/com/flowforge/engines/spark
150+ modules/quality-deequ/src/main/scala/com/flowforge/quality/deequ
151+ )
152+ total=0; missing=0
153+ mapfile -t FILES < <(rg -l "^" "${ROOTS[@]}" --glob "**/*.scala" || true)
154+ offenders=()
155+ for file in "${FILES[@]}"; do
156+ # Classes/traits/objects
157+ while IFS= read -r line; do
158+ ln=${line%%:*}; decl=${line#*:*:}
159+ (( total++ ))
160+ start=$((ln-3)); [ $start -lt 1 ] && start=1
161+ if ! sed -n "$start,$((ln-1))p" "$file" | rg -q "^/\*\*"; then
162+ offenders+=("$file:$ln:$decl")
163+ (( missing++ ))
164+ fi
165+ done < <(rg -n "^\s*(final\s+)?(case\s+)?(class|trait|object)\s+\w+" "$file" || true)
166+ # Public defs (basic heuristic)
167+ while IFS= read -r line; do
168+ ln=${line%%:*}; decl=${line#*:*:}
169+ (( total++ ))
170+ start=$((ln-3)); [ $start -lt 1 ] && start=1
171+ if ! sed -n "$start,$((ln-1))p" "$file" | rg -q "^/\*\*"; then
172+ offenders+=("$file:$ln:$decl")
173+ (( missing++ ))
174+ fi
175+ done < <(rg -n "^\s*def\s+\w+\s*\(" "$file" | rg -v "^\s*(private|protected)\b" || true)
176+ done
177+ cov=$(( ( (total-missing) * 100 ) / ( (total==0) ? 1 : total ) ))
178+ echo "Scaladoc coverage: $cov% ($((total-missing))/$total)"
179+ if [ ${#offenders[@]} -gt 0 ]; then
180+ echo "First 200 missing docs:"; printf '%s\n' "${offenders[@]}" | head -n 200
181+ fi
182+ # ENFORCE: fail when offenders exist (single CI run post-sweep)
183+ if [ ${#offenders[@]} -gt 0 ]; then
184+ echo "❌ Missing Scaladoc found"; exit 1
185+ fi
186+
130187 - name : Try/Try-catch/unsafeRunSync bans (main sources only)
131188 shell : bash
132189 run : |
0 commit comments