Skip to content

Commit a9acdbd

Browse files
Merge pull request #3517 from SwiftPackageIndex/issue-3469-dependency-transition-7
Issue 3469 dependency transition 7
2 parents 8ce0529 + 2a39b3b commit a9acdbd

File tree

11 files changed

+290
-254
lines changed

11 files changed

+290
-254
lines changed

Sources/App/Core/AppEnvironment.swift

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ import FoundationNetworking
2323

2424

2525
struct AppEnvironment: Sendable {
26-
var apiSigningKey: @Sendable () -> String?
27-
var appVersion: @Sendable () -> String?
28-
var collectionSigningCertificateChain: @Sendable () -> [URL]
29-
var collectionSigningPrivateKey: @Sendable () -> Data?
3026
var currentReferenceCache: @Sendable () -> CurrentReferenceCache?
3127
var dbId: @Sendable () -> String?
3228
var fetchDocumentation: @Sendable (_ client: Client, _ url: URI) async throws -> ClientResponse
@@ -91,22 +87,6 @@ extension AppEnvironment {
9187
nonisolated(unsafe) static var logger: Logger!
9288

9389
static let live = AppEnvironment(
94-
apiSigningKey: { Environment.get("API_SIGNING_KEY") },
95-
appVersion: { App.appVersion },
96-
collectionSigningCertificateChain: {
97-
[
98-
SignedCollection.certsDir
99-
.appendingPathComponent("package_collections.cer"),
100-
SignedCollection.certsDir
101-
.appendingPathComponent("AppleWWDRCAG3.cer"),
102-
SignedCollection.certsDir
103-
.appendingPathComponent("AppleIncRootCertificate.cer")
104-
]
105-
},
106-
collectionSigningPrivateKey: {
107-
Environment.get("COLLECTION_SIGNING_PRIVATE_KEY")
108-
.map { Data($0.utf8) }
109-
},
11090
currentReferenceCache: { .live },
11191
dbId: { Environment.get("DATABASE_ID") },
11292
fetchDocumentation: { client, url in try await client.get(url) },

Sources/App/Core/Authentication/User.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ extension User {
3535
struct APITierAuthenticator: AsyncBearerAuthenticator {
3636
var tier: Tier<V1>
3737

38+
@Dependency(\.environment) var environment
39+
3840
func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
39-
guard let signingKey = Current.apiSigningKey() else { throw AppError.envVariableNotSet("API_SIGNING_KEY") }
41+
guard let signingKey = environment.apiSigningKey() else { throw AppError.envVariableNotSet("API_SIGNING_KEY") }
4042
let signer = Signer(secretSigningKey: signingKey)
4143
do {
4244
let key = try signer.verifyToken(bearer.token)

Sources/App/Core/Dependencies/EnvironmentClient.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ import Vapor
2121
struct EnvironmentClient {
2222
// See https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
2323
// regarding the use of XCTFail here.
24+
// Closures returning optionals or Void don't need this, because they automatically get the default failing
25+
// mechanism when they're not set up in a test.
2426
var allowBuildTriggers: @Sendable () -> Bool = { XCTFail("allowBuildTriggers"); return true }
2527
var allowSocialPosts: @Sendable () -> Bool = { XCTFail("allowSocialPosts"); return true }
28+
var apiSigningKey: @Sendable () -> String?
29+
var appVersion: @Sendable () -> String?
2630
var awsAccessKeyId: @Sendable () -> String?
2731
var awsDocsBucket: @Sendable () -> String?
2832
var awsReadmeBucket: @Sendable () -> String?
@@ -32,12 +36,9 @@ struct EnvironmentClient {
3236
var buildTriggerAllowList: @Sendable () -> [Package.Id] = { XCTFail("buildTriggerAllowList"); return [] }
3337
var buildTriggerDownscaling: @Sendable () -> Double = { XCTFail("buildTriggerDownscaling"); return 1 }
3438
var buildTriggerLatestSwiftVersionDownscaling: @Sendable () -> Double = { XCTFail("buildTriggerLatestSwiftVersionDownscaling"); return 1 }
35-
// We're not defaulting current to XCTFail, because its use is too pervasive and would require the vast
36-
// majority of tests to be wrapped with `withDependencies`.
37-
// We can do so at a later time once more tests are transitioned over for other dependencies. This is
38-
// the exact same default behaviour we have with the Current dependency injection: it defaults to
39-
// .development and does not raise an error when not injected.
40-
var current: @Sendable () -> Environment = { .development }
39+
var collectionSigningCertificateChain: @Sendable () -> [URL] = { XCTFail("collectionSigningCertificateChain"); return [] }
40+
var collectionSigningPrivateKey: @Sendable () -> Data?
41+
var current: @Sendable () -> Environment = { XCTFail("current"); return .development }
4142
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
4243
var mastodonPost: @Sendable (_ client: Client, _ post: String) async throws -> Void
4344
var random: @Sendable (_ range: ClosedRange<Double>) -> Double = { XCTFail("random"); return Double.random(in: $0) }
@@ -55,6 +56,8 @@ extension EnvironmentClient: DependencyKey {
5556
.flatMap(\.asBool)
5657
?? Constants.defaultAllowSocialPosts
5758
},
59+
apiSigningKey: { Environment.get("API_SIGNING_KEY") },
60+
appVersion: { App.appVersion },
5861
awsAccessKeyId: { Environment.get("AWS_ACCESS_KEY_ID") },
5962
awsDocsBucket: { Environment.get("AWS_DOCS_BUCKET") },
6063
awsReadmeBucket: { Environment.get("AWS_README_BUCKET") },
@@ -77,6 +80,16 @@ extension EnvironmentClient: DependencyKey {
7780
.flatMap(Double.init)
7881
?? 1.0
7982
},
83+
collectionSigningCertificateChain: {
84+
[
85+
"package_collections.cer",
86+
"AppleWWDRCAG3.cer",
87+
"AppleIncRootCertificate.cer",
88+
].map { SignedCollection.certsDir.appendingPathComponent($0) }
89+
},
90+
collectionSigningPrivateKey: {
91+
Environment.get("COLLECTION_SIGNING_PRIVATE_KEY").map { Data($0.utf8) }
92+
},
8093
current: { (try? Environment.detect()) ?? .development },
8194
mastodonCredentials: {
8295
Environment.get("MASTODON_ACCESS_TOKEN")
@@ -102,7 +115,19 @@ extension EnvironmentClient {
102115

103116

104117
extension EnvironmentClient: TestDependencyKey {
105-
static var testValue: Self { Self() }
118+
static var testValue: Self {
119+
// sas 2024-11-22:
120+
// For a few attributes we provide a default value overriding the XCTFail, because theis use is too
121+
// pervasive and would require the vast majority of tests to be wrapped with `withDependencies`.
122+
// We can do so at a later time once more tests are transitioned over for other dependencies. This is
123+
// the exact same default behaviour we had with the Current dependency injection. It did not have
124+
// a "fail if not set" mechanism and relied on default values only. We're simply preserving this
125+
// mechanism for a few heavily used dependencies at the moment.
126+
var mock = Self()
127+
mock.appVersion = { "test" }
128+
mock.current = { .development }
129+
return mock
130+
}
106131
}
107132

108133

Sources/App/Core/PackageCollection+signing.swift

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

1515
import Basics
16+
import Dependencies
1617
import Fluent
18+
import Vapor
1719
@preconcurrency import PackageCollectionsModel
1820
@preconcurrency import PackageCollectionsSigning
19-
import Vapor
2021

2122

2223
typealias SignedCollection = PackageCollectionSigning.Model.SignedCollection
@@ -42,12 +43,14 @@ extension SignedCollection {
4243
}
4344

4445
static func sign(collection: PackageCollection) async throws -> SignedCollection {
45-
guard let privateKey = Current.collectionSigningPrivateKey() else {
46+
@Dependency(\.environment) var environment
47+
48+
guard let privateKey = environment.collectionSigningPrivateKey() else {
4649
throw AppError.envVariableNotSet("COLLECTION_SIGNING_PRIVATE_KEY")
4750
}
4851

4952
return try await signer.sign(collection: collection,
50-
certChainPaths: Current.collectionSigningCertificateChain(),
53+
certChainPaths: environment.collectionSigningCertificateChain(),
5154
privateKeyPEM: privateKey)
5255
}
5356

Sources/App/Views/PublicPage.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ class PublicPage {
3030
/// The page's full HTML document.
3131
/// - Returns: A fully formed page inside a <html> element.
3232
final func document() -> HTML {
33-
HTML(
33+
@Dependency(\.environment) var environment
34+
return HTML(
3435
.lang(.english),
35-
.comment("Version: \(Current.appVersion())"),
36+
.comment("Version: \(environment.appVersion())"),
3637
.comment("DB Id: \(Current.dbId())"),
3738
head(),
3839
body()

Sources/App/Views/ResourceReloadIdentifier.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import Vapor
2020

2121
struct ResourceReloadIdentifier {
2222
static var value: String {
23+
@Dependency(\.environment) var environment
2324
// In staging or production appVersion will be set to a commit hash or a tag name.
2425
// It will only ever be nil when running in a local development environment.
25-
if let appVersion = Current.appVersion() {
26+
if let appVersion = environment.appVersion() {
2627
return appVersion
2728
} else {
2829
// Return the date of the most recently modified between the JavaScript and CSS resources.

0 commit comments

Comments
 (0)