Skip to content

Commit f1513f9

Browse files
Merge pull request #3483 from SwiftPackageIndex/issue-3469-dependency-transition-2
Issue 3469 dependency transition part 2
2 parents 17c76f6 + 4b9bdef commit f1513f9

12 files changed

+129
-81
lines changed

Sources/App/Controllers/API/API+PackageController+GetRoute.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Dependencies
1516
import DependencyResolution
1617
import Fluent
1718
import Vapor
@@ -101,7 +102,8 @@ extension API.PackageController.GetRoute {
101102
}
102103

103104
static func customCollections(on database: Database, package: Package) async -> [CustomCollection.Details] {
104-
guard Current.environment() == .development else { return [] }
105+
@Dependency(\.environment) var environment
106+
guard environment.current() == .development else { return [] }
105107
do {
106108
try await package.$customCollections.load(on: database)
107109
return package.customCollections.map(\.details)

Sources/App/Core/AppEnvironment.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ struct AppEnvironment: Sendable {
3939
var collectionSigningPrivateKey: @Sendable () -> Data?
4040
var currentReferenceCache: @Sendable () -> CurrentReferenceCache?
4141
var dbId: @Sendable () -> String?
42-
var environment: @Sendable () -> Environment
4342
var fetchDocumentation: @Sendable (_ client: Client, _ url: URI) async throws -> ClientResponse
4443
var fetchHTTPStatusCode: @Sendable (_ url: String) async throws -> HTTPStatus
4544
var fetchLicense: @Sendable (_ client: Client, _ owner: String, _ repository: String) async -> Github.License?
@@ -155,7 +154,6 @@ extension AppEnvironment {
155154
},
156155
currentReferenceCache: { .live },
157156
dbId: { Environment.get("DATABASE_ID") },
158-
environment: { (try? Environment.detect()) ?? .development },
159157
fetchDocumentation: { client, url in try await client.get(url) },
160158
fetchHTTPStatusCode: { url in try await Networking.fetchHTTPStatusCode(url) },
161159
fetchLicense: { client, owner, repo in await Github.fetchLicense(client:client, owner: owner, repository: repo) },

Sources/App/Core/Dependencies/EnvironmentClient.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ struct EnvironmentClient {
2222
// See https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
2323
// regarding the use of XCTFail here.
2424
var allowBuildTriggers: @Sendable () -> Bool = { XCTFail(#function); return true }
25+
// We're not defaulting current to XCTFail, because its use is too pervasive and would require the vast
26+
// majority of tests to be wrapped with `withDependencies`.
27+
// We can do so at a later time once more tests are transitioned over for other dependencies. This is
28+
// the exact same default behaviour we have with the Current dependency injection: it defaults to
29+
// .development and does not raise an error when not injected.
30+
var current: @Sendable () -> Environment = { .development }
2531
}
2632

2733

@@ -30,7 +36,8 @@ extension EnvironmentClient: DependencyKey {
3036
.init(
3137
allowBuildTriggers: {
3238
Environment.get("ALLOW_BUILD_TRIGGERS").flatMap(\.asBool) ?? Constants.defaultAllowBuildTriggering
33-
}
39+
},
40+
current: { (try? Environment.detect()) ?? .development }
3441
)
3542
}
3643
}

Sources/App/Views/Blog/BlogActions+Model.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
// limitations under the License.
1414

1515
import Foundation
16+
17+
import Dependencies
18+
import Ink
1619
import Plot
1720
import Yams
18-
import Ink
21+
1922

2023
enum BlogActions {
2124

@@ -38,7 +41,8 @@ enum BlogActions {
3841
init() throws {
3942
let allSummaries = try Self.allSummaries()
4043

41-
summaries = if Current.environment() == .production {
44+
@Dependency(\.environment) var environment
45+
summaries = if environment.current() == .production {
4246
// Only "published" posts show in production.
4347
allSummaries.filter { $0.published }
4448
} else {
@@ -67,7 +71,7 @@ enum BlogActions {
6771
return try YAMLDecoder().decode([PostSummary].self, from: String(decoding: data, as: UTF8.self))
6872
.reversed()
6973
}
70-
74+
7175
}
7276
}
7377

Sources/App/Views/DocumentationPageProcessor.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import Vapor
1616
import SwiftSoup
1717
import Plot
18+
import Dependencies
19+
1820

1921
struct DocumentationPageProcessor {
2022
let document: SwiftSoup.Document
@@ -104,7 +106,8 @@ struct DocumentationPageProcessor {
104106
}
105107

106108
var metaNoIndex: String? {
107-
guard Current.environment() != .production else { return nil }
109+
@Dependency(\.environment) var environment
110+
guard environment.current() != .production else { return nil }
108111
return Plot.Node.meta(
109112
.name("robots"),
110113
.content("noindex")
@@ -129,7 +132,8 @@ struct DocumentationPageProcessor {
129132
}
130133

131134
var analyticsScript: String? {
132-
guard Current.environment() == .production else { return nil }
135+
@Dependency(\.environment) var environment
136+
guard environment.current() == .production else { return nil }
133137
return PublicPage.analyticsScriptTags
134138
}
135139

@@ -194,10 +198,11 @@ struct DocumentationPageProcessor {
194198
]))
195199
}
196200

201+
@Dependency(\.environment) var environment
197202
return Plot.Node.group(
198203
.header(
199204
.class("spi"),
200-
.if(Current.environment() == .development, stagingBanner()),
205+
.if(environment.current() == .development, stagingBanner()),
201206
.div(
202207
.class("inner breadcrumbs"),
203208
.nav(
@@ -240,6 +245,7 @@ struct DocumentationPageProcessor {
240245
}
241246

242247
var footer: String {
248+
@Dependency(\.environment) var environment
243249
return Plot.Node.footer(
244250
.class("spi"),
245251
.div(
@@ -288,7 +294,7 @@ struct DocumentationPageProcessor {
288294
.text(".")
289295
)
290296
),
291-
.if(Current.environment() == .development, stagingBanner())
297+
.if(environment.current() == .development, stagingBanner())
292298
).render()
293299
}
294300

Sources/App/Views/PublicPage.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
// limitations under the License.
1414

1515
import Foundation
16-
import Vapor
16+
17+
import Dependencies
1718
import Plot
19+
import Vapor
20+
1821

1922
class PublicPage {
2023

@@ -91,7 +94,8 @@ class PublicPage {
9194
/// For non-production environments, this will *always* return true.
9295
/// - Returns: Either nothing, or a <meta> element telling search engines not to index this content.
9396
final func metaNoIndex() -> Node<HTML.HeadContext> {
94-
if Current.environment() == .production && allowIndexing() {
97+
@Dependency(\.environment) var environment
98+
if environment.current() == .production && allowIndexing() {
9599
return .empty
96100
} else {
97101
return .meta(
@@ -167,7 +171,8 @@ class PublicPage {
167171
/// The Plausible analytics code to be inserted into the <head> element.
168172
/// - Returns: A <script> containing the Plausible script tags.
169173
final func analyticsHead() -> Node<HTML.HeadContext> {
170-
return .if(Current.environment() == .production, .raw(PublicPage.analyticsScriptTags))
174+
@Dependency(\.environment) var environment
175+
return .if(environment.current() == .production, .raw(PublicPage.analyticsScriptTags))
171176
}
172177

173178
static var analyticsScriptTags: String {
@@ -273,7 +278,8 @@ class PublicPage {
273278
/// - Returns: Either a <div> element, or nothing.
274279
final func stagingBanner() -> Node<HTML.BodyContext> {
275280
guard !Current.hideStagingBanner() else { return .empty }
276-
if Current.environment() == .development {
281+
@Dependency(\.environment) var environment
282+
if environment.current() == .development {
277283
return .div(
278284
.class("staging"),
279285
.text("This is a staging environment. For live and up-to-date package information, "),

Sources/App/routes.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Dependencies
1516
import Fluent
1617
import Metrics
1718
import Plot
@@ -21,6 +22,8 @@ import VaporToOpenAPI
2122

2223

2324
func routes(_ app: Application) throws {
25+
@Dependency(\.environment) var environment
26+
2427
do { // home page
2528
app.get { req in
2629
if let maintenanceMessage = Current.maintenanceMessage() {
@@ -74,7 +77,7 @@ func routes(_ app: Application) throws {
7477
use: PackageController.maintainerInfo).excludeFromOpenAPI()
7578

7679
// Only serve sitemaps in production.
77-
if Current.environment() == .production {
80+
if environment.current() == .production {
7881
// Package specific site map, including all documentation URLs if available.
7982
// Backend reporting currently disabled to avoid reporting costs for metrics we don't need.
8083
app.group(BackendReportingMiddleware(path: .sitemapPackage, isActive: false)) {
@@ -149,7 +152,7 @@ func routes(_ app: Application) throws {
149152
}
150153

151154
do { // api
152-
155+
153156
// public routes
154157
app.get(SiteURL.api(.version).pathComponents) { req in
155158
API.Version(version: appVersion ?? "Unknown")
@@ -252,21 +255,21 @@ func routes(_ app: Application) throws {
252255
)
253256
}
254257
}
255-
258+
256259
}
257260

258261
do { // RSS
259262
app.group(BackendReportingMiddleware(path: .rss)) {
260263
$0.get(SiteURL.rssPackages.pathComponents, use: RSSFeed.showPackages)
261264
.excludeFromOpenAPI()
262-
265+
263266
$0.get(SiteURL.rssReleases.pathComponents, use: RSSFeed.showReleases)
264267
.excludeFromOpenAPI()
265268
}
266269
}
267270

268271
// Only serve sitemaps in production.
269-
if Current.environment() == .production {
272+
if environment.current() == .production {
270273
do { // Site map index and static page site map
271274
app.group(BackendReportingMiddleware(path: .sitemapIndex)) {
272275
$0.get(SiteURL.siteMapIndex.pathComponents, use: SiteMapController.index)

Tests/AppTests/BlogActionsModelTests.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import XCTest
16+
1517
@testable import App
1618

17-
import XCTest
19+
import Dependencies
20+
1821

1922
class BlogActionsModelTests: AppTestCase {
2023

@@ -34,9 +37,9 @@ class BlogActionsModelTests: AppTestCase {
3437
""".data(using: .utf8)
3538
}
3639

37-
do { // Ensure dev shows all summaries
38-
Current.environment = { .development }
39-
40+
try withDependencies { // Ensure dev shows all summaries
41+
$0.environment.current = { .development }
42+
} operation: {
4043
// MUT
4144
let summaries = try BlogActions.Model().summaries
4245

@@ -55,9 +58,9 @@ class BlogActionsModelTests: AppTestCase {
5558
published: false))
5659
}
5760

58-
do { // Ensure prod shows only published summaries
59-
Current.environment = { .production }
60-
61+
try withDependencies { // Ensure prod shows only published summaries
62+
$0.environment.current = { .production }
63+
} operation: {
6164
// MUT
6265
let summaries = try BlogActions.Model().summaries
6366

Tests/AppTests/Mocks/AppEnvironment+mock.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ extension AppEnvironment {
3838
collectionSigningPrivateKey: AppEnvironment.live.collectionSigningPrivateKey,
3939
currentReferenceCache: { nil },
4040
dbId: { "db-id" },
41-
environment: { .development },
4241
fetchDocumentation: { _, _ in .init(status: .ok) },
4342
fetchHTTPStatusCode: { _ in .ok },
4443
fetchLicense: { _, _, _ in .init(htmlUrl: "https://github.com/foo/bar/blob/main/LICENSE") },

Tests/AppTests/PackageController+routesTests.swift

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,31 +1337,33 @@ class PackageController_routesTests: SnapshotTestCase {
13371337

13381338
func test_siteMap_prod() async throws {
13391339
// Ensure sitemap routing is configured in prod
1340-
// Setup
1341-
Current.environment = { .production }
1342-
// We also need to set up a new app that's configured for production,
1343-
// because app.test is not affected by Current overrides.
1344-
let prodApp = try await setup(.production)
1340+
try await withDependencies {
1341+
$0.environment.current = { .production }
1342+
} operation: {
1343+
// We also need to set up a new app that's configured for production,
1344+
// because app.test is not affected by @Dependency overrides.
1345+
let prodApp = try await setup(.production)
13451346

1346-
do {
1347-
// setup
1348-
let package = Package(url: URL(stringLiteral: "https://example.com/owner/repo0"))
1349-
try await package.save(on: app.db)
1350-
try await Repository(package: package, defaultBranch: "default",
1351-
lastCommitDate: Date.now,
1352-
name: "Repo0", owner: "Owner").save(on: app.db)
1353-
try await Version(package: package, latest: .defaultBranch, packageName: "SomePackage",
1354-
reference: .branch("default")).save(on: app.db)
1355-
1356-
// MUT
1357-
try await prodApp.test(.GET, "/owner/repo0/sitemap.xml") { res async in
1358-
XCTAssertEqual(res.status, .ok)
1347+
do {
1348+
// setup
1349+
let package = Package(url: URL(stringLiteral: "https://example.com/owner/repo0"))
1350+
try await package.save(on: app.db)
1351+
try await Repository(package: package, defaultBranch: "default",
1352+
lastCommitDate: Date.now,
1353+
name: "Repo0", owner: "Owner").save(on: app.db)
1354+
try await Version(package: package, latest: .defaultBranch, packageName: "SomePackage",
1355+
reference: .branch("default")).save(on: app.db)
1356+
1357+
// MUT
1358+
try await prodApp.test(.GET, "/owner/repo0/sitemap.xml") { res async in
1359+
XCTAssertEqual(res.status, .ok)
1360+
}
1361+
} catch {
1362+
try? await prodApp.asyncShutdown()
1363+
throw error
13591364
}
1360-
} catch {
1361-
try? await prodApp.asyncShutdown()
1362-
throw error
1365+
try await prodApp.asyncShutdown()
13631366
}
1364-
try await prodApp.asyncShutdown()
13651367
}
13661368

13671369
func test_siteMap_dev() async throws {

0 commit comments

Comments
 (0)