From e497d7b9cd1dea0c3ebf130aaa1772031038dab7 Mon Sep 17 00:00:00 2001 From: Henrik Jensen <1175002+henrikhimself@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:56:46 +0100 Subject: [PATCH 01/25] Add extension for remote containers --- .aspire/settings.json | 3 + .editorconfig | 527 ++++++++++++++++++ .github/skills/aspire/SKILL.md | 231 ++++++++ .../skills/aspire/references/architecture.md | 341 ++++++++++++ .../skills/aspire/references/cli-reference.md | 307 ++++++++++ .github/skills/aspire/references/dashboard.md | 226 ++++++++ .../skills/aspire/references/deployment.md | 237 ++++++++ .../aspire/references/integrations-catalog.md | 68 +++ .../skills/aspire/references/mcp-server.md | 195 +++++++ .../skills/aspire/references/polyglot-apis.md | 296 ++++++++++ .github/skills/aspire/references/testing.md | 281 ++++++++++ .../aspire/references/troubleshooting.md | 194 +++++++ .gitignore | 491 ++++++++++++++++ .vscode/extensions.json | 6 + .vscode/launch.json | 21 + .vscode/mcp.json | 12 + .vscode/settings.json | 19 + AGENTS.md | 74 +++ Directory.Build.props | 23 + Directory.Build.targets | 15 + Directory.Packages.props | 16 + LICENSE | 222 +++++++- NuGet.config | 12 + README.md | 106 +++- RemoteContainers.slnx | 6 + examples/.editorconfig | 5 + examples/Aspire.AppHost/Aspire.AppHost.csproj | 13 + .../Properties/launchSettings.json | 37 ++ examples/Aspire.AppHost/apphost.cs | 10 + examples/Aspire.AppHost/appsettings.json | 11 + global.json | 7 + src/.editorconfig | 9 + .../DockerApiClient.cs | 280 ++++++++++ .../Models/ContainerPorts.cs | 24 + .../RemoteContainers.Aspire.csproj | 34 ++ .../SshTunnelExtensions.cs | 58 ++ .../SshTunnelLifecycleHook.cs | 51 ++ .../SshTunnelManager.cs | 225 ++++++++ stylecop.json | 8 + 39 files changed, 4678 insertions(+), 23 deletions(-) create mode 100644 .aspire/settings.json create mode 100644 .editorconfig create mode 100644 .github/skills/aspire/SKILL.md create mode 100644 .github/skills/aspire/references/architecture.md create mode 100644 .github/skills/aspire/references/cli-reference.md create mode 100644 .github/skills/aspire/references/dashboard.md create mode 100644 .github/skills/aspire/references/deployment.md create mode 100644 .github/skills/aspire/references/integrations-catalog.md create mode 100644 .github/skills/aspire/references/mcp-server.md create mode 100644 .github/skills/aspire/references/polyglot-apis.md create mode 100644 .github/skills/aspire/references/testing.md create mode 100644 .github/skills/aspire/references/troubleshooting.md create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/mcp.json create mode 100644 .vscode/settings.json create mode 100644 AGENTS.md create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets create mode 100644 Directory.Packages.props create mode 100644 NuGet.config create mode 100644 RemoteContainers.slnx create mode 100644 examples/.editorconfig create mode 100644 examples/Aspire.AppHost/Aspire.AppHost.csproj create mode 100644 examples/Aspire.AppHost/Properties/launchSettings.json create mode 100644 examples/Aspire.AppHost/apphost.cs create mode 100644 examples/Aspire.AppHost/appsettings.json create mode 100644 global.json create mode 100644 src/.editorconfig create mode 100644 src/RemoteContainers.Aspire/DockerApiClient.cs create mode 100644 src/RemoteContainers.Aspire/Models/ContainerPorts.cs create mode 100644 src/RemoteContainers.Aspire/RemoteContainers.Aspire.csproj create mode 100644 src/RemoteContainers.Aspire/SshTunnelExtensions.cs create mode 100644 src/RemoteContainers.Aspire/SshTunnelLifecycleHook.cs create mode 100644 src/RemoteContainers.Aspire/SshTunnelManager.cs create mode 100644 stylecop.json diff --git a/.aspire/settings.json b/.aspire/settings.json new file mode 100644 index 0000000..cac15a7 --- /dev/null +++ b/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../examples/Aspire.AppHost/Aspire.AppHost.csproj" +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7d8983c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,527 @@ +# 20260214 +root = true + +[*] +indent_style = space +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +# Makefiles should use tabs for indentation and not have trailing whitespace +[Makefile] +indent_style = tab + +# XML project files +[*.{csproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec}] +indent_size = 2 + +[*.{json,jsonc}] +indent_size = 2 + +[*.{sh,bash,ps1,,psm1}] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{razor,cshtml}] +charset = utf-8-bom +insert_final_newline = true + +[*.cs] +indent_size = 2 +insert_final_newline = true + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:refactoring +dotnet_style_qualification_for_property = false:refactoring +dotnet_style_qualification_for_method = false:refactoring +dotnet_style_qualification_for_event = false:refactoring + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Require var all the time. +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Non-private readonly fields are PascalCase +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style + +dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly + +dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = warning +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase and start with _ +dotnet_naming_rule.static_fields_should_be_camel_case.severity = warning +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case +dotnet_naming_style.static_field_style.required_prefix = _ + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = warning +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# Async methods should have "Async" suffix +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async +dotnet_naming_rule.async_methods_end_in_async.severity = warning + +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +dotnet_naming_style.end_in_async.required_prefix = +dotnet_naming_style.end_in_async.required_suffix = Async +dotnet_naming_style.end_in_async.capitalization = pascal_case +dotnet_naming_style.end_in_async.word_separator = + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = warning +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = warning +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = warning +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Whitespace options +csharp_style_allow_embedded_statements_on_same_line_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Namespace settings +csharp_style_namespace_declarations = file_scoped + +# Brace settings +csharp_prefer_braces = true + +# SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time +dotnet_diagnostic.SYSLIB1054.severity = warning + +# CA1515: Consider making public types internal +dotnet_diagnostic.CA1515.severity = suggestion + +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = warning + +# CA1034: Nested types should not be visible +dotnet_diagnostic.CA1034.severity = silent + +# CA1047: Do not declare protected member in sealed type +dotnet_diagnostic.CA1047.severity = warning + +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = warning + +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = warning + +# CA1507: Use nameof to express symbol names +dotnet_diagnostic.CA1507.severity = warning + +# CA1508: CA1508: Avoid dead conditional code +dotnet_diagnostic.CA1508.severity = error + +# CA1510: Use ArgumentNullException throw helper +dotnet_diagnostic.CA1510.severity = warning + +# CA1511: Use ArgumentException throw helper +dotnet_diagnostic.CA1511.severity = warning + +# CA1512: Use ArgumentOutOfRangeException throw helper +dotnet_diagnostic.CA1512.severity = warning + +# CA1513: Use ObjectDisposedException throw helper +dotnet_diagnostic.CA1513.severity = warning + +# CA1725: Parameter names should match base declaration +dotnet_diagnostic.CA1725.severity = suggestion + +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning + +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = warning + +# CA1810: Do not initialize unnecessarily +dotnet_diagnostic.CA1810.severity = warning + +# CA1821: Remove empty Finalizers +dotnet_diagnostic.CA1821.severity = warning + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = warning +dotnet_code_quality.CA1822.api_surface = private, internal + +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning + +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = warning + +# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly +dotnet_diagnostic.CA1826.severity = warning + +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = warning + +# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used +dotnet_diagnostic.CA1828.severity = warning + +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = warning + +# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder +dotnet_diagnostic.CA1830.severity = warning + +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1831.severity = warning + +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1832.severity = warning + +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1833.severity = warning + +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1834.severity = warning + +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +dotnet_diagnostic.CA1835.severity = warning + +# CA1836: Prefer IsEmpty over Count +dotnet_diagnostic.CA1836.severity = warning + +# CA1837: Use 'Environment.ProcessId' +dotnet_diagnostic.CA1837.severity = warning + +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +dotnet_diagnostic.CA1838.severity = warning + +# CA1839: Use 'Environment.ProcessPath' +dotnet_diagnostic.CA1839.severity = warning + +# CA1840: Use 'Environment.CurrentManagedThreadId' +dotnet_diagnostic.CA1840.severity = warning + +# CA1841: Prefer Dictionary.Contains methods +dotnet_diagnostic.CA1841.severity = warning + +# CA1842: Do not use 'WhenAll' with a single task +dotnet_diagnostic.CA1842.severity = warning + +# CA1843: Do not use 'WaitAll' with a single task +dotnet_diagnostic.CA1843.severity = warning + +# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream' +dotnet_diagnostic.CA1844.severity = warning + +# CA1845: Use span-based 'string.Concat' +dotnet_diagnostic.CA1845.severity = warning + +# CA1846: Prefer AsSpan over Substring +dotnet_diagnostic.CA1846.severity = warning + +# CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters +dotnet_diagnostic.CA1847.severity = warning + +# CA1848: Use the LoggerMessage delegates +dotnet_diagnostic.CA1848.severity = none + +# CA1852: Seal internal types +dotnet_diagnostic.CA1852.severity = warning + +# CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method +dotnet_diagnostic.CA1854.severity = warning + +# CA1855: Prefer 'Clear' over 'Fill' +dotnet_diagnostic.CA1855.severity = warning + +# CA1856: Incorrect usage of ConstantExpected attribute +dotnet_diagnostic.CA1856.severity = error + +# CA1857: A constant is expected for the parameter +dotnet_diagnostic.CA1857.severity = warning + +# CA1858: Use 'StartsWith' instead of 'IndexOf' +dotnet_diagnostic.CA1858.severity = warning + +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = error + +# CA2007: Consider calling ConfigureAwait on the awaited task +# It is generally appropriate to suppress this warning entirely for projects that represent application code rather than library code +dotnet_diagnostic.CA2007.severity = none + +# CA2008: Do not create tasks without passing a TaskScheduler +dotnet_diagnostic.CA2008.severity = warning + +# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value +dotnet_diagnostic.CA2009.severity = warning + +# CA2011: Avoid infinite recursion +dotnet_diagnostic.CA2011.severity = warning + +# CA2012: Use ValueTask correctly +dotnet_diagnostic.CA2012.severity = warning + +# CA2013: Do not use ReferenceEquals with value types +dotnet_diagnostic.CA2013.severity = warning + +# CA2014: Do not use stackalloc in loops. +dotnet_diagnostic.CA2014.severity = warning + +# CA2016: Forward the 'CancellationToken' parameter to methods that take one +dotnet_diagnostic.CA2016.severity = warning + +# CA2200: Rethrow to preserve stack details +dotnet_diagnostic.CA2200.severity = warning + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = warning + +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = warning + +# CA2245: Do not assign a property to itself +dotnet_diagnostic.CA2245.severity = warning + +# CA2246: Assigning symbol and its member in the same statement +dotnet_diagnostic.CA2246.severity = warning + +# CA2249: Use string.Contains instead of string.IndexOf to improve readability. +dotnet_diagnostic.CA2249.severity = warning + +# CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +dotnet_diagnostic.CS1591.severity = none + +# IDE0005: Remove unnecessary usings +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0011: Curly braces to surround blocks of code +dotnet_diagnostic.IDE0011.severity = warning + +# IDE0029: Use coalesce expression (non-nullable types) +dotnet_diagnostic.IDE0029.severity = warning + +# IDE0030: Use coalesce expression (nullable types) +dotnet_diagnostic.IDE0030.severity = warning + +# IDE0031: Use null propagation +dotnet_diagnostic.IDE0031.severity = warning + +# IDE0035: Remove unreachable code +dotnet_diagnostic.IDE0035.severity = warning + +# IDE0036: Order modifiers +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0038: Use pattern matching to avoid is check followed by a cast (without variable) - also see IDE0020 +dotnet_diagnostic.IDE0038.severity = warning + +# IDE0043: Format string contains invalid placeholder +dotnet_diagnostic.IDE0043.severity = warning + +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = warning + +# IDE0051: Remove unused private members +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0055: All formatting rules +dotnet_diagnostic.IDE0055.severity = suggestion + +# IDE0059: Unnecessary assignment to a value +dotnet_diagnostic.IDE0059.severity = warning + +# IDE0060: Remove unused parameter +dotnet_code_quality_unused_parameters = non_public +dotnet_diagnostic.IDE0060.severity = warning + +# IDE0062: Make local function static +dotnet_diagnostic.IDE0062.severity = warning + +# IDE0073: File header +dotnet_diagnostic.IDE0073.severity = suggestion + +# IDE0161: Convert to file-scoped namespace +dotnet_diagnostic.IDE0161.severity = warning + +# IDE0200: Lambda expression can be removed +dotnet_diagnostic.IDE0200.severity = warning + +# IDE0290: Use primary constructor +dotnet_diagnostic.IDE0290.severity = none + +# IDE2000: Disallow multiple blank lines +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_diagnostic.IDE2000.severity = warning + +# The spacing around a C# keyword is incorrect +dotnet_diagnostic.SA1000.severity = none + +# A closing parenthesis within a C# statement is not spaced correctly +dotnet_diagnostic.SA1009.severity = none + +# An opening square bracket within a C# statement is not spaced correctly +dotnet_diagnostic.SA1010.severity = none + +# A closing square bracket within a C# statement is not spaced correctly. +dotnet_diagnostic.SA1011.severity = none + +# SA1101: A call to an instance member of the local class or a base class is not prefixed with 'this.', within a C# code file +dotnet_diagnostic.SA1101.severity = none + +# SA1200: A C# using directive is placed outside of a namespace element +dotnet_diagnostic.SA1200.severity = none + +# SA1206: The keywords within the declaration of an element do not follow a standard ordering scheme +dotnet_diagnostic.SA1206.severity = none + +# SA1309: A field name in C# begins with an underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1515: A single-line comment within C# code is not preceded by a blank line +dotnet_diagnostic.SA1515.severity = none + +# SA1600: A C# code element is missing a documentation header +dotnet_diagnostic.SA1600.severity = none + +# SA1601: A C# partial element is missing a documentation header +dotnet_diagnostic.SA1601.severity = none + +# SA1633: A C# code file is missing a standard file header +dotnet_diagnostic.SA1633.severity = none diff --git a/.github/skills/aspire/SKILL.md b/.github/skills/aspire/SKILL.md new file mode 100644 index 0000000..937dffa --- /dev/null +++ b/.github/skills/aspire/SKILL.md @@ -0,0 +1,231 @@ +--- +name: aspire +description: 'Aspire skill covering the Aspire CLI, AppHost orchestration, service discovery, integrations, MCP server, VS Code extension, Dev Containers, GitHub Codespaces, templates, dashboard, and deployment. Use when the user asks to create, run, debug, configure, deploy, or troubleshoot an Aspire distributed application.' +--- + +# Aspire — Polyglot Distributed-App Orchestration + +Aspire is a **code-first, polyglot toolchain** for building observable, production-ready distributed applications. It orchestrates containers, executables, and cloud resources from a single AppHost project — regardless of whether the workloads are C#, Python, JavaScript/TypeScript, Go, Java, Rust, Bun, Deno, or PowerShell. + +> **Mental model:** The AppHost is a *conductor* — it doesn't play the instruments, it tells every service when to start, how to find each other, and watches for problems. + +Detailed reference material lives in the `references/` folder — load on demand. + +--- + +## References + +| Reference | When to load | +|---|---| +| [CLI Reference](references/cli-reference.md) | Command flags, options, or detailed usage | +| [MCP Server](references/mcp-server.md) | Setting up MCP for AI assistants, available tools | +| [Integrations Catalog](references/integrations-catalog.md) | Discovering integrations via MCP tools, wiring patterns | +| [Polyglot APIs](references/polyglot-apis.md) | Method signatures, chaining options, language-specific patterns | +| [Architecture](references/architecture.md) | DCP internals, resource model, service discovery, networking, telemetry | +| [Dashboard](references/dashboard.md) | Dashboard features, standalone mode, GenAI Visualizer | +| [Deployment](references/deployment.md) | Docker, Kubernetes, Azure Container Apps, App Service | +| [Testing](references/testing.md) | Integration tests against the AppHost | +| [Troubleshooting](references/troubleshooting.md) | Diagnostic codes, common errors, and fixes | + +--- + +## 1. Researching Aspire Documentation + +The Aspire team ships an **MCP server** that provides documentation tools directly inside your AI assistant. See [MCP Server](references/mcp-server.md) for setup details. + +### Aspire CLI 13.2+ (recommended — has built-in docs search) + +If running Aspire CLI **13.2 or later** (`aspire --version`), the MCP server includes docs search tools: + +| Tool | Description | +|---|---| +| `list_docs` | Lists all available documentation from aspire.dev | +| `search_docs` | Performs weighted lexical search across indexed documentation | +| `get_doc` | Retrieves a specific document by its slug | + +These tools were added in [PR #14028](https://github.com/dotnet/aspire/pull/14028). To update: `aspire update --self --channel daily`. + +For more on this approach, see David Pine's post: https://davidpine.dev/posts/aspire-docs-mcp-tools/ + +### Aspire CLI 13.1 (integration tools only) + +On 13.1, the MCP server provides integration lookup but **not** docs search: + +| Tool | Description | +|---|---| +| `list_integrations` | Lists available Aspire hosting integrations | +| `get_integration_docs` | Gets documentation for a specific integration package | + +For general docs queries on 13.1, use **Context7** as your primary source (see below). + +### Fallback: Context7 + +Use **Context7** (`mcp_context7`) when the Aspire MCP docs tools are unavailable (13.1) or the MCP server isn't running: + +**Step 1 — Resolve the library ID** (one-time per session): + +Call `mcp_context7_resolve-library-id` with `libraryName: ".NET Aspire"`. + +| Rank | Library ID | Use when | +|---|---|---| +| 1 | `/microsoft/aspire.dev` | Primary source. Guides, integrations, CLI reference, deployment. | +| 2 | `/dotnet/aspire` | API internals, source-level implementation details. | +| 3 | `/communitytoolkit/aspire` | Non-Microsoft polyglot integrations (Go, Java, Node.js, Ollama). | + +**Step 2 — Query docs:** + +``` +libraryId: "/microsoft/aspire.dev", query: "Python integration AddPythonApp service discovery" +libraryId: "/communitytoolkit/aspire", query: "Golang Java Node.js community integrations" +``` + +### Fallback: GitHub search (when Context7 is also unavailable) + +Search the official docs repo on GitHub: +- **Docs repo:** `microsoft/aspire.dev` — path: `src/frontend/src/content/docs/` +- **Source repo:** `dotnet/aspire` +- **Samples repo:** `dotnet/aspire-samples` +- **Community integrations:** `CommunityToolkit/Aspire` + +--- + +## 2. Prerequisites & Install + +| Requirement | Details | +|---|---| +| **.NET SDK** | 10.0+ (required even for non-.NET workloads — the AppHost is .NET) | +| **Container runtime** | Docker Desktop, Podman, or Rancher Desktop | +| **IDE (optional)** | VS Code + C# Dev Kit, Visual Studio 2022, JetBrains Rider | + +```bash +# Linux / macOS +curl -sSL https://aspire.dev/install.sh | bash + +# Windows PowerShell +irm https://aspire.dev/install.ps1 | iex + +# Verify +aspire --version + +# Install templates +dotnet new install Aspire.ProjectTemplates +``` + +--- + +## 3. Project Templates + +| Template | Command | Description | +|---|---|---| +| **aspire-starter** | `aspire new aspire-starter` | ASP.NET Core/Blazor starter + AppHost + tests | +| **aspire-ts-cs-starter** | `aspire new aspire-ts-cs-starter` | ASP.NET Core/React starter + AppHost | +| **aspire-py-starter** | `aspire new aspire-py-starter` | FastAPI/React starter + AppHost | +| **aspire-apphost-singlefile** | `aspire new aspire-apphost-singlefile` | Empty single-file AppHost | + +--- + +## 4. AppHost Quick Start (Polyglot) + +The AppHost orchestrates all services. Non-.NET workloads run as containers or executables. + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +// Infrastructure +var redis = builder.AddRedis("cache"); +var postgres = builder.AddPostgres("pg").AddDatabase("catalog"); + +// .NET API +var api = builder.AddProject("api") + .WithReference(postgres).WithReference(redis); + +// Python ML service +var ml = builder.AddPythonApp("ml-service", "../ml-service", "main.py") + .WithHttpEndpoint(targetPort: 8000).WithReference(redis); + +// React frontend (Vite) +var web = builder.AddViteApp("web", "../frontend") + .WithHttpEndpoint(targetPort: 5173).WithReference(api); + +// Go worker +var worker = builder.AddGolangApp("worker", "../go-worker") + .WithReference(redis); + +builder.Build().Run(); +``` + +For complete API signatures, see [Polyglot APIs](references/polyglot-apis.md). + +--- + +## 5. Core Concepts (Summary) + +| Concept | Key point | +|---|---| +| **Run vs Publish** | `aspire run` = local dev (DCP engine). `aspire publish` = generate deployment manifests. | +| **Service discovery** | Automatic via env vars: `ConnectionStrings__`, `services____http__0` | +| **Resource lifecycle** | DAG ordering — dependencies start first. `.WaitFor()` gates on health checks. | +| **Resource types** | `ProjectResource`, `ContainerResource`, `ExecutableResource`, `ParameterResource` | +| **Integrations** | 144+ across 13 categories. Hosting package (AppHost) + Client package (service). | +| **Dashboard** | Real-time logs, traces, metrics, GenAI visualizer. Runs automatically with `aspire run`. | +| **MCP Server** | AI assistants can query running apps and search docs via CLI (STDIO). | +| **Testing** | `Aspire.Hosting.Testing` — spin up full AppHost in xUnit/MSTest/NUnit. | +| **Deployment** | Docker, Kubernetes, Azure Container Apps, Azure App Service. | + +--- + +## 6. CLI Quick Reference + +Valid commands in Aspire CLI 13.1: + +| Command | Description | Status | +|---|---|---| +| `aspire new