Skip to content

Fix findGlobalJsonFile boundary check for custom checkout paths (#21989)#22001

Open
rishabhmalikMS wants to merge 7 commits intomasterfrom
users/rishabhmalikMS/fix_dotnetcorecli
Open

Fix findGlobalJsonFile boundary check for custom checkout paths (#21989)#22001
rishabhmalikMS wants to merge 7 commits intomasterfrom
users/rishabhmalikMS/fix_dotnetcorecli

Conversation

@rishabhmalikMS
Copy link
Copy Markdown
Contributor

@rishabhmalikMS rishabhmalikMS commented Apr 15, 2026

Context

Fixes #21989

Regression introduced in v2.271 by PR #21892 ("fix global.json lookup at repository root"). Users running dotnet test with
Microsoft Testing Platform (MTP) and .slnx files get failures when using custom checkout paths (e.g., checkout: self with path:
in multi-repo pipelines).


Task Name

DotNetCoreCLIV2


Description

findGlobalJsonFile() uses Build.SourcesDirectory as the upper boundary when walking up from workingDirectory to find
global.json. With custom checkout paths (multi-repo checkout or checkout: self with path:), workingDirectory can resolve
outside Build.SourcesDirectory, causing the function to return null. This makes MTP detection fail, so the --solution flag is
not added to dotnet test, which newer .NET SDKs require for MTP projects.

Fix: Add a fallback chain for the search boundary:

  1. Build.SourcesDirectory (default — works for standard checkout)
  2. System.DefaultWorkingDirectory (covers single-checkout with custom path)
  3. Agent.BuildDirectory (covers multi-repo checkout where both above stay at /s)

Risk Assessment (Low)

  • The fix only changes behavior when workingDirectory is already outside Build.SourcesDirectory (currently broken — returns
    null). No change to existing working scenarios.
  • Fallback boundaries are progressively wider but all scoped to the pipeline's build directory.
  • All 50 existing unit tests pass unchanged.

Change Behind Feature Flag (No)

This is a bug fix for a regression. The current behavior (returning null when workingDirectory is outside SourcesDirectory) is
always wrong — there is no scenario where the broken behavior is desirable. A feature flag would leave affected users broken.


Tech Design / Approach

  • findGlobalJsonFile() boundary check now tries three well-known pipeline variables in order: Build.SourcesDirectory
    System.DefaultWorkingDirectoryAgent.BuildDirectory
  • Each is progressively wider but still scoped to the current build
  • The bottom-up search logic (while loop walking up directories) is unchanged
  • Agent.BuildDirectory is the tightest safe boundary that covers all possible checkout locations

Documentation Changes Required (No)

No user-facing documentation changes needed. The fix restores expected behavior.


Unit Tests Added or Updated (Yes)

  • New test: runTestsWithWorkingDirOutsideSourcesDir.ts — simulates the exact multi-repo checkout scenario from issue [BUG]: Solution Flag "--solution" is missing in DotNetCoreCLI #21989
    with Build.SourcesDirectory, System.DefaultWorkingDirectory, and Agent.BuildDirectory all set to different values
  • Updated: L0.ts — added test case verifying Agent.BuildDirectory fallback is used, global.json is found, and --solution
    flag is added
  • All 50 tests pass (49 existing + 1 new)

Additional Testing Performed


Logging Added/Updated (Yes)

  • Debug messages added for each fallback step (which boundary was used and why)
  • No sensitive data exposed
  • Uses tl.debug() consistent with existing logging

Telemetry Added/Updated (No)

No new telemetry needed — this is a boundary check fix in an existing function.


Rollback Scenario and Process (Yes)

Rollback: revert this commit. The change is isolated to findGlobalJsonFile() in dotnetcore.ts. Version bump to 2.273.0 allows
pinning to previous version if needed.


Dependency Impact Assessed and Regression Tested (Yes)

  • No new dependencies added
  • All 50 unit tests pass
  • Only dotnetcore.ts modified (task logic), no changes to shared packages

Checklist

When workingDirectory resolves outside Build.SourcesDirectory (e.g. multi-repo
checkout with custom path), findGlobalJsonFile() returned null, causing MTP
detection to fail and the --solution flag to be omitted for .slnx files.
Add a fallback chain for the search boundary:
1. Build.SourcesDirectory (default)
2. System.DefaultWorkingDirectory (covers single-checkout with custom path)
3. Agent.BuildDirectory (covers multi-repo checkout)

Added unit test for the multi-repo checkout scenario.
@rishabhmalikMS
Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

1 similar comment
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS rishabhmalikMS changed the title Fix findGlobalJsonFile boundary check for custom checkout paths Fix findGlobalJsonFile boundary check for custom checkout paths (#21989) Apr 15, 2026
When workingDirectory resolves outside Build.SourcesDirectory (e.g. multi-repo
checkout with custom path), findGlobalJsonFile() returned null, causing MTP
detection to fail and the --solution flag to be omitted for .slnx files.
Add a fallback chain for the search boundary:
1. Build.SourcesDirectory (default)
2. System.DefaultWorkingDirectory (covers single-checkout with custom path)
3. Agent.BuildDirectory (covers multi-repo checkout)

Added unit test for the multi-repo checkout scenario.
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS
Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS
Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

1 similar comment
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS
Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

1 similar comment
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS
Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

1 similar comment
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

@rishabhmalikMS rishabhmalikMS marked this pull request as ready for review April 22, 2026 04:54
@rishabhmalikMS rishabhmalikMS requested review from a team and tarunramsinghani as code owners April 22, 2026 04:54
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Contributor

@dassayantan24 dassayantan24 left a comment

Choose a reason for hiding this comment

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

.

@@ -147,7 +147,7 @@ export class dotNetExe {
}

private findGlobalJsonFile(): string | null {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can have single reporoot and no fallback

    private findGlobalJsonFile(): string | null {
     const boundary = path.resolve(
         tl.getVariable('Agent.BuildDirectory') || process.cwd()
     );
     let searchDir = path.resolve(this.workingDirectory || process.cwd());

     while (true) {
         const candidate = path.join(searchDir, 'global.json');
         if (tl.exist(candidate)) return candidate;

         const parentDir = path.dirname(searchDir);
         if (parentDir === searchDir) break;

         const rel = path.relative(boundary, parentDir);
         if (rel.startsWith('..') || path.isAbsolute(rel)) break;

         searchDir = parentDir;
     }
     return null;
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Solution Flag "--solution" is missing in DotNetCoreCLI

2 participants