Instructions for GitHub Copilot and other AI coding agents working with the Aspire repository.
Aspire provides tools, templates, and packages for building observable, production-ready distributed applications. At its core is an app model that defines services, resources, and connections in a code-first approach.
- Aspire.Hosting: Application host orchestration and resource management
- Aspire.Dashboard: Web-based dashboard for monitoring and debugging
- Service Discovery: Infrastructure for service-to-service communication
- Integrations: 40+ packages for databases (SQL Server, PostgreSQL, Redis, MongoDB), message queues (RabbitMQ, Kafka), cloud services (Azure), and more
- CLI Tools: Command-line interface for project creation and management
- Project Templates: Starter templates for new Aspire applications
- .NET 10.0
- C# 13 preview features
- xUnit SDK v3 with Microsoft.Testing.Platform for testing
- Microsoft.DotNet.Arcade.Sdk for build infrastructure
- Native AOT compilation for CLI tools
- Multi-platform support (Windows, Linux, macOS, containers)
- Make only high confidence suggestions when reviewing code changes.
- Always use the latest version C#, currently C# 13 features.
- Never change global.json unless explicitly asked to.
- Never change package.json or package-lock.json files unless explicitly asked to.
- Never change NuGet.config files unless explicitly asked to.
- Don't update files under
*/api/*.cs(e.g. src/Aspire.Hosting/api/Aspire.Hosting.cs) as they are generated.
The API files located in */api/*.cs (e.g., src/Aspire.Hosting/api/Aspire.Hosting.cs) track the public API surface that has already been shipped in the latest release. These files are auto-generated and serve as a baseline for API compatibility checks.
When reviewing pull requests:
- Do not comment when new public API is introduced and the API files are not regenerated. This is expected behavior during active development between releases.
- New public APIs should be reviewed for design, naming, and functionality, but the absence of API file updates during PR development is normal.
- API files are regenerated as part of the release process when we ship a new version, not during individual PRs.
- Only flag API file concerns if:
- API files are manually edited (they should never be manually modified)
- There are breaking changes to existing APIs without proper justification
- The PR explicitly claims to update API compatibility but doesn't regenerate the files
The NuGet.config file defines approved package sources for the internal build. External package feeds can break the internal build pipeline.
When reviewing pull requests:
- Flag any changes to NuGet.config that add package sources not from these approved domains:
https://pkgs.dev.azure.com/dncenghttps://dnceng.pkgs.visualstudio.com/public
- Flag any additions of external NuGet feeds such as:
https://api.nuget.org/v3/index.json(nuget.org)- Any other public or third-party package sources
- If a PR adds an external feed, request that:
- The packages be mirrored to an approved internal feed, or
- Use existing internal feeds that already mirror public packages (like dotnet-public, dotnet-eng)
- The wildcard pattern mappings (
<package pattern="*" />) in dotnet-public and dotnet-eng feeds typically provide access to commonly-used public packages
- Apply code-formatting style defined in
.editorconfig. - Prefer file-scoped namespace declarations and single-line using directives.
- Insert a newline before the opening curly brace of any code block (e.g., after
if,for,while,foreach,using,try, etc.). - Ensure that the final return statement of a method is on its own line.
- Use pattern matching and switch expressions wherever possible.
- Use
nameofinstead of string literals when referring to member names. - Place private class declarations at the bottom of the file.
- Declare variables non-nullable, and check for
nullat entry points. - Always use
is nulloris not nullinstead of== nullor!= null. - Trust the C# null annotations and don't add null checks when the type system says a value cannot be null.
Always run restore first to set up the local SDK. Run ./restore.sh (Linux/macOS) or ./restore.cmd (Windows) first to install the local SDK. After restore, you can use standard dotnet commands, which will automatically use the local SDK when available due to the paths configuration in global.json.
- Restore First: Always run
./restore.sh(Linux/macOS) or./restore.cmd(Windows) to set up the local .NET SDK (~30 seconds)
- Full Build:
./build.sh(Linux/macOS) or./build.cmd(Windows) - defaults to restore + build (~3-5 minutes) - Build Only:
./build.sh --build(assumes restore already done) - Skip Native Build: Add
/p:SkipNativeBuild=trueto avoid slow native AOT compilation (~1-2 minutes saved) - Clean Build:
./build.sh --rebuild - Package Generation:
./build.sh --packto create NuGet packages
- If temporarily introducing warnings during refactoring, add
/p:TreatWarningsAsErrors=falseto prevent build failure - Important: All warnings should be addressed before committing any final changes
- Template engine warnings about "Missing generatorVersions" are expected and not errors
- If build fails with SDK errors, run
./restore.shagain to ensure correct .NET 10 RC is installed - Build artifacts go to
./artifacts/directory
- VS Code: Run
./build.shfirst, then use./start-code.shto launch with correct environment - Visual Studio: Run
./build.cmdfirst, then use./startvs.cmdto launch with local SDK environment
When running an App Host from an interactive console (for example, PowerShell or a terminal session you plan to reuse), start it in a background job/process. If you start the App Host in the foreground and then reuse the console for another command (or stop the current interactive session), the App Host process can be terminated.
Run the App Host in the background so it continues running while you execute other commands:
PowerShell (Start-Job)
# From the AppHost project directory
Start-Job -Name "AspireAppHost" -ScriptBlock { dotnet run }
# Inspect output / state
Get-Job -Name "AspireAppHost" | Format-List *
Receive-Job -Name "AspireAppHost" -Keep
# Stop when done
Stop-Job -Name "AspireAppHost"
Remove-Job -Name "AspireAppHost"bash (background process)
# From the AppHost project directory
dotnet run > apphost.log 2>&1 &
# Find and stop later
ps aux | grep dotnet
kill <pid>- We use xUnit SDK v3 with Microsoft.Testing.Platform (https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-intro)
- Do not emit "Act", "Arrange" or "Assert" comments.
- We do not use any mocking framework at the moment.
- Copy existing style in nearby files for test method names and capitalization.
- Do not leave newly-added tests commented out. All added tests should be building and passing.
- Do not use Directory.SetCurrentDirectory in tests as it can cause side effects when tests execute concurrently.
(1) Build from the root with ./build.sh (~3-5 minutes).
(2) If that produces errors, fix those errors and build again. Repeat until the build is successful.
(3) To run tests for a specific project: dotnet test tests/ProjectName.Tests/ProjectName.Tests.csproj --no-build -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
Note that tests for a project can be executed without first building from the root.
(4) To run specific tests, include the filter after --:
dotnet test tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj -- --filter-method "*.TestingBuilderHasAllPropertiesFromRealBuilder" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"Important: Avoid passing --no-build unless you have just built in the same session and there have been no code changes since. In automation or while iterating on code, omit --no-build so changes are compiled and picked up by the test run.
This repo uses Microsoft.Testing.Platform (MTP) as the test runner, not VSTest. The classic --filter argument (before --) uses VSTest filter syntax and will hang or behave unexpectedly with MTP.
# WRONG - VSTest-style filter, will hang with MTP
dotnet test tests/Project.Tests/Project.Tests.csproj --filter "FullyQualifiedName~ClassName"
# CORRECT - MTP-native filters go after the -- separator
dotnet test tests/Project.Tests/Project.Tests.csproj -- --filter-class "*.ClassName"
dotnet test tests/Project.Tests/Project.Tests.csproj -- --filter-method "*.MethodName"All test filtering must use MTP-native switches placed after --. See the filter switches listed below for the full set of options.
When running tests in automated environments (including Copilot agent), always exclude quarantined and outerloop tests to avoid false negatives and long-running tests:
# Correct - excludes quarantined and outerloop tests (use this in automation)
dotnet test tests/Project.Tests/Project.Tests.csproj -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
# For specific test filters, combine with quarantine and outerloop exclusion
dotnet test tests/Project.Tests/Project.Tests.csproj -- --filter-method "TestName" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"Never run all tests without the quarantine and outerloop filters in automated environments, as this will include flaky tests that are known to fail intermittently and long-running tests that slow down CI.
Valid test filter switches include: --filter-class, --filter-not-class, --filter-method, --filter-not-method, --filter-namespace, --filter-not-namespace, --filter-not-trait, --filter-trait
The switches --filter-class and --filter-method expect fully qualified names, unless a filter is used as a prefix like --filter-class "*.SomeClassName" or --filter-method "*.SomeMethodName".
These switches can be repeated to run tests on multiple classes or methods at once, e.g., --filter-method "*.SomeMethodName1" --filter-method "*.SomeMethodName2".
- Single Test Project: Typical runtime ~10-60 seconds per test project
- Full Test Suite: Can take 30+ minutes, use targeted testing instead
/src: Main source code for all Aspire packagesAspire.Hosting/: Core hosting and orchestration infrastructureAspire.Dashboard/: Web dashboard UI (Blazor application)Components/: 40+ integration packages for databases, messaging, cloud servicesAspire.Cli/: Command-line interface tools
/tests: Comprehensive test suites mirroring src structure/playground: Sample applications including TestShop for verification/docs: Documentation including contributing guides and area ownership/eng: Build scripts, tools, and engineering infrastructure/.github: CI/CD workflows, issue templates, and GitHub automation/extension: VS Code extension source code
global.json: Pins .NET SDK version - never modify without explicit request.editorconfig: Code formatting rules, null annotations, diagnostic configurationsDirectory.Build.props: Shared MSBuild properties across all projectsDirectory.Packages.props: Centralized package version managementAspire.slnx: Main solution file (XML-based solution format)
tests.yml: Main test workflow running across Windows/Linux/macOStests-quarantine.yml: Runs quarantined tests separately every 6 hourstests-outerloop.yml: Runs outerloop tests separately every 6 hoursci.yml: Main CI workflow triggered on PRs and pushes to main/release branches- Build validation: Includes package generation, API compatibility checks, template validation
- Workflow matcher maintenance: When changing CI workflow job or step names that are referenced by automation or tests, update the corresponding workflow helpers, behavior tests, and docs together. For the transient rerun workflow, keep
.github/workflows/auto-rerun-transient-ci-failures.js,tests/Infrastructure.Tests/WorkflowScripts/AutoRerunTransientCiFailuresTests.cs, anddocs/ci/auto-rerun-transient-ci-failures.mdaligned with the live workflow YAML.
eng/pipelines/azure-pipelines-public.yml: Weekly scheduled pipeline (Monday midnight UTC) that builds and runs tests on Helix⚠️ AzDO tests are easily broken because they don't run on PRs — only weekly or via manual trigger (/azp run aspire-tests)- Changes to test infrastructure (
eng/Testing.props,eng/Testing.targets,tests/Directory.Build.*,tests/helix/*) should be validated by triggering a manual AzDO run - See docs/ci/azdo-public-pipeline.md for full architecture details including Helix test categories, archive process, and test routing
Dependencies and Hidden Requirements
- Local .NET SDK: Automatically uses local SDK when available after running restore due to paths configuration in global.json
- Package References: Centrally managed via Directory.Packages.props
- API Surface: Public APIs tracked in
src/*/api/*.csfiles (auto-generated, don't edit)
- Build Verification:
./build.shshould complete without errors - Package Generation:
./build.sh --packverifies all packages can be created - Specific Tests: Target individual test projects related to your changes
- Tests that are flaky and don't fail deterministically are marked with the
QuarantinedTestattribute. - Such tests are not run as part of the regular tests workflow (
tests.yml).- Instead they are run in the
Quarantineworkflow (tests-quarantine.yml).
- Instead they are run in the
- A github issue url is used with the attribute
- To reproduce or fix a flaky/quarantined test, use the
fix-flaky-testskill (.github/skills/fix-flaky-test/SKILL.md). - To quarantine or unquarantine a test, use the
test-managementskill (.github/skills/test-management/SKILL.md).
Example: [QuarantinedTest("..issue url..")]
Use these commands in any issue or PR comment. They require write access to the repository.
# Quarantine a flaky test (creates a new PR)
/quarantine-test Namespace.Type.Method https://github.com/dotnet/aspire/issues/1234
# Quarantine multiple tests at once
/quarantine-test TestMethod1 TestMethod2 https://github.com/dotnet/aspire/issues/1234
# Quarantine and push to an existing PR
/quarantine-test TestMethod https://github.com/dotnet/aspire/issues/1234 --target-pr https://github.com/dotnet/aspire/pull/5678
# Unquarantine a test (creates a new PR)
/unquarantine-test Namespace.Type.Method
# Unquarantine and push to an existing PR
/unquarantine-test TestMethod --target-pr https://github.com/dotnet/aspire/pull/5678When you comment on a PR, the changes are automatically pushed to that PR's branch (no need for --target-pr).
For local development, use the QuarantineTools directly:
# Quarantine a test
dotnet run --project tools/QuarantineTools -- -q -i https://github.com/dotnet/aspire/issues/1234 Full.Namespace.Type.Method
# Unquarantine a test
dotnet run --project tools/QuarantineTools -- -u Full.Namespace.Type.Method- Tests that consistently fail due to a known bug or infrastructure issue are marked with the
ActiveIssueattribute. - These tests are completely skipped until the underlying issue is resolved.
- Use this for tests that are blocked, not for flaky tests (use
QuarantinedTestfor flaky tests).
Example: [ActiveIssue("https://github.com/dotnet/aspire/issues/1234")]
# Disable a test due to an active issue (creates a new PR)
/disable-test Namespace.Type.Method https://github.com/dotnet/aspire/issues/1234
# Disable and push to an existing PR
/disable-test TestMethod https://github.com/dotnet/aspire/issues/1234 --target-pr https://github.com/dotnet/aspire/pull/5678
# Enable a previously disabled test (creates a new PR)
/enable-test Namespace.Type.Method
# Enable and push to an existing PR
/enable-test TestMethod --target-pr https://github.com/dotnet/aspire/pull/5678# Disable a test with ActiveIssue
dotnet run --project tools/QuarantineTools -- -q -m activeissue -i https://github.com/dotnet/aspire/issues/1234 Full.Namespace.Type.Method
# Enable a test (remove ActiveIssue)
dotnet run --project tools/QuarantineTools -- -u -m activeissue Full.Namespace.Type.Method- Tests that are long-running, resource-intensive, or require special infrastructure are marked with the
OuterloopTestattribute. - Such tests are not run as part of the regular tests workflow (
tests.yml).- Instead they are run in the
Outerloopworkflow (tests-outerloop.yml).
- Instead they are run in the
- An optional reason can be provided with the attribute
Example: [OuterloopTest("Long running integration test")]
- We use the Verify library (Verify.XunitV3) for snapshot testing in several test projects.
- Snapshot files are stored in
Snapshotsdirectories within test projects. - When tests that use snapshot testing are updated and generate new output, the snapshots need to be accepted.
- Use
dotnet verify accept -yto accept all pending snapshot changes after running tests. - The verify tool is available globally as part of the copilot setup.
The *.Designer.cs files are in the repo, but are intended to match same named *.resx files. If you add/remove/change resources in a resx, make the matching changes in the *.Designer.cs file that matches that resx. The *.Designer.cs files are generated by a Visual Studio design-time tool (ResXFileCodeGenerator or PublicResXFileCodeGenerator) and there is no command-line tool to regenerate them, so they must be updated manually.
Some projects also have *.xlf (XLIFF) translation files in an xlf subdirectory next to the .resx files. After modifying a .resx file, run the following command on the affected project to update the .xlf files:
dotnet build /t:UpdateXlf <path-to-project.csproj>Do not manually edit *.xlf files. They are updated by the UpdateXlf MSBuild target (provided by Microsoft.DotNet.XliffTasks).
- Markdown files should not have multiple consecutive blank lines.
- Code blocks should be formatted with triple backticks (```) and include the language identifier for syntax highlighting.
- JSON code blocks should be indented properly.
- Files matching the pattern
*/localize/templatestrings.*.jsonare localization files. Do not translate their content. It is done by a dedicated workflow.
These instructions are comprehensive and tested. Only search for additional information if:
- The instructions appear outdated or incorrect
- You encounter specific errors not covered here
- You need details about new features not yet documented
For most development tasks, following these instructions should be sufficient to build, test, and validate changes successfully.
- When possible, you should create Typescript files instead of Javascript files.
- You must not use dynamic imports unless absolutely necessary. Instead, use static imports.
- When displaying text to the user, ensure that the strings are localized. New localized strings must be put both in the extension
package.nls.jsonand alsosrc/loc/strings.ts.
The following specialized skills are available in .github/skills/:
- cli-e2e-testing: Guide for writing Aspire CLI end-to-end tests using Hex1b terminal automation
- fix-flaky-test: Reproduces and fixes flaky/quarantined tests using the CI reproduce workflow (
reproduce-flaky-tests.yml). Use this when investigating, reproducing, or fixing a flaky or quarantined test. - dashboard-testing: Guide for writing tests for the Aspire Dashboard using xUnit and bUnit
- test-management: Quarantines or disables flaky/problematic tests using the QuarantineTools utility
- connection-properties: Expert for creating and improving Connection Properties in Aspire resources
- dependency-update: Guides dependency version updates by checking nuget.org, triggering the dotnet-migrate-package Azure DevOps pipeline, and monitoring runs
- startup-perf: Measures Aspire application startup performance using dotnet-trace and the TraceAnalyzer tool
Additional instructions are automatically applied when editing files matching specific patterns:
| Pattern | Instructions File |
|---|---|
src/**/*.cs |
.github/instructions/xmldoc.instructions.md - XML documentation standards |
src/Aspire.Hosting*/README.md |
.github/instructions/hosting-readme.instructions.md - Hosting integration READMEs |
src/Components/**/README.md |
.github/instructions/client-readme.instructions.md - Client integration READMEs |
tools/QuarantineTools/* |
.github/instructions/quarantine.instructions.md - QuarantineTools usage |
tests/**/*.cs |
.github/instructions/test-review-guidelines.instructions.md - Flaky test patterns and test review guidelines |