Point instance badge sources at the PNG !41
merged
merged by cmc on 2026-08-31 23:28 UTC
· krz/gitbay-ios:badge-png-source into main
4 files changed, +66 −4
Layout: unified · split
gitbay.xcodeproj/project.pbxproj
+1 −1
| @@ -591,7 +591,7 @@ |
| 591 | 591 | repositoryURL = "ssh://git@gitbay.org/krz/org-swift.git"; |
| 592 | 592 | requirement = { |
| 593 | 593 | kind = upToNextMinorVersion; |
| 594 | | minimumVersion = 0.3.1; |
| 594 | minimumVersion = 0.4.0; |
| 595 | 595 | }; |
| 596 | 596 | }; |
| 597 | 597 | 8B2898B0303FF3E5001BF44A /* XCRemoteSwiftPackageReference "Highlightr" */ = { |
gitbay.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+2 −2
| @@ -15,8 +15,8 @@ |
| 15 | 15 | "kind" : "remoteSourceControl", |
| 16 | 16 | "location" : "ssh://git@gitbay.org/krz/org-swift.git", |
| 17 | 17 | "state" : { |
| 18 | | "revision" : "6e2ef80a16a9eb61bff7f9928c230f4c5dff8240", |
| 19 | | "version" : "0.3.1" |
| 18 | "revision" : "a5dc89631cf65a183edae1bfa22ef147bd8f1686", |
| 19 | "version" : "0.4.0" |
| 20 | 20 | } |
| 21 | 21 | } |
| 22 | 22 | ], |
gitbay/Views/Repos/ReadmeView.swift
+20 −1
| @@ -22,7 +22,7 @@ struct ReadmeView: View { |
| 22 | 22 | if isOrg { |
| 23 | 23 | // Qualified: `OrgView` here is org-mode, not the organization view. |
| 24 | 24 | OrgSwiftUI.OrgView( |
| 25 | | content, |
| 25 | Self.preferringPNGBadges(content, host: host), |
| 26 | 26 | options: orgOptions, |
| 27 | 27 | styler: OrgSyntaxStyler(colorScheme: colorScheme), |
| 28 | 28 | // org's TODO/DONE are the same open/done distinction the forge already |
| @@ -34,6 +34,25 @@ struct ReadmeView: View { |
| 34 | 34 | } |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | /// Point badge sources on this instance at the PNG the forge also serves. |
| 38 | /// |
| 39 | /// No Apple image decoder reads SVG outside an asset catalog, so the SVG badge a README |
| 40 | /// names cannot render at all. Only badges on the instance being browsed are rewritten — |
| 41 | /// a badge from anywhere else has no PNG to offer, and is left as authored. |
| 42 | static func preferringPNGBadges(_ content: String, host: String) -> String { |
| 43 | guard !host.isEmpty, |
| 44 | let pattern = try? NSRegularExpression( |
| 45 | pattern: "(https://\(NSRegularExpression.escapedPattern(for: host))" |
| 46 | + "/[^/\\s\\]]+/[^/\\s\\]]+/badge/[\\w.-]+)\\.svg" |
| 47 | ) |
| 48 | else { return content } |
| 49 | return pattern.stringByReplacingMatches( |
| 50 | in: content, |
| 51 | range: NSRange(content.startIndex..., in: content), |
| 52 | withTemplate: "$1.png" |
| 53 | ) |
| 54 | } |
| 55 | |
| 37 | 56 | /// gitbay serves raw bytes at `/{owner}/{repo}/raw/{ref}/…` and the file page at |
| 38 | 57 | /// `/blob/…`, so relative images resolve against `raw` and links against `blob`. |
| 39 | 58 | private var orgOptions: OrgRenderOptions { |
gitbayTests/ReadmeBadgeTests.swift
added
+43
| @@ -0,0 +1,43 @@ |
| 1 | import Testing |
| 2 | @testable import gitbay |
| 3 | |
| 4 | @MainActor |
| 5 | struct ReadmeBadgeSourceTests { |
| 6 | |
| 7 | private func rewrite(_ content: String, host: String = "gitbay.org") -> String { |
| 8 | ReadmeView.preferringPNGBadges(content, host: host) |
| 9 | } |
| 10 | |
| 11 | @Test func aBadgeOnThisInstancePointsAtThePNG() { |
| 12 | #expect(rewrite("[[https://gitbay.org/krz/gitbay/builds][file:https://gitbay.org/krz/gitbay/badge/build.svg]]") |
| 13 | == "[[https://gitbay.org/krz/gitbay/builds][file:https://gitbay.org/krz/gitbay/badge/build.png]]") |
| 14 | } |
| 15 | |
| 16 | @Test func aBadgeForAnotherRepoOnTheSameInstanceIsRewritten() { |
| 17 | #expect(rewrite("file:https://gitbay.org/krz/orgo/badge/build.svg") |
| 18 | == "file:https://gitbay.org/krz/orgo/badge/build.png") |
| 19 | } |
| 20 | |
| 21 | /// Somewhere else's badge has no PNG to offer, so it is left as authored. |
| 22 | @Test func aBadgeOnAnotherHostIsLeftAlone() { |
| 23 | let shields = "file:https://img.shields.io/badge/build-passing.svg" |
| 24 | #expect(rewrite(shields) == shields) |
| 25 | } |
| 26 | |
| 27 | /// Only the badge path is rewritten; an SVG committed to the repo is not a badge. |
| 28 | @Test func anSVGThatIsNotABadgeIsLeftAlone() { |
| 29 | let diagram = "file:https://gitbay.org/krz/gitbay/raw/HEAD/docs/diagram.svg" |
| 30 | #expect(rewrite(diagram) == diagram) |
| 31 | } |
| 32 | |
| 33 | @Test func anEmptyHostRewritesNothing() { |
| 34 | let badge = "file:https://gitbay.org/krz/gitbay/badge/build.svg" |
| 35 | #expect(rewrite(badge, host: "") == badge) |
| 36 | } |
| 37 | |
| 38 | /// The host is matched literally: its dots are not wildcards. |
| 39 | @Test func theHostIsNotTreatedAsAPattern() { |
| 40 | let impostor = "file:https://gitbayxorg/krz/gitbay/badge/build.svg" |
| 41 | #expect(rewrite(impostor) == impostor) |
| 42 | } |
| 43 | } |