Skip to content
Open
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
18 changes: 0 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Setup CI for releases (9b70416)
* Add CI (31581ad)

## [0.1.0] - 2024-05-20

### Features
* Initial implementation of parsing (0c04db6)

### Chores
* Setup CI for releases (9b70416)
* Add CI (31581ad)

## [0.1.0] - 2024-05-20

### Features
* Initial implementation of parsing (0c04db6)

### Chores
* Setup CI for releases (9b70416)
* Add CI (31581ad)

116 changes: 13 additions & 103 deletions Package.resolved

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

32 changes: 26 additions & 6 deletions Sources/GitClient/GitClient+Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import Model
extension GitClient {
/// No overview available.
public static let liveValue: GitClient = GitClient { logType in
// Check if we're in a git repository
guard FileManager.default.fileExists(atPath: ".git") else {
throw GitError.repositoryNotFound
}
let arguments: [String]
switch logType {
case let .branch(targetBranch):
Expand All @@ -25,29 +29,42 @@ extension GitClient {
]
}

let log = shell(
let (output, exitCode) = shell(
command: "git",
arguments: arguments
)

guard exitCode == 0 else {
throw GitError.gitCommandFailed("git \(arguments.joined(separator: " "))", exitCode: exitCode)
}

return log.components(separatedBy: "-@-@-@-@-@-@-@-@")
return output.components(separatedBy: "-@-@-@-@-@-@-@-@")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { $0.isEmpty == false }
.compactMap { GitCommit($0) }
} tag: {
let tag = shell(
// Check if we're in a git repository
guard FileManager.default.fileExists(atPath: ".git") else {
throw GitError.repositoryNotFound
}

let (output, exitCode) = shell(
command: "git tag --merged",
arguments: []
)

guard exitCode == 0 else {
throw GitError.gitCommandFailed("git tag --merged", exitCode: exitCode)
}

return tag.split(separator: "\n").map { String($0) }
return output.split(separator: "\n").map { String($0) }
}
}

private func shell(
command: String,
arguments: [String]
) -> String {
) -> (output: String, exitCode: Int) {
let script = "\(command) \(arguments.joined(separator: " "))"

let task = Process()
Expand All @@ -62,8 +79,11 @@ private func shell(
task.standardError = errorPipe

try? task.run()
task.waitUntilExit()

let data = pipe.fileHandleForReading.readDataToEndOfFile()
return (String(data: data, encoding: .utf8) ?? "")
let output = (String(data: data, encoding: .utf8) ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)

return (output: output, exitCode: Int(task.terminationStatus))
}
28 changes: 15 additions & 13 deletions Sources/GitClient/GitClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,38 @@ public struct GitClient {

/// Returns the results of the `git log` command as an array of `GitCommit` that represent the
/// commits in a git repository.
/// - Parameter tag: An optional tag that, when provided, will run the `git log` command from
/// HEAD to that tag.
public func commitsSinceBranch(targetBranch: String) -> [GitCommit] {
_log(.branch(targetBranch))
/// - Parameter targetBranch: The target branch to compare against.
/// - Throws: GitError if git operations fail.
public func commitsSinceBranch(targetBranch: String) throws -> [GitCommit] {
try _log(.branch(targetBranch))
}

/// Returns the results of the `git log` command as an array of `GitCommit` that represent the
/// commits in a git repository.
/// - Parameter tag: An optional tag that, when provided, will run the `git log` command from
/// HEAD to that tag.
public func commitsSinceTag(_ tag: String?) -> [GitCommit] {
_log(.tag(tag))
/// - Throws: GitError if git operations fail.
public func commitsSinceTag(_ tag: String?) throws -> [GitCommit] {
try _log(.tag(tag))
}

/// Returns the results of the `git tag` command as an array of strings that represent the tags on a
/// repo.
public func tag() -> [String] {
_tag()
/// - Throws: GitError if git operations fail.
public func tag() throws -> [String] {
try _tag()
}

var _log: (LogType) -> [GitCommit] = { _ in [] }
var _tag: () -> [String] = { [] }
var _log: (LogType) throws -> [GitCommit] = { _ in [] }
var _tag: () throws -> [String] = { [] }

/// Initializes a `GitClient`.
/// - Parameters:
/// - log: A closure that takes an optional `String` and returns an array of `GitCommit`.
/// - log: A closure that takes a LogType and returns an array of `GitCommit`.
/// - tag: A closure that returns an array of `String` representing git tags.
public init(
log: @escaping (LogType) -> [GitCommit],
tag: @escaping () -> [String]
log: @escaping (LogType) throws -> [GitCommit],
tag: @escaping () throws -> [String]
) {
self._log = log
self._tag = tag
Expand Down
12 changes: 11 additions & 1 deletion Sources/Model/ConventionalCommit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ public struct ConventionalCommit: Equatable {

let typeWithoutScope = type.replacingOccurrences(of: scope ?? "", with: "")

guard !typeWithoutScope.isEmpty else {
return nil
}

switch typeWithoutScope {
case "feat":
self.type = .known(.feat)
Expand All @@ -124,9 +128,15 @@ public struct ConventionalCommit: Equatable {
self.isBreaking = false
}

self.description = String(
let description = String(
commit.subject.suffix(from: commit.subject.index(after: colonIndex))
).trimmingCharacters(in: .whitespacesAndNewlines)

guard !description.isEmpty else {
return nil
}

self.description = description
self.hash = commit.hash
self.scope = scope?
.replacingOccurrences(of: "(", with: "")
Expand Down
31 changes: 31 additions & 0 deletions Sources/Model/GitError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Foundation

/// Errors related to git operations.
public enum GitError: ActionableError {
case repositoryNotFound
case invalidRepository
case gitCommandFailed(String, exitCode: Int)

public var errorDescription: String? {
switch self {
case .repositoryNotFound:
return "No git repository found in current directory"
case .invalidRepository:
return "Invalid git repository structure"
case .gitCommandFailed(let command, let exitCode):
return "Git command '\(command)' failed with exit code \(exitCode)"
}
}

public var recoverySuggestion: String? {
switch self {
case .repositoryNotFound:
return "Ensure you're running this command from within a git repository"
case .invalidRepository:
return
"Check that the git repository is properly initialized and not corrupted"
case .gitCommandFailed(let command, _):
return "Check git status and ensure '\(command)' can be run manually"
}
}
}
20 changes: 20 additions & 0 deletions Sources/Model/ParseError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation

/// Errors related to parsing conventional commits and semantic versions.
public enum ParseError: ActionableError {
case noFormattedCommits(String)

public var errorDescription: String? {
switch self {
case .noFormattedCommits(let message):
return message
}
}

public var recoverySuggestion: String? {
switch self {
case .noFormattedCommits:
return "Ensure at least one commit follows conventional commit format"
}
}
}
Loading
Loading