Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
jobs:
release-build:
name: Release Build
runs-on: macos-15
runs-on: macos-26
outputs:
sha: ${{ steps.sha256.outputs.sha256 }}
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/xcodebuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- generic/platform=tvOS
- generic/platform=watchOS
- generic/platform=visionOS
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
Expand All @@ -47,7 +47,7 @@ jobs:
test:
name: Test
needs: build-libraries
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
Expand Down
22 changes: 22 additions & 0 deletions Sources/SwiftPackageListCore/Directories/Configuration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Configuration.swift
// SwiftPackageListCore
//
// Created by Felix Herrmann on 09.11.25.
//

import Foundation

struct Configuration: Directory {
let url: URL
}

extension Configuration {
var mirrors: Mirrors? {
get throws {
let url = url.appendingPathComponent("mirrors.json")
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
return try Mirrors(url: url)
}
}
}
76 changes: 76 additions & 0 deletions Sources/SwiftPackageListCore/Files/Mirrors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Mirrors.swift
// SwiftPackageListCore
//
// Created by Felix Herrmann on 09.11.25.
//

import Foundation

// swiftlint:disable identifier_name type_name

struct Mirrors: VersionedFile {
let url: URL
let storage: Storage

init(url: URL) throws {
self.url = url
self.storage = try Storage(url: url)
}
}

// MARK: - Storage

extension Mirrors {
enum Storage {
case v1(V1)
}
}

extension Mirrors.Storage: VersionedFileStorage {
private struct Version: Decodable {
let version: Int
}

init(url: URL) throws {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let version = try decoder.decode(Version.self, from: data)

switch version.version {
case 1:
let v1 = try decoder.decode(V1.self, from: data)
self = .v1(v1)
default:
throw RuntimeError("Version \(version.version) of mirrors.json is not supported")
}
}
}

// MARK: - V1

extension Mirrors.Storage {
struct V1: Decodable {
struct Object: Decodable {
let mirror: String
let original: String
}

let object: [Object]
let version: Int
}
}

// MARK: - Mirror

extension Mirrors {
func mirror(for location: String) -> String? {
switch storage {
case .v1(let v1):
let object = v1.object.first { $0.original == location }
return object?.mirror
}
}
}

// swiftlint:enable identifier_name type_name
15 changes: 9 additions & 6 deletions Sources/SwiftPackageListCore/Files/PackageResolved.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ extension PackageResolved.Storage {
// MARK: - Packages

extension PackageResolved {
func packages(in sourcePackages: SourcePackages) throws -> [Package] {
func packages(in sourcePackages: SourcePackages, configuration: Configuration?) throws -> [Package] {
switch self.storage {
case .v1(let packageResolved):
return try packages(pins: packageResolved.object.pins, sourcePackages: sourcePackages)
case .v2(let packageResolved):
return try packages(pins: packageResolved.pins, sourcePackages: sourcePackages)
return try packages(pins: packageResolved.pins, sourcePackages: sourcePackages, configuration: configuration)
case .v3(let packageResolved):
return try packages(pins: packageResolved.pins, sourcePackages: sourcePackages)
return try packages(pins: packageResolved.pins, sourcePackages: sourcePackages, configuration: configuration)
}
}

Expand Down Expand Up @@ -173,22 +173,25 @@ extension PackageResolved {

private func packages(
pins: [PackageResolved.Storage.V2.Pin],
sourcePackages: SourcePackages
sourcePackages: SourcePackages,
configuration: Configuration?
) throws -> [Package] {
let checkouts = sourcePackages.checkouts
let registryDownloads = sourcePackages.registryDownloads
let workspaceState = try sourcePackages.workspaceState
let mirrors = try configuration?.mirrors

return try pins.map { pin -> Package in
let name = workspaceState.packageName(for: pin.identity) ?? pin.identity
let location = mirrors?.mirror(for: pin.location) ?? pin.location

let packageSource: PackageSource?
switch pin.kind {
case .localSourceControl:
let url = URL(fileURLWithPath: pin.location)
let url = URL(fileURLWithPath: location)
packageSource = PackageSource(url: url)
case .remoteSourceControl:
packageSource = checkouts.packageSource(location: pin.location)
packageSource = checkouts.packageSource(location: location)
case .registry:
packageSource = registryDownloads.packageSource(identity: pin.identity, version: pin.state.version)
default:
Expand Down
7 changes: 6 additions & 1 deletion Sources/SwiftPackageListCore/Project/NativeProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ import SwiftPackageList
protocol NativeProject: Project {
var workspaceURL: URL { get }
var packageResolved: PackageResolved { get throws }
var configuration: Configuration? { get }
}

extension NativeProject {
var configuration: Configuration? {
return nil
}

func packages() throws -> [Package] {
let packageResolved = try packageResolved

Expand All @@ -36,6 +41,6 @@ extension NativeProject {
sourcePackages = SourcePackages(url: sourcePackagesDirectory)
}

return try packageResolved.packages(in: sourcePackages)
return try packageResolved.packages(in: sourcePackages, configuration: configuration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ struct SwiftPackage: NativeProject {
return try PackageResolved(url: url)
}
}

var configuration: Configuration? {
let url = workspaceURL
.appendingPathComponent(".swiftpm")
.appendingPathComponent("configuration")
return Configuration(url: url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ struct TuistDependencies: Project {
sourcePackages = SourcePackages(url: sourcePackagesDirectory)
}

return try packageResolved.packages(in: sourcePackages)
return try packageResolved.packages(in: sourcePackages, configuration: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,13 @@ struct XcodeProject: NativeProject {
return try PackageResolved(url: url)
}
}

var configuration: Configuration? {
let url = fileURL
.appendingPathComponent("project.xcworkspace")
.appendingPathComponent("xcshareddata")
.appendingPathComponent("swiftpm")
.appendingPathComponent("configuration")
return Configuration(url: url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ struct XcodeWorkspace: NativeProject {
return try PackageResolved(url: url)
}
}

var configuration: Configuration? {
let url = fileURL
.appendingPathComponent("xcshareddata")
.appendingPathComponent("swiftpm")
.appendingPathComponent("configuration")
return Configuration(url: url)
}
}
56 changes: 56 additions & 0 deletions Tests/SwiftPackageListCoreTests/MirrorsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// MirrorsTests.swift
// SwiftPackageListCoreTests
//
// Created by Felix Herrmann on 09.11.25.
//

import XCTest
@testable import SwiftPackageListCore

final class MirrorsTests: XCTestCase {
func testVersion1() throws {
let url = Bundle.module.url(
forResource: "mirrors_v1",
withExtension: "json",
subdirectory: "Resources/Mirrors"
)
let unwrappedURL = try XCTUnwrap(url)
let mirrors = try Mirrors(url: unwrappedURL)

guard case .v1(let storage) = mirrors.storage else {
XCTFail("\(unwrappedURL.path) was not recognized as v1")
return
}

XCTAssertEqual(storage.version, 1)
XCTAssertEqual(storage.object.count, 2)
}

func testUnsupportedVersion() throws {
let url = Bundle.module.url(
forResource: "mirrors_v999",
withExtension: "json",
subdirectory: "Resources/Mirrors"
)
let unwrappedURL = try XCTUnwrap(url)

XCTAssertThrowsError(try Mirrors(url: unwrappedURL)) { error in
XCTAssertTrue(error is RuntimeError)
XCTAssertEqual((error as? RuntimeError)?.description, "Version 999 of mirrors.json is not supported")
}
}

func testMirror() throws {
let url = Bundle.module.url(
forResource: "mirrors_v1",
withExtension: "json",
subdirectory: "Resources/Mirrors"
)
let unwrappedURL = try XCTUnwrap(url)
let mirrors = try Mirrors(url: unwrappedURL)

XCTAssertEqual(mirrors.mirror(for: "https://github.com/FelixHerrmann/swift-package-list"), "https://github.com/example/swift-package-list")
XCTAssertEqual(mirrors.mirror(for: "https://github.com/FelixHerrmann/test"), "/Users/example/test")
}
}
19 changes: 19 additions & 0 deletions Tests/SwiftPackageListCoreTests/ProjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ final class ProjectTests: XCTestCase {
.appendingPathComponent("Package.resolved")
XCTAssertEqual(try xcodeProject.packageResolved.url, expectedPackageResolvedFileURL)

let expectedConfigurationDirectoryURL = unwrappedURL
.appendingPathComponent("project.xcworkspace")
.appendingPathComponent("xcshareddata")
.appendingPathComponent("swiftpm")
.appendingPathComponent("configuration")
XCTAssertEqual(xcodeProject.configuration?.url, expectedConfigurationDirectoryURL)

XCTAssertEqual(xcodeProject.name, "Project")
XCTAssertEqual(xcodeProject.organizationName, "SwiftPackageList")
}
Expand All @@ -52,6 +59,12 @@ final class ProjectTests: XCTestCase {
.appendingPathComponent("Package.resolved")
XCTAssertEqual(try xcodeWorkspace.packageResolved.url, expectedPackageResolvedFileURL)

let expectedConfigurationDirectoryURL = unwrappedURL
.appendingPathComponent("xcshareddata")
.appendingPathComponent("swiftpm")
.appendingPathComponent("configuration")
XCTAssertEqual(xcodeWorkspace.configuration?.url, expectedConfigurationDirectoryURL)

XCTAssertEqual(xcodeWorkspace.name, "Workspace")
XCTAssertEqual(xcodeWorkspace.organizationName, "SwiftPackageList")
}
Expand All @@ -71,6 +84,12 @@ final class ProjectTests: XCTestCase {
.appendingPathComponent("Package.resolved")
XCTAssertEqual(try swiftPackage.packageResolved.url, expectedPackageResolvedFileURL)

let expectedConfigurationDirectoryURL = unwrappedURL
.deletingLastPathComponent()
.appendingPathComponent(".swiftpm")
.appendingPathComponent("configuration")
XCTAssertEqual(swiftPackage.configuration?.url, expectedConfigurationDirectoryURL)

XCTAssertEqual(swiftPackage.name, "SwiftPackage")
XCTAssertNil(swiftPackage.organizationName)
}
Expand Down
13 changes: 13 additions & 0 deletions Tests/SwiftPackageListCoreTests/Resources/Mirrors/mirrors_v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"object" : [
{
"mirror" : "https://github.com/example/swift-package-list",
"original" : "https://github.com/FelixHerrmann/swift-package-list"
},
{
"mirror" : "/Users/example/test",
"original" : "https://github.com/FelixHerrmann/test"
}
],
"version" : 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": 999
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"identity" : "swift-package-list",
"kind" : "localSourceControl",
"location" : "Users/example/swift-package-list",
"location" : "/Users/example/swift-package-list",
"state" : {
"revision" : "3a1b45c9e994aebaf47e8c4bd631bd79075f4abb",
"version" : "1.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"identity" : "swift-package-list",
"kind" : "localSourceControl",
"location" : "Users/example/swift-package-list",
"location" : "/Users/example/swift-package-list",
"state" : {
"revision" : "3a1b45c9e994aebaf47e8c4bd631bd79075f4abb",
"version" : "1.0.1"
Expand Down