Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Sources/App/Commands/Alerting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ extension [Alerting.BuildInfo] {
}

func validateRunnerIdsPresent() -> Alerting.Validation {
var notSeen = Set(Current.runnerIds())
@Dependency(\.environment) var environment
var notSeen = Set(environment.runnerIds())
for build in self where build.runnerId != nil {
notSeen.remove(build.runnerId!)
if notSeen.isEmpty { return .ok }
Expand All @@ -254,7 +255,8 @@ extension [Alerting.BuildInfo] {
}

func validateRunnerIdsSuccessful() -> Alerting.Validation {
var noSuccess = Set(Current.runnerIds())
@Dependency(\.environment) var environment
var noSuccess = Set(environment.runnerIds())
for build in self where build.runnerId != nil && build.status == .ok {
noSuccess.remove(build.runnerId!)
if noSuccess.isEmpty { return .ok }
Expand Down
17 changes: 0 additions & 17 deletions Sources/App/Core/AppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ struct AppEnvironment: Sendable {
var gitlabPipelineToken: @Sendable () -> String?
var gitlabPipelineLimit: @Sendable () -> Int
var logger: @Sendable () -> Logger
var metricsPushGatewayUrl: @Sendable () -> String?
var plausibleBackendReportingSiteID: @Sendable () -> String?
var processingBuildBacklog: @Sendable () -> Bool
var runnerIds: @Sendable () -> [String]
var setLogger: @Sendable (Logger) -> Void
var shell: Shell
var siteURL: @Sendable () -> String
var storeS3Readme: @Sendable (_ owner: String,
_ repository: String,
_ readme: String) async throws(S3Readme.Error) -> String
Expand Down Expand Up @@ -75,20 +70,8 @@ extension AppEnvironment {
?? Constants.defaultGitlabPipelineLimit
},
logger: { logger },
metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") },
plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") },
processingBuildBacklog: {
Environment.get("PROCESSING_BUILD_BACKLOG").flatMap(\.asBool) ?? false
},
runnerIds: {
Environment.get("RUNNER_IDS")
.map { Data($0.utf8) }
.flatMap { try? JSONDecoder().decode([String].self, from: $0) }
?? []
},
setLogger: { logger in Self.logger = logger },
shell: .live,
siteURL: { Environment.get("SITE_URL") ?? "http://localhost:8080" },
storeS3Readme: { owner, repo, readme throws(S3Readme.Error) in
try await S3Readme.storeReadme(owner: owner, repository: repo, readme: readme)
},
Expand Down
4 changes: 3 additions & 1 deletion Sources/App/Core/AppMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Dependencies
import Metrics
import Prometheus
import Vapor
Expand Down Expand Up @@ -170,7 +171,8 @@ extension AppMetrics {
/// scrape target.
/// - Parameter client: client for POST request
static func push(client: Client, jobName: String) async throws {
guard let pushGatewayUrl = Current.metricsPushGatewayUrl() else {
@Dependency(\.environment) var environment
guard let pushGatewayUrl = environment.metricsPushGatewayUrl() else {
throw AppError.envVariableNotSet("METRICS_PUSHGATEWAY_URL")
}
let url = URI(string: "\(pushGatewayUrl)/metrics/job/\(jobName)")
Expand Down
36 changes: 27 additions & 9 deletions Sources/App/Core/Dependencies/EnvironmentClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ struct EnvironmentClient {
var loadSPIManifest: @Sendable (String) -> SPIManifest.Manifest?
var maintenanceMessage: @Sendable () -> String?
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
var metricsPushGatewayUrl: @Sendable () -> String?
var plausibleBackendReportingSiteID: @Sendable () -> String?
var processingBuildBacklog: @Sendable () -> Bool = { XCTFail("processingBuildBacklog"); return false }
var random: @Sendable (_ range: ClosedRange<Double>) -> Double = { XCTFail("random"); return Double.random(in: $0) }
var runnerIds: @Sendable () -> [String] = { XCTFail("runnerIds"); return [] }

enum FailureMode: String {
case fetchMetadataFailed
Expand All @@ -55,7 +59,8 @@ struct EnvironmentClient {
case repositorySaveFailed
case repositorySaveUniqueViolation
}
var shouldFail: @Sendable (_ failureMode: FailureMode) -> Bool = { _ in false }
var shouldFail: @Sendable (_ failureMode: FailureMode) -> Bool = { _ in XCTFail("shouldFail"); return false }
var siteURL: @Sendable () -> String = { XCTFail("siteURL"); return "" }
}


Expand All @@ -79,10 +84,7 @@ extension EnvironmentClient: DependencyKey {
builderToken: { Environment.get("BUILDER_TOKEN") },
buildTimeout: { Environment.get("BUILD_TIMEOUT").flatMap(Int.init) ?? 10 },
buildTriggerAllowList: {
Environment.get("BUILD_TRIGGER_ALLOW_LIST")
.map { Data($0.utf8) }
.flatMap { try? JSONDecoder().decode([Package.Id].self, from: $0) }
?? []
Environment.decode("BUILD_TRIGGER_ALLOW_LIST", as: [Package.Id].self) ?? []
},
buildTriggerDownscaling: {
Environment.get("BUILD_TRIGGER_DOWNSCALING")
Expand Down Expand Up @@ -118,14 +120,19 @@ extension EnvironmentClient: DependencyKey {
Environment.get("MASTODON_ACCESS_TOKEN")
.map(Mastodon.Credentials.init(accessToken:))
},
metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") },
plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") },
processingBuildBacklog: {
Environment.get("PROCESSING_BUILD_BACKLOG").flatMap(\.asBool) ?? false
},
random: { range in Double.random(in: range) },
runnerIds: { Environment.decode("RUNNER_IDS", as: [String].self) ?? [] },
shouldFail: { failureMode in
let shouldFail = Environment.get("FAILURE_MODE")
.map { Data($0.utf8) }
.flatMap { try? JSONDecoder().decode([String: Double].self, from: $0) } ?? [:]
let shouldFail = Environment.decode("FAILURE_MODE", as: [String: Double].self) ?? [:]
guard let rate = shouldFail[failureMode.rawValue] else { return false }
return Double.random(in: 0...1) <= rate
}
},
siteURL: { Environment.get("SITE_URL") ?? "http://localhost:8080" }
)
}
}
Expand Down Expand Up @@ -156,6 +163,8 @@ extension EnvironmentClient: TestDependencyKey {
mock.appVersion = { "test" }
mock.current = { .development }
mock.hideStagingBanner = { false }
mock.siteURL = { "http://localhost:8080" }
mock.shouldFail = { @Sendable _ in false }
return mock
}
}
Expand All @@ -167,3 +176,12 @@ extension DependencyValues {
set { self[EnvironmentClient.self] = newValue }
}
}


private extension Environment {
static func decode<T: Decodable>(_ key: String, as type: T.Type) -> T? {
Environment.get(key)
.map { Data($0.utf8) }
.flatMap { try? JSONDecoder().decode(type, from: $0) }
}
}
3 changes: 2 additions & 1 deletion Sources/App/Core/Plausible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ enum Plausible {
static let postEventURL = "https://plausible.io/api/event"

static func postEvent(kind: Event.Kind, path: Path, user: User?) async throws {
guard let siteID = Current.plausibleBackendReportingSiteID() else {
@Dependency(\.environment) var environment
guard let siteID = environment.plausibleBackendReportingSiteID() else {
throw Error(message: "PLAUSIBLE_BACKEND_REPORTING_SITE_ID not set")
}
let body = try JSONEncoder().encode(Event(name: .pageview,
Expand Down
4 changes: 3 additions & 1 deletion Sources/App/Core/SiteURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Dependencies
import Plot
import Vapor

Expand Down Expand Up @@ -341,7 +342,8 @@ enum SiteURL: Resourceable, Sendable {
}

static let _absoluteURL: @Sendable (String) -> String = { path in
Current.siteURL() + relativeURL(path)
@Dependency(\.environment) var environment
return environment.siteURL() + relativeURL(path)
}

static let _relativeURL: @Sendable (String) -> String = { path in
Expand Down
4 changes: 3 additions & 1 deletion Sources/App/Views/PackageController/GetRoute.Model+ext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,9 @@ extension API.PackageController.GetRoute.Model {
}

func noCompatibilityInformationExplainer() -> Node<HTML.BodyContext> {
.if(Current.processingBuildBacklog(),
@Dependency(\.environment) var environment

return .if(environment.processingBuildBacklog(),
.group(
.p(
.text("This package currently has no compatibility information. "),
Expand Down
54 changes: 31 additions & 23 deletions Tests/AppTests/AlertingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import XCTest

@testable import App

import XCTest
import Dependencies


class AlertingTests: XCTestCase {
Expand Down Expand Up @@ -77,34 +79,40 @@ class AlertingTests: XCTestCase {

func test_validateRunnerIdsPresent() throws {
let runnerIds = ["a", "b", "c"]
Current.runnerIds = { runnerIds }
let all = runnerIds.map {
Alerting.BuildInfo.mock(runnerId: $0)
withDependencies {
$0.environment.runnerIds = { runnerIds }
} operation: {
let all = runnerIds.map {
Alerting.BuildInfo.mock(runnerId: $0)
}
XCTAssertEqual(all.validateRunnerIdsPresent(), .ok)
XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsPresent(),
.failed(reasons: ["Missing runner id: a"]))
XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsPresent(),
.failed(reasons: ["Missing runner id: a", "Missing runner id: b"]))
}
XCTAssertEqual(all.validateRunnerIdsPresent(), .ok)
XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsPresent(),
.failed(reasons: ["Missing runner id: a"]))
XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsPresent(),
.failed(reasons: ["Missing runner id: a", "Missing runner id: b"]))
}

func test_validateRunnerIdsSuccessful() throws {
let runnerIds = ["a", "b", "c"]
Current.runnerIds = { runnerIds }
let all = runnerIds.map {
Alerting.BuildInfo.mock(runnerId: $0, status: .ok)
withDependencies {
$0.environment.runnerIds = { runnerIds }
} operation: {
let all = runnerIds.map {
Alerting.BuildInfo.mock(runnerId: $0, status: .ok)
}
XCTAssertEqual(all.validateRunnerIdsSuccessful(), .ok)
XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a"]))
XCTAssertEqual(
Array(all.filter { $0.runnerId != "a" })
.appending(.mock(runnerId: "a", status: .failed))
.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a"])
)
XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a", "Runner id without successful builds: b"]))
}
XCTAssertEqual(all.validateRunnerIdsSuccessful(), .ok)
XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a"]))
XCTAssertEqual(
Array(all.filter { $0.runnerId != "a" })
.appending(.mock(runnerId: "a", status: .failed))
.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a"])
)
XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsSuccessful(),
.failed(reasons: ["Runner id without successful builds: a", "Runner id without successful builds: b"]))
}

func test_validateSuccessRateInRange() throws {
Expand Down
4 changes: 2 additions & 2 deletions Tests/AppTests/BuildTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ class BuildTests: AppTestCase {
$0.environment.awsDocsBucket = { "awsDocsBucket" }
$0.environment.builderToken = { "builder token" }
$0.environment.buildTimeout = { 10 }
$0.environment.siteURL = { "http://example.com" }
} operation: {
Current.gitlabPipelineToken = { "pipeline token" }
Current.siteURL = { "http://example.com" }
// setup
let p = try await savePackage(on: app.db, "1")
let v = try Version(package: p, reference: .branch("main"))
Expand Down Expand Up @@ -201,11 +201,11 @@ class BuildTests: AppTestCase {
$0.environment.awsDocsBucket = { "awsDocsBucket" }
$0.environment.builderToken = { "builder token" }
$0.environment.buildTimeout = { 10 }
$0.environment.siteURL = { "http://example.com" }
} operation: {
// Same test as test_trigger above, except we trigger with isDocBuild: true
// and expect a 15m TIMEOUT instead of 10m
Current.gitlabPipelineToken = { "pipeline token" }
Current.siteURL = { "http://example.com" }
// setup
let p = try await savePackage(on: app.db, "1")
let v = try Version(package: p, reference: .branch("main"))
Expand Down
Loading
Loading