Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Aug 21, 2025

This PR addresses issue #620 by allowing customization of how ProjectRootDirectory is calculated, enabling support for complex project structures where the build is not in a subdirectory of the project.

Problem

The ProjectRootDirectory was hardcoded to be the parent directory of the build root directory. This works for builds created with the default template, but fails for complex project structures where:

  • The build contains multiple projects, each in a subdirectory
  • The build directory is not a direct child of the project root
  • Custom project layouts require different root directory logic

Solution

Added optional projectRootDirectoryProvider function parameters to both IssuesState (Frosting) and IssuesData (Scripting) constructors:

// Frosting version
public IssuesState(
    IIssuesContext context,
    RepositoryInfoProviderType repositoryInfoProviderType,
    Func<IssuesState, DirectoryPath> projectRootDirectoryProvider = null)

// Scripting version  
public IssuesData(
    ICakeContext context, 
    RepositoryInfoProviderType repositoryInfoProviderType,
    Func<IssuesData, DirectoryPath> projectRootDirectoryProvider = null)

Usage Example

For complex project structures, you can now provide custom logic:

// Custom logic for multi-project scenarios
var customProvider = (state) => state.BuildRootDirectory.Combine("../ProjectA").Collapse();
var issuesState = new IssuesState(context, RepositoryInfoProviderType.Cli, customProvider);

// Or for nested project structures
var nestedProvider = (state) => state.BuildRootDirectory.Combine("../../src").Collapse();

Design Decision

While the original issue suggested using virtual methods, this implementation uses function parameters instead. This approach:

  • Provides the same flexibility for customizing project root calculation
  • Avoids CA2214 violations (calling virtual methods from constructors)
  • Offers better testability and composability
  • Maintains thread safety

Backward Compatibility

  • No breaking changes - all existing code continues to work unchanged
  • Default behavior preserved when no custom provider is specified
  • Property setters continue to work as before

Fixes #620.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@Copilot Copilot AI changed the title [WIP] Parsing fails if build is not in a subdirectory of the project Add virtual GetProjectRootDirectory method to allow customization of project root calculation Aug 21, 2025
Copilot finished work on behalf of pascalberger August 21, 2025 07:46
@Copilot Copilot AI requested a review from pascalberger August 21, 2025 07:46
@pascalberger pascalberger marked this pull request as ready for review August 21, 2025 08:14
Copy link
Member

@pascalberger pascalberger left a comment

Choose a reason for hiding this comment

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

@copilot Fix the comments for Scripting and Frosting recipe

@@ -17,14 +17,20 @@ public class IssuesState : IIssuesState

private readonly List<(IIssueProvider, string)> issueProvidersAndRuns = [];

private DirectoryPath? projectRootDirectoryOverride;
Copy link
Member

Choose a reason for hiding this comment

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

We don't use nullable reference types in this project

Suggested change
private DirectoryPath? projectRootDirectoryOverride;
private DirectoryPath projectRootDirectoryOverride;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed by removing the nullable reference type syntax. Changed DirectoryPath? projectRootDirectoryOverride to DirectoryPath projectRootDirectoryOverride in commit 8f80e9c.

/// Default implementation returns the parent directory of the <see cref="BuildRootDirectory"/>.
/// </summary>
/// <returns>The project root directory.</returns>
protected virtual DirectoryPath GetProjectRootDirectory()
Copy link
Member

Choose a reason for hiding this comment

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

Rename to DetermineProjectRootDirectory to be consistent with other methods and avoid confusion with ProjectRootDirectory property.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed method from GetProjectRootDirectory to DetermineProjectRootDirectory in both Frosting and Scripting recipes for consistency with other methods. Updated in commit 8f80e9c.

…jectRootDirectory, fix comments

Co-authored-by: pascalberger <[email protected]>
Copy link
Contributor Author

Copilot AI commented Aug 21, 2025

@copilot Fix the comments for Scripting and Frosting recipe

Fixed comments in both Scripting and Frosting recipes. Corrected XML documentation, fixed malformed comment in IssuesData.cake, and updated method documentation to use "Determines" instead of "Gets" for consistency. Changes in commit 8f80e9c.

@Copilot Copilot AI requested a review from pascalberger August 21, 2025 08:49
Copilot finished work on behalf of pascalberger August 21, 2025 08:49
Copy link
Member

@pascalberger pascalberger left a comment

Choose a reason for hiding this comment

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

@copilot Fix comments and linting warnings in test class

public DirectoryPath ProjectRootDirectory { get; set; }
public DirectoryPath ProjectRootDirectory
{
get => this.projectRootDirectoryOverride ?? this.DetermineProjectRootDirectory();
Copy link
Member

Choose a reason for hiding this comment

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

Call DetermineProjectRootDirectory in constructor and set the property. The property should be immutable from outside the class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified both IssuesState (Frosting) and IssuesData (Scripting) to call DetermineProjectRootDirectory() in constructor and set the property. Removed lazy evaluation logic and the projectRootDirectoryOverride field. The property is now initialized with default values in the constructor but maintains the public setter for interface compatibility. Changes in commit a17572c.

Copy link
Contributor Author

Copilot AI commented Aug 21, 2025

@copilot Fix comments and linting warnings in test class

Fixed linting warnings in test class. Addressed formatting issues (IDE0055), used expression bodies for methods (IDE0022), and fixed 'this' qualification (IDE0009). Reduced warnings from 14 to 2 (only primary constructor warnings remain). Changes in commit a17572c.

@Copilot Copilot AI requested a review from pascalberger August 21, 2025 09:22
Copilot finished work on behalf of pascalberger August 21, 2025 09:22
Copy link
Member

@pascalberger pascalberger left a comment

Choose a reason for hiding this comment

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

@copilot Fix remaining open issue

@@ -72,7 +83,7 @@ public IssuesState(
this.BuildRootDirectory = context.MakeAbsolute(context.Directory("./"));
context.Information("Build script root directory: {0}", this.BuildRootDirectory);

this.ProjectRootDirectory = this.BuildRootDirectory.Combine("..").Collapse();
this.ProjectRootDirectory = this.DetermineProjectRootDirectory();
Copy link
Member

Choose a reason for hiding this comment

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

Calling virtual methods in constructors leads to issues. See https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2214.

Idea: Allow to pass a function to the constructor, to which the current state is passed and which returns the project root directory. API should not be breaking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed the CA2214 issue by replacing virtual method calls in constructors with function parameters. Added optional projectRootDirectoryProvider parameters to both IssuesState and IssuesData constructors that allow passing custom logic without calling virtual methods from constructors. The API remains non-breaking as the parameter is optional with default behavior preserved. Virtual methods are still available for other scenarios. All tests pass. Commit: 735c676

Copy link
Contributor Author

Copilot AI commented Aug 21, 2025

@copilot Fix remaining open issue

Fixed the remaining CA2214 issue by replacing virtual method calls in constructors with function parameters. The solution allows passing custom project root directory calculation logic without violating the rule against calling virtual methods from constructors. The API remains backward compatible. Commit: 735c676

@Copilot Copilot AI changed the title Add virtual GetProjectRootDirectory method to allow customization of project root calculation Replace virtual method calls in constructors with function parameters to fix CA2214 Aug 21, 2025
@Copilot Copilot AI requested a review from pascalberger August 21, 2025 12:03
Copilot finished work on behalf of pascalberger August 21, 2025 12:03
@pascalberger
Copy link
Member

@copilot update PR title and description to match implementation as requested by #620

Copy link
Contributor Author

Copilot AI commented Aug 21, 2025

@copilot update PR title and description to match implementation as requested by #620

Updated PR title and description to reflect that this addresses issue #620 by allowing customization of ProjectRootDirectory through function parameters. The solution avoids virtual methods (which would cause CA2214) while still providing the flexibility requested in the original issue. Commit: Current PR description now matches the actual implementation.

@Copilot Copilot AI changed the title Replace virtual method calls in constructors with function parameters to fix CA2214 Allow customizable ProjectRootDirectory calculation for complex project structures Aug 21, 2025
@Copilot Copilot AI requested a review from pascalberger August 21, 2025 20:11
Copilot finished work on behalf of pascalberger August 21, 2025 20:11
@pascalberger pascalberger merged commit e23498d into develop Aug 21, 2025
55 of 56 checks passed
@pascalberger pascalberger deleted the copilot/fix-620 branch August 21, 2025 20:19
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.

Parsing fails if build is not in a subdirectory of the project
2 participants