-
Notifications
You must be signed in to change notification settings - Fork 2
ci: add pull-request gate for format, lint, and tests #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| name: CI | ||
|
|
||
| # The everyday gate: formatting, lint, and the Swift Testing suite on every pull | ||
| # request and every push to main. Signed/notarized release builds live in | ||
| # release.yml and fire on v* tags only — before this workflow existed, a tag | ||
| # push was the first time lint or tests ran anywhere but a developer's machine. | ||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
|
|
||
| # A newer push to the same branch supersedes an in-flight run. | ||
| concurrency: | ||
| group: ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| SCHEME: "MLXBits Image Studio" | ||
| PROJECT: "MLXBits Image Studio.xcodeproj" | ||
|
|
||
| jobs: | ||
| lint: | ||
| name: Format & lint | ||
| runs-on: macos-26 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Install SwiftFormat and SwiftLint | ||
| run: brew install --quiet swiftformat swiftlint | ||
|
|
||
| # Must run before anything invokes xcodebuild: the app target's pre-build | ||
| # phase (project.yml) runs SwiftFormat in write mode, which would silently | ||
| # repair the very violations this step exists to catch. | ||
| - name: SwiftFormat (lint only) | ||
| run: swiftformat --lint --config .swiftformat . | ||
|
|
||
| # --strict promotes warnings to errors, measured against a baseline of the | ||
| # 139 warning-severity violations that predate this workflow. New | ||
| # violations fail the build; the pre-existing backlog does not. The | ||
| # pre-build phase in project.yml emits those 139 as build warnings, which | ||
| # is why they were never noticed. | ||
| # | ||
| # After clearing some (many are autocorrectable with `swiftlint --fix`), | ||
| # regenerate with: | ||
| # swiftlint lint --config .swiftlint.yml \ | ||
| # --write-baseline .swiftlint-baseline.json | ||
| - name: SwiftLint (strict, against baseline) | ||
| run: | | ||
| swiftlint lint \ | ||
| --config .swiftlint.yml \ | ||
| --baseline .swiftlint-baseline.json \ | ||
| --strict \ | ||
| --reporter github-actions-logging | ||
|
|
||
| test: | ||
| name: Test | ||
| runs-on: macos-26 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Install SwiftLint | ||
| run: brew install --quiet swiftlint | ||
|
|
||
| # Same invocation release.yml uses. CODE_SIGNING_ALLOWED=NO because | ||
| # runners carry no Mac Development identity and these are pure-logic | ||
| # tests. Deliberately not -quiet: this log is the analyzer step's input. | ||
| - name: Test (Debug) | ||
| run: | | ||
| set -o pipefail | ||
| mkdir -p build | ||
| xcodebuild test \ | ||
| -project "$PROJECT" \ | ||
| -scheme "$SCHEME" \ | ||
| -destination "platform=macOS" \ | ||
| -resultBundlePath build/TestResults.xcresult \ | ||
| CODE_SIGNING_ALLOWED=NO \ | ||
| | tee build/xcodebuild.log | ||
|
|
||
| # The analyzer_rules block in .swiftlint.yml (unused_import) does NOT run | ||
| # under plain `swiftlint lint` — not here, and not in the pre-build phase | ||
| # in project.yml. Analyzer rules need a full compiler log, which only | ||
| # exists once something has actually built. This is the only place it does. | ||
| # | ||
| # Advisory for now: 16 pre-existing unused_import violations as of | ||
| # 2026-09-16. Once those are cleared, drop the `|| true` and add --strict | ||
| # so new ones fail the build. | ||
| - name: SwiftLint analyzer rules (advisory) | ||
| run: | | ||
| swiftlint analyze \ | ||
| --config .swiftlint.yml \ | ||
| --compiler-log-path build/xcodebuild.log \ | ||
| --reporter github-actions-logging || true | ||
|
|
||
| - name: Upload test results | ||
| if: failure() | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: test-results | ||
| path: build/TestResults.xcresult | ||
| retention-days: 7 | ||
|
|
||
| duplication: | ||
| name: Duplication (advisory) | ||
| # jscpd is pure token analysis — no Xcode, no build — so it runs on Linux | ||
| # rather than burning a macOS runner. | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - uses: actions/setup-node@v7 | ||
| with: | ||
| node-version: "22" | ||
|
|
||
| # Config (thresholds, ignores, Swift-only format) lives in .jscpd.json. | ||
| - name: jscpd | ||
| run: | | ||
| npx --yes jscpd@4 --reporters console . > jscpd.txt 2>&1 || true | ||
| { | ||
| echo "### Duplication" | ||
| echo "" | ||
| echo '```' | ||
| tail -25 jscpd.txt | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| deadcode: | ||
| name: Dead code (advisory) | ||
| # Periphery runs its own full build, so it would roughly double PR | ||
| # wall-clock for output nobody blocks on. Post-merge hygiene instead. | ||
| if: github.event_name == 'push' | ||
| runs-on: macos-26 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - name: Install Periphery | ||
| run: brew install --quiet peripheryapp/periphery/periphery | ||
|
|
||
| # Project, scheme, and retention rules come from .periphery.yml. Without | ||
| # --strict, findings exit 0 and surface as annotations — so a non-zero | ||
| # exit here means the scan itself broke, not that it found dead code. | ||
| - name: Scan | ||
| run: | | ||
| if periphery scan \ | ||
| --disable-update-check \ | ||
| --quiet \ | ||
| --format github-actions \ | ||
| -- CODE_SIGNING_ALLOWED=NO | ||
| then | ||
| echo "Periphery scan completed — findings, if any, are in the annotations." >> "$GITHUB_STEP_SUMMARY" | ||
| else | ||
| echo "Periphery failed to run (build or config problem), see the step log." >> "$GITHUB_STEP_SUMMARY" | ||
| # Findings are advisory; the scan breaking is not. Without this the | ||
| # trailing echo returns 0 and a dead tool reports as a green job. | ||
| exit 1 | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Serialize
testafterlint.Jobs without
needscan run in parallel.xcodebuildcan start before the format gate completes. Addneeds: lintso the workflow enforces its stated ordering and skips costly tests for rejected revisions. (docs.github.com)Proposed fix
test: name: Test + needs: lint runs-on: macos-26🤖 Prompt for AI Agents