feat: Add SARIF output format for GitHub Code Scanning#33
Conversation
Add `-f sarif` producing a SARIF 2.1.0 report so CI pipelines can surface complexity violations as inline PR annotations via GitHub Code Scanning, without swift-complexity having to implement its own PR integration. - One SARIF result per violated metric (cyclomatic_complexity, cognitive_complexity, lcom4_cohesion) so annotations stay actionable - Violation detection reuses ThresholdConfiguration.isExceeded (>= semantics), guaranteeing the results array and exit code 1 always agree - warning level escalates to error at twice the threshold, mirroring the Xcode diagnostics convention - File URIs are relativized to the working directory because Code Scanning requires repository-relative paths - Introduce SwiftComplexityVersion as the single version constant; the CLI and MCP server previously hardcoded "1.0.0" separately and SARIF needs the same value for tool.driver.version - Warn on stderr when sarif is selected without any threshold, since the report would otherwise be silently empty Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu
📊 PR Analysis
📝 Large PR: Consider breaking this into smaller, focused PRs. Automated analysis by swift-complexity CI |
🔍 Complexity Analysis Results✅ All functions are below the complexity threshold of 15. This analysis was performed by swift-complexity itself. |
Scoped to this branch only (push trigger on feature/sarif-output-format) so it does not touch the main CI path. Builds the release binary, generates a SARIF report with a low threshold to guarantee visible violations, and uploads it via github/codeql-action/upload-sarif to confirm GitHub Code Scanning renders inline PR annotations for the new -f sarif output. This workflow will be removed once annotations are confirmed. Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu
📊 PR Analysis
📝 Large PR: Consider breaking this into smaller, focused PRs. Automated analysis by swift-complexity CI |
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
| /// Builds one SARIF result per metric that reaches its effective threshold, | ||
| /// using the same `>=` semantics as the CLI exit-code check so annotations | ||
| /// and CI failures always agree. | ||
| private func complexityResults( |
| } | ||
|
|
||
| /// Mirrors the Xcode diagnostics severity convention for LCOM4. | ||
| private func cohesionResults(for result: ComplexityResult) -> [SARIFResult] { |
|
|
||
| /// GitHub Code Scanning expects URIs relative to the repository root, so | ||
| /// absolute paths under the current working directory are relativized. | ||
| private func relativizedURI(for filePath: String) -> String { |
🔍 Complexity Analysis Results✅ All functions are below the complexity threshold of 15. This analysis was performed by swift-complexity itself. |
The workflow confirmed that GitHub Code Scanning correctly renders inline PR annotations for swift-complexity's SARIF output: with a low threshold, the check run's annotations were correctly scoped to the 3 violations inside SARIFReport.swift (the file this PR actually changes), out of 102 total alerts detected across the wider tree. No longer needed now that annotation rendering is verified. Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu
📊 PR Analysis
📝 Large PR: Consider breaking this into smaller, focused PRs. Automated analysis by swift-complexity CI |
🔍 Complexity Analysis Results✅ All functions are below the complexity threshold of 15. This analysis was performed by swift-complexity itself. |
Release includes SARIF output format (#33) and inline suppression comments (#35). Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu
* feat: Add official GitHub Action and CI integration guide Add a composite action at the repository root so CI adoption becomes a single `uses:` step instead of hand-rolling binary download, checksum verification, and SARIF wiring in every project. Completes the CI adoption path started with SARIF output (#33): action -> SARIF -> upload-sarif -> inline PR annotations. Action design: - Downloads the release binary for the runner platform (macOS arm64/x86_64, Linux x86_64), verifies its published sha256, and runs the analysis; "latest" resolves through the GitHub API using the workflow token to avoid rate limits. - SARIF by default with threshold 10, because without any threshold the report is silently empty; an empty threshold input supports config-file-driven setups. - fail-on-threshold=false ignores exit code 1 for report-only setups, while other non-zero exits always fail. The SARIF upload itself is deliberately left to github/codeql-action/upload-sarif in the caller's workflow, keeping security-events write permission out of this action. - Docs pin to an exact release tag rather than a moving major tag (v1): release.yml triggers on any v* tag and derives the version from the tag name without validation, so pushing a bare v1 tag would start a bogus release. Major-tag automation needs a trigger guard first and is left as a follow-up. The new action-test CI job runs the action from the local checkout (uses: ./) on both macOS and Ubuntu against a pinned released version, then validates the generated SARIF structure with jq - this is the E2E test for the download/verify/run path on both platforms. Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu * fix: Statically link Swift stdlib on Linux and fix action-test The action-test job exposed that Linux release binaries are dynamically linked against the Swift runtime: they fail with a libswiftCore.so loading error on any machine without a Swift toolchain, which defeats the action's promise of toolchain-free CI (and equally affects direct binary downloads and nest installs on Linux). - release.yml: build Linux binaries with --static-swift-stdlib so future releases are self-contained. - ci.yml (test job): use the same flag for the Linux build so static linking breakage is caught on every PR instead of at release time. - ci.yml (action-test): run the Linux leg in a swift:6.2-jammy container as a temporary measure, because the pinned v1.1.0 binaries are still dynamic; a comment marks it for removal once a static release ships. Also set fail-fast: false - the macOS leg had actually passed end-to-end but was cancelled when the Linux leg failed. Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu * fix: Point the loader at the container's Swift runtime in action-test The swift:6.2-jammy container does ship the runtime at /usr/lib/swift/linux, but the v1.1.0 binary embeds a runpath from its build machine (a swiftly toolchain path), so the loader never looks there. Set LD_LIBRARY_PATH on the action step; this lives inside the same temporary block that goes away once a --static-swift-stdlib release ships. Claude-Session: https://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu
Summary
Adds a new
-f sarifoutput format producing a SARIF 2.1.0 report, so CI pipelines can surface complexity violations as inline PR annotations and Security-tab alerts via GitHub Code Scanning — without swift-complexity implementing its own PR integration.This is the first step of the "Now" roadmap (CI adoption in 5 minutes); an official GitHub Action building on this format will follow in a separate PR.
Changes
sarifcase inOutputFormat+SARIFReport.swiftwith a minimal internal Codable model of SARIF 2.1.0cyclomatic_complexity,cognitive_complexity,lcom4_cohesion) so each annotation is actionable on its ownThresholdConfiguration.isExceeded(>=semantics), so a non-emptyresultsarray always coincides with exit code 1warningwhen a metric reaches the threshold,errorat twice the threshold (mirrors the Xcode diagnostics convention); LCOM4 mirrors the existing cohesion diagnostic rule (lowcohesion → warning, LCOM4 >= 5 → error)SwiftComplexityVersion: new single source of truth for the version string; the CLI and MCP server previously hardcoded"1.0.0"in two places, and SARIF needs the same value fortool.driver.version-f sarifis used without--threshold/config, since the report would otherwise be silently emptyoutput-formats.md, README examples with anupload-sarifworkflow snippetVerification
swift test: 86 tests pass, including 3 new SARIF tests (per-metric results & severity, empty-without-threshold, LCOM4 cohesion)swift-complexity Sources -f sarif -t 8 -rproduced 12 results with correct rule ids, levels, relative URIs, and line numbers, and exited with code 1; violation-free input produced an emptyresultsarray and exit 0swift-formatclean via lefthook pre-commithttps://claude.ai/code/session_01GaTHVDhSAx76RbLmVziLQu