Skip to content

Commit d90e146

Browse files
committed
make semver mandatory for PackageEdition(s), implement database migration logic for that change, and make the tags page list tags in semantic order
1 parent 3924ac7 commit d90e146

13 files changed

+190
-200
lines changed

Assets/css/Main.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SemanticVersions/SemanticVersion.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ extension SemanticVersion
1616
}
1717
}
1818

19-
@available(*, deprecated, message: "use 'patch' or switch explicitly instead")
19+
/// Returns true if this is a release version, false if it is a prerelease.
2020
@inlinable public
21-
var release:PatchVersion?
21+
var release:Bool
2222
{
2323
switch self
2424
{
25-
case .release(let version, _): return version
26-
case .prerelease: return nil
25+
case .release: return true
26+
case .prerelease: return false
2727
}
2828
}
2929
}

Sources/UnidocDB/Packages/PackageDatabase.Editions.swift

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import BSON
22
import GitHubAPI
33
import JSONEncoding
44
import MongoDB
5+
import SemanticVersions
56
import SHA1
67
import UnidocAnalysis
78
import UnidocRecords
@@ -107,11 +108,13 @@ extension PackageDatabase.Editions
107108
public
108109
func register(_ tag:__owned GitHub.Tag,
109110
package:Int32,
111+
version:SemanticVersion,
110112
with session:Mongo.Session) async throws -> Int32?
111113
{
112114
// We use the SHA-1 hash as “proof” that the edition has at least one symbol graph.
113115
// Therefore, merely registering tags does not update hashes.
114116
let placement:Placement = try await self.register(package: package,
117+
version: version,
115118
refname: tag.name,
116119
sha1: nil,
117120
with: session)
@@ -121,6 +124,7 @@ extension PackageDatabase.Editions
121124

122125
func register(
123126
package:Int32,
127+
version:SemanticVersion,
124128
refname:String,
125129
sha1:SHA1?,
126130
with session:Mongo.Session) async throws -> Placement
@@ -134,6 +138,8 @@ extension PackageDatabase.Editions
134138
let edition:PackageEdition = .init(id: .init(
135139
package: package,
136140
version: placement.coordinate),
141+
release: version.release,
142+
patch: version.patch,
137143
name: refname,
138144
sha1: sha1)
139145

Sources/UnidocDB/Packages/PackageDatabase.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import GitHubAPI
22
import ModuleGraphs
33
import MongoDB
4+
import SemanticVersions
45
import SymbolGraphs
56
import UnidocAnalysis
67
import UnidocLinker
@@ -74,10 +75,12 @@ extension PackageDatabase
7475

7576
let version:Int32
7677

77-
if let commit:SymbolGraphMetadata.Commit = docs.metadata.commit
78+
if let commit:SymbolGraphMetadata.Commit = docs.metadata.commit,
79+
let semver:SemanticVersion = .init(refname: commit.refname)
7880
{
7981
let placement:Editions.Placement = try await self.editions.register(
8082
package: placement.coordinate,
83+
version: semver,
8184
refname: commit.refname,
8285
sha1: commit.hash,
8386
with: session)
@@ -87,11 +90,21 @@ extension PackageDatabase
8790
else if case .swift = docs.metadata.package,
8891
let tagname:String = docs.metadata.commit?.refname
8992
{
93+
// FIXME: we need a better way to handle this
94+
let semver:SemanticVersion
95+
switch tagname
96+
{
97+
case "swift-5.8.1-RELEASE": semver = .release(.v(5, 8, 1))
98+
case "swift-5.9-RELEASE": semver = .release(.v(5, 9, 0))
99+
case _:
100+
fatalError("unimplemented")
101+
}
90102
/// Standard library symbol graphs don’t come with hashes, so we can’t efficiently
91103
/// “prove” that a particular edition has at least one symbol graph. But we don’t
92104
/// need to query that in the first place.
93105
let placement:Editions.Placement = try await self.editions.register(
94106
package: placement.coordinate,
107+
version: semver,
95108
refname: tagname,
96109
sha1: nil,
97110
with: session)

Sources/UnidocDB/Packages/PackageEdition.swift

+54-20
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,67 @@ struct PackageEdition:Identifiable, Equatable, Sendable
1111
public
1212
let id:Unidoc.Zone
1313

14+
/// Whether or not this edition is a release.
15+
public
16+
var release:Bool
17+
/// The patch version associated with this edition. This might not be trivially-computable
18+
/// from the ``name`` property, for example, `5.9.0` from `swift-5.9-RELEASE`.
19+
public
20+
var patch:PatchVersion
21+
1422
/// The exact ref name associated with this edition.
1523
public
16-
let name:String
24+
var name:String
1725
/// The SHA-1 hash of the git commit associated with this edition.
1826
public
19-
let sha1:SHA1?
27+
var sha1:SHA1?
2028
/// Indicates if the repository host has published a tag of the same name with a different
2129
/// ``sha1`` hash.
2230
public
23-
let lost:Bool
31+
var lost:Bool
2432

2533
@inlinable public
26-
init(id:Unidoc.Zone, name:String, sha1:SHA1?, lost:Bool = false)
34+
init(id:Unidoc.Zone,
35+
release:Bool?,
36+
patch:PatchVersion?,
37+
name:String,
38+
sha1:SHA1?,
39+
lost:Bool = false)
2740
{
2841
self.id = id
42+
43+
// Temporary HACK until we can migrate all the database records.
44+
self.release = release ?? true
45+
if let patch:PatchVersion
46+
{
47+
self.patch = patch
48+
}
49+
else if name == "swift-5.8.1-RELEASE"
50+
{
51+
self.patch = .v(5, 8, 1)
52+
}
53+
else if name == "swift-5.9-RELEASE"
54+
{
55+
self.patch = .v(5, 9, 0)
56+
}
57+
else
58+
{
59+
switch SemanticVersion.init(refname: name)
60+
{
61+
case .release(let version, build: _)?:
62+
self.release = true
63+
self.patch = version
64+
65+
case .prerelease(let version, _, build: _)?:
66+
self.release = false
67+
self.patch = version
68+
69+
case nil:
70+
fatalError("can’t infer patch version from refname: \(name)")
71+
}
72+
}
73+
// End temporary HACK.
74+
2975
self.name = name
3076
self.sha1 = sha1
3177
self.lost = lost
@@ -66,22 +112,8 @@ extension PackageEdition:BSONDocumentEncodable
66112
bson[.package] = self.id.package
67113
bson[.version] = self.id.version
68114

69-
/// Parses and returns this edition's refname as a semantic version. If the refname
70-
/// looks like a semantic version, this will strip leading `v`’s, and zero-extend the
71-
/// patch number.
72-
switch SemanticVersion.init(refname: self.name)
73-
{
74-
case .release(let version, build: _)?:
75-
bson[.release] = true
76-
bson[.patch] = version
77-
78-
case .prerelease(let version, _, build: _)?:
79-
bson[.release] = false
80-
bson[.patch] = version
81-
82-
case nil:
83-
break
84-
}
115+
bson[.release] = self.release
116+
bson[.patch] = self.patch
85117

86118
bson[.name] = self.name
87119
bson[.sha1] = self.sha1
@@ -94,6 +126,8 @@ extension PackageEdition:BSONDocumentDecodable
94126
init(bson:BSON.DocumentDecoder<CodingKey, some RandomAccessCollection<UInt8>>) throws
95127
{
96128
self.init(id: try bson[.id].decode(),
129+
release: try bson[.release]?.decode(),
130+
patch: try bson[.patch]?.decode(),
97131
name: try bson[.name].decode(),
98132
sha1: try bson[.sha1]?.decode(),
99133
lost: try bson[.lost]?.decode() ?? false)

Sources/UnidocPages/Templates/Site.Tags.List.Item.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,23 @@ extension Site.Tags.List.Item
2121
{
2222
init(facet:PackageEditionsQuery.Facet)
2323
{
24-
self.init(edition: facet.edition, graphs: facet.graphs?.count ?? 0)
24+
self.init(
25+
edition: facet.edition,
26+
graphs: facet.graphs?.count ?? 0)
2527
}
2628
}
2729
extension Site.Tags.List.Item:HyperTextOutputStreamable
2830
{
2931
static
3032
func += (tr:inout HTML.ContentEncoder, self:Self)
3133
{
32-
let version:SemanticVersion? = .init(refname: self.edition.name)
3334
let sha1:String? = self.edition.sha1?.description
3435

3536
tr[.td] { $0.class = "refname" } = self.edition.name
3637
tr[.td] { $0.class = "commit" ; $0.title = sha1 } = sha1?.prefix(7) ?? ""
37-
tr[.td] { $0.class = "version" } = version?.description ?? ""
3838
tr[.td] { $0.class = "id" } = "\(self.edition.id.version)"
39+
tr[.td] { $0.class = "version" } = "\(self.edition.patch)"
40+
tr[.td] { $0.class = "release" } = self.edition.release ? "yes" : "no"
3941
tr[.td] { $0.class = "graphs" } = self.graphs > 0 ? "\(self.graphs)" : ""
4042
}
4143
}

Sources/UnidocPages/Templates/Site.Tags.List.swift

+35-41
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,11 @@ extension Site.Tags
1010
private
1111
let package:PackageRecord
1212
private
13-
var latestPrerelease:Item?
14-
private
15-
var latestRelease:Item?
16-
private
1713
var page:[Item]
1814

19-
init(package:PackageRecord,
20-
latestPrerelease:Item? = nil,
21-
latestRelease:Item? = nil,
22-
page:[Item] = [])
15+
init(package:PackageRecord, page:[Item])
2316
{
2417
self.package = package
25-
self.latestPrerelease = latestPrerelease
26-
self.latestRelease = latestRelease
2718
self.page = page
2819
}
2920
}
@@ -32,40 +23,33 @@ extension Site.Tags.List
3223
{
3324
init(from output:PackageEditionsQuery.Output)
3425
{
35-
self.init(package: output.record)
26+
var prereleases:ArraySlice<Item> = output.prereleases.map(Item.init(facet:))[...]
27+
var releases:ArraySlice<Item> = output.releases.map(Item.init(facet:))[...]
3628

37-
var seen:Set<Int32> = []
38-
for facet:PackageEditionsQuery.Facet in output.facets
29+
// Merge the two pre-sorted arrays into a single sorted array.
30+
var items:[Item] = []
31+
items.reserveCapacity(prereleases.count + releases.count)
32+
while
33+
let prerelease:Item = prereleases.first,
34+
let release:Item = releases.first
3935
{
40-
guard
41-
let release:Bool = facet.release
42-
else
43-
{
44-
continue
45-
}
46-
47-
guard case nil = seen.update(with: facet.edition.version)
48-
else
49-
{
50-
continue
51-
}
52-
53-
if release
36+
if release.edition.patch < prerelease.edition.patch
5437
{
55-
self.latestRelease = .init(facet: facet)
38+
items.append(prerelease)
39+
prereleases.removeFirst()
5640
}
5741
else
5842
{
59-
self.latestPrerelease = .init(facet: facet)
60-
}
61-
}
62-
for facet:PackageEditionsQuery.Facet in output.facets
63-
{
64-
if case nil = seen.update(with: facet.edition.version)
65-
{
66-
self.page.append(.init(facet: facet))
43+
items.append(release)
44+
releases.removeFirst()
6745
}
6846
}
47+
48+
// Append any remaining items.
49+
items += prereleases
50+
items += releases
51+
52+
self.init(package: output.record, page: items)
6953
}
7054
}
7155
extension Site.Tags.List:FixedPage
@@ -88,20 +72,30 @@ extension Site.Tags.List:AdministrativePage
8872
{
8973
$0[.th] = "Git Ref"
9074
$0[.th] = "Commit"
91-
$0[.th] = "Semver"
9275
$0[.th] = "ID"
76+
$0[.th] = "Version"
77+
$0[.th] = "Release?"
9378
$0[.th] = "Archives?"
9479
}
9580
}
9681

9782
$0[.tbody]
9883
{
99-
$0[.tr] { $0.class = "latest prerelease" } = self.latestPrerelease
100-
$0[.tr] { $0.class = "latest release" } = self.latestRelease
101-
84+
var modern:(prerelease:Bool, release:Bool) = (true, true)
10285
for item:Item in self.page
10386
{
104-
$0[.tr] = item
87+
if item.edition.release
88+
{
89+
$0[.tr] { $0.class = modern.release ? "modern" : nil } = item
90+
91+
modern = (false, false)
92+
}
93+
else
94+
{
95+
$0[.tr] { $0.class = modern.prerelease ? "modern" : nil } = item
96+
97+
modern.prerelease = false
98+
}
10599
}
106100
}
107101
}

Sources/UnidocQueries/Queries/PackageEditionsQuery.Facet.swift

+3-8
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,13 @@ extension PackageEditionsQuery
99
{
1010
public
1111
var edition:PackageEdition
12-
/// True for a release, false for a prerelease, nil for a branch or an irregular tag.
13-
public
14-
var release:Bool?
1512
public
1613
var graphs:Graphs?
1714

1815
@inlinable public
19-
init(edition:PackageEdition, release:Bool? = nil, graphs:Graphs? = nil)
16+
init(edition:PackageEdition, graphs:Graphs? = nil)
2017
{
2118
self.edition = edition
22-
self.release = release
2319
self.graphs = graphs
2420
}
2521
}
@@ -30,7 +26,6 @@ extension PackageEditionsQuery.Facet:MongoMasterCodingModel
3026
enum CodingKey:String
3127
{
3228
case edition
33-
case release
3429
case graphs
3530
}
3631
}
@@ -39,8 +34,8 @@ extension PackageEditionsQuery.Facet:BSONDocumentDecodable
3934
@inlinable public
4035
init(bson:BSON.DocumentDecoder<CodingKey, some RandomAccessCollection<UInt8>>) throws
4136
{
42-
self.init(edition: try bson[.edition].decode(),
43-
release: try bson[.release]?.decode(),
37+
self.init(
38+
edition: try bson[.edition].decode(),
4439
graphs: try bson[.graphs]?.decode())
4540
}
4641
}

0 commit comments

Comments
 (0)