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
6 changes: 3 additions & 3 deletions Sources/NnexKit/Building/BuildConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
// Created by Nikolai Nobadi on 3/25/25.
//

public struct BuildConfig: Sendable {
public struct BuildConfig {
public let projectName: String
public let projectPath: String
public let buildType: BuildType
public let extraBuildArgs: [String]
public let skipClean: Bool
public let testCommand: CurrentSchema.TestCommand?
public let testCommand: HomebrewFormula.TestCommand?

/// Initializes a new `BuildConfig` with the specified settings.
///
Expand All @@ -22,7 +22,7 @@ public struct BuildConfig: Sendable {
/// - extraBuildArgs: Additional arguments to pass to the build command.
/// - shouldClean: Indicates whether the project should be cleaned before building. Defaults to `true`.
/// - testCommand: An optional command to run tests after building. Defaults to `nil`, meaning no tests will be run.
public init(projectName: String, projectPath: String, buildType: BuildType, extraBuildArgs: [String], skipClean: Bool, testCommand: CurrentSchema.TestCommand?) {
public init(projectName: String, projectPath: String, buildType: BuildType, extraBuildArgs: [String], skipClean: Bool, testCommand: HomebrewFormula.TestCommand?) {
self.projectName = projectName
self.projectPath = projectPath.hasSuffix("/") ? projectPath : projectPath + "/"
self.buildType = buildType
Expand Down
19 changes: 19 additions & 0 deletions Sources/NnexKit/Extensions/String+Matches.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// String+Matches.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

public extension String {
/// Returns `true` if the string matches another value, case-insensitively.
/// - Parameter value: The optional string to compare against.
/// - Returns: `true` if both strings match when lowercased; otherwise, `false`.
func matches(_ value: String?) -> Bool {
guard let value else {
return false
}

return self.lowercased() == value.lowercased()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//
// BrewFormula.swift
// DecodableFormulaTemplate.swift
// nnex
//
// Created by Nikolai Nobadi on 3/20/25.
//

/// Represents a Homebrew formula with metadata and version information.
public struct BrewFormula: Codable {
public struct DecodableFormulaTemplate: Codable {
/// The name of the formula.
public let name: String

Expand Down Expand Up @@ -39,4 +39,4 @@ public struct BrewFormula: Codable {
self.license = license
self.versions = versions
}
}
}
4 changes: 2 additions & 2 deletions Sources/NnexKit/Formula/PublishUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public enum PublishUtilities {
/// - shell: The shell instance to use for building.
/// - Returns: The binary output including path(s) and hash(es).
/// - Throws: An error if the build process fails.
public static func buildBinary(formula: SwiftDataHomebrewFormula, buildType: BuildType, skipTesting: Bool, shell: any NnexShell) throws -> BinaryOutput {
public static func buildBinary(formula: HomebrewFormula, buildType: BuildType, skipTesting: Bool, shell: any NnexShell) throws -> BinaryOutput {
let testCommand = skipTesting ? nil : formula.testCommand
let config = BuildConfig(projectName: formula.name, projectPath: formula.localProjectPath, buildType: buildType, extraBuildArgs: formula.extraBuildArgs, skipClean: false, testCommand: testCommand)
let builder = ProjectBuilder(shell: shell, config: config)
Expand Down Expand Up @@ -49,7 +49,7 @@ public enum PublishUtilities {
/// - assetURLs: The asset URLs from the GitHub release.
/// - Returns: The formula content as a string.
/// - Throws: An error if formula generation fails.
public static func makeFormulaContent(formula: SwiftDataHomebrewFormula, version: String, archivedBinaries: [ArchivedBinary], assetURLs: [String]) throws -> String {
public static func makeFormulaContent(formula: HomebrewFormula, version: String, archivedBinaries: [ArchivedBinary], assetURLs: [String]) throws -> String {
let formulaName = formula.name

if archivedBinaries.count == 1 {
Expand Down
27 changes: 0 additions & 27 deletions Sources/NnexKit/Git/DefaultGitHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,60 +19,33 @@ public struct DefaultGitHandler {

// MARK: - Actions
extension DefaultGitHandler: GitHandler {
/// Adds all changes, commits with a given message, and pushes to the remote repository.
/// - Parameters:
/// - message: The commit message describing the changes.
/// - path: The file path of the repository.
public func commitAndPush(message: String, path: String) throws {
try shell.runAndPrint(bash: makeGitCommand(.addAll, path: path))
try shell.runAndPrint(bash: makeGitCommand(.commit(message: message), path: path))
try shell.runAndPrint(bash: makeGitCommand(.push, path: path))
}

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

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

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

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

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

/// Verifies if the GitHub CLI (gh) is installed and provides installation instructions if not.
public func ghVerification() throws {
if try shell.bash("which gh").contains("not found") {
throw NnexError.missingGitHubCLI
Expand Down
51 changes: 51 additions & 0 deletions Sources/NnexKit/Models/HomebrewFormula.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// HomebrewFormula.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

public struct HomebrewFormula {
public var name: String
public var details: String
public var homepage: String
public var license: String
public var localProjectPath: String
public var uploadType: FormulaUploadType
public var testCommand: TestCommand?
public var extraBuildArgs: [String]

public init(
name: String,
details: String,
homepage: String,
license: String,
localProjectPath: String,
uploadType: FormulaUploadType,
testCommand: TestCommand?,
extraBuildArgs: [String]
) {
self.name = name
self.details = details
self.homepage = homepage
self.license = license
self.localProjectPath = localProjectPath
self.uploadType = uploadType
self.testCommand = testCommand
self.extraBuildArgs = extraBuildArgs
}
}


// MARK: - Dependencies
public extension HomebrewFormula {
enum FormulaUploadType: String {
case binary
case tarball
}

enum TestCommand {
case defaultCommand
case custom(String)
}
}
20 changes: 20 additions & 0 deletions Sources/NnexKit/Models/HomebrewTap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// HomebrewTap.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

public struct HomebrewTap {
public var name: String
public var localPath: String
public var remotePath: String
public var formulas: [HomebrewFormula]

public init(name: String, localPath: String, remotePath: String, formulas: [HomebrewFormula]) {
self.name = name
self.localPath = localPath
self.remotePath = remotePath
self.formulas = formulas
}
}
3 changes: 2 additions & 1 deletion Sources/NnexKit/Releasing/ReleaseStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ private extension ReleaseStore {
guard VersionHandler.isValidVersionNumber(number) else {
throw NnexError.invalidVersionNumber
}

return number

case .increment(let part):
guard let previousVersion = info.previousVersion else {
throw NnexError.noPreviousVersionToIncrement
}

return try VersionHandler.incrementVersion(
for: part,
path: info.projectPath,
Expand Down
12 changes: 12 additions & 0 deletions Sources/NnexKit/Shared/HomebrewTapStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// HomebrewTapStore.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

public protocol HomebrewTapStore {
func loadTaps() throws -> [HomebrewTap]
func updateFormula(_ formula: HomebrewFormula) throws
func saveNewFormula(_ formula: HomebrewFormula, in tap: HomebrewTap) throws
}
65 changes: 65 additions & 0 deletions Sources/NnexKit/SwiftData/Adapters/HomebrewTapStoreAdapter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// HomebrewTapStoreAdapter.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

import Foundation

public final class HomebrewTapStoreAdapter: HomebrewTapStore {
private let context: NnexContext

public init(context: NnexContext) {
self.context = context
}

public func loadTaps() throws -> [HomebrewTap] {
let swiftDataTaps = try context.loadTaps()

return swiftDataTaps.map(HomebrewTapMapper.toDomain)
}

public func updateFormula(_ formula: HomebrewFormula) throws {
let swiftDataFormulas = try context.loadFormulas()
guard let target = swiftDataFormulas.first(where: { $0.name == formula.name }) else {
return
}

target.details = formula.details
target.homepage = formula.homepage
target.license = formula.license
target.localProjectPath = formula.localProjectPath
target.uploadType = CurrentSchema.FormulaUploadType(rawValue: formula.uploadType.rawValue) ?? .binary
target.testCommand = toSwiftDataTestCommand(formula.testCommand)
target.extraBuildArgs = formula.extraBuildArgs

try context.saveChanges()
}

public func saveNewFormula(_ formula: HomebrewFormula, in tap: HomebrewTap) throws {
let swiftDataTaps = try context.loadTaps()
guard let swiftDataTap = swiftDataTaps.first(where: { $0.name == tap.name }) else {
throw NnexError.missingTap
}

let swiftDataFormula = HomebrewFormulaMapper.toSwiftData(formula)

try context.saveNewFormula(swiftDataFormula, in: swiftDataTap)
}
}


// MARK: - Helpers
private extension HomebrewTapStoreAdapter {
func toSwiftDataTestCommand(_ testCommand: HomebrewFormula.TestCommand?) -> CurrentSchema.TestCommand? {
guard let testCommand else { return nil }

switch testCommand {
case .defaultCommand:
return .defaultCommand
case .custom(let command):
return .custom(command)
}
}
}
61 changes: 61 additions & 0 deletions Sources/NnexKit/SwiftData/Mappers/HomebrewFormulaMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// HomebrewFormulaMapper.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

enum HomebrewFormulaMapper {
static func toDomain(_ formula: SwiftDataHomebrewFormula) -> HomebrewFormula {
return .init(
name: formula.name,
details: formula.details,
homepage: formula.homepage,
license: formula.license,
localProjectPath: formula.localProjectPath,
uploadType: .init(rawValue: formula.uploadType.rawValue) ?? .tarball,
testCommand: toDomainTestCommand(formula.testCommand),
extraBuildArgs: formula.extraBuildArgs
)
}

static func toSwiftData(_ formula: HomebrewFormula) -> SwiftDataHomebrewFormula {
return .init(
name: formula.name,
details: formula.details,
homepage: formula.homepage,
license: formula.license,
localProjectPath: formula.localProjectPath,
uploadType: .init(rawValue: formula.uploadType.rawValue) ?? .tarball,
testCommand: toSwiftDataTestCommand(formula.testCommand),
extraBuildArgs: formula.extraBuildArgs
)
}
}


// MARK: - Helpers
private extension HomebrewFormulaMapper {
static func toDomainTestCommand(_ testCommand: CurrentSchema.TestCommand?) -> HomebrewFormula.TestCommand? {
guard let testCommand else { return nil }

switch testCommand {
case .defaultCommand:
return .defaultCommand
case .custom(let command):
return .custom(command)
}
}

static func toSwiftDataTestCommand(_ testCommand: HomebrewFormula.TestCommand?) -> CurrentSchema.TestCommand? {
guard let testCommand else { return nil }

switch testCommand {
case .defaultCommand:
return .defaultCommand
case .custom(let command):
return .custom(command)
}
}
}

18 changes: 18 additions & 0 deletions Sources/NnexKit/SwiftData/Mappers/HomebrewTapMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// HomebrewTapMapper.swift
// nnex
//
// Created by Nikolai Nobadi on 12/11/25.
//

enum HomebrewTapMapper {
static func toDomain(_ tap: SwiftDataHomebrewTap) -> HomebrewTap {
let formulas = tap.formulas.map({ HomebrewFormulaMapper.toDomain($0) })

return .init(name: tap.name, localPath: tap.localPath, remotePath: tap.remotePath, formulas: formulas)
}

static func toSwiftData(_ tap: HomebrewTap) -> SwiftDataHomebrewTap {
return .init(name: tap.name, localPath: tap.localPath, remotePath: tap.remotePath)
}
}
Loading