Skip to content

Remove legacy context APIs - #4248

Merged
thomhurst merged 3 commits into
mainfrom
issue-4227-context-cleanup
Aug 28, 2026
Merged

Remove legacy context APIs#4248
thomhurst merged 3 commits into
mainfrom
issue-4227-context-cleanup

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove legacy environment, file-system, and installer context APIs
  • rename the domain environment contract to IEnvironmentContext and expose rich Folder paths plus host metadata
  • migrate core DI, Azure Pipelines, Git versioning, tests, and architecture docs

Test plan

  • ModularPipelines.Tests.slnf Release build
  • Azure Pipelines solution Release build
  • Git solution Release build
  • 37 focused core tests (36 passed, 1 intentionally skipped)
  • Docusaurus production build
  • format touched solutions

Closes #4227

Summary by CodeRabbit

  • New Features

    • Expanded environment information with environment name, application and content directories, and strongly typed working-directory access.
    • Improved platform detection across Windows, Linux, and macOS.
    • Added validation for blank file and folder paths.
  • Changes

    • Updated environment, file, and installer access through revised context APIs.
    • Updated Git and Azure Pipelines integrations to use the latest context behavior.
  • Documentation

    • Refreshed architecture and context guidance to reflect the updated APIs.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cef4ce55-e675-4019-b024-ccaee80eb8c3

📥 Commits

Reviewing files that changed from the base of the PR and between b90314f and 0f564e5.

📒 Files selected for processing (7)
  • src/ModularPipelines.Git/GitVersioning.cs
  • src/ModularPipelines/Context/Domains/Implementations/EnvironmentContext.cs
  • src/ModularPipelines/Context/PredefinedInstallers.cs
  • src/ModularPipelines/FileSystem/Folder.cs
  • test/ModularPipelines.Git.UnitTests/GitVersioningTests.cs
  • test/ModularPipelines.UnitTests/Helpers/EnvironmentContextTests.cs
  • test/ModularPipelines.UnitTests/Helpers/PredefinedInstallersTests.cs

Included review availability: Your plan provides up to 4 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR consolidates context APIs for v4. It renames and expands the environment context, removes duplicate legacy contexts, updates file and installer access, migrates consumers, and updates tests and documentation.

Changes

Context API consolidation

Layer / File(s) Summary
Environment contract and propagation
src/ModularPipelines/Context/Domains/*, src/ModularPipelines/Context/*, test/ModularPipelines.UnitTests/Context/*, test/ModularPipelines.UnitTests/Helpers/EnvironmentContextTests.cs, docs/architecture/interface-hierarchy.md
IEnvironmentDomainContext becomes IEnvironmentContext. Directory properties use Folder, and environment metadata is added. Pipeline and module contexts expose the renamed contract.
Legacy service removal and file access
src/ModularPipelines/Context/Domains/Implementations/FilesContext.cs, src/ModularPipelines/Context/IEnvironmentContext.cs, src/ModularPipelines/Context/IFileSystemContext.cs, src/ModularPipelines/Context/IInstaller.cs, src/ModularPipelines/Context/Installer.cs, src/ModularPipelines/Context/FileSystemContext.cs, src/ModularPipelines/DependencyInjection/DependencyInjectionSetup.cs, test/ModularPipelines.UnitTests/Context/FilesContextTests.cs, test/ModularPipelines.UnitTests/Helpers/InstallerTests.cs
Legacy context services and their registrations are removed. FilesContext validates paths and creates wrappers through the provider. Installer tests use IInstallersContext.
Provider-backed temporary folders
src/ModularPipelines/FileSystem/Folder.cs, src/ModularPipelines.Git/GitVersioning.cs, test/ModularPipelines.Git.UnitTests/GitVersioningTests.cs
Folder adds provider-based temporary-folder creation. GitVersioning receives IFileSystemProvider and uses the new overload.
Consumer and platform migration
src/ModularPipelines.Azure.Pipelines/AzurePipeline.cs, src/ModularPipelines/Context/PredefinedInstallers.cs, src/ModularPipelines/IBuildSystemDetector.cs, test/ModularPipelines.UnitTests/Helpers/PredefinedInstallersTests.cs, test/ModularPipelines.UnitTests/Context/InterfaceVisibilityTests.cs
Azure variable access uses Environment.Variables. Installer platform checks use OSPlatform, and IBuildSystemDetector is internal.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to 0f564

The PR consolidates and removes public context APIs and changes PowerShell installer selection to treat all non-X86 processes as x64. It is mergeable with explicit owner awareness for external consumer migration/rollback coordination and for defining or validating behavior on ARM and other architectures.

Poem

A rabbit checks the context tree,
New folders bloom where paths once stood.
Old services hop away unseen,
OS platforms now guide the route,
Tests thump their paws: the shapes are good.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 44 functions across 24 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The changes satisfy the linked issue requirements for removing legacy environment, file-system, and installer APIs; renaming and extending the environment contract; migrating consumers; updating docum… Confirm that BuildSystemDetector is internal. If it is public, change its accessibility and add or update a visibility test for it.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: removal of legacy context APIs.
Out of Scope Changes check ✅ Passed The changes are related to the linked issue. The provider-aware temporary-folder update supports GitVersioning migration, and the tests and documentation changes support the API cleanup.
Full details: Linked Issues check

Explanation

The changes satisfy the linked issue requirements for removing legacy environment, file-system, and installer APIs; renaming and extending the environment contract; migrating consumers; updating documentation; and changing IBuildSystemDetector visibility. The provided summary does not confirm whether BuildSystemDetector is internal.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4227-context-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Greptile Summary

The PR removes legacy context APIs, consolidates environment and filesystem capabilities under domain contexts, and migrates dependent integrations and tests.

  • Renames and expands the environment domain contract with provider-backed Folder properties and host metadata
  • Removes legacy environment, filesystem, and installer context surfaces
  • Updates GitVersioning temporary-directory creation to use the configured filesystem provider
  • Migrates Azure Pipelines, dependency injection, tests, and architecture documentation

Confidence Score: 4/5

The PR is not yet safe to merge because GitVersioning can still bypass a configured filesystem provider and write GitVersion.yml to the host repository.

The temporary-folder fix preserves the injected provider, but the same GitVersioning operation constructs its configuration file through the system provider, leaving provider-isolated executions able to access and mutate the real filesystem.

Files Needing Attention: src/ModularPipelines.Git/GitVersioning.cs

Important Files Changed

Filename Overview
src/ModularPipelines.Git/GitVersioning.cs The temporary directory now retains the injected provider, but GitVersion.yml handling still bypasses it and can mutate the host filesystem.
src/ModularPipelines/FileSystem/Folder.cs Adds provider-aware temporary-folder creation used by the Git integration.
src/ModularPipelines/Context/Domains/Implementations/EnvironmentContext.cs Consolidates host and pipeline environment data into the renamed domain context with provider-backed Folder values.
src/ModularPipelines/Context/Domains/Implementations/FilesContext.cs Replaces the legacy filesystem context dependency with direct provider-backed path resolution.
src/ModularPipelines/DependencyInjection/DependencyInjectionSetup.cs Updates registrations for the consolidated context implementations and removed legacy services.

Comments Outside Diff (1)

  1. src/ModularPipelines.Git/GitVersioning.cs, line 113 (link)

    P1 Configuration bypasses filesystem provider

    When a pipeline or ModuleTester replaces IFileSystemProvider and invokes GitVersion without an existing GitVersion.yml, new File(string) uses the system provider for the existence check and write, causing the real host repository to be read or modified instead of the configured filesystem.

Reviews (5): Last reviewed commit: "fix(context): respect environment abstra..." | Re-trigger Greptile

Comment thread src/ModularPipelines.Git/GitVersioning.cs Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Reviewed the context-API cleanup (removal of IEnvironmentContext/IFileSystemContext/IInstaller legacy wrappers, rename of IEnvironmentDomainContextIEnvironmentContext, and the DI/test updates that follow). The consolidation itself is clean and the test/doc updates track the renames correctly. One regression stood out.

Blocking

GitVersioning now bypasses the pipeline's configured IFileSystemProvider

https://github.com/thomhurst/modularpipelines/blob/d6b0b16223079d68262040c34f25ef499581f759/src/ModularPipelines.Git/GitVersioning.cs#L44-L51

GitVersioning's constructor used to take IFileSystemContext and call fileSystemContext.CreateTemporaryFolder(), which internally used the DI-registered IFileSystemProvider. This PR replaces that with the static Folder.CreateTemporaryFolder(), which is hardcoded to SystemFileSystemProvider.Instance (see src/ModularPipelines/FileSystem/Folder.cs:506-515) and ignores whatever provider is registered in the container.

This matters because ModularPipelines.Testing.ModuleTester replaces IFileSystemProvider with an InMemoryFileSystemProvider for isolated module tests (src/ModularPipelines.Testing/ModuleTester.cs:162,186), and the framework generally supports swapping in custom IFileSystemProvider implementations. Any module depending on IGitVersioning will now silently touch the real disk during isolated tests instead of respecting the in-memory/fake provider, breaking the isolation the testing package is built around. (Independently flagged by the greptile-apps bot review on this PR as well.)

Suggested fix: keep injecting the provider abstraction (e.g. take IFileSystemProvider in GitVersioning's constructor and build the temp folder from it directly, the way FilesContext does now) rather than routing through the static Folder.CreateTemporaryFolder() helper.

Minor — abstraction consistency

Environment.Is64BitOperatingSystem used directly instead of the existing IEnvironmentContext.Architecture abstraction

https://github.com/thomhurst/modularpipelines/blob/d6b0b16223079d68262040c34f25ef499581f759/src/ModularPipelines/Context/PredefinedInstallers.cs#L122-L124

Powershell7Async reads _environmentContext.OperatingSystem (mockable, as used in PredefinedInstallersTests.cs) just above, but then falls back to the raw static System.Environment.Is64BitOperatingSystem for architecture instead of _environmentContext.Architecture, which already exists on the same interface and is exercised elsewhere in this PR's own test updates (EnvironmentContextTests.cs now asserts context.Architecture). Using the static call re-introduces an untestable dependency in a method that otherwise reads entirely through the injected context, and is inconsistent with the rest of the class. Not blocking, but worth tidying up while this file is already being touched for the domain-context migration.

Other notes

  • This PR currently shows as having a merge conflict with main (mergeStateStatus: DIRTY) — will need a rebase before merge, independent of the above.

@thomhurst
thomhurst force-pushed the issue-4227-context-cleanup branch from d6b0b16 to b90314f Compare August 28, 2026 12:33
@thomhurst

Copy link
Copy Markdown
Owner Author

Rebased onto current main after #4247. Resolved the environment API overlap by using the renamed domain Variables.Get/GetAll/Set surface. Core test solution and Azure Pipelines Release builds pass; EnvironmentContext tests pass 6/6.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Solid cleanup: the environment/file-system/installer context consolidation is internally consistent — I traced every deleted type (IEnvironmentDomainContext, IFileSystemContext, IInstaller, the old top-level IEnvironmentContext/EnvironmentContext) and confirmed no remaining references anywhere in src/test/docs, the DI registrations are deduplicated correctly (single IEnvironmentContext → domain EnvironmentContext), and the new IFilesContext.GetFile/GetFolder blank-path validation is covered by the new tests.

Finding

GitVersioning's temporary folder now silently bypasses the DI-configured IFileSystemProvider, breaking ModuleTester's in-memory filesystem isolation.

public GitVersioning(IGitInformation gitInformation, ICommandContext command, IModuleLoggerProvider moduleLoggerProvider)
{
_gitInformation = gitInformation;
_command = command;
_moduleLoggerProvider = moduleLoggerProvider;
_temporaryFolder = Folder.CreateTemporaryFolder();
}

Before this PR, GitVersioning took IFileSystemContext in its constructor and called fileSystemContext.CreateTemporaryFolder(), which resolved the temp path through the injected IFileSystemProvider. Now it calls the static Folder.CreateTemporaryFolder() (src/ModularPipelines/FileSystem/Folder.cs:506), which is hard-coded to SystemFileSystemProvider.Instance and has no way to honor a different provider.

This matters because IFileSystemProvider is registered with TryAddSingleton (DependencyInjectionSetup.cs:249) specifically so it can be swapped out — and ModularPipelines.Testing.ModuleTester does exactly that, replacing it with InMemoryFileSystemProvider (ModuleTester.cs:186) to give module tests filesystem isolation. Any module that depends on IGitVersioning and is exercised through ModuleTester will now have GitVersioning's constructor create a real directory on disk the moment it's resolved, regardless of the in-memory provider the test set up — silently defeating the isolation ModuleTester promises.

This is the same concern the Greptile bot flagged on this PR ("GitVersion still creates its tool directory through SystemFileSystemProvider.Instance..."), and it doesn't look like it's been addressed in the latest commit.

Suggested fix: inject IFileSystemProvider into GitVersioning and build the temp folder through it, mirroring how EnvironmentContext now correctly does new Folder(path, fileSystemProvider):

public GitVersioning(
    IGitInformation gitInformation,
    ICommandContext command,
    IModuleLoggerProvider moduleLoggerProvider,
    IFileSystemProvider fileSystemProvider)
{
    _gitInformation = gitInformation;
    _command = command;
    _moduleLoggerProvider = moduleLoggerProvider;

    var path = fileSystemProvider.Combine(
        fileSystemProvider.GetTempPath(),
        fileSystemProvider.GetRandomFileName().Replace(".", string.Empty));
    fileSystemProvider.CreateDirectory(path);
    _temporaryFolder = new Folder(path, fileSystemProvider);
}

This restores the provider-aware behavior the deleted FileSystemContext.CreateTemporaryFolder() had, keeps GitVersioning consistent with the rest of the domain contexts in this PR (which all correctly thread IFileSystemProvider through), and preserves ModuleTester's filesystem-isolation guarantee for any module under test that depends on Git versioning.

(Note: PredefinedInstallers.cs and TempFolder.cs already call the same static Folder.CreateTemporaryFolder() — that's pre-existing and out of scope here; flagging only because this PR is the one that newly introduced this pattern into GitVersioning, which previously was provider-aware.)

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed the current-head Claude blocking review in b7058c9: GitVersioning temp-folder creation is provider-aware again, preserving ModuleTester in-memory filesystem isolation. The matching Greptile thread has the implementation and validation details.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Re-reviewed at head b7058c96. The previously-flagged blocking issue — GitVersioning bypassing the DI-configured IFileSystemProvider — is now fixed: GitVersioning.cs:54 calls Folder.CreateTemporaryFolder(fileSystemProvider), correctly threading the injected provider through and preserving ModuleTester's in-memory filesystem isolation. Good fix.

Two issues remain, one carried over from the earlier review and unaddressed, one new:

1. Environment.Is64BitOperatingSystem still used directly instead of the IEnvironmentContext.Architecture abstraction (carried over, not addressed)

src/ModularPipelines/Context/PredefinedInstallers.cs:121-123:

if (operatingSystem == OSPlatform.Windows)
{
    var arch = Environment.Is64BitOperatingSystem ? "x64" : "x86";

Powershell7Async reads _environmentContext.OperatingSystem (mockable) on the line above, but falls back to the raw static System.Environment.Is64BitOperatingSystem for architecture instead of _environmentContext.Architecture — which this very PR introduced onto the interface and which EnvironmentContextTests.cs now exercises. This re-introduces an untestable, unmockable dependency in a method that otherwise reads entirely through the injected context, and is inconsistent with the rest of the class after the domain-context consolidation. It also means a test that mocks OperatingSystem == OSPlatform.Windows to exercise the 32-bit download URL branch can no longer control which URL is chosen, since bitness now comes from the real test-runner machine rather than the mockable context.

Suggested fix: var arch = _environmentContext.Architecture == Architecture.X64 ? "x64" : "x86"; (or equivalent mapping), consistent with how OperatingSystem is read two lines above.

2. EnvironmentContext's Folder properties now throw instead of degrading to null/empty on blank input

src/ModularPipelines/Context/Domains/Implementations/EnvironmentContext.cs:35-38:

WorkingDirectory = new Folder(workingDirectory.Path, fileSystemProvider);
AppDomainDirectory = new Folder(AppDomain.CurrentDomain.BaseDirectory, fileSystemProvider);
ContentDirectory = new Folder(hostEnvironment.ContentRootPath, fileSystemProvider);

The internal Folder(string, IFileSystemProvider) constructor (Folder.cs:41) passes the path straight into new DirectoryInfo(path) with no null/blank guard, unlike the public implicit string -> Folder? conversion operator (Folder.cs:524-532), which returns null for a null/empty path. Previously, ContentDirectory = _hostEnvironment.ContentRootPath!; went through that implicit conversion and degraded gracefully. Now, if a consumer replaces IHostEnvironment via the public PipelineBuilder.Services DI surface with one that returns a null/empty ContentRootPath (the default PipelineBuilder.CreateHostEnvironment path always guarantees non-blank values via FirstNonEmpty(...), so this is only reachable through a custom override), construction now throws ArgumentException/ArgumentNullException instead of yielding a null/empty Folder.

This is a low-probability edge case given the default construction path is safe, but it's a behavior change worth being deliberate about — either guard the three call sites the same way the old implicit conversion did, or confirm a hard failure is the intended contract now that these properties are non-nullable Folder (as opposed to the old nullable-friendly string conversion).

Other notes

Everything else in the consolidation checks out: I traced the renamed/removed symbols (EnvironmentVariablesVariables, OperatingSystemIdentifierOSPlatform, GetFolder(SpecialFolder) removal, IBuildSystemDetector becoming internal) across src/test/docs and found no stale references. Test coverage for the deleted FileSystemContextTests.cs behaviors is still present via FileTests.cs/FolderTests.cs/FolderProviderTests.cs/FileProviderTests.cs, so that removal isn't a coverage loss.

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed the current-head blocking review in b0b4ce0320:

  • Powershell7Async now selects x86/x64 from the injected IEnvironmentContext.Architecture; regression coverage proves mocked X86 and X64 values control the MSI URL.
  • ContentDirectory is intentionally non-nullable. EnvironmentContext now explicitly rejects a null/empty/whitespace custom ContentRootPath before constructing the Folder, with regression coverage for the blank-path contract.

Validation: focused installer tests 4/4, environment-context tests 7/7, and guarded ModularPipelines.Tests.slnf Release build passed (0 errors; existing test-fixture nullability warnings remain).

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Re-reviewed at head b0b4ce0320. Both issues from the previous review round are now resolved:

  1. Environment.Is64BitOperatingSystemIEnvironmentContext.ArchitecturePredefinedInstallers.cs:123 now reads _environmentContext.Architecture instead of the raw static call, restoring mockability. Covered by the new Powershell7_On_Windows_Uses_Context_Architecture theory in PredefinedInstallersTests.cs, which asserts both X86 and X64 select the correct MSI URL.
  2. EnvironmentContext's Folder properties throwing on blank input — the author chose to make this an explicit, deliberate contract: EnvironmentContext.cs now calls ArgumentException.ThrowIfNullOrWhiteSpace(contentRootPath) before constructing ContentDirectory, rather than silently degrading. This is a reasonable resolution given ContentDirectory is now non-nullable, and it's covered by the new Constructor_Rejects_Blank_Content_Root test.

I also re-traced the rest of the consolidation at this head: IBuildSystemDetector is now correctly internal (resolving the CodeRabbit pre-merge inconclusive check about its accessibility), GitVersioning continues to thread IFileSystemProvider through Folder.CreateTemporaryFolder(provider) with dedicated coverage in the new GitVersioningTests.cs, FilesContext.GetFile/GetFolder validate blank paths via ArgumentException.ThrowIfNullOrWhiteSpace, and the LegacyContextInterfaces_ShouldNotExist test now asserts IEnvironmentContext/IFileSystemContext/IInstaller no longer exist under the old ModularPipelines.Context namespace. No stale references to any removed/renamed types (IEnvironmentDomainContext, EnvironmentDomainContext, IFileSystemContext, IInstaller, OperatingSystemIdentifier) remain anywhere in src, test, or docs.

No new issues found in this round.

Unify environment and file APIs under domain contexts.\n\nRefs #4227
Keep ModuleTester filesystem isolation intact. Refs #4227.
Use the injected architecture for PowerShell selection and make the non-null content-root contract explicit.\n\nRefs #4227
@thomhurst
thomhurst force-pushed the issue-4227-context-cleanup branch from b0b4ce0 to 0f564e5 Compare August 28, 2026 14:00
@thomhurst
thomhurst merged commit 94026d8 into main Aug 28, 2026
16 checks passed
@thomhurst
thomhurst deleted the issue-4227-context-cleanup branch August 28, 2026 14:33
thomhurst added a commit that referenced this pull request Aug 28, 2026
Track the context API consolidation merged in #4248 after rebasing the public API gate.
thomhurst added a commit that referenced this pull request Aug 28, 2026
Track the context API consolidation merged in #4248 after rebasing the public API gate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant