From cfa74d29a22170f5fc340dcef21bde042b2e812e Mon Sep 17 00:00:00 2001 From: ryoya ito Date: Mon, 4 Mar 2024 22:17:33 +0900 Subject: [PATCH 1/4] add summarizer --- Package.resolved | 9 +++++ Package.swift | 11 +++++ Sources/ReleaseSummarizer/App.swift | 62 +++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 Sources/ReleaseSummarizer/App.swift diff --git a/Package.resolved b/Package.resolved index 86c4454..216f287 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "openai", + "kind" : "remoteSourceControl", + "location" : "https://github.com/MacPaw/OpenAI.git", + "state" : { + "revision" : "ac5892fd0de8d283362ddc30f8e9f1a0eaba8cc0", + "version" : "0.2.5" + } + }, { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index fa18339..3012c96 100644 --- a/Package.swift +++ b/Package.swift @@ -7,12 +7,14 @@ let package = Package( platforms: [.macOS(.v10_15)], products: [ .executable(name: "releaseSubscriptions", targets: ["ReleaseSubscriptions"]), + .executable(name: "releaseSummarizer", targets: ["ReleaseSummarizer"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", exact: "1.1.1"), .package(url: "https://github.com/apple/swift-log", exact: "1.4.2"), .package(url: "https://github.com/apple/swift-markdown", branch: "main"), .package(url: "https://github.com/behrang/YamlSwift", exact: "3.4.4"), + .package(url: "https://github.com/MacPaw/OpenAI.git", exact: "0.2.5") ], targets: [ .executableTarget( @@ -29,6 +31,15 @@ let package = Package( .product(name: "Markdown", package: "swift-markdown"), .product(name: "Yaml", package: "YamlSwift"), ]), + .executableTarget( + name: "ReleaseSummarizer", + dependencies: [ + "ReleaseSubscriptionsCore", + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "OpenAI", package: "OpenAI"), + .product(name: "Logging", package: "swift-log"), + ] + ), .testTarget( name: "ReleaseSubscriptionsCoreTests", dependencies: ["ReleaseSubscriptionsCore"]), diff --git a/Sources/ReleaseSummarizer/App.swift b/Sources/ReleaseSummarizer/App.swift new file mode 100644 index 0000000..1a65351 --- /dev/null +++ b/Sources/ReleaseSummarizer/App.swift @@ -0,0 +1,62 @@ +import ArgumentParser +import OpenAI +import Logging + +@main +struct App: AsyncParsableCommand { + @Option(name: .shortAndLong, help: "OpenAI APIToken for generating summary of change log") + var apiToken: String? + + func run() async throws { + guard let apiToken = apiToken else { + Logger.app.error("An argument apiToken is needed. Please pass OpenAI apiKey.") + return + } + + let releaseNote = """ + Added: Observation support (#2593), which both streamlines and soft-deprecates many concepts in the Composable Architecture. See the 1.7 migration guide for more information on how to apply these updates to your applications. + Infrastructure: Add CI for Swift 5.7.1 (#2701). + Infrastructure: Add docs for testing StackAction's case path subscript (thanks @lukeredpath, #2704). + Infrastructure: Fixed examples CI (#2715). + Infrastructure: Fixed TestStore.assert docs (#2720). + Infrastructure: Use XCTExpectFailure from XCTestDynamicOverlay (#2721). + Infrastructure: Documentation typo fix (thanks @JonCox, #2723). + Infrastructure: Fixed concurrency docs (thanks @hmhv, #2726). + Infrastructure: Fixed composing features tutorial (thanks @bricklife, #2698; thanks @Kyome22, #2727). + Infrastructure: Fixed 1.7 migration guide (thanks @Ryu0118, #2731). + """ + + let prompt = """ +以下はソフトウェアのリリース内容です。 +リリース内容をを要約してください。 + +制約事項: +- 日本語で出力する +- 箇条書きで出力する +- 1項目200文字以内で要約する +- ソフトウェアへの変更内容のみに限定する +- Markdown 形式で出力する +- 丁寧語で出力する + +各項目は以下のフォーマットで出力してください。 + +### 項目名 +- 説明 +""" + + + let query = ChatQuery(model: .gpt3_5Turbo_1106, messages: [ + Chat(role: .system, content: prompt), + Chat(role: .user, content: releaseNote), + ]) + let result = try await OpenAI(apiToken: apiToken).chats(query: query) + + for choice in result.choices { + print(choice.message.content!) + } + } +} + +extension Logger { + fileprivate static let app = Logger(label: "io.github.ios-osushi.releasesummarizer") +} From 0bd6b9505500bb31bd2a8174e29899cf6fc08956 Mon Sep 17 00:00:00 2001 From: ryoya ito Date: Sat, 28 Sep 2024 18:21:47 +0900 Subject: [PATCH 2/4] wip --- Package.swift | 9 +- .../Entities/JSONNullable.swift | 12 +- .../Entities/Release.swift | 14 +- Sources/ReleaseSummarizer/App.swift | 54 +++----- .../ReleaseCollector.swift | 25 ++++ .../ReleaseSummarizeGenerator.swift | 120 ++++++++++++++++++ 6 files changed, 182 insertions(+), 52 deletions(-) create mode 100644 Sources/ReleaseSummarizerCore/ReleaseCollector.swift create mode 100644 Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift diff --git a/Package.swift b/Package.swift index 3012c96..1a181bc 100644 --- a/Package.swift +++ b/Package.swift @@ -34,8 +34,15 @@ let package = Package( .executableTarget( name: "ReleaseSummarizer", dependencies: [ - "ReleaseSubscriptionsCore", + "ReleaseSummarizerCore", .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Logging", package: "swift-log"), + ] + ), + .target( + name: "ReleaseSummarizerCore", + dependencies: [ + "ReleaseSubscriptionsCore", .product(name: "OpenAI", package: "OpenAI"), .product(name: "Logging", package: "swift-log"), ] diff --git a/Sources/ReleaseSubscriptionsCore/Entities/JSONNullable.swift b/Sources/ReleaseSubscriptionsCore/Entities/JSONNullable.swift index 6bf40fb..de90272 100644 --- a/Sources/ReleaseSubscriptionsCore/Entities/JSONNullable.swift +++ b/Sources/ReleaseSubscriptionsCore/Entities/JSONNullable.swift @@ -9,18 +9,18 @@ import Foundation /// [iOSDC Japan 2021 | CodableでJSONのNullを出力するためのTips by hirotakan](https://fortee.jp/iosdc-japan-2021/proposal/a79f93a5-1f1b-4017-86ad-64ed6f27d2b8) をもとに作成 @propertyWrapper -struct JSONNullable: Codable { - var wrappedValue: T? - - init(wrappedValue: T?) { +public struct JSONNullable: Codable { + public var wrappedValue: T? + + public init(wrappedValue: T?) { self.wrappedValue = wrappedValue } - init(from decoder: Decoder) throws { + public init(from decoder: Decoder) throws { wrappedValue = try Optional(from: decoder) } - func encode(to encoder: Encoder) throws { + public func encode(to encoder: Encoder) throws { try wrappedValue.encode(to: encoder) } } diff --git a/Sources/ReleaseSubscriptionsCore/Entities/Release.swift b/Sources/ReleaseSubscriptionsCore/Entities/Release.swift index ceb7377..f1d2f45 100644 --- a/Sources/ReleaseSubscriptionsCore/Entities/Release.swift +++ b/Sources/ReleaseSubscriptionsCore/Entities/Release.swift @@ -9,14 +9,14 @@ import Foundation public struct Release: Identifiable { public let id: String - let owner: String - let repository: String - let version: String - let title: String - @JSONNullable var body: String? - let url: URL + public let owner: String + public let repository: String + public let version: String + public let title: String + @JSONNullable public var body: String? + public let url: URL @JSONNullable var createdAt: Date? - @JSONNullable var publishedAt: Date? + @JSONNullable public var publishedAt: Date? let fetchedFromAPIAt: Date } diff --git a/Sources/ReleaseSummarizer/App.swift b/Sources/ReleaseSummarizer/App.swift index 1a65351..1001f76 100644 --- a/Sources/ReleaseSummarizer/App.swift +++ b/Sources/ReleaseSummarizer/App.swift @@ -1,5 +1,7 @@ import ArgumentParser -import OpenAI +import Foundation +import ReleaseSummarizerCore +import ReleaseSubscriptionsCore import Logging @main @@ -13,47 +15,23 @@ struct App: AsyncParsableCommand { return } - let releaseNote = """ - Added: Observation support (#2593), which both streamlines and soft-deprecates many concepts in the Composable Architecture. See the 1.7 migration guide for more information on how to apply these updates to your applications. - Infrastructure: Add CI for Swift 5.7.1 (#2701). - Infrastructure: Add docs for testing StackAction's case path subscript (thanks @lukeredpath, #2704). - Infrastructure: Fixed examples CI (#2715). - Infrastructure: Fixed TestStore.assert docs (#2720). - Infrastructure: Use XCTExpectFailure from XCTestDynamicOverlay (#2721). - Infrastructure: Documentation typo fix (thanks @JonCox, #2723). - Infrastructure: Fixed concurrency docs (thanks @hmhv, #2726). - Infrastructure: Fixed composing features tutorial (thanks @bricklife, #2698; thanks @Kyome22, #2727). - Infrastructure: Fixed 1.7 migration guide (thanks @Ryu0118, #2731). - """ - + let repositories = try Parser.parse() + let components = DateComponents(year: 2024, month: 9, day: 21) + let date = Calendar.current.date(from: components) let prompt = """ -以下はソフトウェアのリリース内容です。 -リリース内容をを要約してください。 - -制約事項: -- 日本語で出力する -- 箇条書きで出力する -- 1項目200文字以内で要約する -- ソフトウェアへの変更内容のみに限定する -- Markdown 形式で出力する -- 丁寧語で出力する - -各項目は以下のフォーマットで出力してください。 - -### 項目名 -- 説明 +次の文章はとあるライブラリのリリース情報です。 +この内容を短くわかりやすい表現で要約してください。 +要約の内容は +・改善点 +・修正点 +・その他 +に分けて日本語で行ってください。 """ + let releases = try await ReleaseCollector.collect(for: repositories, from: date!) + let generatedContent = try await ReleaseSummarizeGenerator.generate(apiToken: apiToken, prompt: prompt, releases: releases) - let query = ChatQuery(model: .gpt3_5Turbo_1106, messages: [ - Chat(role: .system, content: prompt), - Chat(role: .user, content: releaseNote), - ]) - let result = try await OpenAI(apiToken: apiToken).chats(query: query) - - for choice in result.choices { - print(choice.message.content!) - } + print(generatedContent) } } diff --git a/Sources/ReleaseSummarizerCore/ReleaseCollector.swift b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift new file mode 100644 index 0000000..4bdd110 --- /dev/null +++ b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift @@ -0,0 +1,25 @@ +// +// ReleaseCollector.swift +// ReleaseSubscriptions +// +// Created by 伊藤凌也 on 2024/09/28. +// + +import Foundation +import ReleaseSubscriptionsCore + +public struct ReleaseCollector { + public static func collect(for repositories: [GitHubRepository], from: Date, to: Date = Date()) async throws -> [(GitHubRepository, Release)] { + try FileHelper.load(repositories: repositories) + .flatMap { (repository, releases) in + releases.map { (repository, $0) } + } + .filter { (repository, release) in + guard let publishedAt = release.publishedAt else { + return false + } + + return (from...to).contains(publishedAt) + } + } +} diff --git a/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift new file mode 100644 index 0000000..0abfb3a --- /dev/null +++ b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift @@ -0,0 +1,120 @@ +// +// Generator.swift +// ReleaseSubscriptions +// +// Created by 伊藤凌也 on 2024/09/28. +// + +import OpenAI +import ReleaseSubscriptionsCore + +public struct ReleaseSummarizeGenerator { + + /// リリース情報に限った記事の内容を生成する + /// + /// フォーマット + /// + /// + /// ## OSS のリリース情報 + /// + /// iOS アプリ開発でよく使われている OSS のリリース情報です。 + /// + /// ### Apple + /// #### {バージョン} - ライブラリ名 + /// + /// [{release url}]({release url}) + /// + /// ##### 改善点 + /// + /// 内容 + /// + /// ##### 修正点 + /// + /// 内容 + /// + /// ##### その他 + /// + /// 内容 + /// + /// ### サードパーティ + /// + /// ... + /// + public static func generate( + apiToken: String, + prompt: String, + releases: [(GitHubRepository, Release)] + ) async throws -> String { + let grouped: [SlackWebhookDestination: [Release]] = releases.reduce(into: [:]) { partialResult, release in + let (repository, release) = release + + let destination = switch repository { + case let .releases(destination, _): + destination + + case let .tags(destination, _): + destination + } + + if partialResult[destination] != nil { + partialResult[destination]?.append(release) + } else { + partialResult[destination] = [release] + } + } + + var builder = Builder() + for (key, releases) in grouped { + builder.addSection(destination: key) + + for release in releases { + let query = ChatQuery(model: .gpt3_5Turbo_1106, messages: [ + Chat(role: .system, content: prompt), + Chat(role: .user, content: release.body), + ]) + let result = try await OpenAI(apiToken: apiToken).chats(query: query) + let content = result.choices.compactMap(\.message.content).joined() + + builder.addRelease(release: release, generatedContent: content) + } + } + + return builder.result + } + + struct Builder { + var result = """ +## OSS のリリース情報 + +iOS アプリ開発でよく使われている OSS のリリース情報です。 + +""" + + mutating func addSection(destination: SlackWebhookDestination) { + let title = switch destination { + case .primary: + "Apple" + + case .secondary: + "サードパーティ" + } + + result.append("### \(title)") + } + + /// #### {バージョン} - ライブラリ名 + mutating func addRelease(release: Release, generatedContent: String) { + let content = """ +#### \(release.version) - \(release.owner)/\(release.repository)") + +[\(release.url)](\(release.url) + +\(generatedContent) +""" + + result.append("#### \(release.version) - \(release.owner)/\(release.repository)") + result.append("") + result.append("[\(release.url)](\(release.url)") + } + } +} From aa0ddb825d4bc35a0b3f64dfec1d9f06f0ee8d1c Mon Sep 17 00:00:00 2001 From: ryoya ito Date: Sat, 28 Sep 2024 19:17:45 +0900 Subject: [PATCH 3/4] add generator --- Package.resolved | 8 ++--- Package.swift | 6 ++-- Sources/ReleaseSummarizer/App.swift | 22 ++++++++++-- .../ReleaseCollector.swift | 2 +- .../ReleaseSummarizeFileHelper.swift | 23 ++++++++++++ .../ReleaseSummarizeGenerator.swift | 35 +++++++++++-------- 6 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 Sources/ReleaseSummarizerCore/ReleaseSummarizeFileHelper.swift diff --git a/Package.resolved b/Package.resolved index 216f287..dc6174e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,12 +1,12 @@ { "pins" : [ { - "identity" : "openai", + "identity" : "generative-ai-swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/MacPaw/OpenAI.git", + "location" : "https://github.com/google-gemini/generative-ai-swift.git", "state" : { - "revision" : "ac5892fd0de8d283362ddc30f8e9f1a0eaba8cc0", - "version" : "0.2.5" + "revision" : "44b8ce120425f9cf53ca756f3434ca2c2696f8bd", + "version" : "0.5.6" } }, { diff --git a/Package.swift b/Package.swift index 1a181bc..a7648c3 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "ReleaseSubscriptions", - platforms: [.macOS(.v10_15)], + platforms: [.macOS(.v11)], products: [ .executable(name: "releaseSubscriptions", targets: ["ReleaseSubscriptions"]), .executable(name: "releaseSummarizer", targets: ["ReleaseSummarizer"]), @@ -14,7 +14,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-log", exact: "1.4.2"), .package(url: "https://github.com/apple/swift-markdown", branch: "main"), .package(url: "https://github.com/behrang/YamlSwift", exact: "3.4.4"), - .package(url: "https://github.com/MacPaw/OpenAI.git", exact: "0.2.5") + .package(url: "https://github.com/google-gemini/generative-ai-swift.git", exact: "0.5.6"), ], targets: [ .executableTarget( @@ -43,7 +43,7 @@ let package = Package( name: "ReleaseSummarizerCore", dependencies: [ "ReleaseSubscriptionsCore", - .product(name: "OpenAI", package: "OpenAI"), + .product(name: "GoogleGenerativeAI", package: "generative-ai-swift"), .product(name: "Logging", package: "swift-log"), ] ), diff --git a/Sources/ReleaseSummarizer/App.swift b/Sources/ReleaseSummarizer/App.swift index 1001f76..57c35a0 100644 --- a/Sources/ReleaseSummarizer/App.swift +++ b/Sources/ReleaseSummarizer/App.swift @@ -15,8 +15,8 @@ struct App: AsyncParsableCommand { return } - let repositories = try Parser.parse() - let components = DateComponents(year: 2024, month: 9, day: 21) + let repositories = try ReleaseSubscriptionsParser.parse() + let components = DateComponents(year: 2024, month: 9, day: 16) let date = Calendar.current.date(from: components) let prompt = """ 次の文章はとあるライブラリのリリース情報です。 @@ -26,12 +26,28 @@ struct App: AsyncParsableCommand { ・修正点 ・その他 に分けて日本語で行ってください。 +内容は箇条書きの Markdown 形式で出力してください。 +また、各箇条書きの項目は50文字以内で収めて、ですます調で出力してください。 + +出力例: + +##### 改善点 + +内容 + +##### 修正点 + +内容 + +##### その他 + +内容 """ let releases = try await ReleaseCollector.collect(for: repositories, from: date!) let generatedContent = try await ReleaseSummarizeGenerator.generate(apiToken: apiToken, prompt: prompt, releases: releases) - print(generatedContent) + try ReleaseSummarizeFileHelper.writeToFile(fileContent: generatedContent) } } diff --git a/Sources/ReleaseSummarizerCore/ReleaseCollector.swift b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift index 4bdd110..ba59c1b 100644 --- a/Sources/ReleaseSummarizerCore/ReleaseCollector.swift +++ b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift @@ -10,7 +10,7 @@ import ReleaseSubscriptionsCore public struct ReleaseCollector { public static func collect(for repositories: [GitHubRepository], from: Date, to: Date = Date()) async throws -> [(GitHubRepository, Release)] { - try FileHelper.load(repositories: repositories) + try OutputFileHelper.load(repositories: repositories) .flatMap { (repository, releases) in releases.map { (repository, $0) } } diff --git a/Sources/ReleaseSummarizerCore/ReleaseSummarizeFileHelper.swift b/Sources/ReleaseSummarizerCore/ReleaseSummarizeFileHelper.swift new file mode 100644 index 0000000..333a0d6 --- /dev/null +++ b/Sources/ReleaseSummarizerCore/ReleaseSummarizeFileHelper.swift @@ -0,0 +1,23 @@ +// +// File.swift +// ReleaseSubscriptions +// +// Created by 伊藤凌也 on 2024/09/28. +// + +import Foundation +import Logging + +public struct ReleaseSummarizeFileHelper { + public static func writeToFile(fileContent: String) throws { + defer { + Logger.helper.info("🎉 \(#function) finished") + } + Logger.helper.info("ℹ️ \(#function) started") + try fileContent.write(toFile: "./output.md", atomically: true, encoding: .utf8) + } +} + +extension Logger { + fileprivate static let helper = Logger(label: "io.github.ios-osushi.releasesummarizer.filehelper") +} diff --git a/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift index 0abfb3a..090044f 100644 --- a/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift +++ b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift @@ -5,7 +5,8 @@ // Created by 伊藤凌也 on 2024/09/28. // -import OpenAI +import GoogleGenerativeAI +import Logging import ReleaseSubscriptionsCore public struct ReleaseSummarizeGenerator { @@ -68,14 +69,16 @@ public struct ReleaseSummarizeGenerator { builder.addSection(destination: key) for release in releases { - let query = ChatQuery(model: .gpt3_5Turbo_1106, messages: [ - Chat(role: .system, content: prompt), - Chat(role: .user, content: release.body), - ]) - let result = try await OpenAI(apiToken: apiToken).chats(query: query) - let content = result.choices.compactMap(\.message.content).joined() - - builder.addRelease(release: release, generatedContent: content) + Logger.generator.info("Generating release content for \(release.owner)/\(release.repository) ...") + + let model = GenerativeModel(name: "gemini-1.5-flash-latest", apiKey: apiToken) + let result = try await model.generateContent(prompt, release.body ?? "") + + if let content = result.text { + builder.addRelease(release: release, generatedContent: content) + } + + try await Task.sleep(nanoseconds: 5000_000_000) } } @@ -99,22 +102,24 @@ iOS アプリ開発でよく使われている OSS のリリース情報です "サードパーティ" } - result.append("### \(title)") + result.append("### \(title)\n") } /// #### {バージョン} - ライブラリ名 mutating func addRelease(release: Release, generatedContent: String) { let content = """ -#### \(release.version) - \(release.owner)/\(release.repository)") +#### \(release.version) - \(release.owner)/\(release.repository) [\(release.url)](\(release.url) \(generatedContent) -""" - result.append("#### \(release.version) - \(release.owner)/\(release.repository)") - result.append("") - result.append("[\(release.url)](\(release.url)") +""" + result.append(content) } } } + +extension Logger { + fileprivate static let generator = Logger(label: "io.github.ios-osushi.releasesummarizer.generator") +} From d0e6fc8fd5d6f552809a73f6f7889108df3c5562 Mon Sep 17 00:00:00 2001 From: ryoya ito Date: Sat, 28 Sep 2024 23:55:41 +0900 Subject: [PATCH 4/4] refactor --- Sources/ReleaseSummarizer/App.swift | 55 +++++++++++++---- .../ReleaseCollector.swift | 2 +- .../ReleaseSummarizeGenerator.swift | 60 +++++++++---------- 3 files changed, 76 insertions(+), 41 deletions(-) diff --git a/Sources/ReleaseSummarizer/App.swift b/Sources/ReleaseSummarizer/App.swift index 57c35a0..3f725a2 100644 --- a/Sources/ReleaseSummarizer/App.swift +++ b/Sources/ReleaseSummarizer/App.swift @@ -6,19 +6,59 @@ import Logging @main struct App: AsyncParsableCommand { - @Option(name: .shortAndLong, help: "OpenAI APIToken for generating summary of change log") + + static var configuration: CommandConfiguration { + .init( + commandName: "releaseSummarizer" + ) + } + + @Option( + name: .shortAndLong, + help: "Gemini APIKey for generating summary of change log" + ) var apiToken: String? + @Option( + name: .long, + help: "Generate article target date range from. yyyy/MM/dd date format string", + transform: ISO8601DateFormatter().date(from:) + ) + var from: Date? + + @Option( + name: .long, + help: "Generate article target date range to. yyyy/MM/dd date format string. default: today", + transform: ISO8601DateFormatter().date(from:) + ) + var to: Date? + func run() async throws { guard let apiToken = apiToken else { - Logger.app.error("An argument apiToken is needed. Please pass OpenAI apiKey.") + Logger.app.error("An argument apiToken is needed. Please pass Gemini apiKey.") + return + } + + guard let from else { + Logger.app.error("An argument from is needed or invalid. Please pass date range from.") return } let repositories = try ReleaseSubscriptionsParser.parse() - let components = DateComponents(year: 2024, month: 9, day: 16) - let date = Calendar.current.date(from: components) - let prompt = """ + let releases = try await ReleaseCollector.collect( + for: repositories, + from: from, + to: to ?? Date() + ) + let generatedContent = try await ReleaseSummarizeGenerator.generate(apiToken: apiToken, prompt: prompt, releases: releases) + + try ReleaseSummarizeFileHelper.writeToFile(fileContent: generatedContent) + } +} + +extension App { + var prompt: String { +""" 次の文章はとあるライブラリのリリース情報です。 この内容を短くわかりやすい表現で要約してください。 要約の内容は @@ -43,11 +83,6 @@ struct App: AsyncParsableCommand { 内容 """ - - let releases = try await ReleaseCollector.collect(for: repositories, from: date!) - let generatedContent = try await ReleaseSummarizeGenerator.generate(apiToken: apiToken, prompt: prompt, releases: releases) - - try ReleaseSummarizeFileHelper.writeToFile(fileContent: generatedContent) } } diff --git a/Sources/ReleaseSummarizerCore/ReleaseCollector.swift b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift index ba59c1b..1e9cfc7 100644 --- a/Sources/ReleaseSummarizerCore/ReleaseCollector.swift +++ b/Sources/ReleaseSummarizerCore/ReleaseCollector.swift @@ -9,7 +9,7 @@ import Foundation import ReleaseSubscriptionsCore public struct ReleaseCollector { - public static func collect(for repositories: [GitHubRepository], from: Date, to: Date = Date()) async throws -> [(GitHubRepository, Release)] { + public static func collect(for repositories: [GitHubRepository], from: Date, to: Date) async throws -> [(GitHubRepository, Release)] { try OutputFileHelper.load(repositories: repositories) .flatMap { (repository, releases) in releases.map { (repository, $0) } diff --git a/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift index 090044f..d902122 100644 --- a/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift +++ b/Sources/ReleaseSummarizerCore/ReleaseSummarizeGenerator.swift @@ -11,36 +11,36 @@ import ReleaseSubscriptionsCore public struct ReleaseSummarizeGenerator { - /// リリース情報に限った記事の内容を生成する - /// - /// フォーマット - /// - /// - /// ## OSS のリリース情報 - /// - /// iOS アプリ開発でよく使われている OSS のリリース情報です。 - /// - /// ### Apple - /// #### {バージョン} - ライブラリ名 - /// - /// [{release url}]({release url}) - /// - /// ##### 改善点 - /// - /// 内容 - /// - /// ##### 修正点 - /// - /// 内容 - /// - /// ##### その他 - /// - /// 内容 - /// - /// ### サードパーティ - /// - /// ... - /// + // リリース情報に限った記事の内容を生成する + // + // フォーマット + // + // + // ## OSS のリリース情報 + // + // iOS アプリ開発でよく使われている OSS のリリース情報です。 + // + // ### Apple + // #### {バージョン} - ライブラリ名 + // + // [{release url}]({release url}) + // + // ##### 改善点 + // + // 内容 + // + // ##### 修正点 + // + // 内容 + // + // ##### その他 + // + // 内容 + // + // ### サードパーティ + // + // ... + // public static func generate( apiToken: String, prompt: String,