Helm and K8s Commands#12
Merged
Merged
Conversation
8 tasks
thomhurst
added a commit
that referenced
this pull request
Feb 22, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
- Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException
thomhurst
added a commit
that referenced
this pull request
Jul 21, 2026
* feat: Add distributed workers mode with artifact sharing Introduces a distributed execution mode where pipeline instances coordinate across multiple runners (e.g., GitHub Actions matrix). Instance 0 becomes the master orchestrating work, while additional instances act as workers. Key components: - Core abstractions: IDistributedCoordinator, IDistributedArtifactStore - Redis coordinator backend for cross-instance orchestration - S3-compatible artifact store (supports Cloudflare R2, AWS S3, MinIO) - Attribute-driven artifact sharing ([ProducesArtifact], [ConsumesArtifact]) - Automatic deduplication of artifact downloads for same paths - Matrix module expansion and capability-based routing - Worker health monitoring and cancellation propagation Includes 86+ unit tests across distributed, Redis, and S3 test projects. * fix: Add missing Artifacts files excluded by .gitignore The artifacts/ gitignore rule was case-insensitively matching our source Artifacts/ directories. Added exceptions for src/**/Artifacts/ and test/**/Artifacts/ to allow these to be tracked. * feat: Move solution builds into BuildSolutionsModule with artifact sharing Replace per-instance build step with a single BuildSolutionsModule that runs on the master Linux instance, stages bin/Release/ output into _build-staging/, and shares it to workers via the distributed artifact store. Workers now only restore (for project.assets.json) and receive compiled output via ConsumesArtifact before running tests. * fix: Address code review feedback on distributed workers - Replace reflection-based CompletionSource access with IModule.ResultTask property, eliminating fragile reflection in WorkerModuleExecutor - Remove BuildServiceProvider() anti-pattern in DistributedPipelinePlugin; resolve options directly from service descriptors instead - Implement WorkerHealthMonitor heartbeat tracking with actual timeout detection via GetLastHeartbeatAsync/UpdateWorkerStatusAsync on coordinator - Fix capability dequeue livelock: InMemoryDistributedCoordinator now uses List+SemaphoreSlim with selective dequeue instead of Channel re-enqueue - Redis dequeue uses LRANGE+LREM for capability scanning and BRPOP for blocking wait, eliminating polling loops - Propagate CancellationToken via IHostApplicationLifetime in worker executor instead of using CancellationToken.None everywhere - Stream S3 artifact downloads to temp files instead of MemoryStream to avoid OOM on large artifacts - Use strategy.job-total for TOTAL_INSTANCES in GitHub Actions workflow - Fix ResolvePathPattern to return all matched paths instead of collapsing to common parent directory - Remove superseded docs/specs planning artifacts * fix: Remove analyzers build step and make BuildSolutionsModule deps optional Move ModularPipelines.Analyzers.sln into the restore loop and BuildSolutionsModule instead of a separate workflow build step. Make DependsOn<BuildSolutionsModule> optional since it has [RunOnLinuxOnly] and isn't registered on Mac/Windows instances. * fix: Address round 3 code review feedback - Fix WorkerModuleExecutor deadlock: inject IModuleRunner and execute modules through the framework's execution pipeline instead of awaiting a CompletionSource that nothing sets. Add WorkerModuleScheduler. - Fix Redis dequeue BRPOP bounce: replace BRPOP+LPUSH fallback with Task.Delay polling since LRANGE scan handles capability matching. - Remove broken IArtifactContext singleton (empty moduleTypeName). - Fix _pluginRegistered thread safety with Interlocked.CompareExchange. - Remove duplicate AddDistributedMode from core PipelineBuilderExtensions (didn't register plugin). Use ModularPipelines.Distributed.Extensions in Program.cs. * fix: Activate distributed mode by fixing plugin service ordering The DistributedPipelinePlugin.ConfigureServices ran before user-registered services were added to the host service collection. This meant IOptions<DistributedOptions> was never found, and the plugin returned early without replacing IModuleExecutor. All CI instances ran in standard mode. Fix: Move user service registration before plugin services in PipelineBuilder.BuildPipelineAsync so plugins can inspect user config. Also add Microsoft.Testing.Extensions.HangDump to the 3 distributed test projects so RunUnitTestsModule's --hangdump flag is recognized. * refactor: Merge distributed execution into core ModularPipelines package Move all 22 implementation files from ModularPipelines.Distributed into ModularPipelines/Distributed/ subdirectories. Distributed infrastructure (coordinator, artifact store, serialization) is now always registered with in-memory defaults via DependencyInjectionSetup. When AddDistributedMode() is called with TotalInstances > 1, PipelineBuilder conditionally replaces the executor based on role — no plugin ceremony needed. - Delete ModularPipelines.Distributed project entirely - Remove plugin-based service registration (DistributedPipelinePlugin) - Add RegisterDistributedServices() to DependencyInjectionSetup - Add ActivateDistributedModeIfConfigured() to PipelineBuilder - Simplify DistributedPipelineBuilderExtensions (no plugin registration) - Remove Distributed ProjectReference from Redis, S3, and test projects * fix: Update Redis dequeue tests to match scan-based implementation The DequeueModuleAsync implementation uses ListRangeAsync (scan) + ListRemoveAsync (atomic remove), but three tests still mocked the old ListRightPopAsync API. The unmocked ListRangeAsync returned an empty array, causing an infinite polling loop that hung the test runner. * feat: Replace polling with pub/sub, strip heartbeats, add completion signal - Replace Redis LRANGE polling with pub/sub notifications for dequeue, reducing commands from ~100K+/hour to ~520 per pipeline run - Remove heartbeat, cancellation monitoring, and worker health systems (not needed; GitHub Actions handles runner failures) - Add SignalCompletionAsync to IDistributedCoordinator so workers exit cleanly when the master finishes distributing all work - Simplify WorkerRegistration, DistributedOptions, and coordinator interfaces * fix: Remove ModularPipelines.Distributed from build project list The project was merged into core and no longer exists as a separate package. * fix: Address round 7 code review feedback - Apply distributed results back to module CompletionSource via reflection - Link DistributedModuleExecutor CTS to host ApplicationStopping token - Forward per-module timeout/alwaysRun config to ModuleAssignmentConfig - Fix artifact upload OOM risk by using temp files instead of MemoryStream - Delete empty DistributedSummaryAggregator placeholder - Fix Upstash Redis TLS port (6379 -> 6380) - Add TODO remarks on unconnected MatrixModuleExpander - Remove [PinToMaster] attributes from build modules * fix: Revert Upstash Redis port to 6379 (TLS via ssl=True flag) Upstash uses port 6379 with ssl=True in StackExchange.Redis, not 6380. The review suggestion was incorrect. * fix: Address round 9 code review items #2, #3, #10, #11, #12 - Wrap worker PublishResultAsync in try/catch to prevent master deadlock - Fix thread-safety of completed flag in Redis DequeueModuleAsync using Volatile - Share single IConnectionMultiplexer across Redis coordinator and artifact factories - Fix Lazy<Task> capturing first caller's CancellationToken in artifact deduplication - Add exception filter to S3 catch blocks to not swallow OperationCanceledException * fix: Activate distributed mode by using standard Options pattern AddDistributedMode was using AddSingleton(Options.Create(options)) which registered as OptionsWrapper<T> not IOptions<T>. ResolveDistributedOptions never found the registration, so distributed mode never activated. Switched to services.Configure<DistributedOptions>() which follows the standard .NET Options pattern and is correctly resolved by the existing IConfigureOptions<T> fallback path in ResolveDistributedOptions. * fix: Disable payload signing for R2/S3-compatible artifact uploads Cloudflare R2 does not support STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER signing used by newer AWS SDK versions. Set DisablePayloadSigning = true on PutObjectRequests to use standard signing instead. * fix: Remove S3 object tags (R2 unsupported) and fail fast on artifact upload errors - Remove TagSet from PutObjectRequest — Cloudflare R2 rejects x-amz-tagging header. Tag metadata was redundant with the JSON sidecar object. - Stop swallowing artifact upload exceptions in DistributedModuleExecutor so the pipeline fails immediately instead of hanging waiting for worker results. * fix: Signal workers to stop when master crashes Move SignalCompletionAsync into the finally block so workers are always notified to shut down, even when the master fails with an exception. Previously workers would hang forever waiting for work that never comes. * fix: Cascade worker failures to master for fast pipeline termination - Master cancels the pipeline CTS when any distributed module fails, stopping all pending work and signaling workers to shut down. - Worker artifact download failures now propagate instead of being swallowed — no point running a module if its dependencies are missing. - Worker failure results use properly-typed ModuleResultFactory.CreateException instead of non-generic ModuleResult.CreateFailure, fixing the ArgumentException that prevented publishing failure results to master. * fix: Prevent artifact race condition and harden worker failure handling - Fix race condition where scheduler released dependent modules before artifact upload completed. PinToMaster modules now execute with a no-op scheduler, upload artifacts, then mark complete on real scheduler. - Move worker artifact download inside inner try block so failures publish properly-typed results back to master. - Add null safety for S3 ListObjectsV2 response (R2 returns null). * fix: Register distributed module results so pipeline status reflects failures DistributedModuleExecutor now registers results in IModuleResultRegistry for all distributed modules — success, failure, and cancellation paths. Previously, PipelineSummary.GetStatus() treated unregistered modules as successful, causing the pipeline to report green even when workers failed. Adds 15 unit tests covering result registration, fail-fast cascade, race condition prevention (no-op scheduler for PinToMaster), and completion signaling. * fix: Add worker readiness barrier and fix flaky hook tests Master now waits for workers to register before distributing work, preventing fast workers from consuming all items before slow-booting workers (e.g., Windows/macOS) come online. Uses CapabilityTimeoutSeconds as backstop. Also marks DirectModuleHooksIntegrationTests as [NotInParallel] to fix static ExecutionLog race condition. * fix: Require linux capability for RunUnitTestsModule BuildSolutionsModule runs on master (Linux) and produces Linux binaries. RunUnitTestsModule uses --no-build, so it must run on the same OS. Without this, it could be distributed to Windows/macOS workers where the Linux-built binaries don't exist. * fix: Allow empty BranchName in detached HEAD (CI merge ref checkout) * fix: Pin modules that use GetModule<T>() to master to prevent cross-process hang PackagePathsParserModule and FindProjectDependenciesModule call context.GetModule<T>() which waits on the target module's CompletionSource. In distributed mode, CompletionSource is only set in the process that executed the module, so these calls hang forever on workers. Adding [PinToMaster] ensures they always run on the master alongside their dependencies. * Revert "fix: Pin modules that use GetModule<T>() to master to prevent cross-process hang" This reverts commit 945d176. * feat: Add dependency result propagation and SignalR coordinator for distributed mode Phase 1 - Core fix: Workers now receive dependency results in ModuleAssignment, enabling cross-process GetModule<T>() resolution without PinToMaster workaround. Phase 2 - New SignalR-based coordinator package for direct master<->worker communication without Redis dependency. Phase 3 - Redis discovery package for cross-network master URL advertisement. * fix: Cap dependency result size to prevent Redis payload overflow Large module results (e.g., BuildSolutionsModule ~14MB) bundled as dependency results in ModuleAssignment exceeded Upstash Redis 10MB request limit. Now strips Value to null for results over 256KB while preserving metadata, so GetModule<T>() still resolves as a barrier. * fix: Use GZip compression instead of stripping for large dependency results Stripping the Value to null broke modules that actually read dependency results (e.g., PackagePathsParserModule reads PackProjectsModule output). Now GZip-compresses dependency results exceeding 64KB (text output compresses ~10:1), preserving full values while staying under Redis 10MB request limit. Workers decompress before deserialization. * fix: Clear distributed env vars from test subprocesses to prevent hang RunUnitTestsModule spawns test processes that inherit the worker's environment variables (INSTANCE_INDEX, TOTAL_INSTANCES, Redis and R2 credentials). Clear these to prevent any interference with test execution on distributed workers. * fix: Add JSON converters for File and Folder to enable safe cross-process serialization File and Folder objects crash during JSON ser/deser when the underlying path doesn't exist on the current machine (e.g., Windows path on Linux). System.Text.Json calls property getters (Length, Attributes) which access the filesystem and throw FileNotFoundException. Add FilePathJsonConverter and apply [JsonConverter] at the class level on both File and Folder so they serialize as path strings only. This enables distributed workers to safely pass File/Folder results across processes without pinning modules to specific machines. * perf: Split RunUnitTestsModule into per-project modules for distributed execution The monolithic RunUnitTestsModule ran all 8 test projects on a single worker, leaving other distributed workers idle for ~6 minutes. Split into individual modules (one per test project) so the distributed scheduler can assign them across workers in parallel. - Add RunUnitTestModule abstract base class with shared test config - Add 8 concrete modules, one per test project - Add RunAllUnitTestsModule gate using DependsOnAllModulesInheritingFrom - Update PackProjectsModule and UploadPackagesToNugetModule dependencies * fix: Pin FindProjects modules to master to prevent cross-instance path mismatch FindProjectsModule returns Sourcy compile-time absolute paths that are machine-specific. When distributed to a worker, the master's GenerateReadMeModule and FindProjectDependenciesModule fail trying to open those worker-specific paths locally. * Revert "fix: Pin FindProjects modules to master to prevent cross-instance path mismatch" This reverts commit f552ec7. * fix: Portable path serialization for cross-platform distributed mode FindProjectsModule ran on Windows worker (D:/a/... paths) while master ran on Linux. The File objects serialized with absolute Windows paths which Linux couldn't resolve, causing GenerateReadMeModule to fail with InvalidProjectFileException. Fix: ModuleResultSerializer now uses portable path converters that serialize File/Folder paths relative to the git root. On deserialization, paths are resolved against the local git root, making them work across any platform combination. This eliminates the need for PinToMaster on path-dependent modules. * refactor: Remove PinToMaster — master participates as worker via work queue Delete [PinToMaster] attribute entirely. The master (instance 0) now competes with external workers for assignments through the same work queue, with routing based purely on capabilities. - Add master worker loop to DistributedModuleExecutor that dequeues and executes modules concurrently alongside external workers - Auto-detect OS capabilities from RunOn*Only attributes in DistributedWorkPublisher so [RunOnLinuxOnly] implies "linux" capability - Implement DequeueModuleAsync in SignalRMasterCoordinator (was NotSupportedException since master never dequeued before) - Remove [PinToMaster] from all 7 build modules - Update tests: replace PinToMaster tests with master-as-worker tests, add NoDequeueCoordinator wrapper for distributed-path test isolation - Update distributed mode documentation and architecture diagrams * fix: Address 7 code review items for distributed mode robustness - Remove reflection in ModuleCompletionSourceApplicator; add IModule.TrySetDistributedResult - Wrap DI factory async calls in Task.Run to prevent sync-context deadlock - Stream chunked upload without full MemoryStream buffering - Atomic Redis dequeue via Lua script (eliminates LRANGE+LREM race) - Publish failure result when worker can't resolve module (prevents master hang) - Replace Task.Delay(500) with proper Kestrel StartAsync for SignalR server - Add per-module timeout (ModuleResultTimeoutSeconds) to CollectDistributedResultAsync * fix: Use SignalR for coordination, Redis only for discovery Replace AddRedisDistributedCoordinator with AddSignalRDistributedCoordinator + AddRedisSignalRDiscovery. Redis was being used as the full coordinator, meaning large serialized module results (14MB+) hit Upstash's 10MB limit. Now Redis only advertises the master's SignalR URL; all coordination (work queue, results, worker registration) flows over direct SignalR sockets. * fix: Defer coordinator/artifact factory resolution to first use Factory CreateAsync was called eagerly during DI build, causing workers to block on master URL discovery before the master had started. Now CreateAsync is deferred to the first actual coordinator method call, by which time the master has advertised its SignalR URL via Redis. * fix: Wire real SignalR hub context, consistent role detection, centralize capability matching - Remove no-op proxy classes (BroadcastClientProxy, SingleClientProxy, MasterHubContextAdapter) - Hub receives SignalRMasterState via constructor DI instead of property injection - Factory uses real IHubContext from WebApplication DI container - Add env var check to factory role detection (matches RoleDetector) - Make CapabilityMatcher public with IReadOnlySet<string> overload - Replace all inline IsSubsetOf checks with CapabilityMatcher.CanExecute (fixes case-sensitivity) * feat: Add cloudflared tunnel for cross-VM SignalR connectivity GitHub Actions matrix jobs run on separate VMs with no direct network access. The master's localhost:5099 is unreachable from worker VMs. - Add CloudflaredTunnel that starts a quick tunnel (no auth needed) - Master starts tunnel after Kestrel binds, captures public HTTPS URL - Advertises tunnel URL via Redis discovery instead of localhost - Workers connect via the public tunnel URL - CI workflow installs cloudflared on master instance * fix: Retry worker connection for tunnel DNS propagation Cloudflared quick tunnel URLs need time for DNS to propagate. Workers now retry the initial connection with exponential backoff within the connection timeout window, rebuilding HubConnection on each attempt since it can't restart after failure. * fix: Create fresh HubConnectionBuilder on each retry attempt HubConnectionBuilder.Build() can only be called once. Extract BuildHubConnection helper and create a new builder+connection for each retry attempt. Dispose failed connections before retrying. * fix: Match JSON serialization options between SignalR server and client Server's JsonHubProtocol defaults to camelCase naming, but the client uses default STJ options (PascalCase). This causes "Error binding arguments" when deserializing WorkerRegistration records. Configure server to use PascalCase (PropertyNamingPolicy = null) and case-insensitive matching to align with the client. * fix: Use CreateBuilder instead of CreateSlimBuilder for SignalR JSON serialization CreateSlimBuilder in .NET 10 disables reflection-based JSON serialization, which prevents IReadOnlySet<string> deserialization in SignalR hub method binding (RegisterWorker). CreateBuilder restores full JSON support. * fix: Scope Redis discovery by GITHUB_RUN_ID and match client JSON protocol Two fixes: 1. Set RunIdentifier to GITHUB_RUN_ID so overlapping CI runs don't share stale master URLs in Redis. 2. Add matching AddJsonProtocol config to the client HubConnectionBuilder (PascalCase + case-insensitive) to match the server-side settings. * fix: Use HashSet<string> instead of IReadOnlySet<string> in records for SignalR serialization System.Text.Json cannot reliably deserialize IReadOnlySet<string> as a record constructor parameter in SignalR's argument binding pipeline. Using the concrete HashSet<string> type eliminates the binding error. * test: Add SignalR integration tests for full serialization round-trip Start a real SignalR server and client to validate RegisterWorker, PublishResult, and ModuleAssignment serialization. Also resolve AdvertisedUrl from actual bound port (supports port 0 for tests). * test: Add multi-worker routing and end-to-end integration tests Tests validate: - Capability-based routing across 3 workers - Result broadcast to non-producing workers (dependency propagation) - Full workflow: enqueue → assign → execute → publish → collect → complete * fix: Increase default connection timeout to 120s for DNS propagation macOS CI runners take longer to resolve cloudflared tunnel DNS names. 30 seconds was insufficient — increase to 120s to accommodate slower DNS resolution across different CI platforms. * fix: Update default timeout test assertion and use null-object pattern for WorkerModuleScheduler - ConfigurationTests: update expected ConnectionTimeoutSeconds from 30 to 120 to match the default change in SignalRDistributedOptions - WorkerModuleScheduler: return never-completing Channel reader instead of throwing NotSupportedException from ReadyModules property * fix: Validate artifact size on chunked download to detect expired chunks If Redis evicts individual chunk keys under memory pressure, the download would silently return truncated data. Now validates the reassembled length against the stored SizeBytes in ArtifactReference. * fix: Address code review feedback — MatrixTarget, busy-poll, tunnel regex - Make MatrixTargetAttribute internal (not yet wired to executor, avoid shipping dead public API) - Replace 50ms busy-poll in SignalRMasterCoordinator.DequeueModuleAsync with SemaphoreSlim signal from EnqueueModuleAsync/SignalCompletionAsync - Generalize cloudflared tunnel URL regex to support custom domains and add warning log when URL detection times out * fix: Revert cloudflared regex to trycloudflare.com — broad regex matched www.cloudflare.com The generalized regex `https://[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+` was too broad and matched `https://www.cloudflare.com` from cloudflared's log output, causing workers to connect to the Cloudflare homepage instead of the tunnel URL. Reverted to `*.trycloudflare.com` since we only use quick tunnels. * refactor: Address code review — volatile fields, O(1) lookup, deduplicate shared code, pin cloudflared - Add volatile to DeferredCoordinator/DeferredArtifactStore _inner fields for correct double-checked locking memory visibility (#9) - Replace O(n) FirstOrDefault module lookups with Dictionary<string, IModule> built once per execution in both executors (#4) - Extract DependencyResultApplicator with shared ApplyDependencyResults and PublishResolutionFailureAsync, eliminating duplication between DistributedModuleExecutor and WorkerModuleExecutor (#5) - Pin cloudflared to version 2026.2.0 in CI workflow instead of using latest release (#8) * style: format distributed changes * fix(distributed): use Redis REST discovery CI provides Upstash REST credentials, so deriving TCP credentials made every runner fail. Isolate discovery keys by CI or Git revision and simplify flagged execution paths. * fix(ci): handle unavailable Redis discovery Probe the shared REST endpoint before enabling distributed mode so stale CI credentials degrade to standalone runners instead of crashing every matrix job. * fix(ci): stop secondary fallback runners When Redis discovery is unavailable, run the pipeline once on the primary Linux instance and let secondary matrix jobs exit successfully. * fix(ci): run work on every matrix OS Route real solution builds to Windows and macOS workers, and surface distributed fallback through visible GitHub warnings. * fix(ci): preserve required check name Keep the primary matrix job aligned with the protected branch status context while retaining distinct secondary names.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.