Skip to content

Commit

Permalink
Better changes detection in release reporting (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
MahdiBM authored Feb 18, 2025
1 parent a3d2d8f commit 69a86db
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy-penny.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
apt-get install -y zstd
- name: Restore .build
if: ${{ !(github.run_attempt > 1) }} # Because maybe the cache is causing issues
if: ${{ runner.debug != '1' }}
id: "restore-cache"
uses: actions/cache/restore@v4
with:
Expand Down
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"errorLens.statusBarIconsEnabled": true,
"errorLens.scrollbarHackEnabled": true,
"errorLens.delayMode": "debounce",
"errorLens.delay": 500,
"errorLens.editorHoverPartsEnabled": {
"messageEnabled": true,
"sourceCodeEnabled": true,
Expand All @@ -60,7 +59,6 @@
}
},
"terminal.integrated.scrollback": 10000,
"swift.buildArguments": ["--force-resolved-versions"],
"swift.additionalTestArguments": ["--disable-xctest"],
"[yaml]": {
"editor.insertSpaces": true,
Expand Down
1 change: 1 addition & 0 deletions Lambdas/GHHooks/EventHandler/Handlers/ReleaseMaker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct ReleaseMaker {
if let (_, version) = SemanticVersion.fromGitHubTag(release.tagName) {
return (release, version)
}
logger.warning("Could not parse tag", metadata: ["tag": .string(release.tagName)])
return nil
}
}
Expand Down
121 changes: 93 additions & 28 deletions Lambdas/GHHooks/EventHandler/Handlers/ReleaseReporter.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Algorithms
import AsyncHTTPClient
import Collections
import DiscordBM
import GitHubAPI
import Logging
Expand Down Expand Up @@ -47,6 +49,56 @@ struct ReleaseReporter {
}
}

func getPRsRelatedToRelease() async throws -> (Int, [SimplePullRequest]) {
let commits: [Commit] =
if let comparisonRange = self.findComparisonRange() {
try await self.getCommitsInRelease(
comparisonRange: comparisonRange
)
} else if let tagBefore = try await self.getTagBefore() {
try await self.getCommitsInRelease(
comparisonRange: "\(tagBefore)...\(release.tagName)"
)
} else {
try await self.getAllCommits()
}

let maxCommits = 5
let maxPRs = 3
var prs: OrderedDictionary<String, SimplePullRequest> = [:]
prs.reserveCapacity(min(commits.count, maxPRs))

for commit in commits.prefix(maxCommits) {
let newPRs = try await getPRsRelatedToCommit(sha: commit.sha)
for pr in newPRs {
prs[pr.htmlUrl] = pr
if prs.count >= maxPRs { break }
}
}

return (commits.count, Array(prs.values))
}

func findComparisonRange() -> String? {
guard let body = release.body else {
return nil
}
let linkPrefix = "https://github.com/\(repo.fullName)/compare/"
let lines = body.lazy.split(separator: "\n")
let comparisonLine = lines.reversed().first {
$0.contains(linkPrefix)
}
let components = comparisonLine?.split(
whereSeparator: \.isWhitespace
)
let comparisonRange = components?.first {
$0.hasPrefix(linkPrefix)
}?.split(
separator: "/"
).last
return comparisonRange.map(String.init)
}

func getTagBefore() async throws -> String? {
let json = try await context.githubClient.reposListTags(
path: .init(
Expand All @@ -71,34 +123,12 @@ struct ReleaseReporter {
}
}

func getPRsRelatedToRelease() async throws -> (Int, [SimplePullRequest]) {
let commits: [Commit]
if let tagBefore = try await self.getTagBefore() {
commits = try await self.getCommitsInRelease(tagBefore: tagBefore)
} else {
commits = try await self.getAllCommits()
}

let maxCommits = 5
let maxPRs = 3
var prs = [SimplePullRequest]()
prs.reserveCapacity(min(commits.count, maxPRs))

for commit in commits.prefix(maxCommits) where prs.count < 3 {
let newPRs = try await getPRsRelatedToCommit(sha: commit.sha)
prs.append(contentsOf: newPRs)
}
prs = prs.uniqued(on: \.htmlUrl)

return (commits.count, prs)
}

func getCommitsInRelease(tagBefore: String) async throws -> [Commit] {
func getCommitsInRelease(comparisonRange: String) async throws -> [Commit] {
try await context.githubClient.reposCompareCommits(
path: .init(
owner: repo.owner.login,
repo: repo.name,
basehead: "\(tagBefore)...\(release.tagName)"
basehead: comparisonRange
)
).ok.body.json.commits.reversed()
}
Expand Down Expand Up @@ -182,21 +212,56 @@ struct ReleaseReporter {
}

func sendToDiscord(description: String) async throws {
let fullName = repo.fullName.urlPathEncoded()
let image =
"https://opengraph.githubassets.com/\(UUID().uuidString)/\(fullName)/releases/tag/\(release.tagName)"
let image = await findUseableImageLink()

try await self.sendToDiscord(
embed: .init(
title: "[\(repo.uiName)] Release \(release.tagName)".unicodesPrefix(256),
description: description,
url: release.htmlUrl,
color: .cyan,
image: .init(url: .exact(image))
image: image.map { .init(url: .exact($0)) }
)
)
}

func findUseableImageLink() async -> String? {
let fullName = repo.fullName.urlPathEncoded()
func makeImageLink() -> String {
"https://opengraph.githubassets.com/\(UUID().uuidString)/\(fullName)/releases/tag/\(release.tagName)"
}

/// Try a maximum of 3 times
for _ in 0..<3 {
let imageLink = makeImageLink()
var request = HTTPClientRequest(url: imageLink)
request.method = .HEAD
do {
let response = try await context.httpClient.execute(
request,
timeout: .seconds(1),
logger: logger
)
guard let contentLength = response.headers.first(name: "content-length").flatMap(Int.init),
contentLength > 10
else {
continue
}
return imageLink
} catch {
logger.warning(
"GitHub release image did not exist",
metadata: [
"imageLink": "\(imageLink)",
"error": "\(String(reflecting: error))",
]
)
}
}

return nil
}

func sendToDiscord(embed: Embed) async throws {
try await context.discordClient.createMessage(
channelId: Constants.Channels.release.id,
Expand Down
10 changes: 5 additions & 5 deletions Lambdas/GHHooks/GHHooksHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,20 @@ struct GHHooksHandler: LambdaHandler {
]
)
],
/// The `_` suffix is only to make sure Discord doesn't try to inline-render the files.
/// The `x` suffix is only to make sure Discord doesn't try to inline-render the files.
files: [
.init(
data: ByteBuffer(string: "\(error)"),
filename: "error.txt_"
filename: "error.txtx"
),
.init(
data: request.body.map(ByteBuffer.init(string:)) ?? ByteBuffer(),
filename: "body.json_"
filename: "body.jsonx"
),
],
attachments: [
.init(index: 0, filename: "error.txt_"),
.init(index: 1, filename: "body.json_"),
.init(index: 0, filename: "error.txtx"),
.init(index: 1, filename: "body.jsonx"),
]
)
).guardSuccess()
Expand Down
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"location" : "https://github.com/DiscordBM/DiscordBM.git",
"state" : {
"branch" : "main",
"revision" : "60261791b2050f4ff962a97530f14f464af19f6f"
"revision" : "c641e8479544beed928ca5cb8baa6dde4d75b1d6"
}
},
{
Expand Down
11 changes: 11 additions & 0 deletions Tests/PennyTests/Tests/GHHooksTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,17 @@ actor GHHooksTests {
#expect(!"postgres-my/branch#42.99.56-alpha.1345".isPrimaryOrReleaseBranch(repo: repo))
}

@Test
func findComparisonRange() async throws {
let context = try makeContext(
eventName: .release,
eventKey: "release1"
)
let handler = try ReleaseReporter(context: context)
let comparisonRange = handler.findComparisonRange()
#expect(comparisonRange == "1.43.0...1.43.1")
}

@Test
func handleIssueEvent1() async throws {
try await handleEvent(key: "issue1", eventName: .issues, expect: .noResponse)
Expand Down

0 comments on commit 69a86db

Please sign in to comment.