Skip to content

Commit 59039f9

Browse files
authored
Merge pull request #4 from getyourguide/readme-and-improvements
Readme and improvements
2 parents a0ea57b + b29a185 commit 59039f9

File tree

5 files changed

+125
-10
lines changed

5 files changed

+125
-10
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ let package = Package(
3131
dependencies: [
3232
.package(
3333
url: "https://github.com/tuist/GraphViz.git",
34-
branch: "main" // a few commits ahead of the deprecated GraphViz original repo. It also includes Xcode 16 fixes.
34+
// a few commits ahead of the deprecated GraphViz original repo. It also includes Xcode 16 fixes.
35+
revision: "083bccf9e492fd5731dd288a46741ea80148f508"
3536
),
3637
.package(
3738
url: "https://github.com/apple/swift-argument-parser.git",

README.md

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,104 @@
1-
# spmgraph
1+
# spmgraph - SwiftPM dependency graph management
2+
3+
A CLI tool that **unlocks Swift dependency graphs**, giving you extra information and capabilities.
4+
With it you can visualize your dependency graph, run selective testing, and enforce architectural rules for optimal modular setups.
5+
6+
## How to use
7+
8+
`spmgraph` is run for a local `Package.swift` and takes the user configuration from a `SPMGraphConfig.swift` file.
9+
10+
### Edit
11+
Create and edit your spmgraph config
12+
13+
- run `spmgraph edit <'Package.swift' path> <options>`
14+
- it creates an initial `SPMGraphConfig.swift` on the same path as your `Package.swift`
15+
- spmgraph opens up a temporary Swift Package where you configure spmgraph in Swift and build to check that everything is correct
16+
17+
### Visualize
18+
Generate an image with a visual representation of your dependency. **Open the map!**
19+
20+
run `spmgraph visualize <'Package.swift' path> help` for more
21+
22+
### Tests
23+
**Selective testing** based on git changes or a given list of changed files.
24+
The output is a comma separate list of test targets that can be fed into `xcodebuild`'s `-only-testing:TEST-IDENTIFIER` or [fastlane scan](https://docs.fastlane.tools/actions/scan/#scan)'s `only_testing`
25+
26+
run `spmgraph tests <'Package.swift' path> help` for more
27+
28+
### Lint
29+
Verify if the dependency graph follow the defined team and industry best practices.
30+
31+
For example, enforce that
32+
- feature modules don't depend on each other
33+
- Linked dependencies are imported (used) at least once
34+
- Base modules don't depend on feature modules
35+
- the dependency graph isn't too deep
36+
37+
All possible using Swift. Below an example of creating your own lint rule by traversing the dependency graph:
38+
```swift
39+
extension SPMGraphConfig.Lint.Rule {
40+
static let unregisteredLiveModules = Self(
41+
id: "unregisteredLiveModules",
42+
name: "Unregistered Live modules",
43+
abstract: "Live modules need to be added to the app target / feature module as dependencies.",
44+
validate: { package, excludedSuffixes in
45+
let liveModules = package
46+
.modules
47+
.compactMap { module -> Module? in
48+
guard !module.containsOneOf(suffixes: excludedSuffixes), module.isLiveModule else {
49+
return nil
50+
}
51+
return module
52+
}
53+
54+
guard
55+
let featureModule = package
56+
.modules
57+
.first(where: { $0.name == "GetYourGuideFeature" }),
58+
case let featureModuleDependencies = featureModule
59+
.dependencies
60+
.compactMap(\.module)
61+
else {
62+
return [LintError.missingFeatureModule]
63+
}
64+
65+
return liveModules.compactMap { liveModule in
66+
if !featureModuleDependencies.contains(liveModule) {
67+
return LintError.unregisteredLiveModules(
68+
moduleName: liveModule.name,
69+
appModule: featureModule.name
70+
)
71+
}
72+
73+
return nil
74+
}
75+
}
76+
)
77+
}
78+
```
79+
80+
run `spmgraph lint <'Package.swift' path> help` for more
81+
82+
## Requirements
83+
- [graphviz](https://github.com/graphp/graphviz) (available via `brew install graphviz`)
84+
- Xcode 16+ and the Swift 6.0+ toolchain
85+
86+
## Installation
87+
88+
### [Mint](https://github.com/yonaskolb/mint)
89+
90+
```
91+
mint install getyourguide/spmgraph
92+
```
93+
* For optimal build times make sure `~/.mint/bin/spmgraph` is cached on your CI runner.
94+
95+
## Acknowledgments
96+
- Inspired by the work that the [Tuist](https://tuist.dev/) team does for the Apple developers community and their focus on leveraging the dependency graph to provide amazing features for engineers. Also, source of inspiration for our shell abstraction layer.
97+
98+
## Open roadmap
99+
- [ ] Cover the core logic of Lint, Map and Visualize libs with tests
100+
- [ ] Support macros (to become a GitHub issue)
101+
102+
Ideas
103+
- [ ] Lint - see if it can be improved to cover auto-exported dependencies. For example usages of `import Dependencies` justify linking `DependenciesExtras` as a dependency.
104+
- [ ] Add fix it suggestion to lint errors

Sources/SPMGraphConfigSetup/Resources/SPMGraphConfig.txt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import SPMGraphDescriptionInterface
44

55
// MARK: - Instructions
66

7-
// You can leverage the minimal and advanced configurations below.
7+
// You can leverage the default, minimal and advanced configurations below.
88
//
9-
// By default, when using the minimal config, the lint rules are the `.default` ones included
10-
// in SPMGraphConfig.
9+
// When using either the `default` or the `minimal` configurations, the lint rules are the ones
10+
// that are default and built-in.
1111
//
1212
// The advanced configuration contains examples of creating our own dependency graph rules,
1313
// including how to extend the PackageModel types with helpers that, for example, categorize types
@@ -18,14 +18,21 @@ import SPMGraphDescriptionInterface
1818
//
1919
// Have fun! Ah, and don't forget to select `macOS` as the build target ;)!
2020

21+
// MARK: - Default
22+
23+
// the quickest path in case you don't care about the strict mode and custom lint rules
24+
let spmGraphConfig = SPMGraphConfig.default
25+
2126
// MARK: - Minimal
2227

23-
//let spmGraphConfig = SPMGraphConfig(
24-
// lint: SPMGraphConfig.Lint(isStrict: true)
25-
//)
28+
// the default lint rules and strict mode enabled to fail on lint errors
29+
let spmGraphConfig = SPMGraphConfig(
30+
lint: SPMGraphConfig.Lint(isStrict: true)
31+
)
2632

2733
// MARK: - Advanced
2834

35+
// custom configuration, with both default and team specific graph lint rules
2936
let spmGraphConfig = SPMGraphConfig(
3037
lint: SPMGraphConfig.Lint(
3138
rules: .custom + .default, // The default rules + your custom ones

Sources/SPMGraphDescriptionInterface/SPMGraphConfigInterface.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public struct SPMGraphConfig: Sendable {
7373
self.isStrict = isStrict
7474
self.expectedWarningsCount = expectedWarningsCount
7575
}
76+
77+
public static let `default`: Self = .init(isStrict: false)
7678
}
7779

7880
/// Configuration for selective testing.
@@ -110,6 +112,9 @@ public struct SPMGraphConfig: Sendable {
110112
self.tests = tests
111113
self.excludedSuffixes = excludedSuffixes
112114
}
115+
116+
/// The default ``SPMGraphConfig`` with strict mode disabled and the built-in lint rules.
117+
public static let `default`: Self = .init(lint: .default)
113118
}
114119

115120
typealias Validate = (Package, _ excludedSuffixes: [String]) -> [LocalizedError]

Sources/SPMGraphExecutable/Subcommands/Edit.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ struct Edit: AsyncParsableCommand {
5252
discussion: """
5353
It looks for an `SPMGraphConfig.swift` file in the same directory as the `Package.swift` under analyzes. If there's none, it creates a fresh one from a template.
5454
55-
Next, it generates a temporary package for editing your `SPMGraphConfig.swift`, where you customize multiple settings, from the expected warnings count to
56-
writing your own dependency graph rules in Swift code.
55+
Next, it generates a temporary package for editing your `SPMGraphConfig.swift`, where you customize multiple settings, from the expected warnings count to writing your own dependency graph rules in Swift code.
5756
5857
Once the `SPMGraphConfig.swift` is edited, your configuration is dynamic loaded into spmgraph and leveraged on all other commands.
5958
""",

0 commit comments

Comments
 (0)