diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32e2e2b..cfdfde5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,16 @@ jobs: - name: Gate (tools/test.sh, release) run: tools/test.sh + lint: + name: Lint (shared SwiftLint and SwiftFormat config) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: SwiftLint + run: docker run --rm -v "$PWD:/work" -w /work --entrypoint swiftlint ghcr.io/realm/swiftlint:0.65.1 lint --strict --quiet + - name: SwiftFormat + run: docker run --rm -v "$PWD:/work" -w /work --entrypoint swiftformat ghcr.io/nicklockwood/swiftformat:0.64.4 --lint . + private-refs: name: No private references runs-on: ubuntu-latest diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml new file mode 100644 index 0000000..1b34482 --- /dev/null +++ b/.github/workflows/periphery.yml @@ -0,0 +1,45 @@ +# Weekly unused-code scan (Periphery). A copy of the suite's template; edit it there. +# Builds the package and its tests, scans with --retain-public (a kit's public API is used by the apps), and keeps one +# open issue, "Periphery weekly scan", up to date with the findings. Findings are for triage; nothing is deleted here. +name: Periphery + +on: + schedule: + - cron: "17 3 * * 0" + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + scan: + runs-on: macos-26 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Install Periphery + run: brew install periphery && periphery version + - name: Build with the index store + run: swift build --build-system native --build-tests + - name: Scan + run: | + periphery scan --retain-public --skip-build --index-store-path .build/debug/index/store \ + --format csv --relative-results > periphery.csv + echo "count=$(( $(wc -l < periphery.csv) - 1 ))" >> "$GITHUB_ENV" + - name: Update the issue + env: + GH_TOKEN: ${{ github.token }} + run: | + title="Periphery weekly scan" + echo "Periphery $(periphery version), commit ${GITHUB_SHA::7}, $(date -u +%Y-%m-%d): **${count} findings** (run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})." > body.md + printf '\n| Kind | Name | Location | Hint |\n|---|---|---|---|\n' >> body.md + python3 - >> body.md <<'PY' + import csv + for r in list(csv.DictReader(open('periphery.csv')))[:100]: + print(f"| {r['Kind']} | `{r['Name']}` | {r['Location']} | {r['Hints']} |") + PY + [ "$count" -le 100 ] || echo "| … | $((count - 100)) more in the run's log | | |" >> body.md + cat periphery.csv + n=$(gh issue list --state open --search "in:title \"$title\"" --json number,title -q ".[] | select(.title == \"$title\") | .number" | head -1) + if [ -n "$n" ]; then gh issue edit "$n" --body-file body.md; else gh issue create --title "$title" --body-file body.md; fi diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..475b591 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,5 @@ +# The suite's one SwiftFormat config. tools/lint/sync.sh copies it to every Swift repo as .swiftformat; edit it here only. +# CI runs `swiftformat --lint .`: whitespace and a few unambiguous fixes, nothing that reflows code. +--swiftversion 6.2 +--exclude .build,build,References,DerivedData +--rules trailingSpace,linebreakAtEndOfFile,consecutiveBlankLines,duplicateImports,semicolons,spaceAroundParens,emptyBraces,todos diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..0244b9e --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,33 @@ +# The suite's one SwiftLint config. tools/lint/sync.sh copies it to every Swift repo as .swiftlint.yml; edit it here only. +# Rules that catch a mistake or keep files tidy, all fixable without touching a number or a name. Not enabled, on purpose: +# identifier_name and type_name (the ported Python names: nx, q, E0), line_length (the house style runs to about 150 columns), +# force_unwrapping and force_try (tests and fixtures), identical_operands (`x != x` is a NaN test in numeric code), +# orphaned_doc_comment (a `// delete at` note between a doc comment and its declaration is the house idiom). +excluded: + - .build + - build + - References + - DerivedData +only_rules: + - duplicate_imports + - duplicate_enum_cases + - duplicated_key_in_dictionary_literal + - duplicate_conditions + - unused_optional_binding + - unused_setter_value + - discarded_notification_center_observer + - unneeded_override + - control_statement + - void_return + - trailing_whitespace + - trailing_newline + - leading_whitespace + - vertical_whitespace + - comment_spacing + - return_arrow_whitespace + - legacy_constructor + - legacy_cggeometry_functions + - legacy_nsgeometry_functions + - superfluous_disable_command + - blanket_disable_command + - invalid_swiftlint_command diff --git a/Sources/SpectroEDX/Spectroscopy/Proposer/ElementProposer.swift b/Sources/SpectroEDX/Spectroscopy/Proposer/ElementProposer.swift index 0cdb0d4..3000e6a 100644 --- a/Sources/SpectroEDX/Spectroscopy/Proposer/ElementProposer.swift +++ b/Sources/SpectroEDX/Spectroscopy/Proposer/ElementProposer.swift @@ -133,7 +133,6 @@ public nonisolated enum ProposerError: Error, Equatable { case cancelled } - public nonisolated struct ElementProposer: Sendable { package var currie: Currie /// The elements tested; nil is `defaultPool`. diff --git a/Sources/SpectroEELS/EELSComposition.swift b/Sources/SpectroEELS/EELSComposition.swift index 519b33a..89ddf8c 100644 --- a/Sources/SpectroEELS/EELSComposition.swift +++ b/Sources/SpectroEELS/EELSComposition.swift @@ -28,7 +28,7 @@ public enum EELSQuantification { var columns: [Int] = [] if let dup = symbols.enumerated().first(where: { i, s in symbols[..= 20) } - /// Where eXSpy's signed maximum stops early (the criterion_split cases: 3 and 4 elements), the default `.absolute` rule runs on /// and ends nearer the fixed point; the difference is bounded by the stopping error. Without these two cases the comparison above /// would hold trivially (every two-element set, and every other set here, stops on the same iteration under both rules).