Repository navigation
314 lines (273 loc) · 9.96 KB
/
Copy pathci.yml
File metadata and controls
314 lines (273 loc) · 9.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# This workflow is managed by gh actions-lock.
# SPDX-License-Identifier: MPL-2.0
# This workflow is managed by gh actions-lock.
# Main CI workflow for hypatia
# Tests Rust (adapters, cli, fixer, data) and the Elixir rules. The Haskell
# (registry) jobs below are dormant-gated on the tree's absence; the Logtalk
# rule engine was retired 2026-03-06 (absorbed into lib/rules/*.ex).
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_dispatch:
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings
RUST_BACKTRACE: 1
jobs:
# ============================================================================
# Rust Jobs (adapters, cli, fixer, data)
# ============================================================================
rust-check:
name: Rust Check & Clippy
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: clippy, rustfmt
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
# Every cargo invocation in this workflow passes --locked: the committed
# Cargo.lock is a hard constraint, not a hint (#841). A failure reading
# "the lock file ... needs to be updated but --locked was passed" means
# Cargo.lock is out of date w.r.t. the manifests -- regenerate it
# deliberately and commit it; never drop --locked to get green.
- name: Run cargo check
run: cargo check --workspace --all-targets --locked
- name: Run clippy
run: cargo clippy --workspace --all-targets --locked -- -D warnings
rust-fmt:
name: Rust Format
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
rust-test:
name: Rust Tests
runs-on: ubuntu-latest
timeout-minutes: 30
needs: [rust-check]
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Run tests
run: cargo test --workspace --all-targets --locked
- name: Run doc tests
run: cargo test --workspace --doc --locked
rust-test-coverage:
name: Rust Coverage
runs-on: ubuntu-latest
timeout-minutes: 60
needs: [rust-test]
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2.87.24
with:
tool: cargo-llvm-cov
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Generate coverage report
run: cargo llvm-cov --workspace --lcov --output-path lcov.info
# ============================================================================
# Haskell Jobs (registry) — dormant-ready.
# The Haskell sub-project (registry/) is currently absent (its source was
# removed). `detect-haskell` gates the Haskell jobs on the presence of
# registry/hypatia.cabal so they SKIP cleanly while it is gone and
# re-activate automatically the moment it lands again. This realises the
# intent that was previously only described in a comment but never enforced,
# which left haskell-build and haskell-lint failing unconditionally.
# ============================================================================
detect-haskell:
name: Detect Haskell tree
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
present: ${{ steps.check.outputs.present }}
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Check for registry/hypatia.cabal
id: check
run: |
if [ -f registry/hypatia.cabal ]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::notice::registry/hypatia.cabal absent — Haskell build/lint jobs skipped (dormant)."
fi
haskell-build:
name: Haskell Build & Test
runs-on: ubuntu-latest
timeout-minutes: 60
needs: detect-haskell
if: needs.detect-haskell.outputs.present == 'true'
# registry/ is the Haskell sub-project; gate this job on its presence so
# PRs that don't touch (and don't ship) the Haskell tree don't fail here.
# The job re-activates the moment `registry/hypatia.cabal` lands.
defaults:
run:
working-directory: registry
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Setup Haskell
uses: haskell-actions/setup@v2.12.1
id: setup
with:
ghc-version: '9.6'
cabal-version: '3.10'
cabal-update: true
- name: Configure build
run: |
cabal configure --enable-tests --enable-benchmarks --disable-documentation
cabal build all --dry-run
- name: Restore cached dependencies
uses: actions/cache/restore@v6.1.0
id: cache
env:
key: ${{ runner.os }}-ghc-${{ steps.setup.outputs.ghc-version }}-cabal-${{ steps.setup.outputs.cabal-version }}
with:
path: ${{ steps.setup.outputs.cabal-store }}
key: ${{ env.key }}-plan-${{ hashFiles('registry/hypatia.cabal') }}
restore-keys: ${{ env.key }}-
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: cabal build all --only-dependencies
- name: Save cached dependencies
uses: actions/cache/save@v6.1.0
if: steps.cache.outputs.cache-hit != 'true'
with:
path: ${{ steps.setup.outputs.cabal-store }}
key: ${{ steps.cache.outputs.cache-primary-key }}
- name: Build
run: cabal build all
- name: Run tests
run: cabal test all
- name: Check documentation
run: cabal haddock all
haskell-lint:
name: HLint
runs-on: ubuntu-latest
timeout-minutes: 60
needs: detect-haskell
if: needs.detect-haskell.outputs.present == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Setup HLint
uses: haskell-actions/hlint-setup@v2.4.10
with:
version: '3.8'
- name: Run HLint
uses: haskell-actions/hlint-run@v2.4.10
with:
path: registry/
fail-on: warning
# ============================================================================
# K9iser — validates the contract manifest and emits k9 artefacts.
# DOG-04 from testing-and-benchmarking/TESTING-TAXONOMY.adoc § VII.
#
# Validates k9iser.toml parses cleanly and every declared source path
# exists. If the `k9iser` CLI is on PATH, also runs `k9iser build`
# and uploads the generated artefacts. Absence of the CLI is a warning,
# not a failure — the parse + path check is the hard gate.
# ============================================================================
k9iser-validate:
name: k9iser manifest + build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Parse k9iser.toml + verify declared paths exist
run: |
set -euo pipefail
cargo build --release --manifest-path scripts/ci-tools/Cargo.toml \
--bin check-k9iser-paths
./scripts/ci-tools/target/release/check-k9iser-paths k9iser.toml
- name: Run k9iser build (if CLI available)
run: |
set -euo pipefail
if command -v k9iser >/dev/null 2>&1; then
k9iser build
ls -la generated/k9iser/ || true
else
echo "::warning::k9iser CLI not on PATH — skipping build step. Install from hyperpolymath/k9iser to enable full DOG-04 enforcement."
fi
- name: Upload k9 artefacts
if: hashFiles('generated/k9iser/**') != ''
uses: actions/upload-artifact@v7.0.1
with:
name: k9iser-artefacts
path: generated/k9iser/
retention-days: 14
# ============================================================================
# Combined Status Check
# ============================================================================
ci-status:
name: CI Status
runs-on: ubuntu-latest
timeout-minutes: 30
needs:
- rust-check
- rust-fmt
- rust-test
- rust-test-coverage
- haskell-build
- haskell-lint
- k9iser-validate
if: always()
steps:
- name: Check all jobs status
run: |
if [[ "${{ needs.rust-check.result }}" == "failure" ]] || \
[[ "${{ needs.rust-fmt.result }}" == "failure" ]] || \
[[ "${{ needs.rust-test.result }}" == "failure" ]] || \
[[ "${{ needs.haskell-build.result }}" == "failure" ]] || \
[[ "${{ needs.haskell-lint.result }}" == "failure" ]] || \
[[ "${{ needs.k9iser-validate.result }}" == "failure" ]]; then
echo "One or more jobs failed"
exit 1
fi
echo "All CI jobs passed successfully"