Skip to content

Commit 5348264

Browse files
Merge pull request #2879 from SwiftPackageIndex/issue-2873
Issue 2873
2 parents b278448 + 3c0bfa3 commit 5348264

File tree

3 files changed

+117
-6
lines changed

3 files changed

+117
-6
lines changed

Sources/App/Commands/Analyze.swift

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,8 @@ extension Analyze {
479479
/// - Returns: future
480480
static func applyVersionDelta(on transaction: Database,
481481
delta: VersionDelta) async throws {
482-
// Preserve existing default branch doc archives to prevent a documentation gap
483-
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2288
484-
if let oldDefaultBranchDocArchives = delta.toDelete.first(where: { $0.isBranch })?.docArchives,
485-
let newDefaultBranch = delta.toAdd.first(where: { $0.isBranch} ) {
486-
newDefaultBranch.docArchives = oldDefaultBranchDocArchives
487-
}
482+
// Preserve certain existing default branch properties
483+
carryOverDefaultBranchData(versionDelta: delta)
488484

489485
try await delta.toDelete.delete(on: transaction)
490486
delta.toDelete.forEach {
@@ -499,6 +495,38 @@ extension Analyze {
499495
}
500496
}
501497

498+
499+
/// If `versionDelta` removes and adds a default branch version, copy certain properties
500+
/// over to the new version in order to avoid gaps in data display until downstream processes
501+
/// have processed the new version.
502+
/// - Parameter versionDelta: The version change
503+
static func carryOverDefaultBranchData(versionDelta: VersionDelta) {
504+
guard versionDelta.toDelete.filter(\.isBranch).count <= 1 else {
505+
Current.logger().warning("versionDelta.toDelete has more than one branch version")
506+
return
507+
}
508+
guard versionDelta.toAdd.filter(\.isBranch).count <= 1 else {
509+
Current.logger().warning("versionDelta.toAdd has more than one branch version")
510+
return
511+
}
512+
guard let oldDefaultBranch = versionDelta.toDelete.first(where: \.isBranch),
513+
let newDefaultBranch = versionDelta.toAdd.first(where: \.isBranch)
514+
else { return }
515+
// Preserve existing default branch doc archives to prevent a documentation gap
516+
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2288
517+
if let existingDocArchives = oldDefaultBranch.docArchives {
518+
newDefaultBranch.docArchives = existingDocArchives
519+
}
520+
// Preserve dependency information
521+
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2873
522+
if let existingResolvedDependencies = oldDefaultBranch.resolvedDependencies {
523+
newDefaultBranch.resolvedDependencies = existingResolvedDependencies
524+
}
525+
if let existingProductDependencies = oldDefaultBranch.productDependencies {
526+
newDefaultBranch.productDependencies = existingProductDependencies
527+
}
528+
}
529+
502530

503531
/// Run `swift package dump-package` for a package at the given path.
504532
/// - Parameters:

Tests/AppTests/AnalyzerTests.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,66 @@ class AnalyzerTests: AppTestCase {
14591459
}
14601460
}
14611461

1462+
func test_issue_2873() async throws {
1463+
// Ensure we preserve dependency counts from previous default branch version
1464+
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2873
1465+
// setup
1466+
let pkg = try await savePackageAsync(on: app.db, id: .id0, "https://github.com/foo/1".url, processingStage: .ingestion)
1467+
try await Repository(package: pkg,
1468+
defaultBranch: "main",
1469+
name: "1",
1470+
owner: "foo",
1471+
stars: 100).save(on: app.db)
1472+
Current.git.commitCount = { _ in 12 }
1473+
Current.git.getTags = { _ in [] }
1474+
Current.git.hasBranch = { _, _ in true }
1475+
Current.git.firstCommitDate = { _ in .t0 }
1476+
Current.git.lastCommitDate = { _ in .t1 }
1477+
Current.git.revisionInfo = { _, _ in .init(commit: "sha1", date: .t0) }
1478+
Current.git.shortlog = { _ in "10\tPerson 1" }
1479+
Current.shell.run = { cmd, path in
1480+
if cmd == .swiftDumpPackage { return .packageDump(name: "foo1") }
1481+
return ""
1482+
}
1483+
1484+
// MUT and validation
1485+
1486+
// first analysis pass
1487+
try await Analyze.analyze(client: app.client, database: app.db, logger: app.logger, mode: .id(.id0))
1488+
do { // validate
1489+
let pkg = try await Package.query(on: app.db).first()
1490+
// numberOfDependencies is nil here, because we've not yet received the info back from the build
1491+
XCTAssertEqual(pkg?.scoreDetails?.numberOfDependencies, nil)
1492+
}
1493+
1494+
do { // receive build report - we could send an actual report here via the API but let's just update
1495+
// the field directly instead, we're not testing build reporting after all
1496+
let version = try await Version.query(on: app.db).first()
1497+
version?.resolvedDependencies = .some([.init(packageName: "dep",
1498+
repositoryURL: "https://github.com/some/dep")])
1499+
try await version?.save(on: app.db)
1500+
}
1501+
1502+
// second analysis pass
1503+
try await Analyze.analyze(client: app.client, database: app.db, logger: app.logger, mode: .id(.id0))
1504+
do { // validate
1505+
let pkg = try await Package.query(on: app.db).first()
1506+
// numberOfDependencies is 1 now, because we see the updated version
1507+
XCTAssertEqual(pkg?.scoreDetails?.numberOfDependencies, 1)
1508+
}
1509+
1510+
// now we simulate a new version on the default branch
1511+
Current.git.revisionInfo = { _, _ in .init(commit: "sha2", date: .t1) }
1512+
1513+
// third analysis pass
1514+
try await Analyze.analyze(client: app.client, database: app.db, logger: app.logger, mode: .id(.id0))
1515+
do { // validate
1516+
let pkg = try await Package.query(on: app.db).first()
1517+
// numberOfDependencies must be preserved as 1, even though we've not built this version yet
1518+
XCTAssertEqual(pkg?.scoreDetails?.numberOfDependencies, 1)
1519+
}
1520+
}
1521+
14621522
}
14631523

14641524

@@ -1559,3 +1619,24 @@ private enum TestError: Error {
15591619
case simulatedFetchError
15601620
case unknownCommand
15611621
}
1622+
1623+
1624+
private extension String {
1625+
static func packageDump(name: String) -> Self {
1626+
#"""
1627+
{
1628+
"name": "\#(name)",
1629+
"products": [
1630+
{
1631+
"name": "p1",
1632+
"targets": ["t1"],
1633+
"type": {
1634+
"executable": null
1635+
}
1636+
}
1637+
],
1638+
"targets": [{"name": "t1", "type": "executable"}]
1639+
}
1640+
"""#
1641+
}
1642+
}

Tests/AppTests/Helpers/Version+init.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extension Version {
1414
docArchives: [DocArchive]? = nil,
1515
latest: Kind? = nil,
1616
packageName: String? = nil,
17+
productDependencies: [ProductDependency]? = nil,
1718
publishedAt: Date? = nil,
1819
reference: Reference = .branch("main"),
1920
releaseNotes: String? = nil,
@@ -31,6 +32,7 @@ extension Version {
3132
self.docArchives = docArchives
3233
self.latest = latest
3334
self.packageName = packageName
35+
self.productDependencies = productDependencies
3436
self.publishedAt = publishedAt
3537
self.reference = reference
3638
self.releaseNotes = releaseNotes

0 commit comments

Comments
 (0)