Skip to content

Commit 4dee7ed

Browse files
Merge pull request #41 from nikolainobadi/decouple-swift-data
Decouple swift data
2 parents 3f57056 + 757f912 commit 4dee7ed

33 files changed

+777
-823
lines changed

Sources/NnexKit/Building/BuildConfig.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
// Created by Nikolai Nobadi on 3/25/25.
66
//
77

8-
public struct BuildConfig: Sendable {
8+
public struct BuildConfig {
99
public let projectName: String
1010
public let projectPath: String
1111
public let buildType: BuildType
1212
public let extraBuildArgs: [String]
1313
public let skipClean: Bool
14-
public let testCommand: CurrentSchema.TestCommand?
14+
public let testCommand: HomebrewFormula.TestCommand?
1515

1616
/// Initializes a new `BuildConfig` with the specified settings.
1717
///
@@ -22,7 +22,7 @@ public struct BuildConfig: Sendable {
2222
/// - extraBuildArgs: Additional arguments to pass to the build command.
2323
/// - shouldClean: Indicates whether the project should be cleaned before building. Defaults to `true`.
2424
/// - testCommand: An optional command to run tests after building. Defaults to `nil`, meaning no tests will be run.
25-
public init(projectName: String, projectPath: String, buildType: BuildType, extraBuildArgs: [String], skipClean: Bool, testCommand: CurrentSchema.TestCommand?) {
25+
public init(projectName: String, projectPath: String, buildType: BuildType, extraBuildArgs: [String], skipClean: Bool, testCommand: HomebrewFormula.TestCommand?) {
2626
self.projectName = projectName
2727
self.projectPath = projectPath.hasSuffix("/") ? projectPath : projectPath + "/"
2828
self.buildType = buildType
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// String+Matches.swift
3+
// nnex
4+
//
5+
// Created by Nikolai Nobadi on 12/11/25.
6+
//
7+
8+
public extension String {
9+
/// Returns `true` if the string matches another value, case-insensitively.
10+
/// - Parameter value: The optional string to compare against.
11+
/// - Returns: `true` if both strings match when lowercased; otherwise, `false`.
12+
func matches(_ value: String?) -> Bool {
13+
guard let value else {
14+
return false
15+
}
16+
17+
return self.lowercased() == value.lowercased()
18+
}
19+
}

Sources/NnexKit/Formula/BrewFormula.swift renamed to Sources/NnexKit/Formula/DecodableFormulaTemplate.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
//
2-
// BrewFormula.swift
2+
// DecodableFormulaTemplate.swift
33
// nnex
44
//
55
// Created by Nikolai Nobadi on 3/20/25.
66
//
77

88
/// Represents a Homebrew formula with metadata and version information.
9-
public struct BrewFormula: Codable {
9+
public struct DecodableFormulaTemplate: Codable {
1010
/// The name of the formula.
1111
public let name: String
1212

@@ -39,4 +39,4 @@ public struct BrewFormula: Codable {
3939
self.license = license
4040
self.versions = versions
4141
}
42-
}
42+
}

Sources/NnexKit/Formula/PublishUtilities.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public enum PublishUtilities {
1414
/// - shell: The shell instance to use for building.
1515
/// - Returns: The binary output including path(s) and hash(es).
1616
/// - Throws: An error if the build process fails.
17-
public static func buildBinary(formula: SwiftDataHomebrewFormula, buildType: BuildType, skipTesting: Bool, shell: any NnexShell) throws -> BinaryOutput {
17+
public static func buildBinary(formula: HomebrewFormula, buildType: BuildType, skipTesting: Bool, shell: any NnexShell) throws -> BinaryOutput {
1818
let testCommand = skipTesting ? nil : formula.testCommand
1919
let config = BuildConfig(projectName: formula.name, projectPath: formula.localProjectPath, buildType: buildType, extraBuildArgs: formula.extraBuildArgs, skipClean: false, testCommand: testCommand)
2020
let builder = ProjectBuilder(shell: shell, config: config)
@@ -49,7 +49,7 @@ public enum PublishUtilities {
4949
/// - assetURLs: The asset URLs from the GitHub release.
5050
/// - Returns: The formula content as a string.
5151
/// - Throws: An error if formula generation fails.
52-
public static func makeFormulaContent(formula: SwiftDataHomebrewFormula, version: String, archivedBinaries: [ArchivedBinary], assetURLs: [String]) throws -> String {
52+
public static func makeFormulaContent(formula: HomebrewFormula, version: String, archivedBinaries: [ArchivedBinary], assetURLs: [String]) throws -> String {
5353
let formulaName = formula.name
5454

5555
if archivedBinaries.count == 1 {

Sources/NnexKit/Git/DefaultGitHandler.swift

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,60 +19,33 @@ public struct DefaultGitHandler {
1919

2020
// MARK: - Actions
2121
extension DefaultGitHandler: GitHandler {
22-
/// Adds all changes, commits with a given message, and pushes to the remote repository.
23-
/// - Parameters:
24-
/// - message: The commit message describing the changes.
25-
/// - path: The file path of the repository.
2622
public func commitAndPush(message: String, path: String) throws {
2723
try shell.runAndPrint(bash: makeGitCommand(.addAll, path: path))
2824
try shell.runAndPrint(bash: makeGitCommand(.commit(message: message), path: path))
2925
try shell.runAndPrint(bash: makeGitCommand(.push, path: path))
3026
}
3127

32-
/// Retrieves the remote URL of the repository located at the given path.
33-
/// - Parameter path: The file path of the repository.
34-
/// - Returns: A string representing the remote URL.
3528
public func getRemoteURL(path: String) throws -> String {
3629
return try shell.getGitHubURL(at: path)
3730
}
3831

39-
/// Retrieves the previous release version from the repository at the given path.
40-
/// - Parameter path: The file path of the repository.
41-
/// - Returns: A string representing the previous release version.
4232
public func getPreviousReleaseVersion(path: String) throws -> String {
4333
return try shell.bash(makeGitHubCommand(.getPreviousReleaseVersion, path: path))
4434
}
4535

46-
/// Initializes a new Git repository at the given path.
47-
/// - Parameter path: The file path where the repository should be initialized.
4836
public func gitInit(path: String) throws {
4937
try GitStarter(path: path, shell: shell).gitInit()
5038
}
5139

52-
/// Initializes a new remote repository on GitHub with specified details and returns the repository URL.
53-
/// - Parameters:
54-
/// - tapName: The name of the remote repository.
55-
/// - path: The file path where the repository is located.
56-
/// - projectDetails: A description of the repository.
57-
/// - visibility: The visibility of the repository (public or private).
58-
/// - Returns: A string representing the repository URL.
5940
public func remoteRepoInit(tapName: String, path: String, projectDetails: String, visibility: RepoVisibility) throws -> String {
6041
let info = RepoInfo(name: tapName, details: projectDetails, visibility: visibility, canUploadFromNonMainBranch: false)
6142
return try GitHubRepoStarter(path: path, shell: shell, repoInfo: info).repoInit()
6243
}
6344

64-
/// Creates a new release with one or more archived binaries and returns all asset URLs.
65-
/// - Parameters:
66-
/// - version: The version number for the release.
67-
/// - archivedBinaries: The archived binary files to upload to the release.
68-
/// - releaseNoteInfo: Information for generating release notes.
69-
/// - path: The file path of the repository.
70-
/// - Returns: An array of asset URLs, with the primary asset URL first, followed by additional asset URLs.
7145
public func createNewRelease(version: String, archivedBinaries: [ArchivedBinary], releaseNoteInfo: ReleaseNoteInfo, path: String) throws -> [String] {
7246
return try createReleaseWithAllBinaries(version: version, archivedBinaries: archivedBinaries, releaseNoteInfo: releaseNoteInfo, path: path)
7347
}
7448

75-
/// Verifies if the GitHub CLI (gh) is installed and provides installation instructions if not.
7649
public func ghVerification() throws {
7750
if try shell.bash("which gh").contains("not found") {
7851
throw NnexError.missingGitHubCLI
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// HomebrewFormula.swift
3+
// nnex
4+
//
5+
// Created by Nikolai Nobadi on 12/11/25.
6+
//
7+
8+
public struct HomebrewFormula {
9+
public var name: String
10+
public var details: String
11+
public var homepage: String
12+
public var license: String
13+
public var localProjectPath: String
14+
public var uploadType: FormulaUploadType
15+
public var testCommand: TestCommand?
16+
public var extraBuildArgs: [String]
17+
18+
public init(
19+
name: String,
20+
details: String,
21+
homepage: String,
22+
license: String,
23+
localProjectPath: String,
24+
uploadType: FormulaUploadType,
25+
testCommand: TestCommand?,
26+
extraBuildArgs: [String]
27+
) {
28+
self.name = name
29+
self.details = details
30+
self.homepage = homepage
31+
self.license = license
32+
self.localProjectPath = localProjectPath
33+
self.uploadType = uploadType
34+
self.testCommand = testCommand
35+
self.extraBuildArgs = extraBuildArgs
36+
}
37+
}
38+
39+
40+
// MARK: - Dependencies
41+
public extension HomebrewFormula {
42+
enum FormulaUploadType: String {
43+
case binary
44+
case tarball
45+
}
46+
47+
enum TestCommand {
48+
case defaultCommand
49+
case custom(String)
50+
}
51+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// HomebrewTap.swift
3+
// nnex
4+
//
5+
// Created by Nikolai Nobadi on 12/11/25.
6+
//
7+
8+
public struct HomebrewTap {
9+
public var name: String
10+
public var localPath: String
11+
public var remotePath: String
12+
public var formulas: [HomebrewFormula]
13+
14+
public init(name: String, localPath: String, remotePath: String, formulas: [HomebrewFormula]) {
15+
self.name = name
16+
self.localPath = localPath
17+
self.remotePath = remotePath
18+
self.formulas = formulas
19+
}
20+
}

Sources/NnexKit/Releasing/ReleaseStore.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ private extension ReleaseStore {
4545
guard VersionHandler.isValidVersionNumber(number) else {
4646
throw NnexError.invalidVersionNumber
4747
}
48+
4849
return number
49-
5050
case .increment(let part):
5151
guard let previousVersion = info.previousVersion else {
5252
throw NnexError.noPreviousVersionToIncrement
5353
}
54+
5455
return try VersionHandler.incrementVersion(
5556
for: part,
5657
path: info.projectPath,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// HomebrewTapStore.swift
3+
// nnex
4+
//
5+
// Created by Nikolai Nobadi on 12/11/25.
6+
//
7+
8+
public protocol HomebrewTapStore {
9+
func loadTaps() throws -> [HomebrewTap]
10+
func updateFormula(_ formula: HomebrewFormula) throws
11+
func saveNewFormula(_ formula: HomebrewFormula, in tap: HomebrewTap) throws
12+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// HomebrewTapStoreAdapter.swift
3+
// nnex
4+
//
5+
// Created by Nikolai Nobadi on 12/11/25.
6+
//
7+
8+
import Foundation
9+
10+
public final class HomebrewTapStoreAdapter: HomebrewTapStore {
11+
private let context: NnexContext
12+
13+
public init(context: NnexContext) {
14+
self.context = context
15+
}
16+
17+
public func loadTaps() throws -> [HomebrewTap] {
18+
let swiftDataTaps = try context.loadTaps()
19+
20+
return swiftDataTaps.map(HomebrewTapMapper.toDomain)
21+
}
22+
23+
public func updateFormula(_ formula: HomebrewFormula) throws {
24+
let swiftDataFormulas = try context.loadFormulas()
25+
guard let target = swiftDataFormulas.first(where: { $0.name == formula.name }) else {
26+
return
27+
}
28+
29+
target.details = formula.details
30+
target.homepage = formula.homepage
31+
target.license = formula.license
32+
target.localProjectPath = formula.localProjectPath
33+
target.uploadType = CurrentSchema.FormulaUploadType(rawValue: formula.uploadType.rawValue) ?? .binary
34+
target.testCommand = toSwiftDataTestCommand(formula.testCommand)
35+
target.extraBuildArgs = formula.extraBuildArgs
36+
37+
try context.saveChanges()
38+
}
39+
40+
public func saveNewFormula(_ formula: HomebrewFormula, in tap: HomebrewTap) throws {
41+
let swiftDataTaps = try context.loadTaps()
42+
guard let swiftDataTap = swiftDataTaps.first(where: { $0.name == tap.name }) else {
43+
throw NnexError.missingTap
44+
}
45+
46+
let swiftDataFormula = HomebrewFormulaMapper.toSwiftData(formula)
47+
48+
try context.saveNewFormula(swiftDataFormula, in: swiftDataTap)
49+
}
50+
}
51+
52+
53+
// MARK: - Helpers
54+
private extension HomebrewTapStoreAdapter {
55+
func toSwiftDataTestCommand(_ testCommand: HomebrewFormula.TestCommand?) -> CurrentSchema.TestCommand? {
56+
guard let testCommand else { return nil }
57+
58+
switch testCommand {
59+
case .defaultCommand:
60+
return .defaultCommand
61+
case .custom(let command):
62+
return .custom(command)
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)