Use three-dot diff in ProjectCapability detection workflow#52986
Use three-dot diff in ProjectCapability detection workflow#52986
Conversation
- Add comprehensive documentation for ProjectCapability items across all SDKs - Create GitHub Actions workflow to detect new capabilities in PRs - Update documentation index with link to project capabilities docs Co-authored-by: baronfel <573979+baronfel@users.noreply.github.com>
- Update SDK-PR-guide.md with section on adding ProjectCapabilities - Add comprehensive README to project-capabilities directory - Improve navigation and discoverability of documentation Co-authored-by: baronfel <573979+baronfel@users.noreply.github.com>
The target name in the source code has a typo (Declartions instead of Declarations). Document the actual name as it exists while noting the typo. Co-authored-by: baronfel <573979+baronfel@users.noreply.github.com>
Change from two-dot (..) to three-dot (...) diff syntax to show only PR changes without including unrelated target branch changes. This follows GitHub workflow best practices. Co-authored-by: baronfel <573979+baronfel@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces in-repo documentation for MSBuild ProjectCapability items across the SDKs in this repo and adds a GitHub Actions workflow that detects newly-added capabilities in PRs (using a three-dot diff) to prompt authors to update that documentation.
Changes:
- Add a Project Capabilities documentation landing page plus per-SDK capability docs.
- Update existing documentation indices/guidance to point to the new capability docs.
- Add a
pull_request_targetworkflow to detect newProjectCapabilityitems viagit diff ...and apply a label + reminder comment.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/detect-project-capabilities.yml |
Adds workflow to diff .targets changes and detect new ProjectCapability includes, then label/comment. |
documentation/README.md |
Links the new Project Capabilities documentation from the documentation index. |
documentation/project-docs/SDK-PR-guide.md |
Adds contributor guidance for documenting newly-added ProjectCapability items. |
documentation/project-docs/project-capabilities.md |
Adds an overview/entry point that links to each per-SDK capability doc. |
documentation/project-docs/project-capabilities/README.md |
Adds a readme describing the per-SDK docs and how to contribute/update them. |
documentation/project-docs/project-capabilities/Microsoft.NET.Sdk.md |
Documents capabilities defined by Microsoft.NET.Sdk targets. |
documentation/project-docs/project-capabilities/Microsoft.NET.Sdk.Web.md |
Documents capabilities defined by Web SDK project-system/design-time targets. |
documentation/project-docs/project-capabilities/Microsoft.NET.Sdk.Worker.md |
Documents capabilities defined by Worker SDK targets. |
documentation/project-docs/project-capabilities/Microsoft.NET.Sdk.Razor.md |
Documents capabilities defined by Razor SDK design-time targets. |
documentation/project-docs/project-capabilities/Microsoft.NET.Publish.md |
Documents publish-related capabilities added by Microsoft.NET.Publish.targets. |
documentation/project-docs/project-capabilities/Microsoft.NET.Build.Containers.md |
Documents container build capability(ies) added by container targets. |
| const { data: comments } = await github.rest.issues.listComments({ | ||
| owner, | ||
| repo, | ||
| issue_number | ||
| }); |
There was a problem hiding this comment.
issues.listComments is called without pagination; by default GitHub returns only the first page (typically 30 comments). On PRs with many comments, the existing reminder comment may not be found, causing duplicates to be posted instead of updated. Use Octokit pagination (or request per_page: 100 and iterate pages) when searching for an existing comment containing the identifier.
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number | |
| }); | |
| const comments = await github.paginate( | |
| github.rest.issues.listComments, | |
| { | |
| owner, | |
| repo, | |
| issue_number, | |
| per_page: 100 | |
| } | |
| ); |
| | Capability | Always Added | Conditional | Purpose | | ||
| |------------|--------------|-------------|---------| | ||
| | `CrossPlatformExecutable` | No | .NET Core executables | Indicates cross-platform executable | | ||
| | `NativeAOT` | No | .NET 8+ (non-WPF/WinForms) | Enables Native AOT UI features | | ||
| | `SupportsComputeRunCommand` | Yes | - | Supports customizable run protocol | | ||
| | `GenerateDocumentationFile` | Yes | - | Supports XML documentation generation | | ||
| | `SupportsHotReload` | No | C#/VB .NET 6+ | Enables Hot Reload functionality | | ||
| | `ReferenceManagerAssemblies` (removed) | N/A | .NET Core projects | Hides legacy reference UI | |
There was a problem hiding this comment.
The markdown table has an extra leading | (rows start with || ...), which renders as an unintended empty first column in GitHub Markdown. Change these rows to start with a single | so the table columns align correctly.
| ## Summary Table | ||
|
|
||
| | Capability | Always Added | Conditional | Purpose | | ||
| |------------|--------------|-------------|---------| | ||
| | `DotNetCoreWeb` | Yes | - | Primary web project identifier | | ||
| | `AspNetCore` | Yes | - | ASP.NET Core framework indicator | | ||
| | `Web` | Yes | - | General web project features | | ||
| | `AppServicePublish` | Yes | - | Azure App Service publishing | | ||
| | `AspNetCoreInProcessHosting` | No | .NET Core 3.0+ | IIS in-process hosting support | | ||
| | `SupportHierarchyContextSvc` | Yes* | Not in VS designtime | Hierarchy context service | | ||
| | `DynamicDependentFile` | Yes* | Not in VS designtime | Dynamic dependent file tracking | | ||
| | `DynamicFileNesting` | Yes* | Not in VS designtime | File nesting rules | | ||
| | `LocalUserSecrets` | No | When user secrets configured | User Secrets management | | ||
|
|
||
| \* These capabilities are added by the SDK if not already provided by Visual Studio's design-time targets. | ||
|
|
There was a problem hiding this comment.
The summary table has an extra leading | (it uses || at the start of rows), which renders as an empty first column in Markdown. Use a single leading | for each row to avoid misaligned columns.
| ## Summary Table | ||
|
|
||
| | Capability | Always Added | Conditional | Purpose | | ||
| |------------|--------------|-------------|---------| | ||
| | `DotNetCoreRazor` | Yes | - | Core Razor support indicator | | ||
| | `DotNetCoreRazorConfiguration` | No | Razor 3.0+ | Advanced language service config | | ||
| | `WebNestingDefaults` | Yes | - | Web-style file nesting | | ||
| | `SupportsTypeScriptNuGet` | Yes | - | TypeScript NuGet support | | ||
| | `SupportHierarchyContextSvc` | No | Non-Web SDK projects only | Hierarchy context service | | ||
| | `DynamicDependentFile` | No | Non-Web SDK projects only | Dependent file tracking | | ||
| | `DynamicFileNesting` | No | Non-Web SDK projects only | File nesting rules | | ||
|
|
There was a problem hiding this comment.
The markdown table in this section starts each row with ||, which creates an unintended blank first column when rendered on GitHub. Update the table rows to start with a single | so the table formats correctly.
| | Capability | Trigger Property | Minimum TFM | Purpose | | ||
| |------------|------------------|-------------|---------| | ||
| | `IsAotCompatible` | `IsAotCompatible=true` | .NET 7.0 | Library AOT compatibility marker | | ||
| | `IsTrimmable` | `IsTrimmable=true` or `IsAotCompatible=true` | .NET 6.0 | Library trim compatibility marker | | ||
| | `PublishAot` | `PublishAot=true` | .NET 7.0 | Native AOT compilation | | ||
| | `PublishReadyToRun` | `PublishReadyToRun=true` | .NET Core 3.0 | ReadyToRun pre-compilation | | ||
| | `PublishSingleFile` | `PublishSingleFile=true` | .NET Core 3.0 | Single-file bundling | | ||
| | `PublishTrimmed` | `PublishTrimmed=true` | .NET Core 3.0 | IL trimming | |
There was a problem hiding this comment.
In this table, the “Minimum TFM” column is potentially misleading: the capabilities in Microsoft.NET.Publish.targets are added based on publish-related properties, without any TFM gating at the point where the ProjectCapability items are created. Consider renaming the column (e.g., “Feature introduced” / “Recommended minimum TFM”) or clarifying that the capability is still added even when targeting older TFMs (and tooling/targets may warn/ignore accordingly).
| ## Summary Table | ||
|
|
||
| | Capability | Always Added | Conditional | Purpose | | ||
| |------------|--------------|-------------|---------| | ||
| | `NetSdkOCIImageBuild` | Yes | - | OCI/Docker container build support | | ||
|
|
There was a problem hiding this comment.
The summary table row starts with ||, which creates an extra empty first column in rendered Markdown. Use a single leading | for the table rows to keep the columns aligned.
| // Pattern to match ProjectCapability Include="..." | ||
| // Matches: <ProjectCapability Include="CapabilityName" /> | ||
| // Also handles: <ProjectCapability Include="CapabilityName">...</ProjectCapability> | ||
| const capabilityPattern = /<ProjectCapability\s+Include=["']([^"']+)["']/gi; | ||
|
|
||
| // Pattern to match ProjectCapability Remove="..." | ||
| // We'll track these separately but won't flag them as needing docs | ||
| const removePattern = /<ProjectCapability\s+Remove=["']([^"']+)["']/gi; |
There was a problem hiding this comment.
The regex used to detect newly-added capabilities only matches when the Include attribute immediately follows <ProjectCapability (e.g., <ProjectCapability Include="..." ...>). In this repo there are valid patterns where another attribute comes first (e.g., Condition before Include in Microsoft.NET.Publish.targets), which means this workflow will miss some new capabilities. Update the pattern to match Include anywhere within the opening tag (e.g., scan <ProjectCapability tags and then extract Include= regardless of attribute order).
| // Pattern to match ProjectCapability Include="..." | |
| // Matches: <ProjectCapability Include="CapabilityName" /> | |
| // Also handles: <ProjectCapability Include="CapabilityName">...</ProjectCapability> | |
| const capabilityPattern = /<ProjectCapability\s+Include=["']([^"']+)["']/gi; | |
| // Pattern to match ProjectCapability Remove="..." | |
| // We'll track these separately but won't flag them as needing docs | |
| const removePattern = /<ProjectCapability\s+Remove=["']([^"']+)["']/gi; | |
| // Pattern to match ProjectCapability Include="..." where Include can appear in any attribute order | |
| // Matches: <ProjectCapability Include="CapabilityName" />, <ProjectCapability Condition="..." Include="CapabilityName" />, etc. | |
| // Also handles: <ProjectCapability Include="CapabilityName">...</ProjectCapability> | |
| const capabilityPattern = /<ProjectCapability\b[^>]*\bInclude=["']([^"']+)["']/gi; | |
| // Pattern to match ProjectCapability Remove="..." where Remove can appear in any attribute order | |
| // We'll track these separately but won't flag them as needing docs | |
| const removePattern = /<ProjectCapability\b[^>]*\bRemove=["']([^"']+)["']/gi; |
| name: Detect New ProjectCapability Items | ||
|
|
||
| on: | ||
| # Use pull_request_target for safe execution from forks | ||
| # This runs in the context of the base branch, not the PR head | ||
| pull_request_target: | ||
| types: [opened, synchronize] | ||
| branches: | ||
| - main | ||
| - release/* | ||
|
|
There was a problem hiding this comment.
The PR title/description indicates this change is limited to switching git diff from two-dot to three-dot syntax, but this PR also adds substantial new documentation and a new workflow. Please update the PR title/description to reflect the full scope (or consider splitting into separate PRs) so reviewers and release notes tooling aren’t misled.
| ## Summary Table | ||
|
|
||
| | Capability | Always Added | Conditional | Purpose | | ||
| |------------|--------------|-------------|---------| | ||
| | `DotNetCoreWorker` | Yes | - | Primary worker service identifier | | ||
| | `SupportHierarchyContextSvc` | Yes | - | Hierarchy context service | | ||
| | `DynamicDependentFile` | Yes | - | Dynamic dependent file tracking | | ||
| | `DynamicFileNesting` | Yes | - | File nesting rules | | ||
| | `LocalUserSecrets` | Yes | - | User Secrets management | | ||
| | `WebNestingDefaults` | Yes | - | Web-style file nesting patterns | | ||
| | `DynamicFileNestingEnabled` | Yes | - | File nesting enabled flag | | ||
|
|
There was a problem hiding this comment.
The markdown summary table rows start with || ..., which produces an empty first column when rendered. Update the table to use a single leading | per row so the columns align as intended.
| ## Summary Table | ||
|
|
||
| | Capability | Trigger Property | Minimum TFM | Purpose | | ||
| |------------|------------------|-------------|---------| | ||
| | `IsAotCompatible` | `IsAotCompatible=true` | .NET 7.0 | Library AOT compatibility marker | | ||
| | `IsTrimmable` | `IsTrimmable=true` or `IsAotCompatible=true` | .NET 6.0 | Library trim compatibility marker | | ||
| | `PublishAot` | `PublishAot=true` | .NET 7.0 | Native AOT compilation | | ||
| | `PublishReadyToRun` | `PublishReadyToRun=true` | .NET Core 3.0 | ReadyToRun pre-compilation | | ||
| | `PublishSingleFile` | `PublishSingleFile=true` | .NET Core 3.0 | Single-file bundling | | ||
| | `PublishTrimmed` | `PublishTrimmed=true` | .NET Core 3.0 | IL trimming | | ||
|
|
There was a problem hiding this comment.
The summary table uses || at the start of rows, which renders with an empty first column in GitHub Markdown. Change the table to use a single leading | per row so it formats correctly.
The workflow was using two-dot diff syntax, which includes commits from the target branch not present in the PR, potentially causing false positives in capability detection.
Changes
Changed git diff syntax in
.github/workflows/detect-project-capabilities.yml:Three-dot diff shows only commits unique to the PR (changes since the merge base), while two-dot diff shows all commits reachable from head but not base, including unrelated target branch commits.
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.