Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a3ea634
Add package fixture
marinofelipe Oct 25, 2025
d0a0e48
Update package resolution
marinofelipe Oct 25, 2025
1ef8b78
Delete older fixtures
marinofelipe Oct 25, 2025
3647c48
Fix rules and make error equatable
marinofelipe Oct 25, 2025
8510ab6
Add config tests
marinofelipe Oct 25, 2025
bdf9469
Enable SPMGraphLintInput memberwise init
marinofelipe Oct 25, 2025
47c6e70
Reuse test plan
marinofelipe Oct 25, 2025
002fa72
Compile loading package fixture
marinofelipe Oct 25, 2025
88f88ca
Use orka runner
marinofelipe Oct 26, 2025
0be21fe
Improve error handling
marinofelipe Oct 26, 2025
eadac0d
Improve comment
marinofelipe Oct 26, 2025
e5e9a65
Build and run tests on both toolchains
marinofelipe Oct 26, 2025
f3c0e2d
Fix Xcode v in CI
marinofelipe Oct 26, 2025
74bbc89
Fix concurrency error
marinofelipe Oct 26, 2025
927afbc
Create fixtures support module
marinofelipe Oct 26, 2025
9943c29
Declare target
marinofelipe Oct 26, 2025
a204204
Upadte tests to use it
marinofelipe Oct 26, 2025
3609b22
Bump SPM to 6.2, increase min v to 6.1
marinofelipe Oct 26, 2025
b80b597
Bump config dependency and update readme
marinofelipe Oct 26, 2025
8771397
Disable parallel tests
marinofelipe Oct 26, 2025
6b75486
Fix test
marinofelipe Oct 26, 2025
9250189
Update scheme
marinofelipe Oct 26, 2025
ef0431e
Update readme
marinofelipe Oct 26, 2025
e6e4ce9
format input var
marinofelipe Oct 26, 2025
c17e551
remove unused input arg
marinofelipe Oct 26, 2025
582d44f
Improve config build dir docs
marinofelipe Oct 27, 2025
14b0df1
Document config build dir caching
marinofelipe Oct 27, 2025
36847d1
Merge pull request #37 from getyourguide/fix-actions
marinofelipe Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

# Fixture packages
**/Fixtures/**/.swiftpm/
41 changes: 39 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/spmgraph-Package.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,45 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:spmgraph.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SPMGraphExecutableTests"
BuildableName = "SPMGraphExecutableTests"
BlueprintName = "SPMGraphExecutableTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SPMGraphLintTests"
BuildableName = "SPMGraphLintTests"
BlueprintName = "SPMGraphLintTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SPMGraphDescriptionInterfaceTests"
BuildableName = "SPMGraphDescriptionInterfaceTests"
BlueprintName = "SPMGraphDescriptionInterfaceTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand Down
14 changes: 7 additions & 7 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ let package = Package(
dependencies: [
.target(name: "SPMGraphExecutable"),
]
),
.testTarget(
name: "SPMGraphDescriptionInterfaceTests",
dependencies: [
.target(name: "SPMGraphDescriptionInterface"),
.target(name: "Core"),
.product(
name: "ArgumentParser",
package: "swift-argument-parser"
),
],
resources: [.copy("Fixtures/PackageFixture")]
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@ public extension SPMGraphConfig.Lint.Rule {
validate: { package, excludedSuffixes in
let liveModules = package.modules
.filter { !$0.containsOneOf(suffixes: excludedSuffixes) }
.filter(\.isLiveModule)
.filter(isLiveModule)
.sorted()

let errors: [SPMGraphConfig.Lint.Error] =
liveModules
.map { liveModule in
liveModule.dependencies
.compactMap(\.module)
.filter(\.isLiveModule == true)
.filter(isLiveModule)
.filter { !excludedDependencies.contains($0.name) }
.map { dependency in
SPMGraphConfig.Lint.Error.liveModuleLiveDependency(
Expand Down Expand Up @@ -223,7 +223,7 @@ public extension SPMGraphConfig.Lint.Rule {

- Note: For `@_exported` usages, there will be an error in case only the exported module is used.
For example, module Networking exports module NetworkingHelpers, if only NetworkingHelpers is used by a target
there will be a lint error, while if both Networking and NetworkingHelpers are used there will be no error.
there will be a lint error, while if both Networking and NetworkingHelpers are used there will be no error.
""",
validate: { package, excludedSuffixes in
let errors: [SPMGraphConfig.Lint.Error] = package.modules
Expand Down Expand Up @@ -355,7 +355,7 @@ extension Module: @retroactive Comparable {
}

extension SPMGraphConfig.Lint {
enum Error: LocalizedError {
enum Error: LocalizedError, Equatable {
case liveModuleLiveDependency(moduleName: String, liveDependencyName: String)
case baseOrInterfaceModuleLiveDependency(moduleName: String, liveDependencyName: String)
case unusedDependencies(moduleName: String, dependencyName: String)
Expand Down
6 changes: 4 additions & 2 deletions Sources/SPMGraphLint/SPMGraphLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import SPMGraphDescriptionInterface

// MARK: - Input

public struct SPMGraphLintInput {
public struct SPMGraphLintInput: Equatable {
/// "Directory path of Package.swift file"
let spmPackageDirectory: AbsolutePath
/// A custom build directory for the package used to edit and load the SPMGraphConfig.
Expand All @@ -40,9 +40,11 @@ public struct SPMGraphLintInput {
let expectedWarningsCount: UInt
/// Relative path for an output file with the formatted lint results. By default the errors are added only listed in the sdtout.
let outputFilePath: String?
}

public extension SPMGraphLintInput {
/// Makes an instance of ``SPMGraphLintInput``
public init(
init(
spmPackageDirectory: String,
configBuildDirectory: String?,
excludedSuffixes: [String],
Expand Down
40 changes: 0 additions & 40 deletions Tests/Fixtures/Package/Package.swift

This file was deleted.

8 changes: 0 additions & 8 deletions Tests/Fixtures/Package/Sources/TargetA/Example.swift

This file was deleted.

11 changes: 0 additions & 11 deletions Tests/Fixtures/Package/Sources/TargetB/Example.swift

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
//
//
// Copyright (c) 2025 GetYourGuide GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

import Basics
import Foundation
import PackageModel
import Workspace

func loadFixturePackage() async throws -> Package {
let path = Bundle.module
.path(forResource: "Package", ofType: "swift", inDirectory: "Fixtures/PackageFixture")?
.replacingOccurrences(of: "/Package.swift", with: "")
let fixturePackagePath = try AbsolutePath(validating: path!)

let observability = ObservabilitySystem { print("\($0): \($1)") }
let workspace = try Workspace(forRootPackage: fixturePackagePath)

return try await workspace.loadRootPackage(
at: fixturePackagePath,
observabilityScope: observability.topScope
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "LintFixturePackage",
products: [
.library(
name: "LintLibrary",
targets: [
"BaseModule",
"InterfaceModule",
"FeatureModule",
"NetworkingLive",
]
),
],
targets: [
// Base module - should not depend on Live modules
.target(
name: "BaseModule",
dependencies: []
),

// Interface module - should not depend on Live modules
.target(
name: "InterfaceModule",
dependencies: ["BaseModule"]
),

// Feature module - can depend on Live modules
.target(
name: "FeatureModule",
dependencies: [
"InterfaceModule",
"NetworkingLive",
]
),

// Live module - should not depend on other Live modules
.target(
name: "NetworkingLive",
dependencies: ["InterfaceModule"]
),

// Another Live module for testing Live -> Live dependency rule
.target(
name: "StorageLive",
dependencies: ["NetworkingLive"] // This violates the rule
),

// Module with unused dependency
.target(
name: "ModuleWithUnusedDep",
dependencies: [
"BaseModule", // This is unused in the actual code
"InterfaceModule",
]
),

// Test targets (should be excluded)
.testTarget(
name: "BaseModuleTests",
dependencies: ["BaseModule"]
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public struct BaseModule {
public init() {}

public func baseFunction() -> String {
"Base Module"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import InterfaceModule
import LiveModule

public struct FeatureModule {
private let interface = InterfaceModule()
private let live = LiveModule()

public init() {}

public func featureFunction() -> String {
"Feature: \(interface.interfaceFunction()) + \(live.liveFunction())"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import BaseModule

public protocol InterfaceModuleProtocol {
func interfaceFunction() -> String
}

public struct InterfaceModule: InterfaceModuleProtocol {
private let base = BaseModule()

public init() {}

public func interfaceFunction() -> String {
"Interface Module with \(base.baseFunction())"
}
}
Loading
Loading