Skip to content

Add Swift Package Manager scanner - #31

Open
jthDEV wants to merge 1 commit into
mainfrom
feature/swift-package-manager-scan
Open

Add Swift Package Manager scanner#31
jthDEV wants to merge 1 commit into
mainfrom
feature/swift-package-manager-scan

Conversation

@jthDEV

@jthDEV jthDEV commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Ports dependency resolution from the now-retired ts-spm plugin into ts-scan's own Scanner interface, replacing the need for a separate plugin
  • Detects Package.swift and parses the JSON tree produced by swift package show-dependencies --format json into ts-scan's Dependency/DependencyScan model, using the swift purl type as key/purl prefix
  • Registers SwiftScanner alongside the other package-manager scanners; --swift:ignore, --swift:executable and --swift:forward become available like for the other PM-backed scanners

Dependencies added

None — uses only the swift executable already required by Swift Package Manager projects.

Test plan

  • pytest tests/test_swift.py — accepts()/scan() unit tests with a mocked swift package show-dependencies JSON tree (including diamond-dependency de-duplication)
  • pytest tests/ — full existing suite still passes

Ports the dependency resolution logic from the retired ts-spm plugin
(https://github.com/TrustSource/ts-spm) into ts-scan's own Scanner
interface. Detects Package.swift and parses the JSON tree produced by
`swift package show-dependencies --format json`.
@jthDEV
jthDEV requested a review from gr-markin August 25, 2026 23:04
Comment thread tests/test_swift.py
@@ -0,0 +1,83 @@
import json
from subprocess import CompletedProcess
Comment thread tests/test_swift.py
def test_accepts_requires_package_swift(tmp_path):
scanner = SwiftScanner()

assert not scanner.accepts(tmp_path)
Comment thread tests/test_swift.py

(tmp_path / 'Package.swift').write_text('// swift-tools-version:5.9\n')

assert scanner.accepts(tmp_path)
Comment thread tests/test_swift.py

scan = scanner.scan(tmp_path)

assert scan is not None
Comment thread tests/test_swift.py
scan = scanner.scan(tmp_path)

assert scan is not None
assert scan.module == 'MyPackage'
Comment thread tests/test_swift.py

arg_parser = next(d for d in root.dependencies if d.name == 'swift-argument-parser')
assert arg_parser.versions == ['1.2.0']
assert arg_parser.repoUrl == 'https://github.com/apple/swift-argument-parser'
Comment thread tests/test_swift.py
arg_parser = next(d for d in root.dependencies if d.name == 'swift-argument-parser')
assert arg_parser.versions == ['1.2.0']
assert arg_parser.repoUrl == 'https://github.com/apple/swift-argument-parser'
assert arg_parser.key == 'swift:swift-argument-parser'
Comment thread tests/test_swift.py
assert arg_parser.key == 'swift:swift-argument-parser'

log = next(d for d in root.dependencies if d.name == 'swift-log')
assert [d.name for d in log.dependencies] == ['swift-argument-parser']
Comment thread tests/test_swift.py

# The transitive occurrence of swift-argument-parser under swift-log resolves
# to the same Dependency instance as the direct one.
assert log.dependencies[0] is arg_parser
Comment thread tests/test_swift.py
lambda *args, **kwargs: CompletedProcess(args, 0, stdout=b'')
)

assert scanner.scan(tmp_path) is None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants