diff --git a/AGENTS.md b/AGENTS.md index 2814dd8499..df08cc7934 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,10 +98,7 @@ swift test --filter AWSCognitoAuthPluginUnitTests # Specific target - **Unit tests**: XCTest, defined in Package.swift (19 test targets) - **Integration tests**: Xcode host app projects under `AmplifyPlugins//Tests/HostApp/` - **Conventions**: Mock via behavior protocols, use `AmplifyTestCommon` for shared utilities, `AmplifyAsyncTesting` for async helpers -- **Test documentation (MANDATORY)**: Every new or modified test method - **must** have a Given/When/Then doc comment. No exceptions — this applies - to unit tests, integration tests, and regression tests alike. Reviewers - should reject PRs that add tests without this structure. +- **Test documentation**: Use Given/When/Then doc comments on all test methods: ```swift /// Test description /// diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift index 64ed58c26e..e0a78f6cac 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift @@ -251,13 +251,8 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol { private func resumeExistingSubscriptions() { log.debug("[AppSyncRealTimeClient] Resuming existing subscriptions") - for (id, subscription) in subscriptions { + for (id, _) in subscriptions { Task { [weak self] in - // Reset local state so subscribe() re-sends .start to the - // server. After a reconnect, the server has no memory of - // prior subscriptions, so stale local .subscribed state must - // not short-circuit the resubscription. - await subscription.prepareForResubscribe() do { if let cancellable = try await self?.startSubscription(id) { await self?.storeInConnectionCancellables(cancellable) diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeSubscription.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeSubscription.swift index a8e4b5e14d..b56fbcb645 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeSubscription.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeSubscription.swift @@ -84,15 +84,6 @@ actor AppSyncRealTimeSubscription { state.send(.subscribed) } - /// Reset local subscription state so `subscribe()` will actually resend - /// the `.start` request after a WebSocket reconnect. The server drops - /// subscription state when the connection dies, so local `.subscribed` - /// state is stale and must not short-circuit the resubscription path. - /// Fix for https://github.com/aws-amplify/amplify-swift/issues/3976 - func prepareForResubscribe() { - state.send(.none) - } - func unsubscribe() async throws { guard state.value == .subscribed else { log.debug("[AppSyncRealTimeSubscription-\(id)] Subscription should be subscribed to be unsubscribed") diff --git a/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/AppSyncRealTimeClientTests.swift b/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/AppSyncRealTimeClientTests.swift index ddab577c1b..ec5a3600cc 100644 --- a/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/AppSyncRealTimeClientTests.swift +++ b/AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/AppSyncRealTimeClientTests.swift @@ -169,100 +169,6 @@ class AppSyncRealTimeClientTests: XCTestCase { withExtendedLifetime(cancellables) { } } - /// End-to-end regression test for https://github.com/aws-amplify/amplify-swift/issues/3976 - /// against a real AppSync backend. Simulates the scenePhase-triggered - /// NWPath recycle by driving two .online states through an injected - /// AmplifyNetworkMonitor and asserts that the subscription is actually - /// re-established (server issues a second start_ack). - /// - /// - Given: - /// - An AppSyncRealTimeClient wired to a real AmplifyNetworkMonitor - /// and a real AppSync endpoint from the bundled config. - /// - A live subscription that has already received .subscribed from - /// the server (first start_ack confirmed). - /// - When: - /// - After the WebSocketClient's internal sink has attached (200ms), - /// updateState(.online) is called twice on the network monitor, - /// producing the (.online, .online) tuple from issue #3976. - /// - Then: - /// - The WebSocket is recycled, AppSyncRealTimeClient reconnects, - /// resumeExistingSubscriptions() re-sends `start`, and the server - /// returns a second start_ack — causing a second .subscribed event. - func testSubscribe_afterOnlineToOnlinePathChange_shouldRecycleAndResubscribe() async throws { - var cancellables = Set() - - let data = try TestConfigHelper.retrieve( - forResource: GraphQLModelBasedTests.amplifyConfiguration - ) - let amplifyConfig = try JSONDecoder().decode(JSONValue.self, from: data) - let (endpoint, apiKey) = (amplifyConfig.api?.plugins?.awsAPIPlugin?.asObject?.values - .map { ($0.endpoint?.stringValue, $0.apiKey?.stringValue) } - .first { $0.0 != nil && $0.1 != nil } - .map { ($0.0!, $0.1!) })! - - // Inject a real AmplifyNetworkMonitor we can drive directly. - let networkMonitor = AmplifyNetworkMonitor() - - let webSocketClient = WebSocketClient( - url: AppSyncRealTimeClientFactory.appSyncRealTimeEndpoint(URL(string: endpoint)!), - handshakeHttpHeaders: [ - URLRequestConstants.Header.webSocketSubprotocols: "graphql-ws", - URLRequestConstants.Header.userAgent: AmplifyAWSServiceConfiguration.userAgentLib + " (intg-test-3976)" - ], - interceptor: APIKeyAuthInterceptor(apiKey: apiKey), - networkMonitor: networkMonitor - ) - let client = AppSyncRealTimeClient( - endpoint: URL(string: endpoint)!, - requestInterceptor: APIKeyAuthInterceptor(apiKey: apiKey), - webSocketClient: webSocketClient - ) - defer { Task { await client.reset() } } - - // Wait for WebSocketClient's internal sink to attach to the monitor's - // publisher (it's kicked off via a Task in init). Prime with .online - // AFTER the subscriber is attached so the PassthroughSubject actually - // delivers the event. This gets the scan to (.none, .online) — - // WebSocketClient will ignore it because autoConnect is still false. - try await Task.sleep(nanoseconds: 200_000_000) - await networkMonitor.updateState(.online) - - let firstSubscribed = expectation(description: "Initial subscription established") - let resubscribedAfterPathChange = expectation(description: "Subscription re-established after (.online, .online)") - resubscribedAfterPathChange.assertForOverFulfill = false - - let id = UUID().uuidString - let subscribedCount = AtomicInt() - let subscription = try await client.subscribe( - id: id, - query: Self.appSyncQuery(with: subscriptionRequest) - ).sink { event in - if case .subscribed = event { - let count = subscribedCount.increment() - if count == 1 { - firstSubscribed.fulfill() - } else { - resubscribedAfterPathChange.fulfill() - } - } - } - cancellables.insert(subscription) - - try await client.connect() - await fulfillment(of: [firstSubscribed], timeout: 10) - - // Simulate the path-recycle: second .online emission produces - // (.online, .online) through the scan — the exact bug tuple. - // In the buggy code, nothing happens; WebSocketClient keeps the - // zombie connection. With the fix, it should tear down and reconnect, - // and AppSyncRealTimeClient.resumeExistingSubscriptions() should - // re-subscribe. - await networkMonitor.updateState(.online) - - await fulfillment(of: [resubscribedAfterPathChange], timeout: 15) - withExtendedLifetime(cancellables) { } - } - private func makeOneSubscription( id: String = UUID().uuidString, onSubscriptionEvents: ((AppSyncSubscriptionEvent) -> Void)? @@ -295,14 +201,3 @@ class AppSyncRealTimeClientTests: XCTestCase { } } - -private final class AtomicInt: @unchecked Sendable { - private var value: Int = 0 - private let lock = NSLock() - func increment() -> Int { - lock.lock() - defer { lock.unlock() } - value += 1 - return value - } -} diff --git a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift index 61a5e08aca..f8705e21dc 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/WebSocket/WebSocketClient.swift @@ -292,23 +292,6 @@ extension WebSocketClient { case (.offline, .online): log.debug("[WebSocketClient] NetworkMonitor - Device back online") await createConnectionAndRead() - case (.online, .online): - // NWPathMonitor's pathUpdateHandler only fires on real path - // changes, so a second .satisfied emission while we were already - // online means the underlying path was swapped (e.g., iOS recycled - // the TCP route during a scenePhase transition). The existing - // URLSessionWebSocketTask is now bound to a stale route and any - // further reads/writes will silently fail, leaving the client in - // a zombie state that cached consumers can't recover from. - // Fix for https://github.com/aws-amplify/amplify-swift/issues/3976 - guard connection?.state == .running else { - log.debug("[WebSocketClient] NetworkMonitor - Path changed but connection is not running, skipping recycle") - break - } - log.debug("[WebSocketClient] NetworkMonitor - Network path changed while online, recycling connection") - connection?.cancel(with: .invalid, reason: nil) - subject.send(.disconnected(.invalid, nil)) - await createConnectionAndRead() default: break } diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift index 7013cd4474..4fe4326a4f 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/WebSocket/WebSocketClientTests.swift @@ -139,168 +139,6 @@ class WebSocketClientTests: XCTestCase { await fulfillment(of: [reconnectExpectation], timeout: timeout) } - /// Regression test for https://github.com/aws-amplify/amplify-swift/issues/3976. - /// When iOS recycles the TCP route during a scenePhase transition, - /// NWPathMonitor reports .satisfied both before and after, producing - /// (.online, .online) through AmplifyNetworkMonitor's scan. Before the - /// fix, WebSocketClient.onNetworkStateChange hit `default: break` and - /// left the stale URLSessionWebSocketTask in place — a zombie. - /// - /// - Given: - /// - A WebSocketClient connected via a MockNetworkMonitor whose scan - /// seed is (.online, .online), so one updateState(.online) produces - /// the bug tuple deterministically. - /// - autoConnectOnNetworkStatusChange is true. - /// - When: - /// - The mock emits .online, producing an (.online, .online) tuple. - /// - Then: - /// - WebSocketClient sends a `.disconnected` event (stale task torn down). - /// - WebSocketClient emits a fresh `.connected` event (new connection). - func testWebSocketClient_whenNetworkPathChangesWhileOnline_shouldRecycleConnection() async throws { - var cancellables = Set() - guard let endpoint = try localWebSocketServer?.start() else { - XCTFail("Local WebSocket server failed to start") - return - } - - let mockNetworkMonitor = MockNetworkMonitor() - let webSocketClient = WebSocketClient(url: endpoint, networkMonitor: mockNetworkMonitor) - await verifyConnected(webSocketClient, autoConnectOnNetworkStatusChange: true) - - let disconnectExpectation = expectation(description: "Path change should force a disconnect") - let reconnectExpectation = expectation(description: "Path change should trigger a reconnect") - - await webSocketClient.publisher.sink { event in - switch event { - case .disconnected: - disconnectExpectation.fulfill() - case .connected: - reconnectExpectation.fulfill() - default: - break - } - } - .store(in: &cancellables) - - // Simulate NWPathMonitor firing .satisfied again after a path recycle. - // The scan seed in MockNetworkMonitor is (.online, .online), so sending - // .online produces exactly the (.online, .online) tuple from issue #3976. - await mockNetworkMonitor.updateState(.online) - - await fulfillment( - of: [disconnectExpectation, reconnectExpectation], - timeout: timeout, - enforceOrder: true - ) - } - - /// Integration-level companion to the mock-based test above. Proves the - /// fix works with the real AmplifyNetworkMonitor's scan seed (.none, .none) - /// — i.e., the bug is not an artifact of MockNetworkMonitor's seeding. - /// Drives state through `updateState`, the same seam WebSocketClient - /// itself uses when reporting connectionLost. Tolerates spontaneous - /// NWPathMonitor firings on watchOS by counting `.connected` events - /// instead of using the strict verifyConnected helper. - /// - /// - Given: - /// - A WebSocketClient wired to a real AmplifyNetworkMonitor with its - /// natural scan seed (.none, .none). - /// - A publisher sink attached before connect() so no events are lost - /// while the client's internal sink is still attaching. - /// - autoConnectOnNetworkStatusChange is true. - /// - When: - /// - The monitor's updateState(.online) is called twice, producing - /// (.none, .online) then (.online, .online) through the scan. - /// - Then: - /// - A second `.connected` event is observed (initial connect + recycle - /// reconnect), confirming the WebSocket was torn down and rebuilt. - func testWebSocketClient_withRealNetworkMonitor_whenPathChangesWhileOnline_shouldRecycle() async throws { - var cancellables = Set() - guard let endpoint = try localWebSocketServer?.start() else { - XCTFail("Local WebSocket server failed to start") - return - } - - let realNetworkMonitor = AmplifyNetworkMonitor() - let webSocketClient = WebSocketClient(url: endpoint, networkMonitor: realNetworkMonitor) - - let initialConnect = expectation(description: "Initial WebSocket connect") - let reconnectAfterPathChange = expectation(description: "Reconnect after (.online, .online)") - let connectedCounter = AtomicInt() - - await webSocketClient.publisher.sink { event in - if case .connected = event { - let count = connectedCounter.increment() - if count == 1 { - initialConnect.fulfill() - } else if count == 2 { - reconnectAfterPathChange.fulfill() - } - } - // Tolerate .disconnected / .error / .string / .data events, - // which can arrive from NWPathMonitor-driven recycling or from - // LocalWebSocketServer teardown. - } - .store(in: &cancellables) - - await webSocketClient.connect( - autoConnectOnNetworkStatusChange: true, - autoRetryOnConnectionFailure: false - ) - await fulfillment(of: [initialConnect], timeout: timeout) - - // WebSocketClient.init spawns its sink via Task { startNetworkMonitor() }; - // by the time initialConnect fulfils, the sink is attached. - // Prime the scan so (previous, next) reaches (.online, .online) on - // the second updateState — first reaches (.none, .online). - await realNetworkMonitor.updateState(.online) - - // Second .online emission → scan produces (.online, .online) — - // the exact tuple from issue #3976. With the fix, this triggers a - // recycle that yields a second `.connected`. - await realNetworkMonitor.updateState(.online) - - await fulfillment(of: [reconnectAfterPathChange], timeout: timeout) - } - - /// Characterizes the input signal that drives issue #3976. Proves that - /// the real AmplifyNetworkMonitor.publisher emits the (.online, .online) - /// tuple when two .online states are sent consecutively — which is what - /// WebSocketClient.onNetworkStateChange receives during a scenePhase- - /// triggered NWPath recycle. Does not exercise the fix; passes both - /// before and after. - /// - /// - Given: - /// - A fresh AmplifyNetworkMonitor instance. - /// - A publisher sink that watches for (.online, .online) tuples. - /// - When: - /// - updateState(.online) is called twice consecutively. - /// - Then: - /// - The publisher emits an (.online, .online) tuple via its scan — - /// confirming this is the exact signal WebSocketClient must handle. - func testAmplifyNetworkMonitor_whenOnlineEmittedTwice_publishesOnlineOnlineTuple() async throws { - var cancellables = Set() - let monitor = AmplifyNetworkMonitor() - - let expectOnlineOnline = expectation(description: "publisher emits (.online, .online)") - expectOnlineOnline.assertForOverFulfill = false - - monitor.publisher.sink { tuple in - if tuple.0 == .online && tuple.1 == .online { - expectOnlineOnline.fulfill() - } - } - .store(in: &cancellables) - - // Two consecutive .online emissions must produce an (.online, .online) - // tuple through the scan — the exact input that triggers issue #3976 - // in WebSocketClient.onNetworkStateChange. - await monitor.updateState(.online) - await monitor.updateState(.online) - - await fulfillment(of: [expectOnlineOnline], timeout: timeout) - } - func testAutoRetry_whenReceiveTransientFailureFromServer() async throws { var cancellables = Set() guard let endpoint = try localWebSocketServer?.start() else { @@ -357,17 +195,6 @@ class WebSocketClientTests: XCTestCase { } -private final class AtomicInt: @unchecked Sendable { - private var value: Int = 0 - private let lock = NSLock() - func increment() -> Int { - lock.lock() - defer { lock.unlock() } - value += 1 - return value - } -} - private class MockNetworkMonitor: WebSocketNetworkMonitorProtocol { typealias State = AmplifyNetworkMonitor.State let subject = PassthroughSubject() diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json index 6ddcffc028..1488014a8c 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/package-lock.json @@ -30,9 +30,9 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -43,7 +43,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.14.1", + "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -135,6 +135,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -223,9 +224,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -248,14 +249,14 @@ } }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "~1.20.3", + "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", @@ -274,7 +275,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.14.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -397,9 +398,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -461,6 +462,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -574,9 +576,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -740,13 +742,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -812,6 +814,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" diff --git a/Gemfile b/Gemfile index 3b0fac1afc..fb4f11411e 100644 --- a/Gemfile +++ b/Gemfile @@ -2,8 +2,8 @@ source 'https://rubygems.org' -gem 'xcpretty', '0.3.0' -gem 'fastlane', '2.205.1' +gem 'xcpretty', '0.4.1' +gem 'fastlane', '2.235.0' gem 'jazzy', '0.15.1' eval_gemfile('fastlane/Pluginfile') diff --git a/Gemfile.lock b/Gemfile.lock index b45cc732fe..c3661ad593 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,10 +9,8 @@ GIT GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml + CFPropertyList (3.0.8) + abbrev (0.1.2) activesupport (7.2.3.1) base64 benchmark (>= 0.3) @@ -30,11 +28,11 @@ GEM algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) - artifactory (3.0.15) + artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.4.0) - aws-partitions (1.1196.0) - aws-sdk-core (3.240.0) + aws-partitions (1.1255.0) + aws-sdk-core (3.250.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -42,11 +40,11 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.118.0) - aws-sdk-core (~> 3, >= 3.239.1) + aws-sdk-kms (1.128.0) + aws-sdk-core (~> 3, >= 3.248.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.208.0) - aws-sdk-core (~> 3, >= 3.234.0) + aws-sdk-s3 (1.224.0) + aws-sdk-core (~> 3, >= 3.248.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) aws-sigv4 (1.12.1) @@ -54,7 +52,7 @@ GEM babosa (1.0.4) base64 (0.3.0) benchmark (0.5.0) - bigdecimal (4.0.1) + bigdecimal (4.1.2) claide (1.1.0) cocoapods (1.15.2) addressable (~> 2.8) @@ -99,18 +97,18 @@ GEM highline (~> 2.0.0) concurrent-ruby (1.3.6) connection_pool (3.0.2) + csv (3.3.5) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) dotenv (2.8.1) drb (2.2.3) emoji_regex (3.2.3) escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.99.0) + excon (0.112.0) faraday (1.10.5) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -123,9 +121,9 @@ GEM faraday-rack (~> 1.0) faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) + faraday-cookie_jar (0.0.8) faraday (>= 0.8.0) - http-cookie (~> 1.0.0) + http-cookie (>= 1.0.0) faraday-em_http (1.0.0) faraday-em_synchrony (1.0.1) faraday-excon (1.1.0) @@ -136,19 +134,23 @@ GEM faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday-retry (1.0.4) + faraday_middleware (1.2.1) faraday (~> 1.0) - fastimage (2.2.6) - fastlane (2.205.1) - CFPropertyList (>= 2.3, < 4.0.0) + fastimage (2.4.1) + fastlane (2.235.0) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) + aws-sdk-s3 (~> 1.197) babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) + colored (~> 1.2) commander (~> 4.6) + csv (~> 3.3) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -156,73 +158,87 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.1.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.3.0) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) - jwt (>= 2.1.0, < 3) + jwt (>= 2.1.0, < 4) + logger (>= 1.6, < 2.0) mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) naturally (~> 2.2) - optparse (~> 0.1.1) + nkf (~> 0.2) + optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) + security (= 0.1.5) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.1.0) ffi (1.17.0) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.33.0) - google-apis-core (>= 0.9.1, < 2.a) - google-apis-core (0.11.3) + google-apis-androidpublisher_v3 (0.101.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-core (0.18.0) addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) + googleauth (~> 1.9) + httpclient (>= 2.8.3, < 3.a) mini_mime (~> 1.0) + mutex_m representable (~> 3.0) retriable (>= 2.0, < 4.a) - rexml - google-apis-iamcredentials_v1 (0.16.0) - google-apis-core (>= 0.9.1, < 2.a) - google-apis-playcustomapp_v1 (0.12.0) - google-apis-core (>= 0.9.1, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) + google-apis-iamcredentials_v1 (0.27.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-playcustomapp_v1 (0.17.0) + google-apis-core (>= 0.15.0, < 2.a) + google-apis-storage_v1 (0.63.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.8.0) + google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.0) - google-cloud-storage (1.44.0) + google-cloud-env (2.2.2) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.6.0) + google-cloud-storage (1.60.0) addressable (~> 2.8) digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) + google-apis-core (>= 0.18, < 2) + google-apis-iamcredentials_v1 (~> 0.18) + google-apis-storage_v1 (>= 0.42) google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) + googleauth (~> 1.9) mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) + google-logging-utils (0.2.0) + googleauth (1.16.2) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) - httpclient (2.8.3) + httpclient (2.9.0) + mutex_m i18n (1.14.8) concurrent-ruby (~> 1.0) jazzy (0.15.1) @@ -236,49 +252,51 @@ GEM sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) jmespath (1.6.2) - json (2.6.3) - jwt (2.9.1) + json (2.19.7) + jwt (3.2.0) base64 liferaft (0.0.6) logger (1.7.0) - mini_magick (4.12.0) + mini_magick (4.13.2) mini_mime (1.1.5) mini_portile2 (2.8.7) minitest (5.27.0) molinillo (0.8.0) - multi_json (1.15.0) - multipart-post (2.0.0) + multi_json (1.21.1) + multipart-post (2.4.1) mustache (1.1.1) - nanaimo (0.3.0) + mutex_m (0.3.0) + nanaimo (0.4.0) nap (1.1.0) - naturally (2.2.1) + naturally (2.3.0) netrc (0.11.0) nkf (0.2.0) open4 (1.3.4) - optparse (0.1.1) + optparse (0.8.1) os (1.1.4) - plist (3.6.0) + ostruct (0.6.3) + plist (3.7.2) public_suffix (4.0.7) - rake (13.0.6) + rake (13.4.2) redcarpet (3.6.0) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) - retriable (3.1.2) - rexml (3.4.2) - rouge (2.0.7) + retriable (3.8.0) + rexml (3.4.4) + rouge (3.28.0) ruby-macho (2.5.1) ruby2_keywords (0.0.5) - rubyzip (2.3.2) + rubyzip (2.4.1) sassc (2.4.0) ffi (~> 1.9) securerandom (0.4.1) - security (0.1.3) - signet (0.19.0) + security (0.1.5) + signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) + jwt (>= 1.5, < 4.0) multi_json (~> 1.10) simctl (1.6.10) CFPropertyList @@ -286,11 +304,11 @@ GEM sqlite3 (1.7.3) mini_portile2 (~> 2.8.0) terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.8.1) + tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) typhoeus (1.4.1) @@ -298,22 +316,19 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (1.8.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.25.0) + xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) - xcpretty (0.3.0) - rouge (~> 2.0.7) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) @@ -321,10 +336,10 @@ PLATFORMS ruby DEPENDENCIES - fastlane (= 2.205.1) + fastlane (= 2.235.0) fastlane-plugin-release_actions! jazzy (= 0.15.1) - xcpretty (= 0.3.0) + xcpretty (= 0.4.1) BUNDLED WITH 2.5.22 diff --git a/canaries/example/Gemfile.lock b/canaries/example/Gemfile.lock index c064dcd244..9d12784a6c 100644 --- a/canaries/example/Gemfile.lock +++ b/canaries/example/Gemfile.lock @@ -3,6 +3,7 @@ GEM specs: CFPropertyList (3.0.5) rexml + abbrev (0.1.2) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) artifactory (3.0.17) @@ -27,13 +28,15 @@ GEM aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - base64 (0.2.0) + base64 (0.3.0) + benchmark (0.5.0) bigdecimal (4.0.1) claide (1.1.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) + csv (3.3.5) declarative (0.0.20) digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) @@ -70,15 +73,19 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.4.0) - fastlane (2.228.0) - CFPropertyList (>= 2.3, < 4.0.0) + fastlane (2.235.0) + CFPropertyList (>= 2.3, < 5.0.0) + abbrev (~> 0.1) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) + aws-sdk-s3 (~> 1.197) babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) + base64 (~> 0.2) + benchmark (>= 0.1.0) + bundler (>= 2.4.0, < 5.0.0) colored (~> 1.2) commander (~> 4.6) + csv (~> 3.3) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -86,20 +93,24 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) - fastlane-sirp (>= 1.0.0) + fastlane-sirp (>= 1.1.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-env (>= 1.6.0, < 2.3.0) google-cloud-storage (~> 1.31) highline (~> 2.0) http-cookie (~> 1.0.5) json (< 3.0.0) - jwt (>= 2.1.0, < 3) + jwt (>= 2.1.0, < 4) + logger (>= 1.6, < 2.0) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3) naturally (~> 2.2) + nkf (~> 0.2) optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.5) @@ -112,30 +123,30 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.4.1) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-sirp (1.0.0) - sysrandom (~> 1.0) + fastlane-sirp (1.1.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.3) + google-apis-core (0.18.0) addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) + googleauth (~> 1.9) + httpclient (>= 2.8.3, < 3.a) mini_mime (~> 1.0) + mutex_m representable (~> 3.0) retriable (>= 2.0, < 4.a) - rexml google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-playcustomapp_v1 (0.13.0) - google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.17.0) + google-apis-core (>= 0.15.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) google-cloud-core (1.8.0) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) + google-cloud-env (2.2.2) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) google-cloud-errors (1.5.0) google-cloud-storage (1.47.0) addressable (~> 2.8) @@ -145,9 +156,12 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) + google-logging-utils (0.2.0) + googleauth (1.16.2) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -157,17 +171,20 @@ GEM httpclient (2.8.3) jmespath (1.6.2) json (2.6.1) - jwt (2.10.2) + jwt (3.2.0) base64 logger (1.7.0) mini_magick (4.13.2) mini_mime (1.1.5) multi_json (1.15.0) multipart-post (2.4.1) + mutex_m (0.3.0) nanaimo (0.3.0) naturally (2.3.0) + nkf (0.2.0) optparse (0.6.0) os (1.1.4) + ostruct (0.6.3) plist (3.7.2) public_suffix (7.0.5) rake (13.0.6) @@ -189,7 +206,6 @@ GEM simctl (1.6.10) CFPropertyList naturally - sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3)