ci.yml 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # Main quality gate (task 42).
  2. #
  3. # Per-PR legs (all must be green):
  4. # fmt, clippy, audit, deny, sqlx-prepare-check, nextest, doc-tests,
  5. # fuzz-build (nightly leg inside `fuzz/`, so the pinned stable
  6. # `rust-toolchain.toml` at the root is never violated).
  7. # Scheduled legs (not per-PR):
  8. # mutants (weekly budgeted mutation run over the pure security modules),
  9. # fuzz-run (bounded run of every fuzz target; the per-PR leg only builds).
  10. #
  11. # Must NOT gate on miri for tokio/sqlx/redis; no lint suppressions anywhere.
  12. name: ci
  13. on:
  14. push:
  15. branches: ["main"]
  16. pull_request: {}
  17. schedule:
  18. # Weekly budgeted mutation run (Sunday 03:00 UTC).
  19. - cron: "0 3 * * 0"
  20. workflow_dispatch: {}
  21. permissions:
  22. contents: read
  23. env:
  24. CARGO_TERM_COLOR: always
  25. SQLX_OFFLINE: "true"
  26. jobs:
  27. fmt:
  28. name: fmt
  29. runs-on: ubuntu-latest
  30. steps:
  31. - uses: actions/checkout@v4
  32. - uses: dtolnay/rust-toolchain@stable
  33. with:
  34. components: rustfmt
  35. - name: Check formatting
  36. run: cargo fmt --check
  37. clippy:
  38. name: clippy
  39. runs-on: ubuntu-latest
  40. steps:
  41. - uses: actions/checkout@v4
  42. - uses: dtolnay/rust-toolchain@stable
  43. with:
  44. components: clippy
  45. - name: Clippy (all targets, warnings denied)
  46. run: cargo clippy --all-targets -- -D warnings
  47. audit:
  48. name: audit
  49. runs-on: ubuntu-latest
  50. steps:
  51. - uses: actions/checkout@v4
  52. - uses: dtolnay/rust-toolchain@stable
  53. - name: Install cargo-audit
  54. run: cargo install cargo-audit --locked
  55. - name: Audit advisories
  56. run: cargo audit
  57. deny:
  58. name: deny
  59. runs-on: ubuntu-latest
  60. steps:
  61. - uses: actions/checkout@v4
  62. - uses: dtolnay/rust-toolchain@stable
  63. - name: Install cargo-deny
  64. run: cargo install cargo-deny --locked
  65. - name: Check supply chain (ring-only TLS enforced in deny.toml)
  66. run: cargo deny check
  67. sqlx-prepare-check:
  68. name: sqlx-prepare-check
  69. runs-on: ubuntu-latest
  70. services:
  71. postgres:
  72. image: postgres:16
  73. env:
  74. POSTGRES_USER: postgres
  75. POSTGRES_PASSWORD: sqlxcheck
  76. POSTGRES_DB: postgres
  77. ports:
  78. - 5432:5432
  79. options: >-
  80. --health-cmd "pg_isready -U postgres"
  81. --health-interval 5s
  82. --health-timeout 5s
  83. --health-retries 10
  84. steps:
  85. - uses: actions/checkout@v4
  86. - uses: dtolnay/rust-toolchain@stable
  87. - name: Install sqlx-cli
  88. run: cargo install sqlx-cli --locked --version 0.9.0 --no-default-features --features postgres,rustls
  89. - name: Apply migrations to the check database
  90. env:
  91. DATABASE_URL: postgres://postgres:sqlxcheck@127.0.0.1:5432/postgres
  92. run: cargo sqlx migrate run
  93. - name: Fail if the committed .sqlx cache is stale
  94. env:
  95. DATABASE_URL: postgres://postgres:sqlxcheck@127.0.0.1:5432/postgres
  96. run: cargo sqlx prepare --check --workspace
  97. doc-links:
  98. name: doc-links
  99. runs-on: ubuntu-latest
  100. steps:
  101. - uses: actions/checkout@v4
  102. - uses: actions/setup-python@v5
  103. with:
  104. python-version: "3.x"
  105. - name: Check documentation links (markdown-link-check equivalent)
  106. run: python3 scripts/check_doc_links.py
  107. nextest:
  108. name: nextest
  109. runs-on: ubuntu-latest
  110. steps:
  111. - uses: actions/checkout@v4
  112. - uses: dtolnay/rust-toolchain@stable
  113. - name: Install cargo-nextest
  114. uses: taiki-e/install-action@v2
  115. with:
  116. tool: nextest
  117. - name: Install redis-server (in-process Sentinel/Cluster fixtures)
  118. run: sudo apt-get update && sudo apt-get install -y redis-server
  119. - name: Start shared test services (Postgres + Redis)
  120. run: docker compose -f compose.test.yaml up -d
  121. - name: Full test suite (shared services; testcontainers for lifecycle)
  122. run: cargo nextest run --all-targets
  123. - name: Stop shared test services
  124. if: always()
  125. run: docker compose -f compose.test.yaml down -v
  126. doc-tests:
  127. name: doc-tests
  128. runs-on: ubuntu-latest
  129. steps:
  130. - uses: actions/checkout@v4
  131. - uses: dtolnay/rust-toolchain@stable
  132. - name: Documentation tests
  133. run: cargo test --doc
  134. fuzz-build:
  135. name: fuzz-build
  136. runs-on: ubuntu-latest
  137. defaults:
  138. run:
  139. working-directory: fuzz
  140. steps:
  141. - uses: actions/checkout@v4
  142. # The `fuzz/rust-toolchain.toml` in the working directory selects
  143. # nightly via rustup override; the root stable pin is untouched.
  144. - name: Install cargo-fuzz (nightly, per fuzz/rust-toolchain.toml)
  145. run: cargo install cargo-fuzz --locked
  146. - name: Build all fuzz targets
  147. run: cargo fuzz build
  148. fuzz-run:
  149. name: fuzz-run
  150. # Scheduled bounded fuzzing. `fuzz-build` (above) only compiles the
  151. # targets per PR; this leg actually executes them, so a crash is found
  152. # without a human starting it. It is bounded and scheduled, not 24/7:
  153. # continuous coverage is OSS-Fuzz/ClusterFuzzLite, tracked in docs/TODO.md.
  154. if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
  155. runs-on: ubuntu-latest
  156. timeout-minutes: 70
  157. strategy:
  158. fail-fast: false
  159. matrix:
  160. target: [token_form, dcr_json, jar_claims]
  161. defaults:
  162. run:
  163. working-directory: fuzz
  164. steps:
  165. - uses: actions/checkout@v4
  166. # `fuzz/rust-toolchain.toml` selects nightly; the root stable pin is
  167. # untouched, exactly as in `fuzz-build`.
  168. - name: Install cargo-fuzz (nightly)
  169. run: cargo install cargo-fuzz --locked
  170. # `uses` steps resolve paths from the workspace root, not the job's
  171. # `working-directory`; keep the fuzz/ prefix explicit.
  172. - name: Restore corpus for ${{ matrix.target }}
  173. uses: actions/cache/restore@v4
  174. with:
  175. path: fuzz/corpus/${{ matrix.target }}
  176. key: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
  177. restore-keys: |
  178. fuzz-corpus-${{ matrix.target }}-
  179. - name: Fuzz ${{ matrix.target }} (bounded)
  180. run: cargo fuzz run ${{ matrix.target }} -- -max_total_time=600 -rss_limit_mb=2048
  181. # Save only on success; a per-run key grows the corpus while the prefix
  182. # `restore-keys` always pick the newest entry.
  183. - name: Save corpus for ${{ matrix.target }}
  184. if: success()
  185. uses: actions/cache/save@v4
  186. with:
  187. path: fuzz/corpus/${{ matrix.target }}
  188. key: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
  189. - name: Upload crash artifacts
  190. if: failure()
  191. uses: actions/upload-artifact@v4
  192. with:
  193. name: fuzz-crash-${{ matrix.target }}
  194. path: fuzz/artifacts/${{ matrix.target }}
  195. if-no-files-found: ignore
  196. mutants:
  197. name: mutants
  198. # Scheduled budgeted job only: never blocks a PR.
  199. if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
  200. runs-on: ubuntu-latest
  201. timeout-minutes: 60
  202. steps:
  203. - uses: actions/checkout@v4
  204. - uses: dtolnay/rust-toolchain@stable
  205. - name: Install cargo-mutants
  206. run: cargo install cargo-mutants --locked
  207. - name: Budgeted mutation run (see .cargo/mutants.toml)
  208. run: cargo mutants --timeout 1800 --test-tool nextest