Skip to content

Commit 85feb63

Browse files
committed
Add swift-format and swiftlint configurations with CI workflows
Configurations: - Add .swift-format with 4-space indentation and 120 char line length - Add .swiftlint.yml with Swift Testing compatibility (backtick test names) - Disable trailing_comma rule (handled by swift-format for better diffs) CI workflows: - Add GitHub Actions for CI (swift test) - Add workflow for swift-format validation - Add workflow for swiftlint validation - Add dependabot.yml for dependency updates - Add FUNDING.yml Formatting: - Applied swift-format to all test files - Removed redundant explicit initializer per UseSynthesizedInitializer rule All 80 tests passing with clean swiftlint output.
1 parent d3d91b8 commit 85feb63

15 files changed

+368
-6
lines changed

.github/FUNDING.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# These are supported funding model platforms
2+
3+
github: [coenttb]
4+
patreon: # Replace with a single Patreon username
5+
open_collective: # Replace with a single Open Collective username
6+
ko_fi: # Replace with a single Ko-fi username
7+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+
liberapay: # Replace with a single Liberapay username
10+
issuehunt: # Replace with a single IssueHunt username
11+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12+
polar: # Replace with a single Polar username
13+
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14+
thanks_dev: # Replace with a single thanks.dev username
15+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

.github/dependabot.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "swift"
4+
directory: "/"
5+
schedule:
6+
interval: "monthly"
7+
- package-ecosystem: "github-actions"
8+
directory: "/"
9+
schedule:
10+
interval: "monthly"

.github/workflows/ci.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
concurrency:
13+
group: ci-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
# Primary development workflow: Latest Swift on macOS with debug build
18+
macos-latest:
19+
name: macOS (Swift 6.2, debug)
20+
runs-on: macos-26
21+
steps:
22+
- uses: actions/checkout@v5
23+
24+
- name: Select Xcode 26.0
25+
run: sudo xcode-select -s /Applications/Xcode_26.0.app
26+
27+
- name: Print Swift version
28+
run: swift --version
29+
30+
- name: Cache Swift packages
31+
uses: actions/cache@v4
32+
with:
33+
path: .build
34+
key: macos-swift62-spm-${{ hashFiles('Package.swift', 'Package@*.swift') }}
35+
restore-keys: |
36+
macos-swift62-spm-
37+
38+
# Note: swift test builds automatically, no separate build step needed
39+
- name: Test
40+
run: swift test -c debug
41+
42+
- name: Validate Package.swift
43+
run: swift package dump-package
44+
45+
- name: Check for API breaking changes
46+
id: api-diff
47+
run: |
48+
echo "## API Breaking Changes Check" >> $GITHUB_STEP_SUMMARY
49+
if swift package diagnose-api-breaking-changes --breakage-allowlist-path .swift-api-breakage-allowlist 2>&1 | tee api-diff.txt; then
50+
echo "✅ No API breaking changes detected" >> $GITHUB_STEP_SUMMARY
51+
else
52+
echo "⚠️ API breaking changes detected:" >> $GITHUB_STEP_SUMMARY
53+
echo '```' >> $GITHUB_STEP_SUMMARY
54+
cat api-diff.txt >> $GITHUB_STEP_SUMMARY
55+
echo '```' >> $GITHUB_STEP_SUMMARY
56+
echo "::warning::API breaking changes detected. Review changes before release."
57+
fi
58+
continue-on-error: true
59+
60+
# Production validation: Latest Swift on Linux with release build
61+
linux-latest:
62+
name: Ubuntu (Swift 6.2, release)
63+
runs-on: ubuntu-latest
64+
container: swift:6.2
65+
steps:
66+
- uses: actions/checkout@v5
67+
68+
- name: Cache Swift packages
69+
uses: actions/cache@v4
70+
with:
71+
path: .build
72+
key: linux-swift62-spm-${{ hashFiles('Package.swift', 'Package@*.swift') }}
73+
restore-keys: linux-swift62-spm-
74+
75+
# Note: swift test builds automatically in release mode
76+
- name: Test (release)
77+
run: swift test -c release
78+
79+
# Compatibility check: Minimum supported Swift version (6.2)
80+
# Update to swift:6.3 when available
81+
linux-compat:
82+
name: Ubuntu (Swift 6.2, compatibility)
83+
runs-on: ubuntu-latest
84+
container: swift:6.2
85+
steps:
86+
- uses: actions/checkout@v5
87+
88+
- name: Cache Swift packages
89+
uses: actions/cache@v4
90+
with:
91+
path: .build
92+
key: linux-swift62-compat-spm-${{ hashFiles('Package.swift', 'Package@*.swift') }}
93+
restore-keys: linux-swift62-compat-spm-
94+
95+
# Note: swift test builds automatically
96+
- name: Test (Swift 6.2)
97+
run: swift test -c release

.github/workflows/swift-format.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Swift Format
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
swift_format:
10+
name: swift-format
11+
runs-on: ubuntu-latest
12+
container: swift:6.2
13+
permissions:
14+
contents: write
15+
steps:
16+
- uses: actions/checkout@v5
17+
18+
- name: Cache swift-format
19+
id: cache-swift-format
20+
uses: actions/cache@v4
21+
with:
22+
path: /usr/local/bin/swift-format
23+
key: ${{ runner.os }}-swift-format-${{ hashFiles('.github/workflows/swift-format.yml') }}
24+
25+
- name: Install swift-format
26+
if: steps.cache-swift-format.outputs.cache-hit != 'true'
27+
run: |
28+
git clone --depth 1 --branch $(git ls-remote --tags https://github.com/apple/swift-format.git | grep -o 'refs/tags/[0-9]*\.[0-9]*\.[0-9]*$' | sort -V | tail -1 | sed 's/refs\/tags\///') https://github.com/apple/swift-format.git
29+
cd swift-format
30+
swift build -c release
31+
cp .build/release/swift-format /usr/local/bin/
32+
cd ..
33+
rm -rf swift-format
34+
35+
- name: Format
36+
run: swift-format format --recursive --in-place --ignore-unparsable-files Sources Tests
37+
38+
- uses: stefanzweifel/git-auto-commit-action@v7
39+
with:
40+
commit_message: Run swift-format
41+
branch: 'main'

.github/workflows/swiftlint.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: SwiftLint
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
lint:
13+
name: SwiftLint
14+
runs-on: ubuntu-latest
15+
container: swift:6.2
16+
steps:
17+
- uses: actions/checkout@v5
18+
19+
- name: Restore swiftlint cache
20+
id: cache-swiftlint-restore
21+
uses: actions/cache/restore@v4
22+
with:
23+
path: /usr/local/bin/swiftlint
24+
key: ${{ runner.os }}-swiftlint-0.62.2
25+
26+
- name: Install swiftlint
27+
if: steps.cache-swiftlint-restore.outputs.cache-hit != 'true'
28+
run: |
29+
git clone --depth 1 --branch 0.62.2 https://github.com/realm/SwiftLint.git
30+
cd SwiftLint
31+
swift build -c release
32+
cp .build/release/swiftlint /usr/local/bin/
33+
cd ..
34+
rm -rf SwiftLint
35+
36+
- name: Lint
37+
run: swiftlint lint --strict --reporter github-actions-logging
38+
39+
- name: Save swiftlint cache
40+
uses: actions/cache/save@v4
41+
if: always() && steps.cache-swiftlint-restore.outputs.cache-hit != 'true'
42+
with:
43+
path: /usr/local/bin/swiftlint
44+
key: ${{ runner.os }}-swiftlint-0.62.2

.swift-format

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"version": 1,
3+
"lineLength": 120,
4+
"indentation": {
5+
"spaces": 4
6+
},
7+
"tabWidth": 8,
8+
"maximumBlankLines": 1,
9+
"respectsExistingLineBreaks": true,
10+
"lineBreakBeforeControlFlowKeywords": false,
11+
"lineBreakBeforeEachArgument": false,
12+
"lineBreakBeforeEachGenericRequirement": false,
13+
"prioritizeKeepingFunctionOutputTogether": true,
14+
"indentConditionalCompilationBlocks": true,
15+
"lineBreakAroundMultilineExpressionChainComponents": false,
16+
"fileScopedDeclarationPrivacy": {
17+
"accessLevel": "private"
18+
},
19+
"rules": {
20+
"AllPublicDeclarationsHaveDocumentation": false,
21+
"AlwaysUseLowerCamelCase": true,
22+
"AmbiguousTrailingClosureOverload": false,
23+
"BeginDocumentationCommentWithOneLineSummary": false,
24+
"DoNotUseSemicolons": true,
25+
"DontRepeatTypeInStaticProperties": true,
26+
"FileScopedDeclarationPrivacy": true,
27+
"FullyIndirectEnum": true,
28+
"GroupNumericLiterals": true,
29+
"IdentifiersMustBeASCII": true,
30+
"NeverForceUnwrap": false,
31+
"NeverUseForceTry": false,
32+
"NeverUseImplicitlyUnwrappedOptionals": false,
33+
"NoAccessLevelOnExtensionDeclaration": true,
34+
"NoBlockComments": true,
35+
"NoCasesWithOnlyFallthrough": true,
36+
"NoEmptyTrailingClosureParentheses": true,
37+
"NoLabelsInCasePatterns": true,
38+
"NoLeadingUnderscores": false,
39+
"NoParensAroundConditions": true,
40+
"NoVoidReturnOnFunctionSignature": true,
41+
"OneCasePerLine": true,
42+
"OneVariableDeclarationPerLine": true,
43+
"OnlyOneTrailingClosureArgument": true,
44+
"OrderedImports": true,
45+
"ReturnVoidInsteadOfEmptyTuple": true,
46+
"UseLetInEveryBoundCaseVariable": true,
47+
"UseShorthandTypeNames": true,
48+
"UseSingleLinePropertyGetter": true,
49+
"UseSynthesizedInitializer": true,
50+
"UseTripleSlashForDocumentationComments": true,
51+
"UseWhereClausesInForLoops": false,
52+
"ValidateDocumentationComments": false
53+
}
54+
}

.swiftlint.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# SwiftLint Configuration for swift-standards
2+
# Shared across all packages in the workspace
3+
# Optimized for library/framework development with Swift Testing
4+
5+
# Disabled rules
6+
disabled_rules:
7+
# Type inference & Swift idioms
8+
- explicit_type_interface # Type inference is idiomatic Swift
9+
- explicit_init # Prefer MyType() over MyType.init()
10+
- extension_access_modifier # Prefer access on members, not extensions
11+
- explicit_acl # Don't require `internal` everywhere
12+
13+
# Development flexibility
14+
- todo # TODOs are fine in development
15+
- force_cast # Sometimes needed in tests
16+
- force_try # Sometimes needed in tests
17+
18+
# Length restrictions (swift-format handles line length)
19+
- line_length # HTML attributes can be 300+ chars; swift-format handles this
20+
- file_length # Comprehensive test files are long
21+
22+
# Domain complexity
23+
- cyclomatic_complexity # Lookup tables and parsing logic have inherent complexity
24+
- function_parameter_count # Time/Date/RFC initializers legitimately need many parameters
25+
- large_tuple # Component tuples are acceptable for internal use
26+
27+
# Documentation patterns
28+
- file_header # No consistent pattern across packages
29+
- missing_docs # Documentation via code comments, not strictly required
30+
31+
# Swift Testing compatibility
32+
- identifier_name # Swift Testing uses backtick names with spaces
33+
- type_name # @Suite uses descriptive names with spaces
34+
35+
# False positives for our patterns
36+
- optional_data_string_conversion # False positive on [UInt8] -> String conversion
37+
- for_where # Not always clearer than for+if
38+
- contains_over_first_not_nil # False positives on custom firstIndex implementations
39+
- sorted_imports # Handled by swift-format's OrderedImports rule
40+
- trailing_comma # Handled by swift-format (adds trailing commas for better diffs)
41+
42+
# Opt-in rules for code quality
43+
opt_in_rules:
44+
# Bug prevention
45+
- unused_optional_binding # Catch unused let bindings
46+
47+
# Code quality & cleanliness
48+
- implicit_optional_initialization # Don't initialize optionals to nil (formerly redundant_optional_initialization)
49+
- redundant_void_return # Don't write -> Void
50+
- empty_string # Use .isEmpty not == ""
51+
- vertical_whitespace # Consistent spacing
52+
53+
# Analyzer rules (require --analyze flag)
54+
analyzer_rules:
55+
- unused_import # Remove unnecessary imports
56+
57+
# Performance optimizations
58+
- empty_count # Use .isEmpty instead of .count == 0
59+
- first_where # Prefer first(where:) over filter().first
60+
- last_where # Prefer last(where:) over filter().last
61+
- contains_over_range_nil_comparison # Better performance
62+
- sorted_first_last # Prefer sorted().first/last
63+
64+
# Style consistency
65+
- closure_spacing # Consistent closure formatting
66+
- operator_usage_whitespace # Consistent operator spacing
67+
68+
# Safety
69+
- array_init # Safer array initialization
70+
- fatal_error_message # Require messages in fatalError
71+
- legacy_random # Use modern random APIs
72+
- overridden_super_call # Ensure super is called when needed
73+
74+
# Paths
75+
included:
76+
- Sources
77+
- Tests
78+
79+
excluded:
80+
- .build
81+
- .swiftpm
82+
83+
# Rule configurations
84+
85+
# Allow longer functions for complex performance logic
86+
function_body_length:
87+
warning: 60
88+
error: 100
89+
90+
# Allow larger types for comprehensive test suites
91+
type_body_length:
92+
warning: 400
93+
error: 600
94+
95+
# Nesting depth
96+
nesting:
97+
type_level: 2

Tests/IEEE_754 Tests/Double+IEEE_754 Tests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Tests for Double IEEE 754 extensions
55

66
import Testing
7+
78
@testable import IEEE_754
89

910
@Suite("Double+IEEE_754 - Direct bytes() method")

Tests/IEEE_754 Tests/Endianness Tests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Tests for little-endian and big-endian byte order handling
55

66
import Testing
7+
78
@testable import IEEE_754
89

910
@Suite("IEEE 754 - Double Endianness")

Tests/IEEE_754 Tests/Float+IEEE_754 Tests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Tests for Float IEEE 754 extensions
55

66
import Testing
7+
78
@testable import IEEE_754
89

910
@Suite("Float+IEEE_754 - Direct bytes() method")

0 commit comments

Comments
 (0)