Commit 90fb344898
Verified · cmc
Layout: unified · split
gitbay/Views/Repos/ReadmeView.swift +20 −1
| @@ -22,7 +22,7 @@ struct ReadmeView: View { | |||
| 22 | if isOrg { | 22 | if isOrg { |
| 23 | // Qualified: `OrgView` here is org-mode, not the organization view. | 23 | // Qualified: `OrgView` here is org-mode, not the organization view. |
| 24 | OrgSwiftUI.OrgView( | 24 | OrgSwiftUI.OrgView( |
| 25 | content, | 25 | Self.preferringPNGBadges(content, host: host), |
| 26 | options: orgOptions, | 26 | options: orgOptions, |
| 27 | styler: OrgSyntaxStyler(colorScheme: colorScheme), | 27 | styler: OrgSyntaxStyler(colorScheme: colorScheme), |
| 28 | // org's TODO/DONE are the same open/done distinction the forge already | 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 | /// gitbay serves raw bytes at `/{owner}/{repo}/raw/{ref}/…` and the file page at | 56 | /// gitbay serves raw bytes at `/{owner}/{repo}/raw/{ref}/…` and the file page at |
| 38 | /// `/blob/…`, so relative images resolve against `raw` and links against `blob`. | 57 | /// `/blob/…`, so relative images resolve against `raw` and links against `blob`. |
| 39 | private var orgOptions: OrgRenderOptions { | 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 | } | ||