Clean up unused projects.
This commit is contained in:
@@ -1,222 +0,0 @@
|
|||||||
---
|
|
||||||
name: aspire-deployment
|
|
||||||
description: "**WORKFLOW SKILL** — Deploy Aspire apps from AppHost models to Docker Compose, Kubernetes, Azure, or AWS. WHEN: \"deploy Aspire app\", \"publish Aspire artifacts\", \"deploy to Azure Container Apps\", \"generate Kubernetes artifacts\", \"tear down Aspire deployment\". INVOKES: aspire CLI, Aspire docs, target cloud/container CLIs. FOR SINGLE OPERATIONS: use generic Azure, Kubernetes, Docker, or AWS tools only when no Aspire AppHost exists."
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: Microsoft
|
|
||||||
version: "0.0.1"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Aspire Deployment
|
|
||||||
|
|
||||||
Use this skill when the task is to publish, preview, validate, deploy, or tear down an Aspire application deployment. This skill owns Aspire deployment routing. Do not start with a generic Azure, Docker, Kubernetes, Helm, or Bicep workflow until you have checked whether the workspace is an Aspire app.
|
|
||||||
|
|
||||||
Aspire deployment starts from the AppHost model. Treat `aspire deploy`, `aspire publish`, `aspire destroy`, `aspire do`, and the deployment environment resources in the AppHost as the primary path.
|
|
||||||
|
|
||||||
Keep this as one skill with target-specific references. Load only the reference files that match the target you discover or the user requests.
|
|
||||||
|
|
||||||
## Routing precedence
|
|
||||||
|
|
||||||
This skill wins over generic cloud deployment skills when both conditions are true:
|
|
||||||
|
|
||||||
1. The user asks to deploy, publish, generate deployment artifacts, create Bicep/Helm/Compose/CDK output, host on Azure or AWS, deploy to Azure or AWS, deploy to Kubernetes, deploy to Docker Compose, tear down deployed resources, or validate a deployment.
|
|
||||||
2. The workspace has Aspire markers:
|
|
||||||
- Aspire workspace configuration
|
|
||||||
- C# or TypeScript AppHost files
|
|
||||||
- an AppHost project
|
|
||||||
- AppHost code using Aspire distributed application builder APIs
|
|
||||||
|
|
||||||
If Aspire markers are present but this skill was not automatically invoked, switch to this skill before continuing. Prefer Aspire CLI commands such as `aspire ls`, `aspire config list`, `aspire ps`, and `aspire describe` for workspace orientation.
|
|
||||||
|
|
||||||
## Guiding principles
|
|
||||||
|
|
||||||
### Use Aspire docs before changing deployment code
|
|
||||||
|
|
||||||
Before adding target packages, editing the AppHost, or using an unfamiliar deployment API, use Aspire docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "deploy with Aspire"
|
|
||||||
aspire docs search "Docker Compose deployment"
|
|
||||||
aspire docs search "Kubernetes deployment"
|
|
||||||
aspire docs search "Azure Container Apps deployment"
|
|
||||||
aspire docs search "Azure App Service deployment"
|
|
||||||
aspire docs search "Azure Kubernetes Service deployment"
|
|
||||||
aspire docs get "deploy-to-azure-kubernetes-service-aks"
|
|
||||||
aspire docs get "<slug-from-search-results>"
|
|
||||||
```
|
|
||||||
|
|
||||||
When you need exact C# or TypeScript API shape, use API docs too. Search both languages when you are not sure which AppHost language the repo uses:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "<deployment API or concept>" --language csharp
|
|
||||||
aspire docs api search "<deployment API or concept>" --language typescript
|
|
||||||
aspire docs api get "<id-from-api-search>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not invent package names, builder methods, overloads, or deployment commands. API shapes differ between C# and TypeScript AppHosts.
|
|
||||||
|
|
||||||
### Prefer Aspire-native deployment
|
|
||||||
|
|
||||||
Use Aspire deployment targets and CLI commands first:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire deploy --list-steps
|
|
||||||
aspire publish
|
|
||||||
aspire deploy
|
|
||||||
aspire destroy
|
|
||||||
aspire do <step>
|
|
||||||
```
|
|
||||||
|
|
||||||
Use target-specific tooling only after Aspire has generated artifacts or when the target docs call for it:
|
|
||||||
|
|
||||||
- Docker Compose: inspect generated `aspire-output/docker-compose.yaml` and `.env*`; Aspire can also run `docker compose up` through `aspire deploy`.
|
|
||||||
- Kubernetes: inspect generated Helm chart output; use Helm/kubectl when applying published artifacts yourself.
|
|
||||||
- Azure: use `aspire add <azure-target>`, `aspire publish`, and `aspire deploy` through the AppHost deployment environment.
|
|
||||||
- AWS: use `aspire add aws` to add the integration, inspect generated CDK/CloudFormation output, and follow the AWS integrations repository guidance.
|
|
||||||
|
|
||||||
### Ask where to deploy only when ambiguous
|
|
||||||
|
|
||||||
Do not ask for target selection when the user already chose a target such as Docker Compose, Kubernetes, Azure Container Apps, Azure App Service, Azure Kubernetes Service (AKS), or AWS. Use the chosen target and continue with its reference.
|
|
||||||
|
|
||||||
If the user did not explicitly choose a deployment target and the AppHost does not already contain exactly one deployment environment, ask where they want to deploy before adding integrations, editing the AppHost, publishing artifacts, or deploying. Use a single multiple-choice question:
|
|
||||||
|
|
||||||
> Where do you want to deploy this Aspire app?
|
|
||||||
|
|
||||||
Show these choices:
|
|
||||||
|
|
||||||
| Choice | Aspire add command | Use when |
|
|
||||||
|--------|--------------------|----------|
|
|
||||||
| Docker Compose | `aspire add docker` | The user wants local/server container deployment artifacts for Docker or Podman. |
|
|
||||||
| Kubernetes | `aspire add kubernetes` | The user has an existing Kubernetes cluster and wants Helm/Kubernetes artifacts or direct cluster deployment. |
|
|
||||||
| Azure Container Apps | `aspire add azure-appcontainers` | The user wants an Azure-managed container platform for distributed apps and services. |
|
|
||||||
| Azure App Service | `aspire add azure-appservice` | The user wants Azure website hosting for web apps/APIs that fit the App Service model. |
|
|
||||||
| Azure Kubernetes Service (AKS) | `aspire add azure-kubernetes` | The user wants Aspire to provision and deploy to Azure-managed Kubernetes. |
|
|
||||||
| AWS | `aspire add aws` | The user wants Aspire to publish/deploy through the AWS Aspire integrations and AWS CDK. |
|
|
||||||
|
|
||||||
If the user says only "Azure", ask again with just the Azure choices: Azure Container Apps, Azure App Service, or Azure Kubernetes Service (AKS). If the AppHost already contains exactly one deployment environment and the user did not ask to change targets, use that target and tell the user what was detected.
|
|
||||||
|
|
||||||
### Ask before creating cloud resources when intent is not explicit
|
|
||||||
|
|
||||||
Cloud deploys can create billable resources. If the user asked for a plan, preview, validation, or "make this deployable", stop after the deployment plan/artifacts and ask before running the command that provisions resources.
|
|
||||||
|
|
||||||
If the user explicitly asked to deploy now, continue through preflight and deployment, but still surface any target choice, subscription/resource group ambiguity, or missing parameter decisions before provisioning.
|
|
||||||
|
|
||||||
### Keep Azure deployment Aspire-native
|
|
||||||
|
|
||||||
The Azure deployment path in this skill is `aspire add <azure-target>`, AppHost environment configuration, `aspire publish`, and `aspire deploy`. Do not route Azure deployment work through a separate Azure deployment tool or generated infrastructure workflow.
|
|
||||||
|
|
||||||
## Default workflow
|
|
||||||
|
|
||||||
1. **Orient to the Aspire workspace.**
|
|
||||||
- Start with `aspire ls` to list AppHosts in the current scope, then use `aspire.config.json`, AppHost project files, or `aspire ps` if more context is needed.
|
|
||||||
- If no AppHost exists, stop deployment work and invoke the `aspireify` skill to initialize/wire the AppHost before continuing.
|
|
||||||
- Identify C# vs TypeScript AppHost.
|
|
||||||
- Prefer Aspire CLI commands for discovery and state inspection.
|
|
||||||
2. **Clarify or infer the deployment target.**
|
|
||||||
- If the user named Docker Compose, Kubernetes, Azure Container Apps, Azure App Service, Azure Kubernetes Service (AKS), or AWS, load that target reference without asking again.
|
|
||||||
- If they only said "deploy", inspect existing AppHost target environment resources.
|
|
||||||
- If exactly one target environment already exists, use it and state what was detected.
|
|
||||||
- If multiple targets exist, none exists, or the user says only "Azure", ask where to deploy using the choices above.
|
|
||||||
3. **Load target and app-type references.**
|
|
||||||
- Docker Compose: [references/docker-compose.md](references/docker-compose.md)
|
|
||||||
- Kubernetes and Azure Kubernetes Service (AKS): [references/kubernetes.md](references/kubernetes.md)
|
|
||||||
- Azure Container Apps/App Service/Azure Kubernetes Service (AKS): [references/azure.md](references/azure.md)
|
|
||||||
- AWS: [references/aws.md](references/aws.md)
|
|
||||||
- JavaScript app resources: [references/javascript.md](references/javascript.md)
|
|
||||||
- CI/CD or GitHub Actions automation: [references/cicd.md](references/cicd.md)
|
|
||||||
4. **Use Aspire docs search for current guidance.**
|
|
||||||
- Search and get the target deployment docs.
|
|
||||||
- Search API docs before editing AppHost code.
|
|
||||||
5. **Apply the target code changes.**
|
|
||||||
- Run the target's `aspire add ...` command if the integration is missing.
|
|
||||||
- Add the deployment environment resource to the AppHost.
|
|
||||||
- Do not add explicit compute-environment assignment for the common single-environment case. Only disambiguate when the AppHost has multiple deployment environments. In C# this is usually `WithComputeEnvironment(...)`; for TypeScript AppHosts, verify the current language-specific docs before assuming an equivalent.
|
|
||||||
- Add only the target-specific customization APIs the deployment needs, such as endpoint exposure, Helm settings, Compose file customization, Azure site/container app customization, or AWS publish target overrides.
|
|
||||||
6. **Preflight the deployment model.**
|
|
||||||
- Confirm the target integration package exists in the AppHost.
|
|
||||||
- Confirm the AppHost has the target environment resource.
|
|
||||||
- Confirm compute resources are assigned to the target environment only when multiple compute environments exist. A single compute environment is the common case and can be inferred.
|
|
||||||
- Inventory parameters, secrets, connection strings, external endpoints, container registries, and target-specific prerequisites.
|
|
||||||
- For Azure or AWS, confirm auth, target account/subscription, region/location, and resource group/stack context.
|
|
||||||
7. **Preview before applying.**
|
|
||||||
- Run `aspire publish --list-steps` or `aspire deploy --list-steps`.
|
|
||||||
- Use `aspire publish -o <scratch-or-output-path>` when artifact review is requested.
|
|
||||||
- Treat published artifacts as a preview/handoff. `aspire deploy` resolves values and applies the deployment from the AppHost model; it does not consume a previously published output directory.
|
|
||||||
- Summarize resources, endpoints, parameters, secrets, identities, and generated artifacts.
|
|
||||||
8. **Deploy or hand off.**
|
|
||||||
- Run `aspire deploy` when the user asked to deploy and preflight is complete.
|
|
||||||
- Run a named step with `aspire do <step>` only when the user asked for a specific pipeline step.
|
|
||||||
- For published artifacts, explain the target-native apply step.
|
|
||||||
9. **Destroy only when explicitly requested.**
|
|
||||||
- Run `aspire destroy` to execute the selected AppHost/environment's target destroy pipeline.
|
|
||||||
- Confirm the AppHost, environment, target account/subscription/cluster, and destructive intent before running it.
|
|
||||||
- Use `--yes` only when the user or CI workflow already made teardown intent explicit.
|
|
||||||
- Prefer `aspire destroy` over target-native delete commands unless you are troubleshooting failed teardown or cleaning up unmanaged leftovers.
|
|
||||||
10. **Verify the outcome.**
|
|
||||||
- Use target output, `aspire describe`, cloud CLI, Docker Compose, kubectl, or endpoint checks appropriate to the target.
|
|
||||||
- After destroy, verify target resources are removed or record any leftovers that require manual cleanup.
|
|
||||||
|
|
||||||
## AppHost target detection
|
|
||||||
|
|
||||||
Search the AppHost for deployment environment resources:
|
|
||||||
|
|
||||||
| Target | Aspire add command | Integration | AppHost environment concept |
|
|
||||||
|--------|--------------------|-------------|-----------------------------|
|
|
||||||
| Docker Compose | `aspire add docker` | Docker hosting | Docker Compose environment |
|
|
||||||
| Kubernetes | `aspire add kubernetes` | Kubernetes hosting | Kubernetes environment |
|
|
||||||
| Azure Container Apps | `aspire add azure-appcontainers` | Azure Container Apps hosting | Azure Container Apps environment |
|
|
||||||
| Azure App Service | `aspire add azure-appservice` | Azure App Service hosting | Azure App Service environment |
|
|
||||||
| Azure Kubernetes Service (AKS) | `aspire add azure-kubernetes` | Azure Kubernetes hosting | Azure Kubernetes Service (AKS) environment |
|
|
||||||
| AWS | `aspire add aws` | AWS hosting | AWS CDK environment |
|
|
||||||
|
|
||||||
Use this table only for orientation. Before editing code, verify the current API in Aspire docs for the AppHost language.
|
|
||||||
|
|
||||||
## Parameter and secret preflight
|
|
||||||
|
|
||||||
Parameters are deployment inputs. They may be supplied by configuration files, user secrets, environment variables, command-line args, interactive prompts, or CI/CD secret stores depending on the target and command.
|
|
||||||
|
|
||||||
Before deployment, report:
|
|
||||||
|
|
||||||
- Parameter name from AppHost parameter APIs, including config-backed parameters
|
|
||||||
- Whether it is secret
|
|
||||||
- Expected provider syntax such as `Parameters__name`; for parameter names with dashes, use underscores in environment variables, for example `registry-endpoint` becomes `Parameters__registry_endpoint`
|
|
||||||
- Where it flows, such as a project environment variable, connection string, Key Vault secret, Helm Secret, Compose `.env`, or Azure app setting
|
|
||||||
- Whether a value appears configured or missing
|
|
||||||
|
|
||||||
Use `aspire secret list` for AppHost user secrets when appropriate, but do not print secret values. For deployment artifacts, inspect generated placeholders and mappings, not raw secret content.
|
|
||||||
|
|
||||||
## Target references
|
|
||||||
|
|
||||||
- [references/docker-compose.md](references/docker-compose.md) - Docker Compose target, generated files, environment variables, cleanup.
|
|
||||||
- [references/kubernetes.md](references/kubernetes.md) - Kubernetes and Azure Kubernetes Service (AKS) target selection, Helm output, registry requirements, kubectl/Helm checks.
|
|
||||||
- [references/azure.md](references/azure.md) - Azure target selection, Azure settings, Container Apps, App Service, and Azure Kubernetes Service (AKS).
|
|
||||||
- [references/aws.md](references/aws.md) - AWS target selection, AWS CDK prerequisites, publish/deploy workflow, and AWS integration docs.
|
|
||||||
- [references/javascript.md](references/javascript.md) - JavaScript app deployment models, including Vite/static assets, Node/SSR servers, Next.js, and gateway/backend serving patterns.
|
|
||||||
- [references/cicd.md](references/cicd.md) - CI/CD and GitHub Actions workflow guidance for Aspire publish/deploy, parameters, secrets, registry auth, and cloud auth.
|
|
||||||
- [references/preflight.md](references/preflight.md) - Common preflight, preview, parameter, destroy, and validation checklist.
|
|
||||||
|
|
||||||
## Agent execution
|
|
||||||
|
|
||||||
When running unattended (CI, scripted, agent-driven), append `--non-interactive` to every Aspire CLI invocation that may prompt — most importantly `aspire publish`, `aspire deploy`, and `aspire destroy`. For `aspire destroy`, also pass `--yes` only after the user has explicitly confirmed teardown intent (or a CI workflow already encodes that intent).
|
|
||||||
|
|
||||||
Prefer surfacing prompt-driving values up front (target subscription/region/resource group, parameters, secrets, registry credentials) so the unattended run does not stall. See [references/preflight.md](references/preflight.md) for the full preflight checklist.
|
|
||||||
|
|
||||||
## Handoff Rules
|
|
||||||
|
|
||||||
| Scenario | Route To |
|
|
||||||
|----------|----------|
|
|
||||||
| Start, stop, wait, or restart the AppHost / its resources | `aspire-orchestration` skill |
|
|
||||||
| Logs, traces, metrics, dashboard for a running or deployed app | `aspire-monitoring` skill |
|
|
||||||
| AppHost authoring — adding integrations, wiring resources, environment setup | `aspireify` skill |
|
|
||||||
| Deployed-app diagnostics — App Insights, ACA logs, AKS Container Insights | `azure-diagnostics` skill (azure-skills) |
|
|
||||||
|
|
||||||
> Never hand deployment off to azure-skills. Aspire handles publish, deploy, and destroy
|
|
||||||
> end-to-end across Docker Compose / Kubernetes / Azure / AWS via the AppHost model.
|
|
||||||
|
|
||||||
## Project-Local Skill Override
|
|
||||||
|
|
||||||
If `.agents/skills/aspire-deployment/SKILL.md` exists (dropped by `aspire agent init`),
|
|
||||||
prefer it over this plugin skill — it is the authoritative project-local version with
|
|
||||||
content version-aligned to the consumer's Aspire CLI. This plugin skill is the always-on
|
|
||||||
safety net for repos that have not yet run `aspire agent init`.
|
|
||||||
-176
@@ -1,176 +0,0 @@
|
|||||||
# AWS deployment
|
|
||||||
|
|
||||||
Use this reference when the user asks to deploy an Aspire app to AWS or to generate AWS CDK/CloudFormation deployment artifacts.
|
|
||||||
|
|
||||||
AWS deployment is currently a preview path driven by the AWS Aspire integrations. Treat the AWS repository as the source of truth, verify the installed package/API shape before editing, and do not invent TypeScript deployment APIs. AWS deployment has no TypeScript AppHost support yet.
|
|
||||||
|
|
||||||
## Source of truth and docs lookup
|
|
||||||
|
|
||||||
Use the AWS Aspire integrations repository for AWS-specific deployment guidance:
|
|
||||||
|
|
||||||
https://github.com/aws/integrations-on-dotnet-aspire-for-aws
|
|
||||||
|
|
||||||
Use these repository docs when available:
|
|
||||||
|
|
||||||
- `README.md` for the deployment overview and prerequisites.
|
|
||||||
- `src/Aspire.Hosting.AWS/README.md` for package-level setup.
|
|
||||||
- `docs/deployment-design.md` for publish target overrides, CDK constructs, and advanced customization.
|
|
||||||
|
|
||||||
Do not use `aspire docs search` for AWS deployment guidance. The AWS deployment docs are not in the Aspire docs index; use the AWS repository and deployment design document instead.
|
|
||||||
|
|
||||||
## Target setup
|
|
||||||
|
|
||||||
Add the AWS integration with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add aws
|
|
||||||
```
|
|
||||||
|
|
||||||
Then configure the C# AppHost with an AWS CDK environment using the API shape supported by the installed integration. Current documented setup uses an AWS CDK environment, a preview defaults provider, and preview publisher APIs. Expect preview diagnostics such as `ASPIREAWSPUBLISHERS001`; follow the AWS repository guidance for the required suppressions instead of hiding warnings broadly.
|
|
||||||
|
|
||||||
## Code changes to make
|
|
||||||
|
|
||||||
Make these changes in the C# AppHost when the AWS integration docs still show the C#-only preview API:
|
|
||||||
|
|
||||||
1. Run `aspire add aws` if the AppHost does not already reference `Aspire.Hosting.AWS`.
|
|
||||||
2. Add the AWS CDK environment near the top of the AppHost, before resources that should deploy:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
#pragma warning disable ASPIREAWSPUBLISHERS001
|
|
||||||
|
|
||||||
var builder = DistributedApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
builder.AddAWSCDKEnvironment(
|
|
||||||
name: "<stack-name>",
|
|
||||||
cdkDefaultsProviderFactory: CDKDefaultsProviderFactory.Preview_V1);
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Keep existing Aspire resources in the AppHost. Default AWS deployment mapping handles common resources without per-resource publish calls.
|
|
||||||
4. Keep `WithReference(...)` relationships between resources. The AWS deployment system uses references for environment variables, VPC attachment, and security group connectivity where supported.
|
|
||||||
5. Add AWS-specific resources only when the app actually needs them and the AWS docs show the current shape, such as `AddAWSLambdaFunction` for Lambda projects/handlers.
|
|
||||||
6. Add publish override methods only when the user asks for a specific AWS shape or the default mapping is wrong. For example, use an ECS Fargate/ALB publish target only after verifying the current method name in the AWS deployment design document.
|
|
||||||
7. Do not invent a TypeScript AWS deployment environment. If the AppHost is TypeScript, stop and report that the current AWS deployment integration is C# AppHost-focused.
|
|
||||||
|
|
||||||
Do not add AWS CDK constructs directly as the first approach. Start with Aspire resources and references, then use AWS construct callbacks or custom CDK stacks only for explicit infrastructure customization.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
Check:
|
|
||||||
|
|
||||||
- AWS credentials are available for the target account.
|
|
||||||
- The AWS region is known and matches the user's intended deployment target.
|
|
||||||
- Node.js 22.x is installed when the AWS CDK tooling requires it.
|
|
||||||
- AWS CDK is installed and available.
|
|
||||||
- The target account and region are bootstrapped for CDK before first deployment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cdk bootstrap aws://<account-id>/<region>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Docker is available when compute resource images need to be built.
|
|
||||||
- Required AppHost parameters are configured or can be prompted.
|
|
||||||
- If the AppHost project references unsigned AWS CDK packages and the build reports package signing diagnostics, follow the AWS repository guidance for the narrow project warning suppression.
|
|
||||||
|
|
||||||
Use AWS CLI to verify identity and region before deploying:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aws sts get-caller-identity
|
|
||||||
aws configure list
|
|
||||||
aws configure get region
|
|
||||||
cdk --version
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not print access keys, secret keys, session tokens, or resolved secret parameter values.
|
|
||||||
|
|
||||||
## Preview and publish
|
|
||||||
|
|
||||||
Use Aspire publish to generate deployment artifacts before applying them when the user wants review or validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire publish -o aws-artifacts
|
|
||||||
```
|
|
||||||
|
|
||||||
For AWS CDK deployment, the AWS integration transforms Aspire resources into CDK constructs and synthesizes CloudFormation templates under `cdk.out` in the output location. Inspect the generated CDK output before deploying when the user asked for a preview:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
find aws-artifacts -maxdepth 3 -type f | sort
|
|
||||||
cdk ls --app aws-artifacts/cdk.out
|
|
||||||
cdk diff --app aws-artifacts/cdk.out
|
|
||||||
```
|
|
||||||
|
|
||||||
The exact output path can vary with the installed integration; inspect the publish output and generated files instead of assuming a fixed directory layout.
|
|
||||||
|
|
||||||
## Deploy
|
|
||||||
|
|
||||||
Deploy with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
The AWS integration runs the publish step and then uses AWS CDK deployment against the configured AWS account and region.
|
|
||||||
|
|
||||||
Use `aspire deploy --list-steps` before applying changes when the user asked for validation or when the AppHost has custom AWS publish targets.
|
|
||||||
|
|
||||||
## Destroy
|
|
||||||
|
|
||||||
Use Aspire to run the AWS target's destroy pipeline:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire destroy --environment <name>
|
|
||||||
```
|
|
||||||
|
|
||||||
For AWS, `aspire destroy` delegates to the AWS deployment target for the selected AppHost/environment. Confirm the AWS account, region, CDK stack name, AppHost, and environment before running destroy. Use `--yes` only after destructive intent is explicit. Use AWS CLI or CDK destroy commands only to investigate failed teardown or clean up resources that the Aspire AWS target did not manage.
|
|
||||||
|
|
||||||
## Resource mapping and customization
|
|
||||||
|
|
||||||
Current AWS guidance says resources are mapped to AWS services by default and can be overridden with publish extension methods. Keep these rules in mind:
|
|
||||||
|
|
||||||
- Web projects can map to ECS Fargate-style targets by default.
|
|
||||||
- Lambda project resources can map to AWS Lambda.
|
|
||||||
- Redis can map to ElastiCache.
|
|
||||||
- `WithReference()` drives connectivity such as environment variables, VPC attachment, and security groups.
|
|
||||||
- Custom CDK stacks and construct callbacks are advanced customization points; use them only when the user asks for infrastructure customization and verify the exact API in the AWS design document.
|
|
||||||
|
|
||||||
Do not assume every Aspire resource has an AWS publish target. If a resource has no supported mapping, call that out before deployment rather than implying it will be provisioned.
|
|
||||||
|
|
||||||
## Troubleshooting live AWS state
|
|
||||||
|
|
||||||
Use AWS CLI to inspect live resources that Aspire deployed through CDK/CloudFormation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE UPDATE_ROLLBACK_COMPLETE
|
|
||||||
aws cloudformation describe-stacks --stack-name "<stack-name>"
|
|
||||||
aws cloudformation describe-stack-events --stack-name "<stack-name>" --max-items 20
|
|
||||||
aws cloudformation list-stack-resources --stack-name "<stack-name>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use service-specific commands based on the resources in the generated CDK output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ECS/Fargate
|
|
||||||
aws ecs list-clusters
|
|
||||||
aws ecs list-services --cluster "<cluster-name>"
|
|
||||||
aws ecs describe-services --cluster "<cluster-name>" --services "<service-name>"
|
|
||||||
|
|
||||||
# Lambda
|
|
||||||
aws lambda list-functions
|
|
||||||
aws lambda get-function --function-name "<function-name>"
|
|
||||||
|
|
||||||
# ElastiCache
|
|
||||||
aws elasticache describe-cache-clusters --show-cache-node-info
|
|
||||||
```
|
|
||||||
|
|
||||||
When a deploy fails, inspect CloudFormation stack events first, then the specific service that failed. Match live stack/resource names back to the AWS CDK environment name, Aspire deployment output, and generated `cdk.out` templates.
|
|
||||||
|
|
||||||
## Common checks
|
|
||||||
|
|
||||||
- Confirm the generated AWS stack name and region before deployment.
|
|
||||||
- Confirm credentials match the intended account.
|
|
||||||
- Confirm CDK bootstrap has run for the account and region.
|
|
||||||
- Treat preview deployment APIs as subject to change; re-check the AWS repository before making assumptions.
|
|
||||||
- When customization is needed, use the AWS repository's deployment design document and verify the exact API for the AppHost language before editing.
|
|
||||||
- Confirm any AppHost parameters that become CloudFormation/CDK inputs, especially names containing punctuation, by inspecting the synthesized output.
|
|
||||||
- If an ECS service is unhealthy, inspect ECS service events, task status, logs, image pull permissions, and security groups.
|
|
||||||
- If CloudFormation rolls back, read the first failed stack event rather than only the final rollback event.
|
|
||||||
-316
@@ -1,316 +0,0 @@
|
|||||||
# Azure deployment
|
|
||||||
|
|
||||||
Use this reference when the user asks to deploy an Aspire app to Azure, Azure Container Apps, Azure App Service, or Azure Kubernetes Service (AKS).
|
|
||||||
|
|
||||||
## Use Aspire-native Azure deployment
|
|
||||||
|
|
||||||
For Aspire apps, start from the AppHost deployment model. Azure deployment should go through the Azure hosting integrations, AppHost environment resources, `aspire publish`, and `aspire deploy`.
|
|
||||||
|
|
||||||
Do not generate or hand-edit Azure infrastructure as the source of truth unless the user explicitly wants a published artifact handoff. The AppHost Azure environment determines what gets provisioned.
|
|
||||||
|
|
||||||
## Choose Azure target
|
|
||||||
|
|
||||||
| Target | Aspire add command | Best fit | Integration | Environment concept |
|
|
||||||
|--------|--------------------|----------|-------------|---------------------|
|
|
||||||
| Azure Container Apps | `aspire add azure-appcontainers` | Distributed/containerized services with internal/external ingress | Azure Container Apps hosting | Azure Container Apps environment |
|
|
||||||
| Azure App Service | `aspire add azure-appservice` | Public web apps/APIs that fit the App Service website model | Azure App Service hosting | Azure App Service environment |
|
|
||||||
| Azure Kubernetes Service (AKS) | `aspire add azure-kubernetes` | Kubernetes workloads where Aspire should provision Azure Kubernetes infrastructure | Azure Kubernetes hosting | Azure Kubernetes Service (AKS) environment |
|
|
||||||
|
|
||||||
If the user only says "Azure", ask which target unless the AppHost already contains exactly one Azure environment resource or the scenario strongly implies one.
|
|
||||||
|
|
||||||
Use these choices:
|
|
||||||
|
|
||||||
| Choice | Aspire add command | Use when |
|
|
||||||
|--------|--------------------|----------|
|
|
||||||
| Azure Container Apps | `aspire add azure-appcontainers` | The user wants an Azure-managed container platform for distributed apps and services. |
|
|
||||||
| Azure App Service | `aspire add azure-appservice` | The user wants Azure website hosting for web apps/APIs that fit the App Service model. |
|
|
||||||
| Azure Kubernetes Service (AKS) | `aspire add azure-kubernetes` | The user wants Aspire to provision and deploy to Azure-managed Kubernetes. |
|
|
||||||
|
|
||||||
## Docs to load
|
|
||||||
|
|
||||||
Always start with current docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "Azure Container Apps deployment"
|
|
||||||
aspire docs search "Azure App Service deployment"
|
|
||||||
aspire docs search "Azure Kubernetes Service deployment"
|
|
||||||
aspire docs search "Azure Kubernetes Service hosting integration"
|
|
||||||
aspire docs get "deploy-to-azure-container-apps"
|
|
||||||
aspire docs get "configure-azure-container-apps-environments"
|
|
||||||
aspire docs get "deploy-to-azure-app-service"
|
|
||||||
aspire docs get "set-up-azure-app-service-in-the-apphost"
|
|
||||||
aspire docs get "deploy-to-azure-kubernetes-service-aks"
|
|
||||||
aspire docs get "external-parameters"
|
|
||||||
```
|
|
||||||
|
|
||||||
Some Azure deployment searches can return noisy integration results. Search first so you can find renamed pages, then prefer the known deployment slugs above when they are available. If one of these known slugs is not available in the installed Aspire docs, inspect the closest current replacement before editing.
|
|
||||||
|
|
||||||
Use API docs before editing. Search for the target environment, endpoint, and customization concepts in the AppHost language you detected:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "AddAzureContainerAppEnvironment" --language csharp
|
|
||||||
aspire docs api search "AddAzureContainerAppEnvironment" --language typescript
|
|
||||||
aspire docs api search "AddAzureAppServiceEnvironment" --language csharp
|
|
||||||
aspire docs api search "AddAzureAppServiceEnvironment" --language typescript
|
|
||||||
aspire docs api search "AddAzureKubernetesEnvironment" --language csharp
|
|
||||||
aspire docs api search "AddAzureKubernetesEnvironment" --language typescript
|
|
||||||
aspire docs api search "WithExternalHttpEndpoints" --language csharp
|
|
||||||
aspire docs api search "WithExternalHttpEndpoints" --language typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shared Azure preflight
|
|
||||||
|
|
||||||
Check:
|
|
||||||
|
|
||||||
- Azure CLI is installed when local deploy uses Azure CLI credentials.
|
|
||||||
- The user is authenticated (`az login`) or another `Azure:CredentialSource` is configured.
|
|
||||||
- Target subscription, location, and resource group are known.
|
|
||||||
- Required AppHost parameters are configured or can be prompted.
|
|
||||||
- The AppHost has the correct Azure target integration and environment resource.
|
|
||||||
- Production-only resources are not hidden behind run-mode-only execution context checks.
|
|
||||||
- Compute resources are assigned to the intended Azure environment when multiple compute environments exist.
|
|
||||||
|
|
||||||
Azure deployment settings:
|
|
||||||
|
|
||||||
| Setting | Environment variable | Purpose |
|
|
||||||
|---------|----------------------|---------|
|
|
||||||
| `Azure:SubscriptionId` | `Azure__SubscriptionId` | Target subscription |
|
|
||||||
| `Azure:Location` | `Azure__Location` | Azure region |
|
|
||||||
| `Azure:ResourceGroup` | `Azure__ResourceGroup` | Resource group |
|
|
||||||
| `Azure:CredentialSource` | `Azure__CredentialSource` | Credential source override |
|
|
||||||
|
|
||||||
For local development, `aspire secret set` can store these values for the AppHost:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire secret set "Azure:SubscriptionId" "<subscription-id>"
|
|
||||||
aspire secret set "Azure:Location" "<region>"
|
|
||||||
aspire secret set "Azure:ResourceGroup" "<resource-group>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not use `aspire secret set` as the deployment parameter mechanism. It is a local/dev convenience today. For publish/deploy, TypeScript AppHosts, CI, and other non-interactive deploys, set deployment settings and AppHost parameters as environment variables on the `aspire deploy` process:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
Azure__SubscriptionId="<subscription-id>" \
|
|
||||||
Azure__Location="westus2" \
|
|
||||||
Azure__ResourceGroup="my-app-rg" \
|
|
||||||
Parameters__api_key="<secret-value>" \
|
|
||||||
aspire deploy --apphost ./apphost.ts --environment Production --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not print secret values. Subscription/resource group/location are not secrets, but still summarize them carefully.
|
|
||||||
|
|
||||||
If a parameter name contains `-`, use `_` in the environment variable name. For example, AppHost parameter `registry-endpoint` maps to `Parameters__registry_endpoint`.
|
|
||||||
|
|
||||||
If `az account show` reports a tenant but `aspire deploy` later prompts during `fetch-tenant`, do not assume `aspire secret set "Azure:TenantId" ...` will answer that prompt. Tenant selection can still be a pipeline prompt. Run the deploy in a real interactive terminal/PTY, or make the Azure CLI login context unambiguous before deploying, for example with `az login --tenant <tenant-id>` or `azure/login`'s `tenant-id` input in GitHub Actions.
|
|
||||||
|
|
||||||
## Azure Container Apps
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add azure-appcontainers
|
|
||||||
```
|
|
||||||
|
|
||||||
Code changes:
|
|
||||||
|
|
||||||
1. Add an Azure Container Apps environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var aca = builder.AddAzureContainerAppEnvironment("aca");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const aca = await builder.addAzureContainerAppEnvironment("aca");
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add `.WithExternalHttpEndpoints()` to C# compute resources that should be publicly reachable, such as projects, JavaScript/Python executables, containers, Dockerfiles, and similar workloads. For TypeScript AppHosts, use the endpoint/external endpoint API returned by docs.
|
|
||||||
3. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each Azure Container Apps workload; in C#, add `.WithComputeEnvironment(aca)` to each compute resource that should deploy there. For TypeScript AppHosts, verify the current language-specific docs before assuming an equivalent assignment API.
|
|
||||||
4. Do not add `PublishAsAzureContainerApp(...)` / `publishAsAzureContainerApp(...)` for a default Container App deployment. Add it only when the user needs per-resource Container App customization. Use Container App Job APIs only for worker/job resources that should run as jobs.
|
|
||||||
5. Add managed Azure resources for production dependencies when appropriate, then keep normal `WithReference` / `withReference` connections so Aspire wires app settings, identities, and connection details.
|
|
||||||
|
|
||||||
Key checks:
|
|
||||||
|
|
||||||
- One external HTTP/HTTP2 target-port group becomes the main ingress.
|
|
||||||
- Multiple external endpoint groups are not supported.
|
|
||||||
- External non-HTTP endpoints are not supported.
|
|
||||||
- HTTP/HTTP2 and TCP endpoints cannot be mixed on the same target port.
|
|
||||||
- The external HTTP endpoints API marks endpoints reachable outside the Container Apps environment; verify the exact API name for C# or TypeScript before editing.
|
|
||||||
- Internal services should not be made external unless the user wants public access.
|
|
||||||
- HTTP endpoints are upgraded to HTTPS by default for deployed endpoint URLs; only disable the upgrade when the user explicitly wants HTTP.
|
|
||||||
- Volumes and bind mounts become Azure Files-backed mounts, with generated storage accounts, file shares, and managed environment storage.
|
|
||||||
- Aspire provisions or attaches ACR, builds images, pushes images, and grants pull permissions with managed identity.
|
|
||||||
- The Aspire dashboard is included by default unless the environment disables it.
|
|
||||||
|
|
||||||
Use the per-resource Container Apps customization API only when customization is required, and verify the exact API name for the AppHost language.
|
|
||||||
|
|
||||||
## Azure App Service
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add azure-appservice
|
|
||||||
```
|
|
||||||
|
|
||||||
Code changes:
|
|
||||||
|
|
||||||
1. Add an Azure App Service environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var appService = builder.AddAzureAppServiceEnvironment("appservice");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const appService = await builder.addAzureAppServiceEnvironment("appservice");
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add `.WithExternalHttpEndpoints()` to the C# web-facing compute resources that should become websites, such as projects, JavaScript/Python executables, containers, Dockerfiles, and similar workloads. For TypeScript AppHosts, use the endpoint/external endpoint API returned by docs.
|
|
||||||
3. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each App Service workload; in C#, add `.WithComputeEnvironment(appService)` to each website compute resource that should deploy there.
|
|
||||||
4. Keep background workers, infrastructure containers, and internal-only services out of App Service unless the docs say the resource is supported. Move dependencies to managed Azure resources or choose Container Apps/Azure Kubernetes Service (AKS).
|
|
||||||
5. Do not add `PublishAsAzureAppServiceWebsite(...)` / `publishAsAzureAppServiceWebsite(...)` for a default website deployment. Add it only when the user needs website customization such as app settings, deployment slots, tags, or infrastructure callbacks.
|
|
||||||
6. Use `SkipEnvironmentVariableNameChecks()` only when the user intentionally accepts App Service's dashed-setting behavior and the app does not depend on the original dashed key at runtime.
|
|
||||||
|
|
||||||
Key checks:
|
|
||||||
|
|
||||||
- App Service is a public website model.
|
|
||||||
- Supported workloads are web-facing project resources and Dockerfile-backed web containers.
|
|
||||||
- External HTTP/HTTPS endpoints are required for deployed websites.
|
|
||||||
- Only HTTP/HTTPS endpoints are supported.
|
|
||||||
- App Service supports a single target port for a deployed website. Multi-port public apps do not fit this target.
|
|
||||||
- Internal-only endpoints and arbitrary infrastructure containers do not fit this target.
|
|
||||||
- App Service upgrades external HTTP endpoint URLs to HTTPS by default.
|
|
||||||
- The environment creates an App Service Plan with default SKU `P0V3`/Premium Linux, a container registry, managed identity, and the dashboard by default.
|
|
||||||
- Application Insights is optional and must be enabled/configured intentionally.
|
|
||||||
- Environment variable names containing `-` fail validation because App Service removes dashes at runtime; prefer dash-free connection names rather than bypassing validation.
|
|
||||||
- Use managed Azure resources for databases, caches, and brokers.
|
|
||||||
|
|
||||||
Use the per-site App Service customization API only for app settings, deployment slots, tags, validation overrides, or other per-site changes, and verify the exact API name for the AppHost language.
|
|
||||||
|
|
||||||
## Azure Kubernetes Service (AKS)
|
|
||||||
|
|
||||||
Setup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add azure-kubernetes
|
|
||||||
```
|
|
||||||
|
|
||||||
Code changes:
|
|
||||||
|
|
||||||
1. Add an Azure Kubernetes Service (AKS) environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var aks = builder.AddAzureKubernetesEnvironment("aks");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const aks = await builder.addAzureKubernetesEnvironment("aks");
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each Azure Kubernetes Service (AKS) workload; in C#, add `.WithComputeEnvironment(aks)` to each compute resource that should deploy there, such as projects, JavaScript/Python executables, containers, Dockerfiles, and similar workloads.
|
|
||||||
3. Do not add a separate plain Kubernetes environment for the same Azure Kubernetes Service (AKS) target. The Azure Kubernetes environment owns the Kubernetes/Helm deployment path.
|
|
||||||
4. Use Azure Kubernetes Service (AKS) customization APIs only when needed, such as node pools, system node pool SKU/count, subnet integration, workload identity, custom ACR, or Application Gateway for Containers.
|
|
||||||
5. Use Kubernetes Gateway/Ingress/service customization APIs for public exposure. Do not assume adding the environment makes every workload public.
|
|
||||||
|
|
||||||
For Azure Kubernetes Service (AKS) details, also load [kubernetes.md](kubernetes.md).
|
|
||||||
|
|
||||||
Azure Kubernetes Service (AKS) uses the Azure Kubernetes integration to provision Azure Kubernetes Service (AKS) infrastructure and then deploys through an inner Kubernetes/Helm environment. It auto-creates ACR by default, supports explicit ACR replacement, and can customize node pools, subnets, workload identity, and Application Gateway for Containers. Verify exact APIs before editing.
|
|
||||||
|
|
||||||
## Preview and deploy
|
|
||||||
|
|
||||||
List pipeline steps:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire deploy --list-steps
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate artifacts without applying:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish -o azure-artifacts
|
|
||||||
```
|
|
||||||
|
|
||||||
Deploy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
For local Azure deploys, prefer a real interactive terminal for the first apply. Azure deployment can prompt for values that are not AppHost parameters, such as tenant selection when multiple tenants are available. Use `--non-interactive` only after configuring deploy-time values with environment variables and confirming the Azure CLI login context is unambiguous.
|
|
||||||
|
|
||||||
Do not pipe an interactive `aspire deploy` through `tee`, `tail`, or another command when prompts may appear. The pipe can make the current terminal non-interactive and selection prompts will fail. Use the attached terminal output and the Aspire CLI log path printed by the command instead of capturing the transcript with a pipe.
|
|
||||||
|
|
||||||
Use `--environment <name>` for staging/production context:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy --environment staging
|
|
||||||
```
|
|
||||||
|
|
||||||
Published Azure artifacts are for preview or handoff. `aspire deploy` resolves parameters and applies the Azure deployment from the AppHost model.
|
|
||||||
|
|
||||||
## Destroy
|
|
||||||
|
|
||||||
Use Aspire to run the Azure target's destroy pipeline:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire destroy --environment <name>
|
|
||||||
```
|
|
||||||
|
|
||||||
For Azure, `aspire destroy` delegates to the Azure deployment target for the selected AppHost/environment. Confirm the Azure subscription, resource group, environment name, and AppHost before running destroy. Use `--yes` only after the user has explicitly approved teardown or in an environment-protected cleanup workflow. Use Azure CLI after destroy to verify resource removal or investigate leftovers; do not start with `az group delete` or target-specific delete commands for resources managed by the Aspire Azure target unless `aspire destroy` cannot complete.
|
|
||||||
|
|
||||||
## Common troubleshooting
|
|
||||||
|
|
||||||
Use Azure CLI to inspect live Azure state that Aspire deployed, while keeping deployment changes in the AppHost and `aspire deploy`.
|
|
||||||
|
|
||||||
Start by confirming the active subscription and resource group:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
az account show --query "{name:name, id:id, tenantId:tenantId}" --output table
|
|
||||||
az group show --name "<resource-group>" --query "{name:name, location:location, provisioningState:properties.provisioningState}" --output table
|
|
||||||
az deployment group list --resource-group "<resource-group>" --query "[].{name:name, state:properties.provisioningState, timestamp:properties.timestamp}" --output table
|
|
||||||
az resource list --resource-group "<resource-group>" --query "[].{name:name, type:type, location:location}" --output table
|
|
||||||
```
|
|
||||||
|
|
||||||
Use target-specific inspection commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Azure Container Apps
|
|
||||||
az containerapp list --resource-group "<resource-group>" --query "[].{name:name, state:properties.runningStatus, fqdn:properties.configuration.ingress.fqdn}" --output table
|
|
||||||
az containerapp revision list --resource-group "<resource-group>" --name "<container-app-name>" --query "[].{name:name, active:properties.active, healthState:properties.healthState}" --output table
|
|
||||||
az containerapp logs show --resource-group "<resource-group>" --name "<container-app-name>" --tail 100
|
|
||||||
|
|
||||||
# Azure App Service
|
|
||||||
az webapp list --resource-group "<resource-group>" --query "[].{name:name, state:state, hostNames:defaultHostName}" --output table
|
|
||||||
az webapp config appsettings list --resource-group "<resource-group>" --name "<web-app-name>" --query "[].name" --output table
|
|
||||||
az webapp log tail --resource-group "<resource-group>" --name "<web-app-name>"
|
|
||||||
|
|
||||||
# Azure Kubernetes Service (AKS)
|
|
||||||
az aks show --resource-group "<resource-group>" --name "<cluster-name>" --query "{name:name, state:provisioningState, fqdn:fqdn, kubernetesVersion:kubernetesVersion}" --output table
|
|
||||||
az aks get-credentials --resource-group "<resource-group>" --name "<cluster-name>"
|
|
||||||
kubectl get pods,svc,ingress --all-namespaces
|
|
||||||
helm list --all-namespaces
|
|
||||||
```
|
|
||||||
|
|
||||||
When troubleshooting generated Azure resources, match the live resource names and tags back to the Aspire deployment summary, AppHost environment resource name, and selected `--environment` value. Do not print secret values; for Key Vault or app settings, inspect key names and references rather than values.
|
|
||||||
|
|
||||||
- Missing Azure settings: configure `Azure__SubscriptionId`, `Azure__Location`, and optionally `Azure__ResourceGroup` on the deploy process.
|
|
||||||
- Wrong subscription: check `az account show` and compare to `Azure__SubscriptionId` or AppHost config.
|
|
||||||
- Failed resource group deployment: inspect the failed deployment with `az deployment group show --resource-group "<resource-group>" --name "<deployment-name>"` and then inspect operation errors with `az deployment operation group list --resource-group "<resource-group>" --name "<deployment-name>" --query "[?properties.provisioningState=='Failed']"`.
|
|
||||||
- Parameter prompts in CI: provide `Parameters__*` environment variables through pipeline secrets/variables.
|
|
||||||
- Secrets to Key Vault: use the Azure Key Vault hosting integration and secret APIs only after confirming docs and user intent.
|
|
||||||
- Public endpoint surprise: inspect external endpoint configuration and target endpoint rules before deployment.
|
|
||||||
- Container Apps revision unhealthy: check revision health, ingress, image pull, managed identity, and logs with `az containerapp revision list` and `az containerapp logs show`.
|
|
||||||
- App Service unsupported resource: move backing services to managed Azure resources or choose Container Apps/Azure Kubernetes Service (AKS) instead.
|
|
||||||
- App Service dashed setting failure: rename the connection/environment key if possible; bypass validation only when the app does not depend on the original dashed key at runtime.
|
|
||||||
- Azure Kubernetes Service (AKS) workload not reachable: refresh credentials with `az aks get-credentials`, then inspect pods, services, ingress/gateway resources, and Helm release status with `kubectl` and `helm`.
|
|
||||||
-342
@@ -1,342 +0,0 @@
|
|||||||
# CI/CD and GitHub Actions deployment
|
|
||||||
|
|
||||||
Use this reference when the user asks to automate Aspire publish/deploy in CI/CD, create a GitHub Actions workflow, publish release artifacts, push container images, or run deployment validation in a pipeline.
|
|
||||||
|
|
||||||
CI/CD should still start from the AppHost model. The pipeline should install the Aspire CLI, install the AppHost/resource toolchains needed by the repo, restore/build the workspace, provide AppHost parameters through CI secrets/variables, run Aspire publish/deploy/list-step commands, and then either upload generated artifacts or deploy with the target's credentials.
|
|
||||||
|
|
||||||
## Docs to load
|
|
||||||
|
|
||||||
Use these searches and docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "ci"
|
|
||||||
aspire docs search "github actions"
|
|
||||||
aspire docs search "external parameters deployment"
|
|
||||||
aspire docs get "testing-in-cicd-pipelines"
|
|
||||||
aspire docs get "example-app-lifecycle-workflow"
|
|
||||||
aspire docs get "external-parameters"
|
|
||||||
```
|
|
||||||
|
|
||||||
`testing-in-cicd-pipelines` is primarily about tests, but it includes useful CI facts: Linux runners have Docker available, CI needs explicit timeouts for tests, Azure credentials must be configured explicitly, and secrets should come from CI variables. `example-app-lifecycle-workflow` is a worked GitHub Actions example for publishing Aspire artifacts and pushing images.
|
|
||||||
|
|
||||||
## Decide publish, deploy, or handoff
|
|
||||||
|
|
||||||
Ask which CI/CD outcome the user wants if it is not clear:
|
|
||||||
|
|
||||||
| Outcome | Use when | Typical command |
|
|
||||||
|---------|----------|-----------------|
|
|
||||||
| Validate deployment model | PR or pre-merge check should prove the AppHost can produce deployment steps/artifacts | `aspire publish --list-steps` or `aspire deploy --list-steps` |
|
|
||||||
| Publish artifacts | CI should produce Compose/Helm/CDK/Bicep or other target artifacts for review or later apply | `aspire publish -o <output>` |
|
|
||||||
| Push images only | CI should build/push project images but not deploy infrastructure | `aspire do <push-step>` after checking `aspire deploy --list-steps` |
|
|
||||||
| Deploy from CI | Protected branch/environment should provision/update target infrastructure | `aspire deploy --environment <name>` |
|
|
||||||
| Destroy from CI | Explicit cleanup workflow should tear down an Aspire-owned deployment | `aspire destroy --environment <name> --yes` |
|
|
||||||
|
|
||||||
Do not assume `aspire deploy` consumes a previous `aspire publish` output. `aspire deploy` applies directly from the AppHost model and resolves values for the selected environment.
|
|
||||||
|
|
||||||
## GitHub Actions workflow shape
|
|
||||||
|
|
||||||
Use this baseline shape, then add the setup block that matches the AppHost/resource graph and layer target-specific auth and parameters on top:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: production
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Aspire CLI
|
|
||||||
run: |
|
|
||||||
curl -sSL https://aspire.dev/install.sh | bash
|
|
||||||
echo "$HOME/.aspire/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Inspect Aspire deployment steps
|
|
||||||
run: |
|
|
||||||
aspire ls
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire deploy --list-steps
|
|
||||||
```
|
|
||||||
|
|
||||||
Add one or both setup blocks before Aspire commands:
|
|
||||||
|
|
||||||
| AppHost/resources | Setup guidance |
|
|
||||||
|-------------------|----------------|
|
|
||||||
| C# AppHost or .NET project resources | Install .NET with `actions/setup-dotnet`, then use the repo's restore/build command. |
|
|
||||||
| TypeScript AppHost or JavaScript resources | Install Node with `actions/setup-node`, then run the repo's package-manager install/build commands. |
|
|
||||||
| Mixed C#/TypeScript graph | Use both setup blocks so the AppHost and all compute resources can be restored, built, and published. |
|
|
||||||
|
|
||||||
C# AppHost setup:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
|
|
||||||
- name: Restore and build .NET workspace
|
|
||||||
run: |
|
|
||||||
dotnet restore
|
|
||||||
dotnet build --no-restore
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost setup:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22.x
|
|
||||||
|
|
||||||
- name: Install and build TypeScript workspace
|
|
||||||
run: |
|
|
||||||
npm ci
|
|
||||||
npm run build --if-present
|
|
||||||
```
|
|
||||||
|
|
||||||
Use repository-specific setup instead of the generic snippets when the repo already has wrapper scripts, a local SDK restore step, a package manager other than npm, an AppHost `package.json` in a subdirectory, or a required build command.
|
|
||||||
|
|
||||||
## Parameters and secrets
|
|
||||||
|
|
||||||
Map AppHost parameters through workflow `env:` using Aspire configuration environment-variable conventions:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
Parameters__registry_endpoint: ghcr.io
|
|
||||||
Parameters__registry_repository: ${{ github.repository }}
|
|
||||||
Parameters__api_key: ${{ secrets.API_KEY }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
- AppHost parameter `name` maps to `Parameters__name`.
|
|
||||||
- Parameter names with dashes use underscores in environment variables, for example `registry-endpoint` becomes `Parameters__registry_endpoint`.
|
|
||||||
- Secret parameters should come from GitHub Actions secrets or environment secrets.
|
|
||||||
- Do not print secret values. Prefer listing parameter names/status before deployment.
|
|
||||||
- Use GitHub Environments for production secrets and required reviewers when `aspire deploy` provisions cloud resources.
|
|
||||||
|
|
||||||
## Container registry auth
|
|
||||||
|
|
||||||
For GitHub Container Registry, authenticate before Aspire push/publish/deploy steps that build and push images:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then provide registry parameters if the AppHost models them:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Push images with Aspire
|
|
||||||
env:
|
|
||||||
Parameters__registry_endpoint: ghcr.io
|
|
||||||
Parameters__registry_repository: ${{ github.repository }}
|
|
||||||
run: aspire do <push-step>
|
|
||||||
```
|
|
||||||
|
|
||||||
Get the exact push/build step from `aspire deploy --list-steps` or `aspire publish --list-steps`; do not hardcode `push` unless the target docs/listed steps show it.
|
|
||||||
|
|
||||||
## Publish artifacts in GitHub Actions
|
|
||||||
|
|
||||||
Use publish when the pipeline should produce artifacts for review or later apply:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Publish Aspire artifacts
|
|
||||||
env:
|
|
||||||
Parameters__registry_endpoint: ghcr.io
|
|
||||||
Parameters__registry_repository: ${{ github.repository }}
|
|
||||||
run: aspire publish -o ./aspire-output
|
|
||||||
|
|
||||||
- name: Upload Aspire artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: aspire-output
|
|
||||||
path: ./aspire-output
|
|
||||||
```
|
|
||||||
|
|
||||||
Treat published output as potentially sensitive. Docker Compose `.env.<environment>`, Helm values/secrets, CDK output, and target-specific deployment state can include resolved parameter values or secret references. Upload only what the user intends to retain.
|
|
||||||
|
|
||||||
## Deploy from GitHub Actions
|
|
||||||
|
|
||||||
Use deploy only when the workflow is intentionally allowed to modify infrastructure:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Deploy with Aspire
|
|
||||||
env:
|
|
||||||
Parameters__registry_endpoint: ghcr.io
|
|
||||||
Parameters__registry_repository: ${{ github.repository }}
|
|
||||||
run: aspire deploy --environment production --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
Add target-specific authentication before this step:
|
|
||||||
|
|
||||||
- Docker Compose: runner needs Docker/Podman and access to the target Docker host if deploying remotely.
|
|
||||||
- Kubernetes: configure `kubectl` context and registry pull auth before `aspire deploy`.
|
|
||||||
- Azure: authenticate Azure CLI or Azure SDK credentials before `aspire deploy`; set `Azure__SubscriptionId`, `Azure__Location`, and optionally `Azure__ResourceGroup`.
|
|
||||||
- AWS: configure AWS credentials/region, install CDK prerequisites, and bootstrap the account/region before `aspire deploy`.
|
|
||||||
|
|
||||||
Never route Azure deployment through a separate Azure deployment tool from this skill. Keep Azure deployment Aspire-native with `aspire deploy`, and use Azure CLI only for authentication and live state inspection.
|
|
||||||
|
|
||||||
## Destroy from CI/CD
|
|
||||||
|
|
||||||
Use `aspire destroy` for teardown workflows that intentionally run the AppHost deployment target's destroy pipeline:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Destroy with Aspire
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
||||||
env:
|
|
||||||
Azure__SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
Azure__Location: ${{ vars.AZURE_LOCATION }}
|
|
||||||
Azure__ResourceGroup: ${{ vars.AZURE_RESOURCE_GROUP }}
|
|
||||||
run: aspire destroy --environment production --yes --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep destroy jobs manually triggered or gated by a protected GitHub Environment. Reuse the same target authentication, AppHost path, environment, and parameter conventions as deploy. Do not put destroy into normal validation or deploy jobs unless the workflow owns temporary infrastructure and teardown is part of the tested lifecycle. If CI also created external infrastructure that is outside the Aspire deployment target, such as a temporary Kubernetes cluster or registry, clean that up in separate explicit provider-specific steps after the Aspire destroy step.
|
|
||||||
|
|
||||||
## Azure GitHub Actions auth
|
|
||||||
|
|
||||||
Use the repository's preferred Azure auth pattern. For OIDC with `azure/login`, ensure the workflow has `id-token: write` and the cloud app/federated credential is configured:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Azure login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then provide Aspire Azure settings:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
Azure__SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
Azure__Location: ${{ vars.AZURE_LOCATION }}
|
|
||||||
Azure__ResourceGroup: ${{ vars.AZURE_RESOURCE_GROUP }}
|
|
||||||
```
|
|
||||||
|
|
||||||
If the workflow uses service-principal secrets instead of OIDC, set `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_CLIENT_SECRET` as secrets for Azure SDK authentication. Do not echo these values.
|
|
||||||
|
|
||||||
## Azure GitHub Actions deployment pattern
|
|
||||||
|
|
||||||
A real Azure Aspire deployment workflow can be as small as checkout, AppHost toolchain setup, Aspire CLI install, Azure login, and `aspire deploy`. Use these checked-in workflow references when the user wants GitHub Actions to deploy directly to Azure through Aspire and gate it with a GitHub Environment:
|
|
||||||
|
|
||||||
- C# AppHost: [github-actions-azure-csharp.yml](github-actions-azure-csharp.yml)
|
|
||||||
- TypeScript AppHost: [github-actions-azure-typescript.yml](github-actions-azure-typescript.yml)
|
|
||||||
|
|
||||||
The C# AppHost reference has this shape:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: Aspire Deploy CI/CD
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [live]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
aspire_deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Deploy with Aspire
|
|
||||||
environment:
|
|
||||||
name: production
|
|
||||||
env:
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
|
||||||
DOTNET_NOLOGO: true
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
lfs: false
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
dotnet-quality: preview
|
|
||||||
|
|
||||||
- name: Install Aspire CLI
|
|
||||||
run: curl -sSL https://aspire.dev/install.sh | bash
|
|
||||||
|
|
||||||
- name: Add Aspire CLI to PATH
|
|
||||||
run: echo "$HOME/.aspire/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
|
|
||||||
- name: Deploy with Aspire
|
|
||||||
run: aspire deploy --apphost ./src/apphost.cs --environment Production --non-interactive
|
|
||||||
env:
|
|
||||||
Azure__SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
Azure__Location: ${{ vars.AZURE_LOCATION || 'eastus' }}
|
|
||||||
Azure__ResourceGroup: ${{ vars.AZURE_RESOURCE_GROUP }}
|
|
||||||
Parameters__admin_password: ${{ secrets.ADMIN_PASSWORD }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a GitHub Environment named `production` and store deployment values there so approvals, branch rules, environment variables, and environment secrets apply to the deployment job:
|
|
||||||
|
|
||||||
| GitHub Environment value | Example names |
|
|
||||||
|--------------------------|---------------|
|
|
||||||
| Variables | `AZURE_LOCATION`, `AZURE_RESOURCE_GROUP`, app build variables that are safe to expose |
|
|
||||||
| Secrets | `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`, AppHost parameter secrets such as `ADMIN_PASSWORD` |
|
|
||||||
|
|
||||||
Adapt the example instead of copying it blindly:
|
|
||||||
|
|
||||||
- Start from the external `.yml` reference that matches the AppHost language, then adjust paths, package manager commands, package-manager caching, target branch, and parameter names.
|
|
||||||
- Use `--apphost <path>` when the workflow should pin a specific AppHost, such as a single-file `apphost.cs`, `apphost.ts`, or an AppHost project file.
|
|
||||||
- For TypeScript AppHosts, replace the .NET setup with Node/package-manager setup, deploy with `--apphost <path-to-apphost.ts> --non-interactive`, and provide deployment settings and AppHost parameters through the deploy step's `env:`.
|
|
||||||
- Keep `id-token: write` for Azure OIDC login.
|
|
||||||
- Put non-secret deployment settings in GitHub Environment variables when possible, such as `AZURE_LOCATION` and `AZURE_RESOURCE_GROUP`.
|
|
||||||
- Put secret AppHost parameters in GitHub Environment secrets and pass them as `Parameters__*`.
|
|
||||||
- Add app-specific build metadata only when the app consumes it. For example, Vite apps need a `VITE_` prefix for values intended for client-side build-time exposure.
|
|
||||||
- Add extra Azure settings only when the AppHost declares them, such as `Azure__PostgresLocation` for a custom PostgreSQL region.
|
|
||||||
- Add a separate preflight step with `aspire ls` and `aspire deploy --list-steps` when the workflow should show planned deployment steps before applying changes.
|
|
||||||
|
|
||||||
## Validation and troubleshooting
|
|
||||||
|
|
||||||
Before applying changes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire ls
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire deploy --list-steps
|
|
||||||
```
|
|
||||||
|
|
||||||
After deployment, use the target reference's validation commands:
|
|
||||||
|
|
||||||
- Docker Compose: `docker compose ps` against generated files or the target Compose project.
|
|
||||||
- Kubernetes: `kubectl get pods`, `kubectl get svc`, and `helm status`.
|
|
||||||
- Azure: `az account show`, `az resource list`, target-specific `az containerapp`, `az webapp`, or `az aks` commands.
|
|
||||||
- AWS: `aws cloudformation describe-stacks`, stack events, and service-specific AWS CLI commands.
|
|
||||||
|
|
||||||
Common CI/CD failures:
|
|
||||||
|
|
||||||
- Missing parameter: add `Parameters__*` env vars or GitHub Environment secrets.
|
|
||||||
- Wrong registry path: compare `Parameters__registry_endpoint` and `Parameters__registry_repository` to generated image names.
|
|
||||||
- Docker unavailable: use `ubuntu-*` GitHub-hosted runners for Linux containers or a configured self-hosted runner.
|
|
||||||
- Cloud auth uses the wrong identity/subscription/account: print identity metadata only, not secrets.
|
|
||||||
- Publish output contains sensitive values: reduce uploaded paths or use environment-protected artifacts.
|
|
||||||
- Deployment step name changed: rerun `aspire deploy --list-steps` and update `aspire do <step>`.
|
|
||||||
-155
@@ -1,155 +0,0 @@
|
|||||||
# Docker Compose deployment
|
|
||||||
|
|
||||||
Use this reference when the user asks for Docker Compose deployment, local container deployment artifacts, or a non-cloud deployment package.
|
|
||||||
|
|
||||||
## Docs to load
|
|
||||||
|
|
||||||
Always start with current docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "Docker Compose deployment"
|
|
||||||
aspire docs search "Docker hosting integration"
|
|
||||||
aspire docs search "Docker Compose environment variables"
|
|
||||||
aspire docs get "deploy-to-docker-compose"
|
|
||||||
aspire docs get "docker-integration"
|
|
||||||
aspire docs get "<slug>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use API docs before editing. Search in the AppHost language you detected:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "Docker Compose environment" --language csharp
|
|
||||||
aspire docs api search "Docker Compose environment" --language typescript
|
|
||||||
aspire docs api search "Docker Compose service customization" --language csharp
|
|
||||||
aspire docs api search "Docker Compose service customization" --language typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
## Target setup
|
|
||||||
|
|
||||||
Expected package and AppHost environment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add docker
|
|
||||||
```
|
|
||||||
|
|
||||||
Add a Docker Compose environment resource using the C# or TypeScript API shape returned by Aspire docs. When a Docker Compose environment exists, compatible resources are automatically included in generated Compose output. Use the per-resource Docker Compose customization API only for customization.
|
|
||||||
|
|
||||||
## Code changes to make
|
|
||||||
|
|
||||||
Make these changes in the AppHost, not in the generated Compose output:
|
|
||||||
|
|
||||||
1. Run `aspire add docker` if the AppHost does not already reference the Docker hosting integration.
|
|
||||||
2. Add a Docker Compose environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var compose = builder.AddDockerComposeEnvironment("docker-compose");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const compose = await builder.addDockerComposeEnvironment("docker-compose");
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each Docker Compose workload; in C#, add `.WithComputeEnvironment(compose)` to each compute resource that should land in Compose.
|
|
||||||
4. For TypeScript AppHosts, verify the current language-specific docs before assuming an equivalent assignment API.
|
|
||||||
5. Keep normal app model relationships such as `WithReference`, endpoints, parameters, and connection strings in the AppHost. They flow into Compose environment variables and service dependencies.
|
|
||||||
6. Use customization APIs only for real Compose customization:
|
|
||||||
- C#: `compose.ConfigureComposeFile(...)`, `compose.ConfigureEnvFile(...)`, and `resource.PublishAsDockerComposeService(...)`.
|
|
||||||
- TypeScript: `compose.configureComposeFile(...)`, `compose.configureEnvFile(...)`, and `resource.publishAsDockerComposeService(...)`.
|
|
||||||
|
|
||||||
Do not hand-edit `docker-compose.yaml` as the durable fix unless the user explicitly wants to eject generated artifacts.
|
|
||||||
|
|
||||||
## Preflight
|
|
||||||
|
|
||||||
Check:
|
|
||||||
|
|
||||||
- Docker or Podman is installed and running.
|
|
||||||
- Docker must be at least 28.0.0, and Podman must be at least 5.0.0 for current Aspire CLI environment checks.
|
|
||||||
- The AppHost has a Docker Compose environment resource.
|
|
||||||
- The repo does not rely on local bind mounts that will be invalid on the target Docker host.
|
|
||||||
- Parameters and secrets are represented as placeholders in `.env` after `aspire publish` and resolved in `.env.<environment>` after prepare/deploy.
|
|
||||||
- Any fixed ports are intentional and do not conflict on the deployment host.
|
|
||||||
|
|
||||||
Use `ASPIRE_CONTAINER_RUNTIME=docker` or `ASPIRE_CONTAINER_RUNTIME=podman` only when the user needs to force a runtime.
|
|
||||||
|
|
||||||
## Preview and publish
|
|
||||||
|
|
||||||
Generate artifacts without starting containers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected output includes:
|
|
||||||
|
|
||||||
- `aspire-output/docker-compose.yaml`
|
|
||||||
- `aspire-output/.env`
|
|
||||||
- resource Dockerfiles when needed
|
|
||||||
|
|
||||||
For environment-specific output and image build without running the whole deploy, use the target's prepare step if docs/list-steps show it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy --list-steps
|
|
||||||
aspire do prepare-docker-compose --environment staging
|
|
||||||
```
|
|
||||||
|
|
||||||
The exact step name depends on the Docker Compose environment resource name. Use the step shown by `aspire deploy --list-steps`: for an environment resource named `docker-compose`, the prepare step is `prepare-docker-compose`; for one named `compose`, it is `prepare-compose`.
|
|
||||||
|
|
||||||
## Deploy and destroy
|
|
||||||
|
|
||||||
Deploy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Aspire generates Compose output, builds images, writes environment-specific `.env` files, and runs Compose.
|
|
||||||
|
|
||||||
Run the Docker Compose target's destroy pipeline only when requested:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire destroy
|
|
||||||
```
|
|
||||||
|
|
||||||
For Docker Compose, `aspire destroy` delegates to the Compose deployment target for the selected AppHost/environment. Use Docker or Compose commands after destroy only to verify cleanup or investigate leftover containers, networks, volumes, or generated files.
|
|
||||||
|
|
||||||
## Common decisions
|
|
||||||
|
|
||||||
### Publish-only vs deploy
|
|
||||||
|
|
||||||
Use `aspire publish` when the user wants files to review or hand to another deployment system. Use `aspire deploy` when they want Aspire to start the Compose deployment.
|
|
||||||
|
|
||||||
`aspire publish` writes `docker-compose.yaml` and `.env` with blank placeholders for captured values. Prepare/deploy writes `.env.<environment>` with resolved values. Do not expect `aspire deploy` to consume a previously published directory.
|
|
||||||
|
|
||||||
### Customizing generated Compose
|
|
||||||
|
|
||||||
Use docs-backed APIs:
|
|
||||||
|
|
||||||
- The Compose file customization API for global Compose model changes.
|
|
||||||
- The environment file customization API for generated `.env` changes.
|
|
||||||
- The per-resource Docker Compose service customization API for service-level changes.
|
|
||||||
|
|
||||||
Do not hand-edit generated `docker-compose.yaml` as the source of truth unless the user explicitly wants to eject the artifact.
|
|
||||||
|
|
||||||
### Environment files and bind mounts
|
|
||||||
|
|
||||||
Generated `.env` values are intentionally separated from environment-specific `.env.<environment>` values:
|
|
||||||
|
|
||||||
- `.env` is a publish-time placeholder file and preserves existing user values when possible.
|
|
||||||
- `.env.<environment>` is written by prepare/deploy with resolved parameter, image, and bind-mount values.
|
|
||||||
- Project images are represented through image placeholders such as `<RESOURCE>_IMAGE` until prepare/deploy resolves them.
|
|
||||||
- Bind mount source paths are replaced by `<RESOURCE>_BINDMOUNT_<index>` placeholders because local paths often do not exist on another Docker host.
|
|
||||||
- Docker socket mounts are left as the platform socket path instead of being placeholderized.
|
|
||||||
|
|
||||||
Treat `.env.<environment>` as potentially sensitive.
|
|
||||||
|
|
||||||
### Compose project name
|
|
||||||
|
|
||||||
Aspire uses a generated Compose project name based on the environment resource name and, when available, the AppHost path hash. This prevents common collisions between different AppHosts using the same environment name.
|
|
||||||
|
|
||||||
### Service host names
|
|
||||||
|
|
||||||
When a service needs another service's Compose host name, use the Docker Compose environment's host address expression API from docs. Do not hardcode generated service names unless the docs or artifact prove them.
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
name: Aspire Deploy CSharp
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches: [live]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Deploy CSharp AppHost with Aspire
|
|
||||||
environment:
|
|
||||||
name: production
|
|
||||||
env:
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
|
||||||
DOTNET_NOLOGO: true
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
# actions/checkout@v4
|
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
# actions/setup-dotnet@v4
|
|
||||||
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
dotnet-quality: preview
|
|
||||||
|
|
||||||
- name: Install Aspire CLI
|
|
||||||
run: |
|
|
||||||
curl -sSL https://aspire.dev/install.sh | bash
|
|
||||||
echo "$HOME/.aspire/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
# azure/login@v2
|
|
||||||
uses: azure/login@1384c340ab2dda50fed2bee3041d1d87018aa5e8
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
|
|
||||||
- name: Deploy with Aspire
|
|
||||||
run: aspire deploy --apphost ./src/AppHost/AppHost.csproj --environment Production
|
|
||||||
env:
|
|
||||||
Azure__SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
Azure__Location: ${{ vars.AZURE_LOCATION || 'eastus' }}
|
|
||||||
Azure__ResourceGroup: ${{ vars.AZURE_RESOURCE_GROUP }}
|
|
||||||
Parameters__admin_password: ${{ secrets.ADMIN_PASSWORD }}
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
name: Aspire Deploy TypeScript
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches: [live]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Deploy TypeScript AppHost with Aspire
|
|
||||||
environment:
|
|
||||||
name: production
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
# actions/checkout@v4
|
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
# actions/setup-node@v4
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
|
||||||
with:
|
|
||||||
node-version: 22.x
|
|
||||||
|
|
||||||
- name: Install and build workspace
|
|
||||||
run: |
|
|
||||||
npm ci
|
|
||||||
npm run build --if-present
|
|
||||||
|
|
||||||
- name: Install Aspire CLI
|
|
||||||
run: |
|
|
||||||
curl -sSL https://aspire.dev/install.sh | bash
|
|
||||||
echo "$HOME/.aspire/bin" >> "$GITHUB_PATH"
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
# azure/login@v2
|
|
||||||
uses: azure/login@1384c340ab2dda50fed2bee3041d1d87018aa5e8
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
|
|
||||||
- name: Deploy with Aspire
|
|
||||||
run: aspire deploy --apphost ./apphost.ts --environment Production --non-interactive
|
|
||||||
env:
|
|
||||||
Azure__SubscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
Azure__Location: ${{ vars.AZURE_LOCATION || 'eastus' }}
|
|
||||||
Azure__ResourceGroup: ${{ vars.AZURE_RESOURCE_GROUP }}
|
|
||||||
Parameters__admin_password: ${{ secrets.ADMIN_PASSWORD }}
|
|
||||||
-126
@@ -1,126 +0,0 @@
|
|||||||
# JavaScript app deployment
|
|
||||||
|
|
||||||
Use this reference whenever an Aspire deployment includes JavaScript app resources, such as Vite, React, Vue, Angular, Astro, Next.js, Nuxt, or plain Node.js apps.
|
|
||||||
|
|
||||||
JavaScript resources are not just another deployment target. They need an explicit production serving model before the target reference can be trusted. A Vite dev server that works during `aspire run` is often build-only during publish/deploy unless the AppHost says who serves the built files.
|
|
||||||
|
|
||||||
## Integration to add
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add javascript
|
|
||||||
```
|
|
||||||
|
|
||||||
This adds `Aspire.Hosting.JavaScript`, which provides `AddJavaScriptApp`, `AddNodeApp`, `AddViteApp`, `AddNextJsApp`, package-manager helpers, and JavaScript publish modes.
|
|
||||||
|
|
||||||
## Docs to load
|
|
||||||
|
|
||||||
Always start with current docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "javascript deployment"
|
|
||||||
aspire docs get "deploy-javascript-apps"
|
|
||||||
aspire docs get "set-up-javascript-apps-in-the-apphost"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use API docs before editing. Search in the AppHost language you detected:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "AddJavaScriptApp" --language csharp
|
|
||||||
aspire docs api search "AddJavaScriptApp" --language typescript
|
|
||||||
aspire docs api search "AddNodeApp" --language csharp
|
|
||||||
aspire docs api search "AddNodeApp" --language typescript
|
|
||||||
aspire docs api search "AddViteApp" --language csharp
|
|
||||||
aspire docs api search "AddViteApp" --language typescript
|
|
||||||
aspire docs api search "AddNextJsApp" --language csharp
|
|
||||||
aspire docs api search "AddNextJsApp" --language typescript
|
|
||||||
aspire docs api search "PublishAsStaticWebsite" --language csharp
|
|
||||||
aspire docs api search "PublishAsStaticWebsite" --language typescript
|
|
||||||
aspire docs api search "PublishAsNodeServer" --language csharp
|
|
||||||
aspire docs api search "PublishAsNodeServer" --language typescript
|
|
||||||
aspire docs api search "PublishAsPackageScript" --language csharp
|
|
||||||
aspire docs api search "PublishAsPackageScript" --language typescript
|
|
||||||
aspire docs api search "PublishWithContainerFiles" --language csharp
|
|
||||||
aspire docs api search "PublishWithContainerFiles" --language typescript
|
|
||||||
aspire docs api search "PublishWithStaticFiles" --language csharp
|
|
||||||
aspire docs api search "PublishWithStaticFiles" --language typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Use `Aspire.Hosting.JavaScript` for Aspire 13+ apps. `Aspire.Hosting.NodeJs` is the old package name; do not add it for new guidance unless the target repo is intentionally pinned to older Aspire.
|
|
||||||
|
|
||||||
## Resource choice
|
|
||||||
|
|
||||||
Choose the AppHost resource based on what the JavaScript app is during local development:
|
|
||||||
|
|
||||||
| Resource | Use when | C# shape | TypeScript shape |
|
|
||||||
|----------|----------|----------|------------------|
|
|
||||||
| JavaScript app | Generic package-script-driven app | `AddJavaScriptApp(...)` | `addJavaScriptApp(...)` |
|
|
||||||
| Node app | A Node process starts a specific script file | `AddNodeApp(...)` | `addNodeApp(...)` |
|
|
||||||
| Vite app | Vite-based browser app or framework dev server | `AddViteApp(...)` | `addViteApp(...)` |
|
|
||||||
| Next.js app | Next.js app using the dedicated helper | `AddNextJsApp(...)` | `addNextJsApp(...)` |
|
|
||||||
|
|
||||||
`AddNextJsApp` and JavaScript publish methods are experimental. In C# AppHosts, follow docs for the `ASPIREJAVASCRIPT001` suppression instead of suppressing warnings broadly.
|
|
||||||
|
|
||||||
## Production serving model
|
|
||||||
|
|
||||||
Choose the production entrypoint before deploying:
|
|
||||||
|
|
||||||
| Production shape | Use when | AppHost pattern |
|
|
||||||
|------------------|----------|-----------------|
|
|
||||||
| Static frontend served by a backend | The backend should serve built frontend files from `wwwroot`, `static`, or similar | Backend resource uses `PublishWithContainerFiles(frontend, "<path>")` |
|
|
||||||
| Static frontend served by a gateway/BFF | YARP or another proxy is the public entrypoint and serves the built frontend | Gateway uses `PublishWithStaticFiles(frontend)` |
|
|
||||||
| Static frontend served by the JavaScript resource | The frontend should deploy as its own static website/container | JavaScript resource uses `PublishAsStaticWebsite(...)` |
|
|
||||||
| SSR or Node.js server with built output | The framework emits a server entrypoint/artifact | JavaScript resource uses `PublishAsNodeServer(...)` |
|
|
||||||
| SSR or Node.js server started by package script | Runtime needs package manager scripts and runtime dependencies | JavaScript resource uses `PublishAsPackageScript(...)` |
|
|
||||||
| Next.js standalone app | Next.js is configured for standalone output | Use `AddNextJsApp(...)` |
|
|
||||||
|
|
||||||
Do not assume the Vite dev server is the production server. During publish/deploy, Vite resources usually build static files unless the AppHost selects a JavaScript publish mode.
|
|
||||||
|
|
||||||
## Code changes to make
|
|
||||||
|
|
||||||
1. Run `aspire add javascript` if the AppHost does not already reference the JavaScript hosting integration.
|
|
||||||
2. Add the appropriate JavaScript resource.
|
|
||||||
|
|
||||||
C# AppHost examples:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var frontend = builder.AddViteApp("frontend", "./frontend");
|
|
||||||
var api = builder.AddNodeApp("api", "./api", "server.js")
|
|
||||||
.WithHttpEndpoint(env: "PORT");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost examples:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const frontend = await builder.addViteApp("frontend", "./frontend");
|
|
||||||
const api = await builder
|
|
||||||
.addNodeApp("api", "./api", "server.js")
|
|
||||||
.withHttpEndpoint({ env: "PORT" });
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Configure package manager and scripts only when defaults are wrong:
|
|
||||||
- C#: `WithNpm(...)`, `WithYarn(...)`, `WithPnpm(...)`, `WithBun(...)`, `WithBuildScript(...)`, `WithRunScript(...)`.
|
|
||||||
- TypeScript: `withNpm(...)`, `withYarn(...)`, `withPnpm(...)`, `withBun(...)`, `withBuildScript(...)`, `withRunScript(...)`.
|
|
||||||
4. Add a production serving model from the table above. This is required for build-only browser apps.
|
|
||||||
5. Add `WithExternalHttpEndpoints()` / `withExternalHttpEndpoints()` only to the resource that owns the public production HTTP surface, such as the backend, gateway, static website, Node server, or Next.js app.
|
|
||||||
6. Keep dev-only gateway/proxy wiring behind run-mode checks when it depends on a dev server URL. Production routing must be configured on the production-serving resource, not assumed from Vite dev proxy settings.
|
|
||||||
7. After choosing the production-serving resource, apply the target reference for Docker Compose, Kubernetes, Azure, or AWS.
|
|
||||||
|
|
||||||
## Target-specific notes
|
|
||||||
|
|
||||||
- Docker Compose: JavaScript resources can publish as containers or static-file-carrying build resources. Inspect generated Dockerfiles, `docker-compose.yaml`, and `.env` placeholders.
|
|
||||||
- Kubernetes and Azure Kubernetes Service (AKS): make sure the production-serving resource becomes the workload with the service/ingress/gateway exposure. Build-only resources must be consumed by another deployable resource or converted with a JavaScript publish method.
|
|
||||||
- Azure Container Apps: only the production-serving compute resource should be external. For static browser apps, `PublishAsStaticWebsite(...)` can be the external resource; for backend-served or gateway-served apps, the backend/gateway is the external resource.
|
|
||||||
- Azure App Service: use this only when the JavaScript app fits the public website model and single target-port constraints. SSR/Node apps need the right startup script/output and external HTTP endpoint.
|
|
||||||
- AWS: verify support in the AWS integrations repo. Do not assume the AWS preview deployment supports every JavaScript publish mode.
|
|
||||||
|
|
||||||
## Common pitfalls
|
|
||||||
|
|
||||||
- A Vite resource that works locally can fail deployment validation if it is still build-only and no deployed resource consumes its files.
|
|
||||||
- Vite dev-server proxy configuration does not automatically become production routing.
|
|
||||||
- `PublishWithContainerFiles(...)` copies built files into a destination container; the destination app still needs to serve those files.
|
|
||||||
- `PublishWithStaticFiles(...)` is for gateway/BFF resources such as YARP serving frontend files.
|
|
||||||
- `PublishAsPackageScript(...)` keeps runtime package dependencies and is larger than a built Node server output; prefer `PublishAsNodeServer(...)` when the framework emits a runnable server artifact.
|
|
||||||
- Next.js standalone deployment requires Next.js standalone output configuration. Validate this before deploying.
|
|
||||||
- Do not expose both a dev frontend resource and the production-serving gateway/backend unless the user intentionally wants two public surfaces.
|
|
||||||
-236
@@ -1,236 +0,0 @@
|
|||||||
# Kubernetes and Azure Kubernetes Service (AKS) deployment
|
|
||||||
|
|
||||||
Use this reference when the user asks for Kubernetes, Helm, kubectl, Azure Kubernetes Service (AKS), or cluster deployment.
|
|
||||||
|
|
||||||
## Choose Kubernetes vs Azure Kubernetes Service (AKS)
|
|
||||||
|
|
||||||
Aspire has two Kubernetes paths:
|
|
||||||
|
|
||||||
| Target | Use when | Integration | Environment concept |
|
|
||||||
|--------|----------|-------------|---------------------|
|
|
||||||
| Existing or externally-managed Kubernetes cluster | The cluster already exists, or the user explicitly wants a provider-managed cluster outside Aspire such as DigitalOcean Kubernetes (DOKS). Current `kubectl` context should point at the target cluster. | Kubernetes hosting | Kubernetes environment |
|
|
||||||
| Azure Kubernetes Service (AKS) | Aspire should provision Azure Kubernetes Service (AKS), ACR, identity, and Azure dependencies | Azure Kubernetes hosting | Azure Kubernetes Service (AKS) environment |
|
|
||||||
|
|
||||||
If the user says "Kubernetes" but not "Azure Kubernetes Service (AKS)", ask whether they want an existing/external cluster or a new Azure Kubernetes Service (AKS) cluster.
|
|
||||||
|
|
||||||
If the user names a non-Azure Kubernetes provider, such as DigitalOcean Kubernetes, use the existing/external Kubernetes path. Aspire will deploy into that cluster with Helm, but it does not own the provider's cluster or registry lifecycle unless a target-specific Aspire integration exists.
|
|
||||||
|
|
||||||
If the user says "Azure Kubernetes Service (AKS)", do not ask the existing-cluster question; use the Azure Kubernetes Service (AKS) path and also load the Azure reference for shared Azure settings.
|
|
||||||
|
|
||||||
## Docs to load
|
|
||||||
|
|
||||||
Always start with current docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "Kubernetes deployment"
|
|
||||||
aspire docs search "Kubernetes hosting integration"
|
|
||||||
aspire docs get "deploy-to-kubernetes-clusters"
|
|
||||||
aspire docs get "kubernetes-integration"
|
|
||||||
aspire docs get "deploy-to-azure-kubernetes-service-aks"
|
|
||||||
aspire docs get "azure-kubernetes-service-aks-integration"
|
|
||||||
aspire docs search "Azure Kubernetes Service hosting integration"
|
|
||||||
aspire docs get "<slug>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use API docs before editing. Search in the AppHost language you detected:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "Kubernetes environment" --language csharp
|
|
||||||
aspire docs api search "Kubernetes environment" --language typescript
|
|
||||||
aspire docs api search "Helm" --language csharp
|
|
||||||
aspire docs api search "Helm" --language typescript
|
|
||||||
aspire docs api search "Kubernetes service customization" --language csharp
|
|
||||||
aspire docs api search "Kubernetes service customization" --language typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
## Existing or external Kubernetes cluster setup
|
|
||||||
|
|
||||||
Expected package and AppHost environment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add kubernetes
|
|
||||||
```
|
|
||||||
|
|
||||||
Add a Kubernetes environment resource using the C# or TypeScript API shape returned by Aspire docs.
|
|
||||||
|
|
||||||
For vanilla Kubernetes and externally-managed clusters, a container registry is required for project/container image deployment because Aspire has no local-registry fallback for Kubernetes. Cluster nodes must pull the built images.
|
|
||||||
|
|
||||||
Add a container registry resource to the AppHost using the language-specific API shape returned by Aspire docs. Aspire can use a single registry as the default target; use per-resource registry assignment only when different workloads must use different registries.
|
|
||||||
|
|
||||||
Verify the registry is reachable from both the agent machine and cluster nodes.
|
|
||||||
|
|
||||||
When the user asks to create a provider-managed cluster or registry outside Aspire, such as a DigitalOcean Kubernetes cluster and DigitalOcean Container Registry, confirm the billable resource choice first. After creation, configure `kubectl`, registry authentication, and any provider-specific registry-to-cluster integration before running `aspire deploy`.
|
|
||||||
|
|
||||||
### Existing/external cluster code changes
|
|
||||||
|
|
||||||
Make these changes in the AppHost:
|
|
||||||
|
|
||||||
1. Run `aspire add kubernetes` if the AppHost does not already reference the Kubernetes hosting integration.
|
|
||||||
2. Add a Kubernetes environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var k8s = builder.AddKubernetesEnvironment("k8s");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const k8s = await builder.addKubernetesEnvironment("k8s");
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Add a container registry for project/container images, for example `builder.AddContainerRegistry(...)` in C# or the TypeScript equivalent returned by API docs. If one registry exists, Aspire can use it as the default target; use `WithContainerRegistry(...)` / `withContainerRegistry(...)` only when a specific resource should use a specific registry.
|
|
||||||
4. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each Kubernetes workload; in C#, add `.WithComputeEnvironment(k8s)` to each compute resource that should deploy to this cluster.
|
|
||||||
5. For TypeScript AppHosts, verify the current language-specific docs before assuming an equivalent assignment API.
|
|
||||||
6. Use `k8s.WithHelm(...)` / `k8s.withHelm(...)` only when the user needs chart name, release name, namespace, chart version, or other Helm settings.
|
|
||||||
7. Use `k8s.AddGateway(...)`, `k8s.AddIngress(...)`, or the TypeScript equivalents when public exposure is required. Otherwise services remain internal by default. For a simple public web frontend on a cloud Kubernetes provider, a per-resource `LoadBalancer` Service can be the direct exposure model; keep backend/internal services as `ClusterIP`.
|
|
||||||
- **Routed endpoints must be marked external.** Any endpoint exposed by an Ingress (`WithPath(...)`) or a Gateway (`WithRoute(...)`), or wired up via `WithDefaultBackend(...)`, must come from a resource that opts in with `.WithExternalHttpEndpoints()` (C#) or `isExternal: true` on the endpoint annotation. `aspire publish` fails fast with an `InvalidOperationException` from `EndpointRoutingValidation` if a non-external endpoint is routed, so always pair `AddIngress`/`AddGateway` plumbing with explicit external opt-in on the target resource. This applies to AKS through `AzureKubernetesEnvironment` as well.
|
|
||||||
8. Use `PublishAsKubernetesService(...)` / `publishAsKubernetesService(...)` only for per-resource Kubernetes manifest customization.
|
|
||||||
|
|
||||||
## Azure Kubernetes Service (AKS) setup
|
|
||||||
|
|
||||||
Expected package and AppHost environment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire add azure-kubernetes
|
|
||||||
```
|
|
||||||
|
|
||||||
Add an Azure Kubernetes Service (AKS) environment resource using the C# or TypeScript API shape returned by Aspire docs.
|
|
||||||
|
|
||||||
Azure Kubernetes Service (AKS) deployment provisions Azure infrastructure, including Azure Kubernetes Service (AKS), ACR, managed identity, and Azure resources modeled in the AppHost.
|
|
||||||
|
|
||||||
Azure Kubernetes Service (AKS) creates an inner Kubernetes environment for Helm deployment and auto-creates an Azure Container Registry unless a registry is explicitly configured.
|
|
||||||
|
|
||||||
### Azure Kubernetes Service (AKS) code changes
|
|
||||||
|
|
||||||
Make these changes in the AppHost:
|
|
||||||
|
|
||||||
1. Run `aspire add azure-kubernetes` if the AppHost does not already reference the Azure Kubernetes hosting integration.
|
|
||||||
2. Add an Azure Kubernetes Service (AKS) environment resource.
|
|
||||||
|
|
||||||
C# AppHost shape:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var aks = builder.AddAzureKubernetesEnvironment("aks");
|
|
||||||
```
|
|
||||||
|
|
||||||
TypeScript AppHost shape:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const aks = await builder.addAzureKubernetesEnvironment("aks");
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Do not add explicit compute-environment assignment for the common single-environment case. Only if the AppHost has multiple compute environments, disambiguate each Azure Kubernetes Service (AKS) workload; in C#, add `.WithComputeEnvironment(aks)` to each compute resource that should deploy there.
|
|
||||||
4. Do not add a plain `AddKubernetesEnvironment` next to `AddAzureKubernetesEnvironment` for the same target. The Azure Kubernetes Service (AKS) environment owns the inner Kubernetes/Helm environment.
|
|
||||||
5. Let the integration create the default Azure Container Registry unless the user needs a specific registry. If they do, use the Azure Kubernetes registry customization API from current docs.
|
|
||||||
6. Use node pool/subnet/customization APIs only when required, for example `WithSystemNodePool(...)`, `AddNodePool(...)`, `WithSubnet(...)`, or the TypeScript equivalents returned by API docs.
|
|
||||||
7. Use Kubernetes Gateway/Ingress APIs for public exposure; do not assume every service becomes public.
|
|
||||||
|
|
||||||
## Preflight
|
|
||||||
|
|
||||||
For all Kubernetes targets:
|
|
||||||
|
|
||||||
- `kubectl` is installed.
|
|
||||||
- `helm` is installed.
|
|
||||||
- AppHost has the correct Kubernetes environment.
|
|
||||||
- AppHost parameters are configured or can be prompted.
|
|
||||||
- External exposure is explicit through Ingress, Gateway API, service customization, or target-specific defaults.
|
|
||||||
- Helm is the default deployment engine; record any customized namespace, release name, chart name, or chart version.
|
|
||||||
- Storage defaults are understood. Kubernetes defaults to `emptyDir`; persistent storage needs explicit storage type/class/size decisions.
|
|
||||||
|
|
||||||
For existing/external clusters:
|
|
||||||
|
|
||||||
- `kubectl config current-context` points to the intended cluster.
|
|
||||||
- Container registry is configured and reachable.
|
|
||||||
- Registry authentication is configured for image push from the agent machine.
|
|
||||||
- Image pull secret or registry auth is configured when the cluster cannot pull from the registry anonymously.
|
|
||||||
- Provider-specific registry attachment is configured when required, for example attaching a provider registry to a managed cluster.
|
|
||||||
- Namespace/release/chart settings are understood if Helm settings are customized.
|
|
||||||
|
|
||||||
For Azure Kubernetes Service (AKS):
|
|
||||||
|
|
||||||
- Azure CLI auth is available (`az login` for local deploy).
|
|
||||||
- Subscription, location, and resource group source are known.
|
|
||||||
- `Azure:SubscriptionId` / `Azure__SubscriptionId` and `Azure:Location` / `Azure__Location` are configured or can be prompted.
|
|
||||||
- Node pool defaults fit the target subscription/region, or the AppHost customizes node pools.
|
|
||||||
|
|
||||||
## Preview and publish
|
|
||||||
|
|
||||||
Generate Helm artifacts without applying them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish -o k8s-artifacts
|
|
||||||
```
|
|
||||||
|
|
||||||
For Azure Kubernetes Service (AKS), publish can also generate Azure infrastructure artifacts.
|
|
||||||
|
|
||||||
Inspect expected output:
|
|
||||||
|
|
||||||
- `Chart.yaml`
|
|
||||||
- `values.yaml`
|
|
||||||
- `templates/`
|
|
||||||
- generated environment values for deploy-time parameter and image resolution
|
|
||||||
- Bicep infrastructure for Azure Kubernetes Service (AKS) targets when generated
|
|
||||||
|
|
||||||
Use list steps before deploy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy --list-steps
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deploy
|
|
||||||
|
|
||||||
Existing/external cluster:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Aspire uses current `kubectl` context and Helm. It deploys application resources into the cluster; it does not create or delete an externally-managed Kubernetes cluster or provider registry.
|
|
||||||
|
|
||||||
Azure Kubernetes Service (AKS):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Aspire provisions Azure resources, builds/pushes images to ACR, generates Helm charts, and installs them into Azure Kubernetes Service (AKS).
|
|
||||||
|
|
||||||
External Helm charts added to a Kubernetes environment install after the main app chart. They are not uninstalled by default during destroy because they may be shared; only treat them as owned by the Aspire app when the AppHost explicitly opts into destroy-time uninstall.
|
|
||||||
|
|
||||||
## Destroy
|
|
||||||
|
|
||||||
Run the destroy pipeline for this AppHost/environment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire destroy --environment <name>
|
|
||||||
```
|
|
||||||
|
|
||||||
For an existing/external cluster, the destroy step runs against the configured cluster context for the selected AppHost/environment. Confirm the current `kubectl` context, namespace/release settings, and AppHost environment before running destroy. It does not delete an externally-created Kubernetes cluster, node pool, provider load balancer account resource, or container registry. Delete provider infrastructure with the provider CLI only when the user explicitly asks to remove that infrastructure too. For Azure Kubernetes Service (AKS), also confirm the Azure subscription and resource group because that target can own Azure infrastructure as well as cluster resources. Use `--yes` only after destructive intent is explicit. Use kubectl, cloud CLI, provider delete commands, or Helm only to diagnose failed teardown or remove leftovers that are not managed by the Aspire deployment target.
|
|
||||||
|
|
||||||
## Native artifact handoff
|
|
||||||
|
|
||||||
If the user wants to apply published artifacts themselves:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm install <release> ./k8s-artifacts
|
|
||||||
helm upgrade <release> ./k8s-artifacts
|
|
||||||
```
|
|
||||||
|
|
||||||
Use values files or `--set` for environment-specific image tags and settings.
|
|
||||||
|
|
||||||
## Exposure and TLS
|
|
||||||
|
|
||||||
By default, generated services use the Kubernetes environment's default service type, which is `ClusterIP`. Public access requires an explicit exposure mechanism such as Ingress, Gateway API, or service type customization. For cloud Kubernetes providers without an ingress controller configured, a frontend `LoadBalancer` Service is often the fastest public smoke-test path, while backend services should usually remain `ClusterIP`. Verify exact APIs in docs before editing because C# and TypeScript AppHost shapes differ.
|
|
||||||
|
|
||||||
Gateway/Ingress TLS can add extra deployment steps for bootstrap secrets, FQDN discovery, and field ownership cleanup. If a deployment fails around TLS or Gateway resources, inspect the listed pipeline steps and the generated manifests before changing the AppHost.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- Empty connection strings: inspect generated ConfigMaps and Secrets and verify env var names in pod specs.
|
|
||||||
- Password/auth failures: inspect Kubernetes Secrets and references; do not print secret values.
|
|
||||||
- ImagePullBackOff: verify image registry, pull secret/identity, and image tags.
|
|
||||||
- Azure Kubernetes Service (AKS) cluster unreachable: refresh credentials with `az aks get-credentials`.
|
|
||||||
- ACR pull issues on Azure Kubernetes Service (AKS): check ACR role assignment with `az aks check-acr`.
|
|
||||||
- Helm conflict on Gateway/TLS resources: inspect `helm status`, `kubectl describe gateway`, and the generated Gateway/Ingress manifests.
|
|
||||||
-189
@@ -1,189 +0,0 @@
|
|||||||
# Common deployment preflight
|
|
||||||
|
|
||||||
Use this reference for every Aspire deployment target.
|
|
||||||
|
|
||||||
## AppHost discovery
|
|
||||||
|
|
||||||
Find the AppHost before choosing commands:
|
|
||||||
|
|
||||||
1. Run `aspire ls` first. It lists all AppHosts in the current scope and is the preferred discovery command.
|
|
||||||
2. If `aspire ls` shows exactly one AppHost, use it.
|
|
||||||
3. If `aspire ls` shows no AppHosts, stop deployment work and invoke the `aspireify` skill to initialize/wire the AppHost before continuing.
|
|
||||||
4. If `aspire ls` shows multiple AppHosts or discovery is still ambiguous, inspect `aspire.config.json`, `*.AppHost.csproj`, `apphost.cs`, or `apphost.ts`.
|
|
||||||
5. For C# project AppHosts, confirm the project references Aspire AppHost support.
|
|
||||||
6. For C# single-file AppHosts, look for the Aspire AppHost SDK directive.
|
|
||||||
7. For TypeScript AppHosts, look for the AppHost file and generated module support.
|
|
||||||
|
|
||||||
Use `--apphost <path>` when discovery is ambiguous, multiple AppHosts exist, or CI/CD should pin a specific AppHost. The path can point to an AppHost project file or supported single-file AppHost, such as `apphost.cs`.
|
|
||||||
|
|
||||||
## Docs lookup checklist
|
|
||||||
|
|
||||||
Use Aspire docs search before changing target configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "ci"
|
|
||||||
aspire docs search "github actions"
|
|
||||||
aspire docs search "deployment overview"
|
|
||||||
aspire docs search "deploy with Aspire"
|
|
||||||
aspire docs search "external parameters deployment"
|
|
||||||
aspire docs search "<target> deployment"
|
|
||||||
aspire docs get "<slug>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use API docs before writing AppHost code. Search both languages until the AppHost language is known:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs api search "<method-or-resource>" --language csharp
|
|
||||||
aspire docs api search "<method-or-resource>" --language typescript
|
|
||||||
aspire docs api get "<id>"
|
|
||||||
```
|
|
||||||
|
|
||||||
If `aspire docs` is unavailable, use official `aspire.dev` docs through the available web/documentation tools. Do not use outdated blog posts or workload-era docs as authority.
|
|
||||||
|
|
||||||
## Target detection checklist
|
|
||||||
|
|
||||||
Inspect AppHost code for existing target environments. API names differ by AppHost language, so use these as concepts rather than exact names:
|
|
||||||
|
|
||||||
- Docker Compose environment
|
|
||||||
- Kubernetes environment
|
|
||||||
- Azure Container Apps environment
|
|
||||||
- Azure App Service environment
|
|
||||||
- Azure Kubernetes Service (AKS) environment
|
|
||||||
- AWS CDK environment
|
|
||||||
|
|
||||||
If none exists, ask for the deployment target unless the user's request clearly names one.
|
|
||||||
|
|
||||||
If more than one exists, ask which one to use or whether to deploy all.
|
|
||||||
|
|
||||||
## Compute environment assignment
|
|
||||||
|
|
||||||
Aspire can infer the compute environment only when there is exactly one compute environment in the model. If multiple deployment environments exist, verify each compute resource is explicitly assigned to the intended environment before publishing or deploying.
|
|
||||||
|
|
||||||
Do not assume a resource deploys just because it appears in run mode. Resources hidden behind run-mode-only conditionals or assigned to a different compute environment will not be part of the target deployment.
|
|
||||||
|
|
||||||
## Preview commands
|
|
||||||
|
|
||||||
Use these before applying changes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish --list-steps
|
|
||||||
aspire deploy --list-steps
|
|
||||||
```
|
|
||||||
|
|
||||||
Treat `--list-steps` as a structural preview, not proof that a later deploy can run unattended. Some targets can emit deploy-time selection prompts that are not AppHost parameters, such as Azure tenant selection. If a prompt appears, answer it in a real interactive terminal/PTY; do not try to satisfy it by setting unrelated AppHost secrets unless the target docs explicitly define that mapping.
|
|
||||||
|
|
||||||
Use publish when the user wants to inspect generated artifacts:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire publish -o ./aspire-output
|
|
||||||
```
|
|
||||||
|
|
||||||
The output path can be a scratch path if the user only asked for a preview. Do not commit generated deployment artifacts unless the user explicitly asks to keep them in source control.
|
|
||||||
|
|
||||||
When running a command that may prompt, do not pipe it through `tee`, `tail`, or similar output filters. Pipes can remove the interactive terminal that selection prompts require. Use the shell/session transcript or the CLI log file path printed by Aspire for diagnostics.
|
|
||||||
|
|
||||||
`aspire publish` and `aspire deploy` are related but not a two-step apply pipeline:
|
|
||||||
|
|
||||||
- `aspire publish` generates target artifacts for review or handoff and leaves unresolved values as target-specific placeholders where possible.
|
|
||||||
- `aspire deploy` resolves parameters and applies deployment steps directly from the AppHost model.
|
|
||||||
- Running `aspire deploy` later does not consume the directory produced by a previous `aspire publish`.
|
|
||||||
|
|
||||||
Use `--environment <name>` when the user wants a staging/production context other than the default. Deployment state and cached values are scoped by AppHost and environment, so changing the environment changes which cached values are used.
|
|
||||||
|
|
||||||
## Destroying deployments
|
|
||||||
|
|
||||||
Use `aspire destroy` to run the AppHost deployment environment's destroy pipeline:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire destroy --environment <name>
|
|
||||||
```
|
|
||||||
|
|
||||||
Treat destroy as a destructive deployment operation:
|
|
||||||
|
|
||||||
- Run it only when the user explicitly asks to tear down or clean up a deployment, or when a test workflow owns temporary infrastructure and teardown is part of that workflow.
|
|
||||||
- Use `aspire destroy --list-steps` when practical to preview the teardown pipeline before applying it.
|
|
||||||
- Confirm the AppHost, environment, target account/subscription/cluster, and resource group/namespace/stack context before applying.
|
|
||||||
- Use the same `--apphost <path>` and `--environment <name>` values used for deployment when discovery or environment scope could be ambiguous.
|
|
||||||
- Use `--yes` only for non-interactive teardown when destructive intent has already been approved, such as an explicit cleanup job.
|
|
||||||
- Do not describe destroy as a Helm, Kubernetes, Docker, Azure, or AWS command. It is an Aspire command that delegates to the selected deployment target's destroy step.
|
|
||||||
- Keep target-native delete commands as troubleshooting or manual-leftover cleanup, not the primary teardown path for resources managed by the Aspire deployment target.
|
|
||||||
|
|
||||||
## Parameters and secrets
|
|
||||||
|
|
||||||
Inventory parameter declarations. API casing differs by AppHost language:
|
|
||||||
|
|
||||||
- direct parameters
|
|
||||||
- secret parameters
|
|
||||||
- config-backed parameters
|
|
||||||
- connection string parameters
|
|
||||||
- target-specific parameter APIs
|
|
||||||
|
|
||||||
Report each parameter without revealing values:
|
|
||||||
|
|
||||||
| Field | What to report |
|
|
||||||
|-------|----------------|
|
|
||||||
| Name | AppHost parameter name |
|
|
||||||
| Secret | whether it is marked secret |
|
|
||||||
| Source | environment variable, appsettings, user secrets, command line, prompt, CI secret |
|
|
||||||
| Consumer | project env var, connection string, Key Vault secret, Compose env, Helm Secret, Azure app setting |
|
|
||||||
| Status | configured, missing, generated, or unknown |
|
|
||||||
|
|
||||||
Use these conventions:
|
|
||||||
|
|
||||||
- AppHost parameter config key: `Parameters:name`
|
|
||||||
- Environment variable provider key: `Parameters__name`
|
|
||||||
- Parameter names with dashes use underscores in environment variables, for example `registry-endpoint` becomes `Parameters__registry_endpoint`
|
|
||||||
- Local/dev-only AppHost secret command: `aspire secret set "Parameters:name" "<value>"`
|
|
||||||
|
|
||||||
`aspire secret set` is a local/dev convenience today, not the deployment parameter path. For TypeScript AppHosts and non-interactive runs, set deploy-time values as environment variables on the `aspire deploy` process:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
Parameters__name="<value>" \
|
|
||||||
aspire deploy --apphost ./apphost.ts --environment Production --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
Never print secret values. If a command prints secrets, redact them in any summary.
|
|
||||||
|
|
||||||
Deployment state can include resolved parameter values. Treat local deployment cache files and generated environment-specific artifacts as sensitive unless the target docs prove otherwise.
|
|
||||||
|
|
||||||
For CI/CD and GitHub Actions, load `references/cicd.md` before adding workflow YAML or pipeline commands.
|
|
||||||
|
|
||||||
## External endpoints and production topology
|
|
||||||
|
|
||||||
Flag production-only endpoint behavior:
|
|
||||||
|
|
||||||
- External HTTP endpoint configuration can change what becomes public in deployment.
|
|
||||||
- JavaScript app resources need an explicit production serving model. Load `references/javascript.md` when the AppHost contains JavaScript, Vite, Node, or Next.js resources.
|
|
||||||
- Azure Container Apps supports internal and external endpoints, with one main HTTP ingress group.
|
|
||||||
- Azure App Service supports a public website model with external HTTP/HTTPS endpoints only.
|
|
||||||
- Kubernetes exposes services through ClusterIP by default unless Ingress, Gateway API, or service type customization is configured.
|
|
||||||
|
|
||||||
Report what will be public, internal, or not exposed based on the target docs and AppHost code.
|
|
||||||
|
|
||||||
## Run vs publish/deploy branching
|
|
||||||
|
|
||||||
Check for AppHost conditionals around execution context and environment:
|
|
||||||
|
|
||||||
- run mode vs publish/deploy mode
|
|
||||||
- development, staging, production, or custom environment checks
|
|
||||||
|
|
||||||
Explain when resources exist only locally or only in publish/deploy mode. If a resource is behind a run-mode-only branch, do not tell the user it will deploy.
|
|
||||||
|
|
||||||
## Target-specific tools
|
|
||||||
|
|
||||||
Use target-native tools only after Aspire has produced artifacts or when verifying the result:
|
|
||||||
|
|
||||||
- Docker Compose: inspect generated Compose files or run Compose status commands against the generated project/files.
|
|
||||||
- Kubernetes: inspect Helm output and use kubectl/Helm against the target cluster.
|
|
||||||
- Azure: use Azure CLI for authentication and resource inspection, while keeping deployment through Aspire target environments.
|
|
||||||
- AWS: use AWS/CDK tooling required by the AWS Aspire integration.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
After deployment, verify with target-appropriate checks:
|
|
||||||
|
|
||||||
- Docker Compose: `docker compose ps` against generated files or endpoint checks.
|
|
||||||
- Kubernetes: `kubectl get pods`, `kubectl get svc`, `helm status`, endpoint checks.
|
|
||||||
- Azure: CLI output, Azure CLI resource inspection, endpoint checks, and dashboard URL when available.
|
|
||||||
|
|
||||||
Do not mark cloud deployment complete until provisioning, deployment, and at least one target-specific health or endpoint check succeeded.
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
---
|
|
||||||
name: aspire-init
|
|
||||||
description: >-
|
|
||||||
**WORKFLOW SKILL** - First-run flow for adding Aspire to a repo. Picks `aspire new`
|
|
||||||
(greenfield) or `aspire init` (existing repo), drops the AppHost skeleton, then hands
|
|
||||||
off to `aspireify` for resource wiring.
|
|
||||||
USE FOR: aspire init, aspire new, aspire-starter, aspire-ts-starter, aspire-py-starter,
|
|
||||||
add Aspire to existing repo, scaffold Aspire app, bootstrap Aspire, no AppHost detected,
|
|
||||||
install aspireify, generated .aspire/modules.
|
|
||||||
DO NOT USE FOR: AppHost wiring on an existing AppHost (use aspireify), start/stop/wait
|
|
||||||
(use aspire-orchestration), deploy/publish (use aspire-deployment), logs/traces (use
|
|
||||||
aspire-monitoring), repo that already has an AppHost.
|
|
||||||
INVOKES: aspire CLI (init, new, doctor), aspireify (handoff after skeleton drop).
|
|
||||||
FOR SINGLE OPERATIONS: Run `aspire init` or `aspire new TEMPLATE` directly.
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: Microsoft
|
|
||||||
version: "0.0.1"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Aspire Init
|
|
||||||
|
|
||||||
> **First-run only.** This skill owns the skeleton drop and template choice for repositories
|
|
||||||
> that do not yet have an Aspire AppHost. Once the skeleton is in place, hand off to
|
|
||||||
> [`aspireify`](../aspireify/SKILL.md) for the actual resource wiring.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
| Requirement | Install |
|
|
||||||
|-------------|---------|
|
|
||||||
| .NET 10.0 SDK | https://dotnet.microsoft.com/download |
|
|
||||||
| Aspire CLI (curl installer) | `curl -sSL https://aspire.dev/install.sh \| bash` |
|
|
||||||
| Aspire CLI (NativeAOT global tool) | `dotnet tool install -g Aspire.Cli` (.NET 10 required) |
|
|
||||||
| Diagnose missing prerequisites | `aspire doctor` |
|
|
||||||
|
|
||||||
> Aspire ships the CLI as a NativeAOT .NET global tool — instant startup, no JIT warmup.
|
|
||||||
> The curl/PowerShell installer remains supported for environments without .NET 10.
|
|
||||||
|
|
||||||
## Detection
|
|
||||||
|
|
||||||
Activate **only** when adding Aspire to a workspace that does not yet have one. Confirm ALL
|
|
||||||
of the following before running `aspire init`:
|
|
||||||
|
|
||||||
| Signal | How to Detect | Meaning |
|
|
||||||
|--------|---------------|---------|
|
|
||||||
| No C# AppHost | No `.csproj` containing `Aspire.AppHost.Sdk` | OK to init |
|
|
||||||
| No file-based AppHost | No `apphost.cs` with `#:sdk Aspire.AppHost.Sdk` | OK to init |
|
|
||||||
| No TypeScript AppHost | No `apphost.ts` in repo root | OK to init |
|
|
||||||
| No Aspire config | No `aspire.config.json` in repo root | OK to init |
|
|
||||||
| User intent | Explicit "add Aspire", "scaffold Aspire", "aspire init" | OK to init |
|
|
||||||
|
|
||||||
If **any** AppHost signal is already present, **do not run `aspire init`**. Route to
|
|
||||||
[`aspireify`](../aspireify/SKILL.md) (re-wire) or
|
|
||||||
[`aspire-orchestration`](../aspire-orchestration/SKILL.md) (lifecycle).
|
|
||||||
|
|
||||||
## Decision: `aspire new` vs `aspire init`
|
|
||||||
|
|
||||||
| Situation | Command | Why |
|
|
||||||
|-----------|---------|-----|
|
|
||||||
| Empty directory or brand-new project | `aspire new <template>` | Generates a full starter solution |
|
|
||||||
| Existing repo with services to model | `aspire init` | Drops minimal skeleton + `aspire.config.json` next to existing code |
|
|
||||||
| User wants a sample to learn from | `aspire new aspire-starter` | Includes ApiService + Web + ServiceDefaults |
|
|
||||||
| User wants the smallest possible scaffold | `aspire new aspire-empty` (C#) or `aspire new aspire-ts-empty` (TS) | No resources pre-wired |
|
|
||||||
| User wants Python services | `aspire new aspire-py-starter` (TypeScript AppHost drives Python) | **Not** `dotnet new` — that template was removed in 13.3 |
|
|
||||||
|
|
||||||
See [references/templates.md](references/templates.md) for the complete template list and
|
|
||||||
options.
|
|
||||||
|
|
||||||
## Workflow A — `aspire new <template>` (new project)
|
|
||||||
|
|
||||||
For brand-new projects in an empty or non-existent directory:
|
|
||||||
|
|
||||||
1. Confirm prerequisites with `aspire doctor` if the CLI install is uncertain.
|
|
||||||
2. Pick a template from [references/templates.md](references/templates.md).
|
|
||||||
3. Run the template, append `--non-interactive` for agent flows:
|
|
||||||
```bash
|
|
||||||
aspire new aspire-starter --name MyApp --output ./MyApp --non-interactive
|
|
||||||
```
|
|
||||||
4. The new directory is fully wired by the template — **no aspireify handoff needed**.
|
|
||||||
5. Route to [`aspire-orchestration`](../aspire-orchestration/SKILL.md) for first run
|
|
||||||
(`aspire start`).
|
|
||||||
|
|
||||||
## Workflow B — `aspire init` (existing repo)
|
|
||||||
|
|
||||||
For repositories that already contain services (Express API, .NET API, Python service, etc.)
|
|
||||||
and need an AppHost added alongside them:
|
|
||||||
|
|
||||||
1. Verify the [Detection](#detection) table — confirm **no** AppHost is present.
|
|
||||||
2. Run `aspire init`, choosing language explicitly for non-interactive flows:
|
|
||||||
```bash
|
|
||||||
aspire init --language csharp --non-interactive
|
|
||||||
# or
|
|
||||||
aspire init --language typescript --non-interactive
|
|
||||||
```
|
|
||||||
3. `aspire init` drops:
|
|
||||||
- The AppHost skeleton (`apphost.cs` with `#:sdk` directives, **or** `apphost.ts` with the
|
|
||||||
generated `.aspire/modules/` folder)
|
|
||||||
- AppHost configuration describing language + AppHost path
|
|
||||||
- The **`aspireify`** agent skill into the project's skill directory (same one
|
|
||||||
`aspire agent init` uses)
|
|
||||||
4. **Hand off to `aspireify`** — `aspire init` does **not** wire resources, projects, or
|
|
||||||
integrations on its own.
|
|
||||||
5. After `aspireify` finishes wiring, validate via `aspire start`
|
|
||||||
([`aspire-orchestration`](../aspire-orchestration/SKILL.md)).
|
|
||||||
|
|
||||||
See [references/init-workflow.md](references/init-workflow.md) for the full sequence
|
|
||||||
including what `aspire.config.json` contains and what to do if `aspire init` fails partway.
|
|
||||||
|
|
||||||
## Handoff Rules
|
|
||||||
|
|
||||||
| After `aspire init` / `aspire new` finishes... | Route To |
|
|
||||||
|------------------------------------------------|----------|
|
|
||||||
| Skeleton dropped, resources need wiring | → `aspireify` skill (in-plugin or project-local) |
|
|
||||||
| Skeleton dropped, validate it starts | → `aspire-orchestration` (run `aspire start`) |
|
|
||||||
| New project from template, ready to run | → `aspire-orchestration` |
|
|
||||||
| User asks to deploy after init | → `aspire-deployment` |
|
|
||||||
| User asks for logs/traces after init | → `aspire-monitoring` |
|
|
||||||
| Existing AppHost detected — do NOT run init | → `aspireify` (re-wire) or `aspire-orchestration` (lifecycle) |
|
|
||||||
|
|
||||||
## Project-Local Skill Override
|
|
||||||
|
|
||||||
If `.agents/skills/aspire-init/SKILL.md` exists project-locally (legacy install from an
|
|
||||||
older `aspire init` run), **warn the user and defer to it**. The legacy project-local skill
|
|
||||||
may carry repo-specific guidance that should not be overridden by this in-plugin skill.
|
|
||||||
|
|
||||||
The project-local `aspireify` skill (installed by `aspire init`) takes precedence
|
|
||||||
over this plugin's in-plugin `aspireify` for the same reason — defer to the project-local
|
|
||||||
copy and warn.
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| Symptom | Cause | Action |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| `aspire init` reports AppHost already exists | Repo already has an AppHost | Stop. Route to `aspireify` (re-wire) or `aspire-orchestration` (lifecycle) |
|
|
||||||
| `aspire init` fails in non-interactive mode without `--language` | Multiple language paths available | Re-run with `--language csharp` or `--language typescript` |
|
|
||||||
| `aspire new` rejects `--output` path | Path exists and is non-empty | Use a different `--output` or empty the directory |
|
|
||||||
| `aspire` command not found | CLI not installed | `dotnet tool install -g Aspire.Cli` (.NET 10) or `curl -sSL https://aspire.dev/install.sh \| bash` |
|
|
||||||
| `aspire doctor` reports missing .NET 10 | SDK missing | Install .NET 10 SDK before retrying |
|
|
||||||
| `aspire init` succeeded but no `aspireify` skill installed | Agent skill directory not detected | Run `aspire agent init` to install `aspireify`, then continue wiring |
|
|
||||||
| Skeleton dropped but resources not wired | Expected — `aspire init` does not wire | Hand off to `aspireify` |
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [templates.md](references/templates.md) — `aspire new` templates and options
|
|
||||||
- [init-workflow.md](references/init-workflow.md) — `aspire init` flow, `aspire.config.json`
|
|
||||||
layout, and `aspireify` handoff
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
# `aspire init` Workflow
|
|
||||||
|
|
||||||
Reference for the `aspire init` flow on existing repositories. Source:
|
|
||||||
https://aspire.dev/reference/cli/commands/aspire-init/.
|
|
||||||
|
|
||||||
## What `aspire init` Does
|
|
||||||
|
|
||||||
`aspire init` initializes Aspire support in an existing repo or workspace. It scaffolds a
|
|
||||||
**minimal AppHost skeleton** plus an `aspire.config.json`, then optionally installs the
|
|
||||||
**`aspireify`** agent skill so the AI coding agent can complete the wiring.
|
|
||||||
|
|
||||||
It does **not**:
|
|
||||||
|
|
||||||
- Wire resources, projects, or integrations into the AppHost
|
|
||||||
- Modify existing project files
|
|
||||||
- Change the repo's .NET SDK version (`global.json` is left alone)
|
|
||||||
- Trust the developer certificate (run `aspire certs trust` separately if needed)
|
|
||||||
|
|
||||||
## Command and Options
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire init [options]
|
|
||||||
```
|
|
||||||
|
|
||||||
| Option | Purpose |
|
|
||||||
|--------|---------|
|
|
||||||
| `--language` | `csharp` or `typescript`. Required in `--non-interactive` mode if both paths are available |
|
|
||||||
| `--channel` | `stable` (default), `staging`, `daily` |
|
|
||||||
| `--non-interactive` | **Required for agent execution.** Disables prompts and spinners |
|
|
||||||
| `--nologo` | Suppress startup banner / telemetry notice |
|
|
||||||
| `--banner` | Show the animated welcome banner |
|
|
||||||
| `-l, --log-level` | `Critical`, `Debug`, `Error`, `Information`, `None`, `Trace`, `Warning` |
|
|
||||||
| `--wait-for-debugger` | Pause until a debugger attaches |
|
|
||||||
| `-?, -h, --help` | Print help |
|
|
||||||
|
|
||||||
## What Gets Dropped
|
|
||||||
|
|
||||||
### C# Path (`--language csharp`)
|
|
||||||
|
|
||||||
- **`apphost.cs`** — single-file AppHost using `#:sdk Aspire.AppHost.Sdk` and `#:package`
|
|
||||||
directives. No `.csproj` is created in the file-based mode.
|
|
||||||
- **`aspire.config.json`** at repo root.
|
|
||||||
|
|
||||||
### TypeScript Path (`--language typescript`)
|
|
||||||
|
|
||||||
- **`apphost.ts`** at repo root.
|
|
||||||
- **`.aspire/modules/`** generated folder (do not edit by hand — regenerate with `aspire add`).
|
|
||||||
- **`aspire.config.json`** at repo root.
|
|
||||||
|
|
||||||
### `aspireify` Skill
|
|
||||||
|
|
||||||
- A Markdown skill file is installed into the AI agent's skill directory — the same
|
|
||||||
directory chosen by `aspire agent init` (e.g., `.agents/skills/aspireify/`,
|
|
||||||
`.github/skills/aspireify/`, `.claude/skills/aspireify/`, or `.opencode/skill/aspireify/`).
|
|
||||||
- The skill instructs the agent to scan the repo, propose a resource graph, edit the
|
|
||||||
AppHost, and validate via `aspire start`.
|
|
||||||
|
|
||||||
## `aspire.config.json` Layout
|
|
||||||
|
|
||||||
| Field | Values | Meaning |
|
|
||||||
|-------|--------|---------|
|
|
||||||
| `appHost.language` | `"csharp"` or `"typescript/nodejs"` | Which AppHost syntax to use |
|
|
||||||
| `appHost.path` | Path to AppHost file or directory | Where the AppHost lives |
|
|
||||||
|
|
||||||
C# has two sub-modes the agent may encounter:
|
|
||||||
|
|
||||||
- **Single-file** — `appHost.path` points at `apphost.cs` (uses `#:sdk` directive).
|
|
||||||
- **Full project** — `appHost.path` points at a directory containing a `.csproj` plus
|
|
||||||
`Program.cs`. In solution-backed repos, full project mode lets the AppHost participate in IDE and solution workflows.
|
|
||||||
|
|
||||||
## End-to-End Sequence
|
|
||||||
|
|
||||||
1. **Pre-flight** — verify no AppHost already exists. If one does, stop and route to
|
|
||||||
`aspireify` or `aspire-orchestration`.
|
|
||||||
2. **Run init**:
|
|
||||||
```bash
|
|
||||||
aspire init --language <csharp|typescript> --non-interactive
|
|
||||||
```
|
|
||||||
3. **Confirm artifacts** — `apphost.cs` (or `apphost.ts` + `.aspire/modules/`) and
|
|
||||||
`aspire.config.json` should be in the repo root.
|
|
||||||
4. **Confirm `aspireify` skill installed** — the agent's skill directory contains
|
|
||||||
`aspireify/SKILL.md`. If missing, run `aspire agent init` to install it.
|
|
||||||
5. **Hand off to `aspireify`** for wiring:
|
|
||||||
- Scan repo and discover existing projects, services, containers
|
|
||||||
- Ask the user clarifying questions (which services to orchestrate, hardcoded ports,
|
|
||||||
whether to map env vars or switch to Aspire service discovery)
|
|
||||||
- Wire resources with `WithReference`, `WaitFor`, endpoints, volumes
|
|
||||||
- Optionally configure OpenTelemetry
|
|
||||||
- Validate with a smoke-test `aspire start`
|
|
||||||
6. **Validate** — once `aspireify` finishes wiring, run `aspire start` (handled by
|
|
||||||
`aspire-orchestration`) and confirm resources reach a healthy state.
|
|
||||||
|
|
||||||
## Project-Local Skill Precedence
|
|
||||||
|
|
||||||
`aspire init` installs `aspireify` into the project's skill directory when an agent skill location is detected.
|
|
||||||
When a project-local `.agents/skills/aspireify/SKILL.md` (or equivalent location) is
|
|
||||||
present, **defer to it and warn the user** — the project-local copy may carry repo-specific
|
|
||||||
guidance.
|
|
||||||
|
|
||||||
The same precedence applies to a legacy `.agents/skills/aspire-init/SKILL.md` from older
|
|
||||||
`aspire init` runs: warn and defer.
|
|
||||||
|
|
||||||
## Failure Modes and Recovery
|
|
||||||
|
|
||||||
| Symptom | Cause | Recovery |
|
|
||||||
|---------|-------|----------|
|
|
||||||
| `aspire init` reports an AppHost already exists | Repo is already an Aspire app | Stop. Route to `aspireify` or `aspire-orchestration` |
|
|
||||||
| `aspire init` fails without `--language` in `--non-interactive` | CLI needs the language explicitly when prompts are disabled | Re-run with `--language csharp` or `--language typescript` |
|
|
||||||
| Skeleton dropped but no `aspireify` skill | Agent skill directory not detected during init | Run `aspire agent init` to install `aspireify`, then continue |
|
|
||||||
| `apphost.cs` references a missing `#:package` | Channel mismatch or transient feed issue | Re-run with `--channel stable` (or `daily` for pre-release) |
|
|
||||||
| `aspire start` after wiring fails immediately | Wiring incomplete or wrong AppHost path | Re-invoke `aspireify`; confirm `aspire.config.json` `appHost.path` is correct |
|
|
||||||
|
|
||||||
## Don't Do This
|
|
||||||
|
|
||||||
- **Don't run `aspire init` if any AppHost signal already exists** — it duplicates the
|
|
||||||
skeleton and confuses subsequent tooling.
|
|
||||||
- **Don't edit `.aspire/modules/`** in TypeScript AppHosts. Use `aspire add` to regenerate APIs;
|
|
||||||
use `aspire restore` if files are missing.
|
|
||||||
- **Don't install the obsolete Aspire workload** (`dotnet workload install aspire`). Use
|
|
||||||
`aspire init`, `aspire new`, or `aspire add` instead.
|
|
||||||
- **Don't perform the resource wiring inside this skill.** Hand off to `aspireify`. This
|
|
||||||
skill's job ends when the skeleton + `aspire.config.json` + `aspireify` skill are in
|
|
||||||
place.
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
# `aspire new` Templates
|
|
||||||
|
|
||||||
Reference for choosing and invoking templates with `aspire new`. Source:
|
|
||||||
https://aspire.dev/reference/cli/commands/aspire-new/
|
|
||||||
|
|
||||||
## Template List
|
|
||||||
|
|
||||||
| Template | Description | AppHost Language |
|
|
||||||
|----------|-------------|------------------|
|
|
||||||
| `aspire-starter` | Starter App (ASP.NET Core / Blazor) | C# |
|
|
||||||
| `aspire-ts-cs-starter` | Starter App (ASP.NET Core / React) | C# |
|
|
||||||
| `aspire-py-starter` | Starter App (FastAPI / React) | **TypeScript** (drives Python) |
|
|
||||||
| `aspire-ts-starter` | Starter App (Express / React) | TypeScript |
|
|
||||||
| `aspire-empty` | Minimal scaffold, no resources pre-wired | C# |
|
|
||||||
| `aspire-ts-empty` | Minimal scaffold, no resources pre-wired | TypeScript |
|
|
||||||
|
|
||||||
> **Important — Python starter changed in 13.3.** `dotnet new aspire-py-starter` was removed.
|
|
||||||
> The current path is `aspire new aspire-py-starter`, and the AppHost is **TypeScript**, not
|
|
||||||
> C#. The TypeScript AppHost orchestrates the FastAPI service plus the React frontend.
|
|
||||||
|
|
||||||
## Common Options
|
|
||||||
|
|
||||||
| Option | Purpose |
|
|
||||||
|--------|---------|
|
|
||||||
| `-n, --name` | Name of the project to create |
|
|
||||||
| `-o, --output` | Output path. Defaults to `./<template-name>` (auto-suffixed if non-empty) |
|
|
||||||
| `-s, --source` | NuGet source for the project templates |
|
|
||||||
| `-v, --version` | Version of the project templates to use |
|
|
||||||
| `--channel` | Template channel: `stable` (default), `staging`, `daily` |
|
|
||||||
| `--non-interactive` | **Required for agent execution.** Disables prompts and spinners |
|
|
||||||
| `--nologo` | Suppress startup banner / telemetry notice |
|
|
||||||
| `--suppress-agent-init` | Skip the post-create prompt to configure AI agent environments |
|
|
||||||
| `-l, --log-level` | `Critical`, `Debug`, `Error`, `Information`, `None`, `Trace`, `Warning` |
|
|
||||||
|
|
||||||
## Template-Specific Options
|
|
||||||
|
|
||||||
### `aspire-py-starter`
|
|
||||||
|
|
||||||
| Option | Values | Default |
|
|
||||||
|--------|--------|---------|
|
|
||||||
| `--use-redis-cache` | `true` / `false` | Prompts interactively |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire new aspire-py-starter --use-redis-cache true --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
## Interactive Prompts (avoid by passing flags)
|
|
||||||
|
|
||||||
| Prompt | What It Decides | Skip With |
|
|
||||||
|--------|------------------|-----------|
|
|
||||||
| Project name | Output directory name | `--name <value>` |
|
|
||||||
| Template version | Which release to use | `--version <value>` |
|
|
||||||
| Output folder | Where files land | `--output <value>` |
|
|
||||||
| Use `*.dev.localhost` URLs? | HTTPS subdomain launch profile vs `http://localhost:<port>` | Pass `--non-interactive` to take the default (No, standard localhost) |
|
|
||||||
|
|
||||||
If a project picks `*.dev.localhost`, the user must trust the developer certificate first:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire certs trust
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Path Validation
|
|
||||||
|
|
||||||
- Invalid characters (e.g., null bytes) are rejected with an error.
|
|
||||||
- Existing **non-empty** directories are rejected — pick a new `--output` or empty the target.
|
|
||||||
- When `--output` is omitted, the CLI derives the directory from the template name and
|
|
||||||
appends a numeric suffix (`./aspire-starter-2`, `./aspire-starter-3`, ...) if the default
|
|
||||||
already exists.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Interactive — CLI prompts for name, output, version
|
|
||||||
aspire new aspire-starter
|
|
||||||
|
|
||||||
# Non-interactive C# empty scaffold pinned to a specific version
|
|
||||||
aspire new aspire-empty --version <version> --name aspireapp --output ./dev --non-interactive
|
|
||||||
|
|
||||||
# Pre-release templates from the daily channel
|
|
||||||
aspire new aspire-starter --channel daily
|
|
||||||
|
|
||||||
# TypeScript-AppHost-driven Python starter with Redis cache
|
|
||||||
aspire new aspire-py-starter --use-redis-cache true --name py-shop --output ./py-shop --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
## When `aspire new` Is Wrong
|
|
||||||
|
|
||||||
| Situation | Use Instead |
|
|
||||||
|-----------|-------------|
|
|
||||||
| Repo already contains services to model | `aspire init` (Workflow B in [SKILL.md](../SKILL.md)) |
|
|
||||||
| AppHost already exists in the repo | `aspireify` (re-wire) or `aspire-orchestration` (lifecycle) |
|
|
||||||
| Need to add an integration to an existing AppHost | `aspire add <package>` (handled by `aspire-orchestration`) |
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
---
|
|
||||||
name: aspire-monitoring
|
|
||||||
description: >-
|
|
||||||
**ANALYSIS SKILL** - Observe Aspire apps: logs, traces, metrics, resource state,
|
|
||||||
telemetry export, browser telemetry, and the standalone dashboard. Routes between local
|
|
||||||
Aspire CLI, AKS workload diagnostics, and deployed Azure resource health.
|
|
||||||
USE FOR: aspire logs, aspire otel logs, aspire otel traces, aspire otel spans, aspire
|
|
||||||
describe, aspire ps, aspire export, aspire dashboard run, --include-hidden, browser logs
|
|
||||||
in dashboard, WithBrowserLogs, App Insights query, AKS pod logs, container app logs.
|
|
||||||
DO NOT USE FOR: start/stop/wait (use aspire-orchestration), deploy/publish/destroy (use
|
|
||||||
aspire-deployment), AppHost code edits like WithBrowserLogs() (use aspireify), Azure
|
|
||||||
provisioning (use azure-prepare).
|
|
||||||
INVOKES: aspire CLI, azure-diagnostics (deployed Azure), kubectl + Container Insights.
|
|
||||||
FOR SINGLE OPERATIONS: Run the aspire CLI command directly for quick log or describe lookups.
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: Microsoft
|
|
||||||
version: "0.0.1"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Aspire Monitoring
|
|
||||||
|
|
||||||
> Aspire CLI provides full observability **locally**. For deployed apps, route to platform-specific tools.
|
|
||||||
|
|
||||||
## Diagnostics Bridge — Where To Look
|
|
||||||
|
|
||||||
| Need | Environment | Tool | Command / Route |
|
|
||||||
|------|------------|------|-----------------|
|
|
||||||
| Console logs | Local dev | Aspire CLI | `aspire logs <resource>` |
|
|
||||||
| Structured logs | Local dev | Aspire CLI | `aspire otel logs [resource]` |
|
|
||||||
| Distributed traces | Local dev | Aspire CLI | `aspire otel traces [resource]` |
|
|
||||||
| Span detail | Local dev | Aspire CLI | `aspire otel spans [resource]` |
|
|
||||||
| Resource state | Local dev | Aspire CLI | `aspire describe` (add `--include-hidden` if a resource is missing) |
|
|
||||||
| Telemetry export | Local dev | Aspire CLI | `aspire export [resource]` |
|
|
||||||
| Standalone dashboard | Any (no AppHost) | Aspire CLI | `aspire dashboard run` (foreground/blocking — see below) |
|
|
||||||
| Browser console / network / screenshots | Local dev (frontend) | Aspire dashboard | Surfaced via `Aspire.Hosting.Browsers` + `WithBrowserLogs()` |
|
|
||||||
| AppHost / deployment definition | Authoring | aspire-deployment skill | → `aspire-deployment` skill |
|
|
||||||
| AKS workload (pod logs, pod state) | Deployed AKS | kubectl + Container Insights | `kubectl logs <pod>`, `kubectl describe pod <pod>`, Container Insights in Azure Monitor |
|
|
||||||
| Azure resource health (App Insights, Front Door, NSP, private endpoint) | Deployed Azure | azure-diagnostics | → `azure-diagnostics` skill |
|
|
||||||
| App Service / Container Apps logs | Deployed Azure | azure-diagnostics | → `azure-diagnostics` skill |
|
|
||||||
| Logs/state | Deployed Docker / Compose | Docker CLI | `docker logs <container>`, `docker compose logs <service>` |
|
|
||||||
|
|
||||||
**Decision tree:**
|
|
||||||
|
|
||||||
1. Is this about **AppHost code or deployment definition**? → `aspire-deployment` skill.
|
|
||||||
2. Is the app **running locally** via `aspire start`? → Aspire CLI.
|
|
||||||
3. Is it deployed to **AKS**? → kubectl + Container Insights for workload; `azure-diagnostics` for the cluster's Azure resources.
|
|
||||||
4. Is it deployed to **other Azure** (App Service, Container Apps)? → `azure-diagnostics`.
|
|
||||||
5. Is it deployed to **Docker / Compose**? → `docker` / `docker compose` CLI.
|
|
||||||
|
|
||||||
See [diagnostics-bridge.md](references/diagnostics-bridge.md) for detailed routing.
|
|
||||||
|
|
||||||
## Investigation Workflow
|
|
||||||
|
|
||||||
When something is wrong, investigate before editing code:
|
|
||||||
|
|
||||||
1. `aspire describe` — check resource state and endpoints
|
|
||||||
2. `aspire otel logs <resource>` — structured logs first
|
|
||||||
3. `aspire logs <resource>` — console output as secondary view
|
|
||||||
4. `aspire otel traces <resource>` — cross-service activity
|
|
||||||
5. `aspire export` — zipped telemetry snapshot for deeper analysis
|
|
||||||
|
|
||||||
## Local Commands Reference
|
|
||||||
|
|
||||||
| Command | Purpose | Example |
|
|
||||||
|---------|---------|---------|
|
|
||||||
| `aspire logs <resource>` | Console stdout/stderr | `aspire logs apiservice` |
|
|
||||||
| `aspire logs --follow` | Stream logs in real-time | `aspire logs apiservice --follow` |
|
|
||||||
| `aspire otel logs` | Structured OpenTelemetry logs | `aspire otel logs` |
|
|
||||||
| `aspire otel traces` | Distributed trace data | `aspire otel traces` |
|
|
||||||
| `aspire otel spans` | Individual span detail | `aspire otel spans` |
|
|
||||||
| `aspire otel logs --trace-id <id>` | Logs correlated to trace (⚠️ verify flag) | `aspire otel logs --trace-id abc123` |
|
|
||||||
| `aspire otel logs --dashboard-url` | Query a standalone dashboard (login URL or base URL + `--api-key`) | `aspire otel logs --dashboard-url "http://localhost:18888/login?t=TOKEN" --follow` |
|
|
||||||
| `aspire otel traces --dashboard-url` | Query a standalone dashboard | `aspire otel traces --dashboard-url "http://localhost:18888/login?t=TOKEN"` |
|
|
||||||
| `aspire describe` | Resource state, endpoints, health | `aspire describe --format Json` |
|
|
||||||
| `aspire describe --include-hidden` | Include proxies, helper containers, migrations | `aspire describe --include-hidden --format Json` |
|
|
||||||
| `aspire ps --format Json` | Resource list with state (filtered) | `aspire ps --format Json` |
|
|
||||||
| `aspire ps --include-hidden --format Json` | Resource list with hidden resources | `aspire ps --include-hidden --format Json` |
|
|
||||||
| `aspire export` | Portable telemetry bundle | `aspire export` |
|
|
||||||
| `aspire dashboard run` | Standalone dashboard (foreground/blocking) | `aspire dashboard run` |
|
|
||||||
|
|
||||||
### Hidden resources are filtered by default
|
|
||||||
|
|
||||||
`aspire ps`, `aspire describe`, and other CLI commands filter out resources marked hidden in the AppHost (proxies, helper containers, migrations). The default output is correct for normal app inspection. Add `--include-hidden` when:
|
|
||||||
|
|
||||||
- Debugging proxies, sidecar/helper containers, or migration jobs.
|
|
||||||
- An expected resource is "missing" from `aspire ps` / `aspire describe`.
|
|
||||||
- Triaging connectivity or wiring issues that may involve infrastructure resources.
|
|
||||||
|
|
||||||
### Tips for Agents
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Use --format Json for machine parsing (supported: ps, describe, start)
|
|
||||||
aspire describe --format Json
|
|
||||||
|
|
||||||
# ✅ When a resource you expect is missing, retry with --include-hidden
|
|
||||||
aspire ps --include-hidden --format Json | jq '.[] | {name, displayName, state, hidden}'
|
|
||||||
|
|
||||||
# ✅ Get endpoints from describe, not guessing ports
|
|
||||||
ENDPOINT=$(aspire describe apiservice --format Json | jq -r '.endpoints[0].url')
|
|
||||||
|
|
||||||
# ✅ Use --apphost <path> when multiple AppHosts exist
|
|
||||||
aspire describe --apphost ./src/MyApp.AppHost/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known Diagnostics Issues
|
|
||||||
|
|
||||||
| Issue | Symptom | Workaround |
|
|
||||||
|-------|---------|-----------|
|
|
||||||
| TS AppHost DNS failure ([#15782](https://github.com/microsoft/aspire/issues/15782)) | `aspire otel` "No such host" for `*.dev.localhost` | Use `--dashboard-url localhost:PORT` |
|
|
||||||
| `--isolated` mode telemetry ([#16107](https://github.com/microsoft/aspire/issues/16107)) | OTEL port not randomized in isolated mode | Avoid `--isolated` if telemetry is needed |
|
|
||||||
| Resource missing from `aspire ps` / `aspire describe` | Hidden-by-default resources such as proxies, helpers, or migrations | Re-run with `--include-hidden` |
|
|
||||||
|
|
||||||
> **Resolved in 13.3**: The standalone-dashboard workaround for [#16236](https://github.com/microsoft/aspire/issues/16236) is obsolete — use `aspire dashboard run` (see below).
|
|
||||||
|
|
||||||
## Standalone Dashboard (`aspire dashboard run`)
|
|
||||||
|
|
||||||
`aspire dashboard run` launches the Aspire Dashboard without an AppHost, so any OTLP-emitting application (Aspire or not) can stream telemetry into it.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire dashboard run
|
|
||||||
# Dashboard: http://localhost:18888/login?t=<TOKEN>
|
|
||||||
# OTLP/gRPC: http://localhost:4317
|
|
||||||
# OTLP/HTTP: http://localhost:4318
|
|
||||||
```
|
|
||||||
|
|
||||||
> ⚠️ **Foreground / blocking.** `aspire dashboard run` does **not** return until you stop it (Ctrl-C). Agents must treat it as a long-running background process — start it with the bash tool's `mode="async"`, capture the dashboard URL and token from initial output, and leave it running. Do **not** invoke it as a one-shot synchronous command, and do **not** wait for it to "finish".
|
|
||||||
|
|
||||||
### Connect the Aspire CLI to a standalone dashboard
|
|
||||||
|
|
||||||
`aspire otel logs` and `aspire otel traces` accept `--dashboard-url`. The simplest form passes the full login URL printed by `aspire dashboard run` — the CLI normalizes it automatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stream structured logs from a standalone dashboard (login URL form)
|
|
||||||
aspire otel logs --dashboard-url "http://localhost:18888/login?t=TOKEN" --follow
|
|
||||||
|
|
||||||
# Search recent traces
|
|
||||||
aspire otel traces --dashboard-url "http://localhost:18888/login?t=TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
For dashboards configured with API-key authentication (e.g., the standalone container image with a separate API key), pass `--api-key` alongside the base `--dashboard-url`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire otel logs --dashboard-url https://my-dashboard.example.com --api-key "$DASHBOARD_API_KEY" --follow
|
|
||||||
```
|
|
||||||
|
|
||||||
The container-image standalone dashboard still works for environments where the CLI isn't available.
|
|
||||||
|
|
||||||
## Browser Telemetry (`Aspire.Hosting.Browsers`)
|
|
||||||
|
|
||||||
The `Aspire.Hosting.Browsers` integration captures **browser console logs, network requests, and screenshots** from frontend resources during local development and surfaces them in the dashboard alongside server-side telemetry. Frontend resources opt in via `WithBrowserLogs()`.
|
|
||||||
|
|
||||||
| Need | Action |
|
|
||||||
|------|--------|
|
|
||||||
| Inspect browser telemetry that is already wired | Open the dashboard; browser logs / network / screenshots appear next to server telemetry for the resource |
|
|
||||||
| Confirm a frontend has it enabled | Check the AppHost for `.WithBrowserLogs()` on the resource (e.g., `AddViteApp("frontend").WithBrowserLogs()`) |
|
|
||||||
| Add `WithBrowserLogs()` to a resource | → **`aspireify` skill** (AppHost authoring) — do not edit the AppHost from this skill |
|
|
||||||
|
|
||||||
When parsing telemetry programmatically, browser logs surface as additional OTLP log records associated with the frontend resource — `aspire otel logs <frontend-resource>` returns them alongside server logs.
|
|
||||||
|
|
||||||
## Dashboard UX Features
|
|
||||||
|
|
||||||
Agents inspecting a running dashboard should know:
|
|
||||||
|
|
||||||
- **Notification center** (bell icon, top-right) — surfaces results of resource commands and lifecycle events. Inline command responses appear here instead of being scraped from the logs panel.
|
|
||||||
- **Rebuild command** — available on container and project resources; rebuilds the image and restarts the resource without restarting the whole AppHost. Result lands in the notification center.
|
|
||||||
- **Structured command results** — custom resource commands return `ExecuteCommandResult` with a `Message` payload that the dashboard renders inline; HTTP commands set `HttpCommandResultMode.Auto | Json | Text | None` to control how the response body is shown.
|
|
||||||
|
|
||||||
> Authoring custom commands or `WithBrowserLogs()` calls is AppHost work — route to **`aspireify`**. This skill is for *observing* what those features surface in the dashboard.
|
|
||||||
|
|
||||||
## Why Aspire CLI Can't Do Remote Diagnostics
|
|
||||||
|
|
||||||
The Aspire CLI talks to a *running AppHost* through a local backchannel socket at `~/.aspire/backchannels/`. This is **by design** — there is no remote backchannel. For deployed apps, route to platform-specific tools (azure-diagnostics, kubectl, docker).
|
|
||||||
|
|
||||||
**Exception**: if a Dashboard is reachable (deployed alongside the app, or running standalone), `aspire otel logs` and `aspire otel traces` can query it via `--dashboard-url` (login URL form) and optional `--api-key` (see the Standalone Dashboard section above). This does **not** apply to `aspire logs` or `aspire describe`.
|
|
||||||
|
|
||||||
## Handoff Rules
|
|
||||||
|
|
||||||
| Scenario | Route To |
|
|
||||||
|----------|----------|
|
|
||||||
| Start/stop/wait/rebuild lifecycle | → `aspire-orchestration` skill |
|
|
||||||
| Deploy, publish, pipeline steps, AppHost compute environment binding | → `aspire-deployment` skill |
|
|
||||||
| AppHost code changes (`WithBrowserLogs()`, custom commands, `WithHttpCommand`) | → `aspireify` skill |
|
|
||||||
| Deployed Azure resource health (App Insights, Front Door, NSP, private endpoint, ACA, App Service) | → `azure-diagnostics` skill (azure-skills) |
|
|
||||||
| AKS workload diagnostics (pod logs, pod state, Container Insights) | → `kubectl` + Azure Monitor Container Insights |
|
|
||||||
| Docker / Compose container logs | → `docker logs` / `docker compose logs` |
|
|
||||||
|
|
||||||
## Project-Local Skill Routing
|
|
||||||
|
|
||||||
If `.agents/skills/aspire/SKILL.md` exists (from `aspire agent init`), see its
|
|
||||||
`references/monitoring.md` for deeper telemetry workflow guidance.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [diagnostics-bridge.md](references/diagnostics-bridge.md) — Local vs deployed routing detail
|
|
||||||
- [monitoring.md](references/monitoring.md) — Telemetry inspection and export patterns
|
|
||||||
- [playwright-handoff.md](references/playwright-handoff.md) — Find the correct Aspire frontend URL before browser testing
|
|
||||||
-209
@@ -1,209 +0,0 @@
|
|||||||
# Diagnostics Bridge — Local vs Deployed
|
|
||||||
|
|
||||||
> **Purpose**: Route diagnostics requests to the correct tool based on where the application is running.
|
|
||||||
|
|
||||||
## Decision Flowchart
|
|
||||||
|
|
||||||
```
|
|
||||||
Is the request about AppHost code or deployment definition?
|
|
||||||
│
|
|
||||||
├── YES → Route to aspire-deployment skill (and aspireify for code edits)
|
|
||||||
│
|
|
||||||
└── NO → Is the app running locally (via aspire start)?
|
|
||||||
│
|
|
||||||
├── YES → Use Aspire CLI
|
|
||||||
│ ├── Console logs → aspire logs <resource>
|
|
||||||
│ ├── Structured logs → aspire otel logs
|
|
||||||
│ ├── Traces → aspire otel traces
|
|
||||||
│ ├── Spans → aspire otel spans
|
|
||||||
│ ├── Resource state → aspire describe (add --include-hidden if missing)
|
|
||||||
│ ├── Export telemetry → aspire export
|
|
||||||
│ ├── Filter by trace → aspire otel logs --trace-id <id> (verify flag)
|
|
||||||
│ └── Standalone dash → aspire dashboard run (foreground/blocking)
|
|
||||||
│
|
|
||||||
└── NO (deployed) → Route by target
|
|
||||||
├── Azure Kubernetes Service (AKS)
|
|
||||||
│ ├── Pod logs → kubectl logs <pod>
|
|
||||||
│ ├── Pod / workload state → kubectl describe pod <pod>, kubectl get pods
|
|
||||||
│ ├── Cluster Azure resources → azure-diagnostics skill
|
|
||||||
│ └── Cluster-wide telemetry → Azure Monitor Container Insights
|
|
||||||
│
|
|
||||||
├── Other Azure (App Service, Container Apps) → azure-diagnostics skill
|
|
||||||
│ ├── App logs → az containerapp logs show / az webapp log tail
|
|
||||||
│ ├── Metrics → az monitor metrics list
|
|
||||||
│ ├── App Insights → az monitor app-insights query
|
|
||||||
│ ├── Resource health → az resource show / AppLens
|
|
||||||
│ └── Front Door / NSP / private endpoint → azure-diagnostics
|
|
||||||
│
|
|
||||||
└── Docker / Compose → Use Docker tooling
|
|
||||||
├── Container logs → docker logs <container>
|
|
||||||
├── Service logs → docker compose logs <service>
|
|
||||||
└── Resource state → docker ps / docker compose ps
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Local Development — Full Aspire CLI Support
|
|
||||||
|
|
||||||
When the app is running locally via `aspire start`, the Aspire CLI provides complete observability:
|
|
||||||
|
|
||||||
### How It Works
|
|
||||||
|
|
||||||
The Aspire CLI communicates with the running AppHost through a **backchannel socket** at `~/.aspire/backchannels/`. This is a local-only IPC mechanism — it cannot connect to remote instances.
|
|
||||||
|
|
||||||
### Available Commands
|
|
||||||
|
|
||||||
| Command | Purpose | Example |
|
|
||||||
|---------|---------|---------|
|
|
||||||
| `aspire logs <resource>` | Console stdout/stderr from a resource | `aspire logs apiservice` |
|
|
||||||
| `aspire logs --follow` | Stream logs in real-time | `aspire logs apiservice --follow` |
|
|
||||||
| `aspire otel logs` | Structured OpenTelemetry log records | `aspire otel logs` |
|
|
||||||
| `aspire otel traces` | Distributed trace data | `aspire otel traces` |
|
|
||||||
| `aspire otel spans` | Individual span-level detail | `aspire otel spans` |
|
|
||||||
| `aspire otel logs --trace-id <id>` | Logs correlated to a specific trace (⚠️ verify flag in your version) | `aspire otel logs --trace-id abc123` |
|
|
||||||
| `aspire otel logs --dashboard-url` | Query a standalone or deployed dashboard | `aspire otel logs --dashboard-url "https://localhost:18888/login?t=TOKEN"` |
|
|
||||||
| `aspire describe` | Resource state, endpoints, health (filtered) | `aspire describe --format Json` |
|
|
||||||
| `aspire describe --include-hidden` | Include hidden resources (proxies, helpers, migrations) | `aspire describe --include-hidden --format Json` |
|
|
||||||
| `aspire ps --include-hidden --format Json` | Resource list including hidden resources | `aspire ps --include-hidden --format Json` |
|
|
||||||
| `aspire export` | Export portable telemetry bundle | `aspire export` |
|
|
||||||
| `aspire dashboard run` | Run the Aspire Dashboard standalone (foreground/blocking) | `aspire dashboard run` |
|
|
||||||
|
|
||||||
### Tips for Agents
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Always use --format Json for machine parsing
|
|
||||||
aspire describe --format Json
|
|
||||||
|
|
||||||
# ✅ When an expected resource is missing, retry with --include-hidden
|
|
||||||
# Hidden-by-default resources (proxies, helper containers, migrations)
|
|
||||||
aspire ps --include-hidden --format Json
|
|
||||||
|
|
||||||
# ✅ Get endpoints from describe, not guessing ports
|
|
||||||
ENDPOINT=$(aspire describe apiservice --format Json | jq -r '.endpoints[0].url')
|
|
||||||
|
|
||||||
# ✅ Correlate logs to a specific request
|
|
||||||
aspire otel logs --trace-id <trace-id-from-otel-traces>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Standalone Dashboard — `aspire dashboard run`
|
|
||||||
|
|
||||||
`aspire dashboard run` runs the Aspire Dashboard without an AppHost — point any OTLP-emitting application at it (Aspire or not) and telemetry shows up live.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire dashboard run
|
|
||||||
# Dashboard: http://localhost:18888/login?t=<TOKEN>
|
|
||||||
# OTLP/gRPC: http://localhost:4317
|
|
||||||
# OTLP/HTTP: http://localhost:4318
|
|
||||||
```
|
|
||||||
|
|
||||||
> ⚠️ **Foreground / blocking.** This command does not return until you stop it (Ctrl-C). Agents must start it as a long-running background process (e.g., bash `mode="async"`), capture the dashboard URL and `t=` token from initial output, and leave it running. Do **not** treat it as a one-shot synchronous command.
|
|
||||||
|
|
||||||
### Connect the CLI to a standalone dashboard
|
|
||||||
|
|
||||||
The `aspire otel logs` and `aspire otel traces` commands accept `--dashboard-url` (and `--api-key` when the dashboard is configured with API-key auth) so the CLI can query a standalone dashboard without an AppHost.
|
|
||||||
|
|
||||||
The simplest form passes the full login URL printed by `aspire dashboard run` — the CLI normalizes login URLs automatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stream structured logs (login URL form — token in URL)
|
|
||||||
aspire otel logs --dashboard-url "http://localhost:18888/login?t=TOKEN" --follow
|
|
||||||
|
|
||||||
# Search recent traces
|
|
||||||
aspire otel traces --dashboard-url "http://localhost:18888/login?t=TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
For dashboards that use a separate API key (e.g., the standalone container image with API-key auth configured), pass `--api-key` alongside the base URL:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire otel logs --dashboard-url https://my-dashboard.example.com --api-key "$DASHBOARD_API_KEY" --follow
|
|
||||||
```
|
|
||||||
|
|
||||||
The container-image standalone dashboard is still available where the CLI isn't an option.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Browser Telemetry (`Aspire.Hosting.Browsers`)
|
|
||||||
|
|
||||||
The `Aspire.Hosting.Browsers` integration captures **browser console logs, network requests, and screenshots** from frontend resources during local development. Frontend resources opt in by calling `WithBrowserLogs()` in the AppHost. The data shows up in the dashboard alongside server-side telemetry.
|
|
||||||
|
|
||||||
| Need | Action |
|
|
||||||
|------|--------|
|
|
||||||
| Inspect existing browser telemetry | Open the dashboard or run `aspire otel logs <frontend-resource>` |
|
|
||||||
| Check whether a frontend has it enabled | Look for `.WithBrowserLogs()` in the AppHost |
|
|
||||||
| Add `WithBrowserLogs()` to a resource | → **`aspireify` skill** (AppHost authoring) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Deployed Applications — Routing
|
|
||||||
|
|
||||||
### Why Aspire CLI Cannot Help Directly
|
|
||||||
|
|
||||||
The Aspire CLI's `aspire logs`, `aspire describe`, and other backchannel commands use the local backchannel socket at `~/.aspire/backchannels/`. This is **by design** — there is no remote backchannel. When an app is deployed, the Aspire CLI cannot reach it directly.
|
|
||||||
|
|
||||||
**Exception:** if a Dashboard is reachable (deployed alongside the app, or running standalone), `aspire otel logs --dashboard-url` and `aspire otel traces --dashboard-url` (with `--api-key` when the dashboard requires it) can query it remotely. This does **not** extend to `aspire logs` or `aspire describe`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Limited remote support via deployed Dashboard — login URL form
|
|
||||||
aspire otel logs --dashboard-url "https://my-dashboard.azurecontainerapps.io/login?t=TOKEN"
|
|
||||||
aspire otel traces --dashboard-url "https://my-dashboard.azurecontainerapps.io/login?t=TOKEN"
|
|
||||||
|
|
||||||
# Or with separate API-key auth
|
|
||||||
aspire otel logs --dashboard-url https://my-dashboard.azurecontainerapps.io --api-key "$DASHBOARD_API_KEY"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Three-way deployed routing
|
|
||||||
|
|
||||||
| Target | Use | Examples |
|
|
||||||
|--------|-----|----------|
|
|
||||||
| **AKS workload (pod logs, pod state, container insights)** | `kubectl` + Azure Monitor Container Insights | `kubectl logs <pod>`, `kubectl describe pod <pod>`, Container Insights queries |
|
|
||||||
| **Azure resource health** (App Insights, Front Door, NSP, private endpoint, ACA, App Service) | `azure-diagnostics` skill (azure-skills) | `az containerapp logs show`, `az monitor app-insights query`, AppLens |
|
|
||||||
| **Docker / Compose** | Docker CLI | `docker logs <container>`, `docker compose logs <service>` |
|
|
||||||
|
|
||||||
### azure-diagnostics — quick reference
|
|
||||||
|
|
||||||
| Need | azure-diagnostics Approach |
|
|
||||||
|------|---------------------------|
|
|
||||||
| Application logs | `az containerapp logs show --name APP -g RG --follow` |
|
|
||||||
| Metrics | `az monitor metrics list --resource RESOURCE_ID` |
|
|
||||||
| App Insights queries | `az monitor app-insights query --analytics-query "KQL"` |
|
|
||||||
| Resource health | AppLens MCP tool or `az resource show` |
|
|
||||||
| Activity log | `az monitor activity-log list -g RG` |
|
|
||||||
| Front Door / NSP / private endpoint | `az network front-door`, `az network perimeter`, AppLens |
|
|
||||||
|
|
||||||
### Production Telemetry — Automatic Configuration
|
|
||||||
|
|
||||||
Aspire auto-configures Application Insights when `AddAzureApplicationInsights()` is used in the AppHost. Deployed apps export OpenTelemetry data to App Insights automatically, providing:
|
|
||||||
|
|
||||||
- Request traces and dependency tracking
|
|
||||||
- Exception logging
|
|
||||||
- Performance metrics
|
|
||||||
- Live Metrics stream
|
|
||||||
- Application Map (service topology)
|
|
||||||
|
|
||||||
No additional configuration is needed — Aspire wires the connection string during deployment.
|
|
||||||
|
|
||||||
## Known Diagnostics Issues
|
|
||||||
|
|
||||||
| Issue | Symptom | Workaround |
|
|
||||||
|-------|---------|-----------|
|
|
||||||
| TS AppHost DNS failure ([#15782](https://github.com/microsoft/aspire/issues/15782)) | `aspire otel` returns "No such host" for `*.dev.localhost` | Use `--dashboard-url localhost:PORT` directly |
|
|
||||||
| `--isolated` mode telemetry ([#16107](https://github.com/microsoft/aspire/issues/16107)) | OTEL port not randomized in isolated mode | Avoid `--isolated` if telemetry is needed |
|
|
||||||
| Resource missing from `aspire ps` / `aspire describe` | Hidden-by-default resources such as proxies, helpers, or migrations | Re-run with `--include-hidden` |
|
|
||||||
|
|
||||||
> **Resolved in 13.3**: The standalone-dashboard workaround for [#16236](https://github.com/microsoft/aspire/issues/16236) is obsolete — `aspire dashboard run` ships in-box (see Standalone Dashboard section above).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary: Where to Look
|
|
||||||
|
|
||||||
| Question | Local Dev | Deployed |
|
|
||||||
|----------|-----------|----------|
|
|
||||||
| "What's the status of my resources?" | `aspire describe` (try `--include-hidden` if missing) | Azure Portal / `az containerapp show` / `kubectl describe pod` |
|
|
||||||
| "Show me the logs" | `aspire logs <resource>` | `az containerapp logs show` / `kubectl logs <pod>` / `docker logs` |
|
|
||||||
| "Show me distributed traces" | `aspire otel traces` | App Insights → Transaction Search |
|
|
||||||
| "Why is this resource unhealthy?" | `aspire describe` + `aspire logs` | AppLens / azure-diagnostics / `kubectl describe pod` |
|
|
||||||
| "What metrics are available?" | Aspire Dashboard (auto-launched or `aspire dashboard run`) | Azure Monitor / App Insights / Container Insights |
|
|
||||||
| "Export telemetry for analysis" | `aspire export` | App Insights export / KQL query |
|
|
||||||
| "Browser console / network logs" | Dashboard (with `WithBrowserLogs()` enabled) — N/A in production |
|
|
||||||
-161
@@ -1,161 +0,0 @@
|
|||||||
# Monitoring
|
|
||||||
|
|
||||||
Use this when the task is about inspecting app state, logs, traces, endpoints, or sharable diagnostics.
|
|
||||||
|
|
||||||
## Scenario: I Need To Know What Is Running And Where The Endpoints Are
|
|
||||||
|
|
||||||
Use these commands when the first job is to inspect current resource state, find URLs, or hand machine-readable app state to another tool.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire describe
|
|
||||||
aspire resources
|
|
||||||
aspire describe --apphost <path>
|
|
||||||
aspire describe --apphost <path> --format Json
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Use `aspire describe` first when you need the current state of the running app before deciding what to do next.
|
|
||||||
- Use `--apphost <path>` when the workspace has multiple AppHosts or discovery is ambiguous.
|
|
||||||
- Prefer `--format Json` when another tool or script needs to consume the result, such as a Playwright handoff or endpoint extraction.
|
|
||||||
|
|
||||||
## Scenario: Something Is Wrong, But Investigate Before Editing Code
|
|
||||||
|
|
||||||
Use these commands when the task is to diagnose behavior in the live app before making code changes.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire otel logs [resource] --format Json
|
|
||||||
aspire otel traces [resource] --format Json
|
|
||||||
aspire otel spans [resource] --format Json
|
|
||||||
aspire otel logs --trace-id <id> --format Json
|
|
||||||
aspire otel logs [resource] --search "connection timeout"
|
|
||||||
aspire otel spans [resource] --search "/api/products"
|
|
||||||
aspire logs [resource]
|
|
||||||
aspire logs [resource] --search "error"
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Prefer structured telemetry before raw console logs when possible.
|
|
||||||
- Use `aspire logs` as a secondary console-output view after checking structured telemetry.
|
|
||||||
- Use the trace-filtered log command when you already have a trace id and want the related log slice.
|
|
||||||
- Use `--search` to filter results by a case-insensitive text match across all fields (messages, attribute keys/values, trace/span IDs, resource names, severity, scope names). This is the fastest way to narrow output when you know what you're looking for.
|
|
||||||
- Prefer `--format Json` when another tool or script needs to consume the result, such as a Playwright handoff or endpoint extraction.
|
|
||||||
- `[resource]` is optional. Include it to filter results to a single resource; omit it to see all resources.
|
|
||||||
- `--search` can be combined with other options like `--format Json`, `--trace-id`, `--limit`, and resource filtering.
|
|
||||||
|
|
||||||
## Scenario: I Need A Sharable Diagnostics Bundle
|
|
||||||
|
|
||||||
Use this command when you need a portable handoff artifact for deeper analysis or for another person to inspect offline.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire export [resource]
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Use `aspire export` when you need a sharable bundle of telemetry and resource state.
|
|
||||||
- `[resource]` is optional. Include it to filter the export to a single resource; omit it to export all resources.
|
|
||||||
- The output is a zip archive (default name `aspire-export-<timestamp>.zip`) containing up to four directories:
|
|
||||||
- `resources/` — one JSON file per resource with resource details (name, type, state, endpoints, environment variables, etc.).
|
|
||||||
- `consolelogs/` — one plain-text file per resource with raw console output lines.
|
|
||||||
- `structuredlogs/` — one JSON file per resource with structured log entries in OTLP format.
|
|
||||||
- `traces/` — one JSON file per resource with distributed traces and spans in OTLP format.
|
|
||||||
- When extracting the export for analysis, look at `resources/` first for an overview, then drill into `consolelogs/`, `structuredlogs/` and `traces/` for detailed diagnostics.
|
|
||||||
|
|
||||||
## Dashboard Links
|
|
||||||
|
|
||||||
Commands like `aspire describe`, `aspire otel logs`, `aspire otel traces`, and `aspire otel spans` may include dashboard URLs in their JSON output. Only use URLs that are explicitly returned by these commands — do not construct dashboard URLs yourself.
|
|
||||||
|
|
||||||
When a dashboard link is returned alongside a resource or telemetry entry, make the resource name, trace ID, or span ID a clickable markdown link using the returned URL.
|
|
||||||
|
|
||||||
## Displaying Resources
|
|
||||||
|
|
||||||
When showing resource state to the user, display the state text with a circle emoji prefix:
|
|
||||||
|
|
||||||
- 🟢 Running, healthy
|
|
||||||
- 🟡 Starting, waiting
|
|
||||||
- 🔴 Failed, error, unhealthy
|
|
||||||
- ⚪ Stopped, exited
|
|
||||||
|
|
||||||
Link resource names to their dashboard page when the dashboard URL is known.
|
|
||||||
|
|
||||||
## Displaying Telemetry
|
|
||||||
|
|
||||||
When showing structured logs, prefix each entry with an emoji matching the log level:
|
|
||||||
|
|
||||||
- 🔴 Error / Critical
|
|
||||||
- 🟡 Warning
|
|
||||||
- 🔵 Information
|
|
||||||
- ⚪ Debug / Trace
|
|
||||||
|
|
||||||
Link resource names to their dashboard resource page. When trace IDs are present, display the first 7 characters and link that value to the full trace detail page.
|
|
||||||
|
|
||||||
When showing traces or spans, use 🟢 for success/unset status and 🔴 for error status. Display only the first 7 characters of trace and span IDs, and link those values to their dashboard detail pages.
|
|
||||||
|
|
||||||
## Production Monitoring Strategy (Azure)
|
|
||||||
|
|
||||||
Aspire auto-configures Application Insights when `AddAzureApplicationInsights()` is used in the AppHost. Deployed Azure apps export OpenTelemetry to App Insights automatically:
|
|
||||||
|
|
||||||
- Request traces and dependency tracking
|
|
||||||
- Exception logging
|
|
||||||
- Performance metrics
|
|
||||||
- Live Metrics stream
|
|
||||||
- Application Map (service topology)
|
|
||||||
|
|
||||||
No additional configuration is needed for Azure once the AppHost and deployment target wire the connection strings during deployment.
|
|
||||||
|
|
||||||
> **Docker Compose / Kubernetes**: Auto-configured App Insights does not apply. These targets require platform-native observability (Prometheus, Grafana, ELK, etc.) unless the app is explicitly configured to export OTEL to an external collector.
|
|
||||||
|
|
||||||
## Deployed App Monitoring — Route by Target
|
|
||||||
|
|
||||||
| Target | Tool | Commands |
|
|
||||||
|--------|------|----------|
|
|
||||||
| Azure Container Apps / App Service | azure-diagnostics | `az containerapp logs show`, `az webapp log tail`, App Insights |
|
|
||||||
| Azure resource health (Front Door, NSP, private endpoint, App Insights) | azure-diagnostics | AppLens, `az monitor app-insights query` |
|
|
||||||
| AKS workload (pods, workloads) | kubectl + Container Insights | `kubectl logs <pod>`, `kubectl describe pod <pod>`, Azure Monitor Container Insights |
|
|
||||||
| Docker / Compose | Docker CLI | `docker logs <container>`, `docker compose logs <service>` |
|
|
||||||
|
|
||||||
## Standalone Dashboard (`aspire dashboard run`)
|
|
||||||
|
|
||||||
`aspire dashboard run` launches the Aspire Dashboard without an AppHost, so any OTLP-emitting application can stream telemetry into it.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire dashboard run
|
|
||||||
# Dashboard: http://localhost:18888/login?t=<TOKEN>
|
|
||||||
# OTLP/gRPC: http://localhost:4317
|
|
||||||
# OTLP/HTTP: http://localhost:4318
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Foreground / blocking.** `aspire dashboard run` does not return until stopped. Agents must treat it as a long-running background process, capture the dashboard URL and token from initial output, and leave it running. Do not invoke it as a one-shot synchronous command, and do not wait for it to "finish".
|
|
||||||
|
|
||||||
### Connect the Aspire CLI to a standalone dashboard
|
|
||||||
|
|
||||||
`aspire otel logs` and `aspire otel traces` accept `--dashboard-url`. The simplest form passes the full login URL printed by `aspire dashboard run`; the CLI normalizes it automatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire otel logs --dashboard-url "http://localhost:18888/login?t=TOKEN" --follow
|
|
||||||
aspire otel traces --dashboard-url "http://localhost:18888/login?t=TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
For dashboards configured with API-key authentication, pass `--api-key` alongside the base `--dashboard-url`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire otel logs --dashboard-url https://my-dashboard.example.com --api-key "$DASHBOARD_API_KEY" --follow
|
|
||||||
```
|
|
||||||
|
|
||||||
## Browser Telemetry
|
|
||||||
|
|
||||||
Frontend resources opted into `Aspire.Hosting.Browsers` via `WithBrowserLogs()` surface browser console logs, network requests, and screenshots in the dashboard alongside server-side telemetry.
|
|
||||||
|
|
||||||
| Need | Action |
|
|
||||||
|------|--------|
|
|
||||||
| Inspect browser telemetry that is already wired | Open the dashboard; browser logs / network / screenshots appear next to server telemetry for the resource |
|
|
||||||
| Confirm a frontend has it enabled | Check the AppHost for `.WithBrowserLogs()` on the resource |
|
|
||||||
| Add `WithBrowserLogs()` to a resource | Route to `aspireify`; this is AppHost authoring, not monitoring |
|
|
||||||
|
|
||||||
## Why Aspire CLI Can't Do Remote Diagnostics
|
|
||||||
|
|
||||||
The Aspire CLI talks to a *running AppHost* through a local backchannel socket at `~/.aspire/backchannels/`. This is by design — there is no remote backchannel. For deployed apps, route to platform-specific tools such as azure-diagnostics, kubectl, or Docker.
|
|
||||||
|
|
||||||
**Exception**: if a Dashboard is reachable (deployed alongside the app, or running standalone), `aspire otel logs` and `aspire otel traces` can query it via `--dashboard-url` and optional `--api-key`. This does not apply to `aspire logs` or `aspire describe`.
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
# Playwright Handoff
|
|
||||||
|
|
||||||
Use this when Playwright CLI is already configured and the next step is browser testing against a running Aspire app.
|
|
||||||
|
|
||||||
## Scenario: I Need The Right Frontend URL Before Browser Testing
|
|
||||||
|
|
||||||
Use these commands when the task is to discover the live frontend endpoint from Aspire state and then hand that URL to Playwright.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire describe --format Json
|
|
||||||
aspire describe --apphost <path> --format Json
|
|
||||||
playwright-cli --help
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Aspire discovers the endpoint first; Playwright uses the discovered endpoint after the handoff.
|
|
||||||
- Prefer `aspire describe --format Json` when the URL needs to be consumed by a script or passed to another tool.
|
|
||||||
- Use `--apphost <path>` when multiple AppHosts exist and the user is asking about one specific app.
|
|
||||||
- Do not guess frontend endpoints without first consulting Aspire state.
|
|
||||||
- If multiple frontends exist, use Aspire state to disambiguate which URL Playwright should use.
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
---
|
|
||||||
name: aspire-orchestration
|
|
||||||
description: >-
|
|
||||||
**WORKFLOW SKILL** — Manage Aspire AppHost lifecycle and recover from file locks,
|
|
||||||
port conflicts, and orphaned processes. WHEN: "start my Aspire app", "aspire start",
|
|
||||||
"aspire stop", "aspire wait", "restart the API service", "file lock error",
|
|
||||||
"MSB3491", "CS2012", "port already in use", "upgrade Aspire CLI", "aspire update --self",
|
|
||||||
"proxies missing in aspire ps", "--include-hidden", "aspire integration list",
|
|
||||||
"aspire integration search", "default watch", "hot reload". INVOKES: aspire CLI
|
|
||||||
(start, stop, wait, ps, resource, integration, add, init, doctor, update, restore).
|
|
||||||
FOR SINGLE OPERATIONS: Run the aspire CLI command directly.
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: Microsoft
|
|
||||||
version: "0.0.1"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Aspire Orchestration
|
|
||||||
|
|
||||||
> **MANDATORY COMPLIANCE** — This skill prevents agent self-harm in Aspire projects.
|
|
||||||
> Violating these rules causes file locks, orphaned processes, and user frustration ([#15801](https://github.com/microsoft/aspire/issues/15801)).
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
| Requirement | Install |
|
|
||||||
|-------------|---------|
|
|
||||||
| .NET 10.0 SDK | https://dotnet.microsoft.com/download |
|
|
||||||
| Aspire CLI (curl/PowerShell) | `curl -sSL https://aspire.dev/install.sh \| bash` |
|
|
||||||
| Aspire CLI (NativeAOT global tool, .NET 10) | `dotnet tool install -g Aspire.Cli` |
|
|
||||||
|
|
||||||
Either install method works. The `dotnet tool install` path produces a NativeAOT binary
|
|
||||||
(instant startup, no JIT warmup) and is the recommended option when .NET 10 is already present.
|
|
||||||
|
|
||||||
## Detection
|
|
||||||
|
|
||||||
Activate when ANY signal is present:
|
|
||||||
|
|
||||||
| Signal | How to Detect | Confidence |
|
|
||||||
|--------|---------------|------------|
|
|
||||||
| C# AppHost | `.csproj` containing `Aspire.AppHost.Sdk` | ✅ Definitive |
|
|
||||||
| File-based C# AppHost | `apphost.cs` or `.cs` file with `#:sdk Aspire.AppHost.Sdk` | ✅ Definitive |
|
|
||||||
| TypeScript AppHost | `apphost.ts` file in project | ✅ Definitive |
|
|
||||||
| Aspire config | `aspire.config.json` in project root | High |
|
|
||||||
| Aspire settings | `.aspire/` directory present | High |
|
|
||||||
| Generated TS modules | `.aspire/modules/` directory present | High |
|
|
||||||
| Service defaults | `Aspire.ServiceDefaults` in project references | Medium |
|
|
||||||
|
|
||||||
See [detection.md](references/detection.md) for detailed fingerprinting.
|
|
||||||
|
|
||||||
## Safety Guardrails
|
|
||||||
|
|
||||||
| Situation | ✅ ALWAYS Do | ❌ NEVER Do |
|
|
||||||
|-----------|-------------|------------|
|
|
||||||
| Start an Aspire app | `aspire start` | `dotnet run` on AppHost |
|
|
||||||
| Wait for resource ready | `aspire wait <resource>` | `curl` / HTTP polling loops |
|
|
||||||
| Code changed in a resource | Prefer resource commands, runtime watch/HMR, dashboard actions, or IDE-managed debugging | `dotnet build` against locked files |
|
|
||||||
| Task complete | `aspire stop` | Leave processes running |
|
|
||||||
| Check resource status | `aspire describe` / `aspire ps` | Manual process inspection |
|
|
||||||
| Working in git worktree | `aspire start --isolated` | `aspire start` without isolation |
|
|
||||||
| Running from AI agent | Add `--non-interactive` to all commands | Assuming interactive terminal |
|
|
||||||
| Editing unfamiliar API | `aspire docs search <topic>` then `aspire docs api search <query>` for API reference | Guessing API shape |
|
|
||||||
| C# AppHost API inspection | Use `dotnet-inspect` skill (if available) for local symbols | Guessing overloads or builder chains |
|
|
||||||
| Adding custom dashboard/resource commands | `aspire docs search "custom resource commands"` first | Inventing `WithCommand` patterns without docs |
|
|
||||||
| Installing Aspire support | Use `aspire add` or `aspire init` | ~~`dotnet workload install aspire`~~ (obsolete) |
|
|
||||||
|
|
||||||
See [safety-guardrails.md](references/safety-guardrails.md) for detailed rules and recovery patterns.
|
|
||||||
|
|
||||||
## Default Workflow
|
|
||||||
|
|
||||||
1. Confirm workspace is Aspire — identify the AppHost
|
|
||||||
2. `aspire start` (or `aspire start --isolated` in worktrees)
|
|
||||||
3. `aspire wait <resource>` before interacting with any resource
|
|
||||||
4. `aspire describe` to inspect state, then work
|
|
||||||
5. If AppHost code changed, rerun `aspire start`; if only one resource changed, prefer the resource's commands/watch/HMR/debug workflow
|
|
||||||
6. `aspire stop` when cleanup is explicitly requested or needed to release locks/ports
|
|
||||||
|
|
||||||
## Quick Reference
|
|
||||||
|
|
||||||
| Task | Command |
|
|
||||||
|------|---------|
|
|
||||||
| Start app (agents) | `aspire start` (background, preferred) |
|
|
||||||
| Start app (human) | `aspire run` (foreground, dashboard) |
|
|
||||||
| Stop app | `aspire stop` |
|
|
||||||
| Wait for resource | `aspire wait <resource>` |
|
|
||||||
| Check status | `aspire ps` or `aspire describe` |
|
|
||||||
| Show hidden resources (proxies, helpers, migrations) | `aspire ps --include-hidden` / `aspire describe --include-hidden` |
|
|
||||||
| Resource operation | `aspire resource <resource-name> <command>` such as `stop`, `start`, or `rebuild` when exposed |
|
|
||||||
| Create new project | `aspire new aspire-starter` |
|
|
||||||
| Add Aspire to existing | `aspire init` (then hand off to `aspireify` skill for wiring) |
|
|
||||||
| Add integration | `aspire add <package>` |
|
|
||||||
| Discover integrations | `aspire integration list --format Json` / `aspire integration search <query> --format Json` |
|
|
||||||
| Upgrade the CLI itself | `aspire update --self` |
|
|
||||||
| Update project package refs | `aspire update` (modifies project files — get user approval) |
|
|
||||||
| Restore generated files | `aspire restore` |
|
|
||||||
| Environment maintenance | `aspire cache clear`, `aspire certs trust`, `aspire certs clean` |
|
|
||||||
| Diagnose environment | `aspire doctor` |
|
|
||||||
| Machine-readable output | `--format Json` (supported: `ps`, `describe`, `start`) |
|
|
||||||
| Look up API reference | `aspire docs api search <query> --language csharp\|typescript` |
|
|
||||||
| Browse API entries | `aspire docs api list <scope>` |
|
|
||||||
| Get API detail | `aspire docs api get <id>` |
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| Symptom | Cause | Action |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| **File lock errors during build (`MSB3491`, `CS2012`)** | **Aspire is running and holds locks on `bin/`, `obj/`, and assemblies.** | **Run `aspire stop` first**, then rebuild or `aspire start`. Do NOT conclude the project has a permanent build failure. |
|
|
||||||
| "Port already in use" | Previous instance running | `aspire stop`, then `aspire start` |
|
|
||||||
| Resource not found | App not started or name wrong | `aspire ps` to check |
|
|
||||||
| Build errors in resource | Code error, not Aspire issue | Fix code, then use resource commands/watch/HMR/debug workflow or rerun `aspire start` if AppHost code changed |
|
|
||||||
| Environment issues | Missing SDK or tools | `aspire doctor` to diagnose |
|
|
||||||
| JSON parse failure from `aspire start` | Mixed human/JSON output ([#15843](https://github.com/microsoft/aspire/issues/15843)) | Strip non-JSON lines before parsing |
|
|
||||||
| `aspire wait` rejects name | Use `displayName` not `name` ([#15842](https://github.com/microsoft/aspire/issues/15842)) | Use `displayName` from `aspire ps --format Json` |
|
|
||||||
| `aspire ps` hangs | AppHost on breakpoint ([#15576](https://github.com/microsoft/aspire/issues/15576)) | Use timeout, check AppHost process |
|
|
||||||
| `aspire agent init` fails | Non-interactive terminal ([#16264](https://github.com/microsoft/aspire/issues/16264)) | Run from standard terminal |
|
|
||||||
| Docker daemon unavailable | Container-backed resources fail to start | Start Docker Desktop, then `aspire start` |
|
|
||||||
| Multiple AppHosts detected | Wrong AppHost targeted | Use `--apphost <path>` to specify explicitly |
|
|
||||||
|
|
||||||
### 🔒 File-Lock Recovery (MSB3491 / CS2012) — Always `aspire stop` First
|
|
||||||
|
|
||||||
When a build fails with `error MSB3491: Could not write to output file ...` or
|
|
||||||
`error CS2012: Cannot open ... for writing`, the project itself is healthy —
|
|
||||||
**Aspire is running and holding file locks** on the resource's output assemblies.
|
|
||||||
The recovery is always the same:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Correct recovery sequence
|
|
||||||
aspire stop # release the locks
|
|
||||||
# ... then either rebuild / restart one resource if the resource exposes commands ...
|
|
||||||
aspire resource <name> rebuild # example: C# project resource with rebuild command
|
|
||||||
# ... or restart the whole AppHost ...
|
|
||||||
aspire start # if AppHost code changed or Aspire was already stopped
|
|
||||||
```
|
|
||||||
|
|
||||||
| ❌ NEVER do | ✅ ALWAYS do |
|
|
||||||
|------------|-------------|
|
|
||||||
| Tell the user the project has a permanent build failure | Recognize the lock as Aspire holding outputs and run `aspire stop` |
|
|
||||||
| `dotnet build` again with locks held | `aspire stop` first, then `dotnet build` (or prefer resource commands/watch/HMR/debug workflow) |
|
|
||||||
| Delete `bin/` / `obj/` to "fix" the lock | `aspire stop` — deletion may succeed but the next build relocks |
|
|
||||||
| `pkill dotnet` or `kill <PID>` to free locks | `aspire stop` — clean shutdown via the CLI, no orphans |
|
|
||||||
| Tell the user to "reboot" or "restart your machine" | `aspire stop` — single command, instant fix |
|
|
||||||
|
|
||||||
The same rule applies to any "file in use", "cannot access the file", or
|
|
||||||
"another process is using" error during a build of an Aspire-managed resource.
|
|
||||||
|
|
||||||
## Handoff Rules
|
|
||||||
|
|
||||||
| Scenario | Route To |
|
|
||||||
|----------|----------|
|
|
||||||
| AppHost wiring after `aspire init` (scan repo, add resources, ServiceDefaults/OTel) | → `aspireify` skill ([`../aspireify/SKILL.md`](../aspireify/SKILL.md)) or project-local `.agents/skills/aspireify/SKILL.md` |
|
|
||||||
| Browser logs (`Aspire.Hosting.Browsers` / `WithBrowserLogs()`) and dashboard authoring | → `aspireify` skill (code edits) and `aspire-monitoring` (discovery) |
|
|
||||||
| Custom resource commands (`WithCommand`, `ExecuteCommandResult`, `HttpCommandResultMode`) | → `aspireify` skill |
|
|
||||||
| Lifecycle hooks (`SubscribeBeforeStart`, `SubscribeAfterResourcesCreated`, BeforeStart pipeline phase) | → `aspireify` skill |
|
|
||||||
| Endpoint authoring (`WithEndpoint` updates, `ExcludeReferenceEndpoint` flag) | → `aspireify` skill |
|
|
||||||
| Deploy, publish, pipeline steps, `aspire destroy` | → `aspire-deployment` skill |
|
|
||||||
| Logs, traces, metrics, dashboard, `aspire dashboard run` | → `aspire-monitoring` skill |
|
|
||||||
| Deployed app diagnostics | → `azure-diagnostics` skill (azure-skills) |
|
|
||||||
|
|
||||||
## Runtime Settings And Environment
|
|
||||||
|
|
||||||
| Variable | Default | Purpose |
|
|
||||||
|----------|---------|---------|
|
|
||||||
| `ASPIRE_ENABLE_CONTAINER_TUNNEL` | `true` | Container tunnel provides uniform host connectivity across Docker Desktop, Docker Engine, and Podman. Set to `false` to opt out. |
|
|
||||||
| `ASPIRE_ENVIRONMENT` | unset | Selects the environment-specific config profile — controls which `appsettings.{environment}.json` is loaded and which environment is reported in dashboard telemetry. |
|
|
||||||
| `ASPIRE_DCP_USE_DEVELOPER_CERTIFICATE` | `true` | The Aspire trusted developer certificate is used by DCP on Windows. Set to `false` to opt out. |
|
|
||||||
| `features.defaultWatchEnabled` | false unless configured | Enables Aspire default watch for supported C# and TypeScript AppHosts. Do not treat this as per-resource rebuild, restart, or hot reload for resource source changes. |
|
|
||||||
|
|
||||||
## TypeScript AppHost Note
|
|
||||||
|
|
||||||
Detection covers TS AppHosts (`apphost.ts`), but **all TS AppHost authoring is delegated to `aspireify`**.
|
|
||||||
Current rules to apply when handing off:
|
|
||||||
|
|
||||||
| Rule | Why |
|
|
||||||
|------|-----|
|
|
||||||
| Prefer unified `withEnvironment(name, value)` over deprecated per-kind helpers (`withEnvironmentEndpoint`, `withEnvironmentParameter`, `withEnvironmentConnectionString`, `withEnvironmentExpression`, `withEnvironmentFromOutput`, `withEnvironmentFromKeyVaultSecret`) | Per-kind helpers are deprecated — single API now handles all value types |
|
|
||||||
| Never edit `.aspire/modules/` directly | Generated; use `aspire add <package>` to regenerate and `aspire restore` to recover missing files |
|
|
||||||
| Use `aspire docs api search <query> --language typescript` for API lookup | TS surface differs from C# |
|
|
||||||
|
|
||||||
## Skill Routing — In-Plugin Sibling Skills
|
|
||||||
|
|
||||||
After `aspire init` drops a skeleton AppHost + `aspire.config.json`, route AppHost wiring
|
|
||||||
(scan repo → propose resource graph → edit AppHost → wire `Aspire.ServiceDefaults` / OTel →
|
|
||||||
validate via `aspire start`) to the in-plugin **aspireify** skill: [`../aspireify/SKILL.md`](../aspireify/SKILL.md).
|
|
||||||
For first-run flows that only need the skeleton drop, see the in-plugin **aspire-init** skill:
|
|
||||||
[`../aspire-init/SKILL.md`](../aspire-init/SKILL.md). This orchestration skill stays focused
|
|
||||||
on lifecycle (start/stop/wait/restart) and never edits AppHost code itself.
|
|
||||||
|
|
||||||
## Project-Local Skill Precedence
|
|
||||||
|
|
||||||
If `.agents/skills/aspire/SKILL.md` exists (from `aspire agent init`), defer to it for:
|
|
||||||
C# AppHost editing, TS AppHost editing, Playwright handoff, investigation workflows.
|
|
||||||
Safety guardrails from this plugin ALWAYS apply.
|
|
||||||
|
|
||||||
If `.agents/skills/aspireify/SKILL.md` exists project-locally (installed by `aspire init` in
|
|
||||||
current Aspire), **warn the user** that a project-local aspireify skill is present and **defer to it**
|
|
||||||
for AppHost wiring instead of the in-plugin sibling. Same precedence rule as the project-local
|
|
||||||
`aspire` skill above: project-local wins, plugin guardrails still apply.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [safety-guardrails.md](references/safety-guardrails.md) — Detailed rules and recovery patterns
|
|
||||||
- [detection.md](references/detection.md) — Project fingerprinting
|
|
||||||
- [app-commands.md](references/app-commands.md) — App lifecycle and bootstrap commands
|
|
||||||
- [resource-management.md](references/resource-management.md) — Resource wait, restart, and operations
|
|
||||||
- [agent-workflows.md](references/agent-workflows.md) — Common agent investigation, integration, TypeScript, and handoff workflows
|
|
||||||
-119
@@ -1,119 +0,0 @@
|
|||||||
# Aspire Agent Workflows
|
|
||||||
|
|
||||||
Use these patterns when a task needs investigation or orchestration rather than a one-off command lookup.
|
|
||||||
|
|
||||||
## Scenario: I Am In A Worktree And Need A Safe Background Run
|
|
||||||
|
|
||||||
Start the AppHost with `aspire start` so the CLI manages background execution. In git worktrees, use `--isolated` to avoid port conflicts and shared local state:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire start --isolated
|
|
||||||
```
|
|
||||||
|
|
||||||
If the next step depends on one resource, wait for it explicitly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire start --isolated
|
|
||||||
aspire wait myapi
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- In a git worktree, rerun `aspire start --isolated` whenever AppHost changes need to be picked up.
|
|
||||||
- Outside worktrees, rerun `aspire start`.
|
|
||||||
- Avoid `aspire run` in normal agent workflows because it blocks the terminal.
|
|
||||||
|
|
||||||
## Scenario: I Changed Code While The AppHost Is Running
|
|
||||||
|
|
||||||
Classify the change before restarting anything:
|
|
||||||
|
|
||||||
1. If the AppHost model, AppHost code, integrations, resource definitions, or AppHost-level configuration changed, rerun the same AppHost start command. In a git worktree, use `aspire start --isolated`.
|
|
||||||
2. If one resource's implementation changed, keep the AppHost running and use a resource-specific workflow.
|
|
||||||
3. If an IDE is managing debugging or hot reload, defer to the IDE and avoid overlapping Aspire CLI restart, rebuild, or watch behavior.
|
|
||||||
|
|
||||||
Use resource commands when the running AppHost already knows about the resource and only that resource needs to be operated on. Choose the resource command path that matches the resource.
|
|
||||||
|
|
||||||
For a C# project resource that exposes rebuild:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire resource api rebuild
|
|
||||||
aspire wait api
|
|
||||||
```
|
|
||||||
|
|
||||||
For a resource that needs a process restart and does not have a better resource-specific command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire resource api stop
|
|
||||||
aspire resource api start
|
|
||||||
aspire wait api
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Use `aspire resource <resource-name> <command>` as the command shape.
|
|
||||||
- Use `aspire resource <resource-name> rebuild` when a C# project resource exposes rebuild and you need the resource to pick up compiled changes.
|
|
||||||
- Use runtime or framework-native hot reload/watch for resource implementation loops when that workflow is available.
|
|
||||||
- For frontend resources, remember that frameworks such as Vite, Next.js, and similar client-side JavaScript stacks often enable hot module replacement (HMR) by default. If HMR is already applying the change through the resource's dev server, do not force a resource or AppHost restart.
|
|
||||||
- Do not restart the whole AppHost just because one resource changed or one resource needs to be rebuilt.
|
|
||||||
- Aspire default watch is controlled by `features.defaultWatchEnabled`; use it for AppHost-centered CLI watch behavior, not as a replacement for resource-specific or IDE hot reload workflows.
|
|
||||||
|
|
||||||
## Scenario: Something Is Wrong, But Do Not Edit Code Yet
|
|
||||||
|
|
||||||
Inspect the live app before editing code:
|
|
||||||
|
|
||||||
1. `aspire describe` to check resource state.
|
|
||||||
2. `aspire otel logs <resource>` to inspect structured logs. Add `--search "<term>"` to filter by keyword.
|
|
||||||
3. `aspire logs <resource>` to inspect console output. Add `--search "<term>"` to filter by keyword.
|
|
||||||
4. `aspire otel traces <resource>` to follow cross-service activity. Add `--search "<term>"` to narrow results.
|
|
||||||
5. `aspire export` when you need a zipped telemetry snapshot for deeper analysis or handoff.
|
|
||||||
|
|
||||||
## Scenario: I Need To Add An Integration, Understand An API, Or Add A Custom Command Safely
|
|
||||||
|
|
||||||
Use integration search to find the package when needed, then use the docs commands for the workflow and the API reference commands if you need the concrete API entry:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire integration search postgres
|
|
||||||
aspire docs search postgres
|
|
||||||
aspire docs get <slug>
|
|
||||||
aspire docs api search postgres --language csharp
|
|
||||||
aspire docs api get <id>
|
|
||||||
aspire add <package>
|
|
||||||
```
|
|
||||||
|
|
||||||
For dashboard or custom resource commands, use docs for the pattern and API docs for the specific entry:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search "custom resource commands"
|
|
||||||
aspire docs get custom-resource-commands
|
|
||||||
aspire docs api search WithCommand --language csharp
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep these points in mind:
|
|
||||||
|
|
||||||
- Read the docs before editing the AppHost so the implementation follows a documented Aspire pattern instead of guessing the workflow.
|
|
||||||
- Use `aspire integration list` when the user asks to discover available integrations before changing the AppHost, or `aspire integration search <query>` when the package ID is unknown.
|
|
||||||
- Use `aspire docs api` when you need the C# or TypeScript reference entry for the exact API you are about to call.
|
|
||||||
- If the AppHost is C# and you need to understand local overloads or builder chains, use the `dotnet-inspect` skill if it is available after checking the Aspire API reference.
|
|
||||||
- After adding an integration, restart with `aspire start` so the updated AppHost takes effect.
|
|
||||||
|
|
||||||
## Scenario: The AppHost Is TypeScript And Generated APIs Matter
|
|
||||||
|
|
||||||
If the AppHost is `apphost.ts`, the `.aspire/modules/` directory contains generated TypeScript modules that expose Aspire APIs.
|
|
||||||
|
|
||||||
- Do not edit `.aspire/modules/` directly.
|
|
||||||
- Use `aspire add <package>` to regenerate the available APIs when adding integrations.
|
|
||||||
- Use `aspire restore` if `.aspire/modules/` disappeared after a pull, clean, or branch switch.
|
|
||||||
- Inspect `.aspire/modules/aspire.ts` after regeneration or restore to see the newly available APIs.
|
|
||||||
|
|
||||||
## Scenario: I Need Secrets, Deployment, Or A Playwright Handoff
|
|
||||||
|
|
||||||
Use `aspire secret` for AppHost user secrets, especially connection strings and passwords:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire secret set Parameters:postgres-password MySecretValue
|
|
||||||
aspire secret list
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `aspire publish` and `aspire deploy` for full deployment work, or `aspire do <step>` when the user only wants one named pipeline step such as seeding data or pushing containers.
|
|
||||||
|
|
||||||
If Playwright CLI is configured in the environment, use Aspire to discover the endpoint first and let Playwright use that discovered URL afterward. When multiple frontends exist or the URL needs to be passed to another tool, prefer `aspire describe --format Json` before the Playwright handoff.
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
# App Commands
|
|
||||||
|
|
||||||
Use this when the task is about app-level lifecycle, bootstrap, or AppHost-wide maintenance.
|
|
||||||
|
|
||||||
## Start The App Safely In The Background
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire start
|
|
||||||
aspire start --isolated
|
|
||||||
aspire stop
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use `aspire start` for normal background AppHost execution.
|
|
||||||
- In git worktrees or when another local instance may already be running, use `aspire start --isolated`.
|
|
||||||
- To restart after AppHost changes, rerun the same start command.
|
|
||||||
- Use `aspire stop` when cleanup is explicitly requested, ports/locks need to be released, or you are finished with a started instance that the user did not ask to keep running.
|
|
||||||
- Avoid `aspire run` in agent workflows — it blocks the terminal.
|
|
||||||
|
|
||||||
### `aspire run` vs `aspire start`
|
|
||||||
|
|
||||||
| Command | Mode | Use Case |
|
|
||||||
|---------|------|----------|
|
|
||||||
| `aspire run` | Foreground (interactive) | Human developer at terminal |
|
|
||||||
| `aspire start` | Background (detached) | **AI agents — always prefer** |
|
|
||||||
| `aspire run --detach` | Background | Alternative to `aspire start` |
|
|
||||||
|
|
||||||
## Create A New Aspire App Or Add Aspire To An Existing App
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire new
|
|
||||||
aspire init
|
|
||||||
aspire init --language typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use `aspire new` when creating a brand-new Aspire app from scratch.
|
|
||||||
- Use `aspire init` when adding Aspire to an existing application.
|
|
||||||
|
|
||||||
## After `aspire init` — Hand Off to `aspireify`
|
|
||||||
|
|
||||||
`aspire init` drops a minimal AppHost skeleton + AppHost configuration into the
|
|
||||||
repo and installs the **`aspireify`** agent skill alongside it. `aspire init` itself does not
|
|
||||||
wire resources, projects, or integrations. Hand off the wiring step to:
|
|
||||||
|
|
||||||
1. The in-plugin sibling skill: [`../../aspireify/SKILL.md`](../../aspireify/SKILL.md), or
|
|
||||||
2. The project-local `.agents/skills/aspireify/SKILL.md` if `aspire init` installed it
|
|
||||||
(project-local wins — defer to it and warn the user).
|
|
||||||
|
|
||||||
The aspireify workflow:
|
|
||||||
|
|
||||||
1. **Scan** the repository — discover .NET projects, Node.js apps, Python/Go services, docker-compose files
|
|
||||||
2. **Present findings** — confirm with user which services to include
|
|
||||||
3. **Wire the AppHost** — add resources using 3-tier API preference:
|
|
||||||
- Tier 1: First-party `Aspire.Hosting.*` (e.g., `AddPostgres`, `AddRedis`, `AddViteApp`, `AddNextJsApp`)
|
|
||||||
- Tier 2: Community Toolkit `CommunityToolkit.Aspire.Hosting.*` (e.g., `AddGolangApp`)
|
|
||||||
- Tier 3: Raw fallbacks (`AddExecutable`, `AddDockerfile`, `AddContainer`)
|
|
||||||
4. **Configure dependencies** — ServiceDefaults for .NET, OTel for non-.NET
|
|
||||||
5. **Validate** — `aspire start` until all resources are healthy
|
|
||||||
|
|
||||||
### Key Init Rules
|
|
||||||
|
|
||||||
- **Never install the obsolete Aspire workload** (`dotnet workload install aspire`)
|
|
||||||
- **Never change the repo's .NET SDK version** (don't modify root `global.json`)
|
|
||||||
- **Never change existing project target frameworks** (older TFMs work with newer AppHost)
|
|
||||||
- **Always use `aspire docs search` before writing AppHost code** — don't guess APIs
|
|
||||||
- **Never hardcode URLs** — use endpoint references (`WithReference`, `WithEnvironment` with expressions)
|
|
||||||
- **Never overwrite existing files** — augment and merge
|
|
||||||
- **Adapt the AppHost to the app**, not the other way around
|
|
||||||
|
|
||||||
### Init Config: `aspire.config.json`
|
|
||||||
|
|
||||||
Read `aspire.config.json` at repo root for init context:
|
|
||||||
|
|
||||||
| Field | Values | Meaning |
|
|
||||||
|-------|--------|---------|
|
|
||||||
| `appHost.language` | `"typescript/nodejs"` or `"csharp"` | AppHost syntax to use |
|
|
||||||
| `appHost.path` | Path to AppHost file/dir | Where to edit |
|
|
||||||
|
|
||||||
C# has two sub-modes:
|
|
||||||
- **Single-file**: `appHost.path` → `apphost.cs` (uses `#:sdk` directive)
|
|
||||||
- **Full project**: `appHost.path` → directory with `.csproj` + `Program.cs`
|
|
||||||
|
|
||||||
## Find The Right AppHost Or Refresh AppHost-Wide Support
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire ps
|
|
||||||
aspire integration list --format Json
|
|
||||||
aspire integration search <query> --format Json
|
|
||||||
aspire add <package>
|
|
||||||
aspire update --self # upgrades the Aspire CLI itself (NativeAOT global tool or curl install)
|
|
||||||
aspire update # updates project package references / aspire.config.json
|
|
||||||
aspire restore
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use `aspire ps` first to discover which AppHost is already running.
|
|
||||||
- Use `aspire integration list --format Json` and `aspire integration search <query> --format Json` for read-only integration discovery.
|
|
||||||
- Use `aspire add <package>` to add integrations and regenerate AppHost APIs when you are ready to mutate the AppHost.
|
|
||||||
- Use **`aspire update --self`** to upgrade the Aspire CLI itself — the safe, no-side-effect upgrade path agents can run unattended.
|
|
||||||
- Use **`aspire update` (no `--self`)** to refresh AppHost package references and bump pinned versions in `aspire.config.json`. This **modifies project files** — get user approval before running unattended (CI / agent flows).
|
|
||||||
- Use `aspire restore` after pulls, cleans, or missing generated files.
|
|
||||||
- Use `--apphost <path>` when the workspace has multiple AppHosts. The CLI's global config validates configured AppHost paths to catch typos early.
|
|
||||||
|
|
||||||
## Key Rules
|
|
||||||
|
|
||||||
- **Never install the obsolete Aspire workload** (`dotnet workload install aspire`). Use `aspire add`, `aspire init`, or `aspire new` instead.
|
|
||||||
- **Never edit `.aspire/modules/` directly** in TypeScript AppHosts. Use `aspire add <package>` to regenerate APIs, `aspire restore` if files are missing.
|
|
||||||
- For unfamiliar C# AppHost APIs, use `aspire docs search` as primary reference. If the `dotnet-inspect` skill is available, use it to inspect local symbols and overloads — but keep docs as the source of truth.
|
|
||||||
- For custom dashboard or resource commands (`WithCommand`), always run `aspire docs search "custom resource commands"` before implementing.
|
|
||||||
|
|
||||||
## Look Up API Reference Before Editing AppHost Code
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire docs search <query>
|
|
||||||
aspire docs get <slug>
|
|
||||||
aspire docs api search <query> --language csharp
|
|
||||||
aspire docs api search <query> --language typescript
|
|
||||||
aspire docs api list <scope>
|
|
||||||
aspire docs api get <id>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use `aspire docs search` and `aspire docs get` for workflow guidance and documented patterns.
|
|
||||||
- Use `aspire docs api search` when you need the C# or TypeScript API reference entry for a resource builder, extension method, or member.
|
|
||||||
- Use `aspire docs api list <scope>` to browse children under a language, package, module, type, or symbol.
|
|
||||||
- Always specify `--language csharp` or `--language typescript` to get the correct API surface.
|
|
||||||
-160
@@ -1,160 +0,0 @@
|
|||||||
# Detection — Recognizing Aspire Projects
|
|
||||||
|
|
||||||
> **Purpose**: How to identify that a project uses Aspire, and which project is the AppHost.
|
|
||||||
|
|
||||||
## Detection Signals
|
|
||||||
|
|
||||||
### 1. C# AppHost (Definitive — Strongest Signal)
|
|
||||||
|
|
||||||
Look for `.csproj` files containing the Aspire AppHost SDK reference:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<Sdk Name="Aspire.AppHost.Sdk" Version="10.0.0" />
|
|
||||||
<!-- ... -->
|
|
||||||
</Project>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Detection method**: Search for `Aspire.AppHost.Sdk` in `.csproj` files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
grep -rl "Aspire.AppHost.Sdk" --include="*.csproj" .
|
|
||||||
```
|
|
||||||
|
|
||||||
This is the **definitive signal** — if a `.csproj` contains this SDK reference, it is an Aspire AppHost project. All Aspire CLI commands should target this project's directory.
|
|
||||||
|
|
||||||
### 1b. File-Based C# AppHost (Definitive)
|
|
||||||
|
|
||||||
Single-file C# AppHosts use `apphost.cs` (or similar `.cs` files) with SDK directives instead of a `.csproj`:
|
|
||||||
|
|
||||||
```cs
|
|
||||||
#:sdk Aspire.AppHost.Sdk
|
|
||||||
#:property IsAspireHost=true
|
|
||||||
|
|
||||||
var builder = DistributedApplication.CreateBuilder(args);
|
|
||||||
// ...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Detection method**: Search for `apphost.cs` or `.cs` files containing `#:sdk Aspire.AppHost.Sdk`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
find . -name "apphost.cs" -not -path "*/node_modules/*"
|
|
||||||
grep -rl "#:sdk Aspire.AppHost.Sdk" --include="*.cs" .
|
|
||||||
```
|
|
||||||
|
|
||||||
File-based AppHosts are run the same way: `aspire start` (never `dotnet apphost.cs` directly).
|
|
||||||
|
|
||||||
### 2. TypeScript AppHost (Definitive)
|
|
||||||
|
|
||||||
Look for an `apphost.ts` file in the project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
find . -name "apphost.ts" -not -path "*/node_modules/*"
|
|
||||||
```
|
|
||||||
|
|
||||||
A TypeScript AppHost uses the `@aspire/apphost` package and defines resources programmatically in TypeScript instead of C#.
|
|
||||||
|
|
||||||
### 3. `.aspire/modules/` Directory (High Confidence)
|
|
||||||
|
|
||||||
Aspire generates a `.aspire/modules/` directory for TypeScript AppHost support files. Its presence strongly indicates an Aspire project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
[ -d ".aspire/modules" ] && echo "Aspire project detected"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. `aspire.config.json` Configuration (High Confidence)
|
|
||||||
|
|
||||||
Aspire 13.2+ uses a rooted `aspire.config.json` file (replaces legacy `aspire.json`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
[ -f "aspire.config.json" ] && echo "Aspire configuration found"
|
|
||||||
# Legacy fallback:
|
|
||||||
[ -f "aspire.json" ] && echo "Legacy Aspire config found (pre-13.2)"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. `.aspire/` Directory (High Confidence)
|
|
||||||
|
|
||||||
The `.aspire/` directory stores Aspire settings and secrets:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
[ -d ".aspire" ] && echo "Aspire settings directory found"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Service Defaults References (Medium Confidence)
|
|
||||||
|
|
||||||
Projects that reference `Aspire.ServiceDefaults` are Aspire service projects (not the AppHost, but part of an Aspire solution):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
grep -rl "Aspire.ServiceDefaults" --include="*.csproj" .
|
|
||||||
```
|
|
||||||
|
|
||||||
This indicates the project is **part of** an Aspire solution, but these are the service projects, not the AppHost. Look for the AppHost SDK reference separately.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Detection Priority
|
|
||||||
|
|
||||||
When scanning a repository, check signals in this order:
|
|
||||||
|
|
||||||
| Priority | Signal | What It Means |
|
|
||||||
|----------|--------|---------------|
|
|
||||||
| 1 | `Aspire.AppHost.Sdk` in `.csproj` | This IS the AppHost — target for `aspire start` |
|
|
||||||
| 1b | `apphost.cs` or `#:sdk Aspire.AppHost.Sdk` in `.cs` | File-based C# AppHost — target for `aspire start` |
|
|
||||||
| 2 | `apphost.ts` file | TypeScript AppHost — target for `aspire start` |
|
|
||||||
| 3 | `.aspire/modules/` directory | Aspire project — look for the AppHost |
|
|
||||||
| 4 | `aspire.config.json` or `.aspire/` | Aspire project — look for the AppHost |
|
|
||||||
| 5 | `Aspire.ServiceDefaults` references | Part of Aspire solution — AppHost is elsewhere |
|
|
||||||
|
|
||||||
## Finding the AppHost Directory
|
|
||||||
|
|
||||||
The Aspire CLI commands must be run from the correct context. After detecting an Aspire project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Find the AppHost project directory
|
|
||||||
APPHOST_DIR=$(dirname $(grep -rl "Aspire.AppHost.Sdk" --include="*.csproj" .))
|
|
||||||
|
|
||||||
# Or for file-based C# AppHost
|
|
||||||
APPHOST_FILE=$(find . -name "apphost.cs" -not -path "*/node_modules/*" | head -1)
|
|
||||||
|
|
||||||
# Or for TypeScript
|
|
||||||
APPHOST_DIR=$(dirname $(find . -name "apphost.ts" -not -path "*/node_modules/*" | head -1))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Project Structures
|
|
||||||
|
|
||||||
### Typical C# Aspire Solution
|
|
||||||
|
|
||||||
```
|
|
||||||
MyApp/
|
|
||||||
├── MyApp.AppHost/ ← AppHost (has Aspire.AppHost.Sdk)
|
|
||||||
│ ├── MyApp.AppHost.csproj
|
|
||||||
│ └── Program.cs
|
|
||||||
├── MyApp.ApiService/ ← Service project
|
|
||||||
│ └── MyApp.ApiService.csproj
|
|
||||||
├── MyApp.Web/ ← Frontend project
|
|
||||||
│ └── MyApp.Web.csproj
|
|
||||||
├── MyApp.ServiceDefaults/ ← Shared defaults
|
|
||||||
│ └── MyApp.ServiceDefaults.csproj
|
|
||||||
├── .aspire/
|
|
||||||
│ └── modules/ ← Aspire-generated
|
|
||||||
├── aspire.config.json
|
|
||||||
└── MyApp.sln
|
|
||||||
```
|
|
||||||
|
|
||||||
### Typical TypeScript Aspire Project
|
|
||||||
|
|
||||||
```
|
|
||||||
MyApp/
|
|
||||||
├── apphost.ts ← TypeScript AppHost
|
|
||||||
├── package.json
|
|
||||||
├── src/
|
|
||||||
│ ├── api/ ← Service project
|
|
||||||
│ └── web/ ← Frontend project
|
|
||||||
├── .aspire/
|
|
||||||
│ └── modules/
|
|
||||||
└── aspire.config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Non-Aspire Projects
|
|
||||||
|
|
||||||
If none of the detection signals are found, this is **not** an Aspire project. Do not apply Aspire-specific rules. Standard .NET commands (`dotnet run`, `dotnet build`) are appropriate for non-Aspire projects.
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
# Resource Management
|
|
||||||
|
|
||||||
Use this when the task is scoped to one resource or depends on a specific resource becoming healthy.
|
|
||||||
|
|
||||||
## Wait For One Resource Before Touching It
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire wait <resource>
|
|
||||||
aspire wait <resource> --status up --timeout 60
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use `aspire wait` before a dependent action when readiness is the blocker.
|
|
||||||
- Add `--status` and `--timeout` for explicit readiness conditions.
|
|
||||||
- Treat readiness as resource-scoped — a missing ready signal is not a reason to restart the whole AppHost.
|
|
||||||
- Use `displayName` from `aspire ps --format Json`, not `name` ([#15842](https://github.com/microsoft/aspire/issues/15842)).
|
|
||||||
|
|
||||||
## Fix Or Operate On One Resource Without Bouncing The Whole App
|
|
||||||
|
|
||||||
```bash
|
|
||||||
aspire resource <resource> start
|
|
||||||
aspire resource <resource> stop
|
|
||||||
aspire resource <resource> <command>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Prefer resource-scoped commands when the task doesn't require an AppHost-wide restart.
|
|
||||||
- If one resource is wedged, use resource-scoped commands such as `stop`, `start`, or `rebuild` when the resource exposes them before escalating to a full AppHost restart.
|
|
||||||
- Use `aspire resource <resource> <command>` when the AppHost exposes resource-specific dashboard or operational commands.
|
|
||||||
- If the resource's own framework watch/HMR/debug workflow is already handling the change, do not force an Aspire resource command.
|
|
||||||
|
|
||||||
## What Changed Determines the Action
|
|
||||||
|
|
||||||
| What Changed | Action | Command |
|
|
||||||
|--------------|--------|---------|
|
|
||||||
| AppHost project (Program.cs, .csproj) | Full restart | `aspire stop` → edit → `aspire start` |
|
|
||||||
| .NET service project (.cs files) | Rebuild/refresh resource if exposed | `aspire resource <name> rebuild` or the resource's IDE/watch workflow |
|
|
||||||
| JavaScript/Python/Go files | Usually no Aspire action | File watchers/HMR handle it automatically |
|
|
||||||
| Configuration (appsettings.json) | Check first | `aspire describe` then decide |
|
|
||||||
| TypeScript AppHost deps | Restore | `aspire restore` |
|
|
||||||
-272
@@ -1,272 +0,0 @@
|
|||||||
# Safety Guardrails — Aspire Agent Rules
|
|
||||||
|
|
||||||
> **Purpose**: Detailed explanation of why each guardrail exists, what goes wrong when violated, and how to recover.
|
|
||||||
|
|
||||||
## Why These Rules Exist
|
|
||||||
|
|
||||||
Issue [#15801](https://github.com/microsoft/aspire/issues/15801) documented 5 specific failures when AI agents work in Aspire projects without guidance. Every rule below directly prevents one or more of these failures.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 1: ALWAYS `aspire start` — NEVER `dotnet run`
|
|
||||||
|
|
||||||
### Why `dotnet run` Is Dangerous for AppHosts
|
|
||||||
|
|
||||||
The AppHost project is an **orchestrator**, not a regular .NET app. Running it with `dotnet run`:
|
|
||||||
|
|
||||||
- **Bypasses the Aspire CLI orchestration layer** — resources don't get managed lifecycle
|
|
||||||
- **No dashboard** — the Aspire developer dashboard won't launch
|
|
||||||
- **No backchannel** — `aspire wait`, `aspire logs`, `aspire describe` won't work
|
|
||||||
- **No resource management** — can't operate on individual resources
|
|
||||||
- **Port conflicts** — resources start without coordinated port allocation
|
|
||||||
- **No cleanup** — orphaned processes when the host exits
|
|
||||||
|
|
||||||
### Correct Pattern
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Start the Aspire app
|
|
||||||
aspire start
|
|
||||||
|
|
||||||
# ✅ Verify it's running
|
|
||||||
aspire ps
|
|
||||||
```
|
|
||||||
|
|
||||||
### `aspire run` vs `aspire start`
|
|
||||||
|
|
||||||
| Command | Mode | Dashboard | Use Case |
|
|
||||||
|---------|------|-----------|----------|
|
|
||||||
| `aspire run` | Foreground (interactive) | Yes, in terminal | Human developer at terminal |
|
|
||||||
| `aspire start` | Background (detached) | No terminal output | **AI agents — always prefer this** |
|
|
||||||
| `aspire run --detach` | Background (same as start) | Yes, separate window | Alternative to `aspire start` |
|
|
||||||
|
|
||||||
For AI agents, **always use `aspire start`** — it runs in the background and returns control to the agent.
|
|
||||||
|
|
||||||
### Recovery If `dotnet run` Was Used
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Kill the dotnet process manually (find PID first)
|
|
||||||
# Then start correctly:
|
|
||||||
aspire start
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 2: ALWAYS `aspire wait` — NEVER `curl` Polling
|
|
||||||
|
|
||||||
### Why `curl` Polling Is Wrong
|
|
||||||
|
|
||||||
Aspire tracks resource readiness internally through health checks, dependency graphs, and startup ordering. Manual HTTP polling:
|
|
||||||
|
|
||||||
- **Doesn't know the correct port** — Aspire assigns ports dynamically
|
|
||||||
- **Doesn't respect dependency ordering** — a resource may respond to HTTP before its dependencies are ready
|
|
||||||
- **Creates false negatives** — health endpoint may not exist or may return 503 during startup
|
|
||||||
- **Wastes time** — retry loops with arbitrary delays vs. event-driven readiness
|
|
||||||
|
|
||||||
### Correct Pattern
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Wait for a specific resource
|
|
||||||
aspire wait apiservice
|
|
||||||
|
|
||||||
# ✅ Wait with extended timeout for slow resources
|
|
||||||
aspire wait postgres --timeout 120
|
|
||||||
|
|
||||||
# ✅ Wait then interact
|
|
||||||
aspire wait apiservice && curl $(aspire describe apiservice --format Json | jq -r '.endpoints[0].url')
|
|
||||||
```
|
|
||||||
|
|
||||||
### Machine-Readable Output
|
|
||||||
|
|
||||||
Always use `--format Json` when parsing output programmatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Get resource endpoints as JSON
|
|
||||||
aspire describe --format Json
|
|
||||||
|
|
||||||
# ❌ Never scrape text output
|
|
||||||
aspire describe | grep "http" # fragile, format may change
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 3: Prefer Resource Commands / Watch — NEVER `dotnet build` With Aspire Running
|
|
||||||
|
|
||||||
### Why `dotnet build` Causes File-Lock Errors
|
|
||||||
|
|
||||||
When Aspire is running, it holds file locks on:
|
|
||||||
|
|
||||||
- Output assemblies (`.dll`, `.pdb`) of managed resources
|
|
||||||
- Build intermediates in `obj/` directories
|
|
||||||
- NuGet package caches being read by running processes
|
|
||||||
|
|
||||||
Running `dotnet build` while Aspire holds these locks produces:
|
|
||||||
|
|
||||||
```
|
|
||||||
error MSB3491: Could not write to output file 'bin/Debug/net10.0/MyService.dll'
|
|
||||||
error CS2012: Cannot open 'obj/Debug/net10.0/MyService.dll' for writing
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent then concludes the project is "un-buildable" — a false conclusion.
|
|
||||||
|
|
||||||
### Recovery If `dotnet build` Was Used and Locks Hit
|
|
||||||
|
|
||||||
When you see `MSB3491` / `CS2012` / "file in use" / "another process is using":
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Single correct recovery
|
|
||||||
aspire stop # release all Aspire-held locks
|
|
||||||
|
|
||||||
# Then EITHER use a resource-scoped command if Aspire is still up + one resource changed:
|
|
||||||
aspire resource <name> rebuild
|
|
||||||
# OR:
|
|
||||||
aspire start # if AppHost code changed or Aspire was fully stopped
|
|
||||||
```
|
|
||||||
|
|
||||||
> 🔒 **`aspire stop` is the ONLY first step.** Do not `pkill dotnet`, do not delete
|
|
||||||
> `bin/`/`obj/`, do not "reboot to release the lock", and do not tell the user the
|
|
||||||
> project has a permanent build failure. The cause is always the same — Aspire is
|
|
||||||
> holding the output files — and the fix is always `aspire stop`.
|
|
||||||
|
|
||||||
| ❌ NEVER (file-lock recovery) | ✅ ALWAYS |
|
|
||||||
|------------------------------|----------|
|
|
||||||
| Say the project has a permanent build failure when you see `MSB3491`/`CS2012` | Recognize Aspire is holding locks and run `aspire stop` |
|
|
||||||
| `pkill dotnet` / `kill <PID>` | `aspire stop` (clean shutdown via the CLI) |
|
|
||||||
| `rm -rf bin obj` to "force" the build | `aspire stop`, then rebuild |
|
|
||||||
| Suggest a reboot | `aspire stop` (single command) |
|
|
||||||
| Re-run `dotnet build` with Aspire still up | `aspire stop` first; prefer resource commands/watch/HMR/debug workflow |
|
|
||||||
|
|
||||||
### What Changed Determines the Action
|
|
||||||
|
|
||||||
| What Changed | Action | Command |
|
|
||||||
|--------------|--------|---------|
|
|
||||||
| AppHost project (Program.cs, .csproj) | Full restart | `aspire stop` → edit → `aspire start` |
|
|
||||||
| .NET service project (.cs files) | Rebuild/refresh resource if exposed | `aspire resource <name> rebuild` or the resource's IDE/watch workflow |
|
|
||||||
| JavaScript/Python/Go files | Usually no Aspire action | File watchers/HMR handle it automatically |
|
|
||||||
| Configuration (appsettings.json) | Check first | `aspire describe` then decide |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 4: Use `aspire stop` For Cleanup — NEVER Leave Unwanted Processes Running
|
|
||||||
|
|
||||||
### Why Cleanup Matters
|
|
||||||
|
|
||||||
Aspire orchestrates multiple processes (your services, databases, message brokers, etc.). Leaving them running causes:
|
|
||||||
|
|
||||||
- **Port conflicts** — next `aspire start` fails because ports are occupied
|
|
||||||
- **File locks** — can't build or modify service code
|
|
||||||
- **Resource consumption** — databases, Redis, etc. consuming memory
|
|
||||||
- **Stale state** — old code running while you've made changes
|
|
||||||
- **Orphaned containers** — Docker containers left running
|
|
||||||
|
|
||||||
### Correct Pattern
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Stop when cleanup is requested or the user did not ask to keep it running
|
|
||||||
aspire stop
|
|
||||||
|
|
||||||
# ✅ Verify everything stopped
|
|
||||||
aspire ps # should show no running resources
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recovery from Orphaned Processes
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if anything is still running
|
|
||||||
aspire ps
|
|
||||||
|
|
||||||
# If aspire ps shows nothing but ports are blocked:
|
|
||||||
# The previous instance may have crashed. Start fresh:
|
|
||||||
aspire start # will clean up orphaned state
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 5: ALWAYS `--format Json` for Machine-Readable Output
|
|
||||||
|
|
||||||
### Why JSON Output Matters for Agents
|
|
||||||
|
|
||||||
Text output is formatted for humans and may change between versions. JSON output is:
|
|
||||||
|
|
||||||
- **Stable** — structured contract unlikely to break
|
|
||||||
- **Parseable** — `jq`, Python, or any JSON parser works
|
|
||||||
- **Complete** — includes fields not shown in text output
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Machine-readable resource list
|
|
||||||
aspire ps --format Json
|
|
||||||
|
|
||||||
# ✅ Get specific resource details
|
|
||||||
aspire describe apiservice --format Json
|
|
||||||
|
|
||||||
# ✅ Parse with jq
|
|
||||||
aspire describe --format Json | jq '.resources[] | select(.state == "Running")'
|
|
||||||
```
|
|
||||||
|
|
||||||
### ⚠️ Known JSON Output Issues
|
|
||||||
|
|
||||||
| Issue | Workaround |
|
|
||||||
|-------|-----------|
|
|
||||||
| `aspire start --format json` may emit human-readable text before JSON ([#15843](https://github.com/microsoft/aspire/issues/15843)) | Strip non-JSON lines before parsing |
|
|
||||||
| `aspire stop` does NOT support `--format json` yet | Use exit code for success/failure |
|
|
||||||
| `aspire ps --format Json` returns `name` and `displayName` fields | Use `displayName` for `aspire wait` — the `name` field may be rejected ([#15842](https://github.com/microsoft/aspire/issues/15842)) |
|
|
||||||
|
|
||||||
### Hidden Resources and `--include-hidden`
|
|
||||||
|
|
||||||
`aspire ps`, `aspire describe`, and other CLI commands **filter out resources marked as
|
|
||||||
hidden in the AppHost** (proxies, helper containers, migration jobs, etc.).
|
|
||||||
This filtering is correct for normal workflows — agents and humans see only the resources they
|
|
||||||
care about, not the implementation scaffolding.
|
|
||||||
|
|
||||||
Use `--include-hidden` when:
|
|
||||||
|
|
||||||
| Situation | Why |
|
|
||||||
|-----------|-----|
|
|
||||||
| Debugging a proxy or sidecar | Proxies are hidden by default; you need their state to diagnose connectivity |
|
|
||||||
| Investigating helper containers | Helper containers (e.g. wait-for-it shims, init containers) are hidden |
|
|
||||||
| Tracking down migration jobs | Migration / seed jobs are typically hidden once they finish |
|
|
||||||
| Expected resources are missing from `aspire ps` | The resource may exist but be marked hidden — confirm with `--include-hidden` before assuming the AppHost is wrong |
|
|
||||||
| Parsing for completeness in agent automation | A full-graph view requires explicit opt-in |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Normal flow — filtered (correct for most tasks)
|
|
||||||
aspire ps --format Json
|
|
||||||
|
|
||||||
# ✅ Debugging / completeness — include hidden resources
|
|
||||||
aspire ps --include-hidden --format Json
|
|
||||||
aspire describe --include-hidden --format Json
|
|
||||||
```
|
|
||||||
|
|
||||||
If a user reports "I can't see my proxy / migration / helper container," reach for
|
|
||||||
`--include-hidden` before assuming the AppHost is misconfigured.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rule 6: ALWAYS `--non-interactive` for Agent Commands
|
|
||||||
|
|
||||||
### Why It Matters
|
|
||||||
|
|
||||||
AI agents run in non-interactive terminals. Some Aspire CLI commands may prompt for confirmation or input. Always pass `--non-interactive` to prevent hangs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ✅ Agent-safe commands
|
|
||||||
aspire start --non-interactive
|
|
||||||
aspire deploy --non-interactive
|
|
||||||
aspire agent init --non-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
> ⚠️ **Known issue**: `aspire agent init --non-interactive` is broken in some versions ([#16264](https://github.com/microsoft/aspire/issues/16264), [#15071](https://github.com/microsoft/aspire/issues/15071)). If it fails, instruct the user to run it from a standard terminal.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recovery Patterns Summary
|
|
||||||
|
|
||||||
| Mistake Made | Recovery Steps |
|
|
||||||
|-------------|---------------|
|
|
||||||
| Used `dotnet run` on AppHost | Kill the process, run `aspire start` |
|
|
||||||
| Used `dotnet build` and got file locks | `aspire stop`, wait 2s, then `dotnet build` or `aspire start` |
|
|
||||||
| Used `curl` polling and got false results | `aspire wait <resource>`, then use endpoints from `aspire describe` |
|
|
||||||
| Left Aspire running, now ports conflict | `aspire stop`, then `aspire start` |
|
|
||||||
| Resource won't start after code change | Fix code, then use resource commands/watch/HMR/debug workflow or restart the AppHost if the AppHost model changed |
|
|
||||||
| Nothing works, environment broken | `aspire doctor` to diagnose, then follow recommendations |
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
---
|
|
||||||
name: aspire
|
|
||||||
description: >-
|
|
||||||
**WORKFLOW SKILL** - Top-level router for Aspire 13.4 distributed apps. Detects the
|
|
||||||
AppHost, enforces safety guardrails, and routes to the right sub-skill.
|
|
||||||
USE FOR: Aspire AppHost detected, aspire CLI, distributed app, cloud-native .NET,
|
|
||||||
aspire start, aspire stop, aspire resource, aspire deploy, aspire destroy, aspire publish,
|
|
||||||
aspire init, aspire new, aspire add, aspire integration list/search, aspire wait,
|
|
||||||
aspire describe, aspire ps, aspire dashboard run, aspire doctor, aspire update,
|
|
||||||
aspire logs, aspire otel, --include-hidden, aspireify, WithBrowserLogs, custom
|
|
||||||
dashboard/resource commands, .aspire/modules recovery, Playwright URL discovery.
|
|
||||||
DO NOT USE FOR: non-Aspire .NET projects (use dotnet directly), Azure provisioning
|
|
||||||
without Aspire (use azure-prepare), container-only repos with no AppHost, ordinary
|
|
||||||
build/test tasks.
|
|
||||||
INVOKES: aspire-init, aspireify, aspire-orchestration, aspire-deployment, aspire-monitoring.
|
|
||||||
FOR SINGLE OPERATIONS: Route directly to the matching sub-skill.
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: Microsoft
|
|
||||||
version: "0.0.1"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Aspire
|
|
||||||
|
|
||||||
Use this skill when the task involves an Aspire distributed application — operating the
|
|
||||||
AppHost or its resources through the Aspire CLI rather than falling back to ad-hoc `dotnet`,
|
|
||||||
`docker`, or shell workflows.
|
|
||||||
|
|
||||||
## Detection
|
|
||||||
|
|
||||||
Activate when ANY signal is present. Use the **Scope** column to decide whether to route to
|
|
||||||
the bootstrap skills (`aspire-init` / `aspireify`) or to a runtime sub-skill:
|
|
||||||
|
|
||||||
| Signal | How to Detect | Confidence | Scope |
|
|
||||||
|--------|---------------|------------|-------|
|
|
||||||
| C# AppHost | `.csproj` containing `Aspire.AppHost.Sdk` | ✅ Definitive | AppHost present → orchestration / deployment / monitoring |
|
|
||||||
| File-based C# AppHost | `apphost.cs` with `#:sdk Aspire.AppHost.Sdk` | ✅ Definitive | AppHost present → orchestration / deployment / monitoring |
|
|
||||||
| TypeScript AppHost | `apphost.ts` file in project | ✅ Definitive | AppHost present → orchestration / deployment / monitoring |
|
|
||||||
| Aspire config without AppHost | `aspire.config.json` present **and no AppHost** above | High | Bootstrap → `aspireify` (skeleton dropped, needs wiring) |
|
|
||||||
| Aspire config with AppHost | `aspire.config.json` present **and** AppHost above | High | AppHost present → orchestration / deployment / monitoring |
|
|
||||||
| Aspire settings | `.aspire/` directory present | High | AppHost present (usually) |
|
|
||||||
| Generated TS modules | `.aspire/modules/` directory present | High | AppHost present (TS) |
|
|
||||||
| Service defaults | `Aspire.ServiceDefaults` in project references | Medium | AppHost present |
|
|
||||||
| **No AppHost, no `aspire.config.json`** | None of the above and user asks to add Aspire | n/a | Bootstrap → `aspire-init` (skeleton drop) |
|
|
||||||
|
|
||||||
## Default Workflow
|
|
||||||
|
|
||||||
0. **Bootstrap branch** — if **no AppHost exists** in the repo, route to
|
|
||||||
[`aspire-init`](../aspire-init/SKILL.md) for the skeleton drop. If an AppHost stub exists
|
|
||||||
but is **unwired** (no resources declared), route to [`aspireify`](../aspireify/SKILL.md).
|
|
||||||
Only continue with the steps below once a wired AppHost is present.
|
|
||||||
1. Confirm workspace is Aspire — identify the AppHost
|
|
||||||
2. `aspire start` (or `aspire start --isolated` in worktrees or whenever shared local state is risky)
|
|
||||||
3. `aspire wait <resource>` before interacting with any resource
|
|
||||||
4. Inspect state with `aspire describe`, `aspire otel logs`, `aspire logs`, `aspire otel traces`, and `aspire export` before making code changes
|
|
||||||
5. Before adding integrations, use `aspire integration search <query>` when the package is unknown, then `aspire add <package>` when ready to mutate the AppHost
|
|
||||||
6. When code changes, decide whether the AppHost model changed or only one resource changed. Re-run `aspire start` after AppHost changes; otherwise prefer resource commands, runtime watch/HMR, dashboard actions, or IDE-managed debugging as appropriate.
|
|
||||||
|
|
||||||
## Key Rules
|
|
||||||
|
|
||||||
- **Always** `aspire start`, **never** `dotnet run` on AppHosts
|
|
||||||
- **Always** `aspire wait <resource>`, **never** manual HTTP polling
|
|
||||||
- Use `aspire resource <resource-name> <command>` for resource operations such as `stop`, `start`, or `rebuild` when available
|
|
||||||
- Do not stop or restart the whole AppHost just because one resource changed
|
|
||||||
- Use `features.defaultWatchEnabled` only for Aspire default watch; do not treat it as per-resource rebuild, restart, or hot reload
|
|
||||||
- Prefer a resource's own framework/runtime hot reload, HMR, or watch workflow when it already handles the change
|
|
||||||
- **Always** `aspire docs search <topic>` before editing unfamiliar AppHost APIs
|
|
||||||
- **Always** `aspire docs api search <query> --language csharp|typescript` for API reference before editing AppHost code
|
|
||||||
- **Always** `--non-interactive` for agent execution
|
|
||||||
- Use `aspire integration list --format Json` and `aspire integration search <query> --format Json` for read-only integration discovery
|
|
||||||
- **Never** install the obsolete Aspire workload
|
|
||||||
- **Never** edit `.aspire/modules/` directly in TypeScript AppHosts
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
| Task | Route To |
|
|
||||||
|------|----------|
|
|
||||||
| Start, stop, wait, restart, rebuild | → [aspire-orchestration](../aspire-orchestration/SKILL.md) |
|
|
||||||
| Create a new Aspire project from a template (`aspire new`) | → [aspire-init](../aspire-init/SKILL.md) (in-plugin) |
|
|
||||||
| Add Aspire to an existing repo (`aspire init`, drop skeleton) | → [aspire-init](../aspire-init/SKILL.md) (in-plugin) |
|
|
||||||
| Wire AppHost / scaffold resource graph / add integrations after `aspire init` | → [aspireify](../aspireify/SKILL.md) (in-plugin) |
|
|
||||||
| Deploy, publish, destroy, pipeline steps | → [aspire-deployment](../aspire-deployment/SKILL.md) |
|
|
||||||
| Logs, traces, metrics, dashboard, browser logs | → [aspire-monitoring](../aspire-monitoring/SKILL.md) |
|
|
||||||
| Deployed app monitoring (Azure) | → `azure-diagnostics` skill (azure-skills plugin) |
|
|
||||||
|
|
||||||
## Sub-Skills
|
|
||||||
|
|
||||||
### aspire-init
|
|
||||||
First-run flow only. Owns the skeleton drop for repos that do **not** yet have an AppHost —
|
|
||||||
picks `aspire new <template>` (greenfield) or `aspire init` (existing repo), runs the CLI,
|
|
||||||
and hands off to `aspireify` for the actual wiring. Self-deactivates once the skeleton is in
|
|
||||||
place. Do **not** use it on a repo that already contains an AppHost.
|
|
||||||
|
|
||||||
### aspireify
|
|
||||||
Agentic AppHost wiring after `aspire init` lands the skeleton. Scans the repo, proposes a
|
|
||||||
resource graph (Postgres / Redis / Rabbit / etc.), edits the AppHost (C#, file-based C#, or
|
|
||||||
TypeScript), wires `Aspire.ServiceDefaults` + OTel, validates with `aspire start`, then
|
|
||||||
self-deactivates. Owns current AppHost authoring patterns (`AddNextJsApp`, `AddViteApp`,
|
|
||||||
`WithBrowserLogs()`, generated `.aspire/modules/`, unified TS `withEnvironment`,
|
|
||||||
endpoint references, and config/secret migration).
|
|
||||||
|
|
||||||
### aspire-orchestration
|
|
||||||
Lifecycle management: start, stop, wait, resource commands, default watch/HMR guidance, and file-lock recovery.
|
|
||||||
Safety guardrails that prevent agent self-harm. Owns `aspire ps` / `aspire describe` /
|
|
||||||
`--include-hidden` inspection and CLI upgrades (`aspire update --self`). Does **not** edit
|
|
||||||
AppHost code — defers to `aspireify` for wiring.
|
|
||||||
|
|
||||||
### aspire-deployment
|
|
||||||
Multi-target deployment and tear-down: `aspire deploy`, `aspire publish`, `aspire destroy`,
|
|
||||||
`aspire do <step>`. Targets: Azure Container Apps, App Service, AKS, Kubernetes (Helm),
|
|
||||||
Docker Compose. Owns current deployment surfaces (Front Door, NSP, AKS hosting, Foundry
|
|
||||||
`AddPromptAgent`, JS `PublishAs*`, `--pipeline-log-level`) and 13.4 API naming.
|
|
||||||
|
|
||||||
### aspire-monitoring
|
|
||||||
Observability: `aspire logs`, `aspire otel`, `aspire describe`, `aspire export`,
|
|
||||||
`aspire dashboard run`. Routes between local Aspire CLI diagnostics, AKS workload tooling,
|
|
||||||
and deployed-Azure platform tools. Surfaces dashboard features (notification center,
|
|
||||||
Rebuild command, browser-logs telemetry).
|
|
||||||
|
|
||||||
## Project-Local Skill Override
|
|
||||||
|
|
||||||
If any of the following exist project-locally (from `aspire agent init` or Aspire
|
|
||||||
`aspire init`), **warn the user** and **defer to the project-local copy** — repo-specific
|
|
||||||
guidance there should not be overridden by the in-plugin sibling:
|
|
||||||
|
|
||||||
| Project-local file | Precedence |
|
|
||||||
|--------------------|-----------|
|
|
||||||
| `.agents/skills/aspire/SKILL.md` | This file (top-level router) defers to it for deeper C# / TS AppHost editing, Playwright handoff, investigation workflows. |
|
|
||||||
| `.agents/skills/aspireify/SKILL.md` | The in-plugin `aspireify` sibling defers to it for AppHost wiring. |
|
|
||||||
| `.agents/skills/aspire-init/SKILL.md` | The in-plugin `aspire-init` sibling defers to it for the skeleton/first-run flow. |
|
|
||||||
|
|
||||||
**Safety guardrails from this plugin always apply** even when project-local skills are
|
|
||||||
active.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
| Requirement | Install |
|
|
||||||
|-------------|---------|
|
|
||||||
| .NET 10.0 SDK | https://dotnet.microsoft.com/download |
|
|
||||||
| Aspire CLI (curl/PowerShell) | `curl -sSL https://aspire.dev/install.sh \| bash` |
|
|
||||||
| Aspire CLI (NativeAOT global tool, .NET 10) | `dotnet tool install -g Aspire.Cli` |
|
|
||||||
|
|
||||||
Either install method works. The `dotnet tool install` path produces a NativeAOT binary
|
|
||||||
(instant startup, no JIT warmup) and is recommended when .NET 10 is already present.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [aspire-13-3-breaking-changes.md](references/aspire-13-3-breaking-changes.md) — Every 13.3
|
|
||||||
breaking change to scrub from agent-generated code, scripts, and CI snippets (rename of
|
|
||||||
`--log-level`, dashboard MCP removal, `NameOutput` → `NameOutputReference`,
|
|
||||||
`AddAndPublishPromptAgent` removal, TS `withEnvironment*` deprecation, and the full
|
|
||||||
13.2 → 13.3 migration checklist).
|
|
||||||
- [../aspire-orchestration/references/agent-workflows.md](../aspire-orchestration/references/agent-workflows.md) — Common agent workflows: worktrees, code changes, investigation, integrations, TypeScript generated APIs, secrets, deployment, and Playwright handoff.
|
|
||||||
- [../aspire-orchestration/references/app-commands.md](../aspire-orchestration/references/app-commands.md) — App lifecycle, bootstrap, update, restore, docs, and integration discovery commands.
|
|
||||||
- [../aspire-orchestration/references/resource-management.md](../aspire-orchestration/references/resource-management.md) — Resource wait and resource-command guidance.
|
|
||||||
- [../aspire-monitoring/references/monitoring.md](../aspire-monitoring/references/monitoring.md) — App state, logs, traces, search filtering, dashboard links, and export workflows.
|
|
||||||
- [../aspire-monitoring/references/playwright-handoff.md](../aspire-monitoring/references/playwright-handoff.md) — Playwright handoff after Aspire endpoint discovery.
|
|
||||||
- [../aspire-deployment/SKILL.md](../aspire-deployment/SKILL.md) — Deployment and pipeline-step workflows.
|
|
||||||
- [../aspireify/references/apphost-wiring.md](../aspireify/references/apphost-wiring.md) — C# and TypeScript AppHost API lookup and wiring patterns.
|
|
||||||
-110
@@ -1,110 +0,0 @@
|
|||||||
# Aspire 13.3 Breaking Changes — Agent Reference
|
|
||||||
|
|
||||||
Single, agent-facing scrub list of every 13.3 breaking change that may affect agent-generated
|
|
||||||
code, scripts, CI snippets, or skill routing. Source:
|
|
||||||
[Aspire 13.3 release notes](https://aspire.dev/whats-new/aspire-13-3/).
|
|
||||||
|
|
||||||
> Use this page when reviewing AppHost code, CI YAML, or shell snippets for 13.3 compatibility.
|
|
||||||
> Agents must scrub for these patterns before recommending or generating code.
|
|
||||||
|
|
||||||
## Quick scrub table
|
|
||||||
|
|
||||||
| Change | Migration |
|
|
||||||
|--------|-----------|
|
|
||||||
| `--log-level` → `--pipeline-log-level` on `aspire publish` / `aspire deploy` | Update CI/CD scripts to use `--pipeline-log-level <level>`. |
|
|
||||||
| Dashboard MCP server **removed** along with `ASPIRE_DASHBOARD_MCP_ENDPOINT_URL` | Use AppHost-level MCP via `aspire agent init`. See [Dashboard MCP migration](#dashboard-mcp-migration). |
|
|
||||||
| `NameOutput` → `NameOutputReference` (Azure Network resources) | Replace every `*.NameOutput` with `*.NameOutputReference`. |
|
|
||||||
| `OtlpEndpointEnvironmentVariableName` property removed | Remove the property; OTLP endpoint env var is managed automatically. |
|
|
||||||
| `AksSkuTier` enum removed | Delete the reference. AKS control-plane defaults to **Free** SKU. |
|
|
||||||
| `AddAndPublishPromptAgent` API removed | Use `AddPromptAgent` — returns a working `AzurePromptAgentResource`. |
|
|
||||||
| Kubernetes Ingress / Gateway routing types moved namespaces | Update `using` directives where these types are referenced directly. |
|
|
||||||
| `package.json` `engines.node` no longer drives Node image selection | Pin Node version explicitly via `WithDockerfile` or your Dockerfile base image. |
|
|
||||||
| `dotnet new aspire-py-starter` removed | Use `aspire new aspire-py-starter` (template moved to Aspire CLI; .NET SDK no longer required). |
|
|
||||||
| TypeScript per-kind `withEnvironment*` helpers `@deprecated` | Use unified `withEnvironment(name, value)`. See [TS withEnvironment migration](#typescript-withenvironment-migration). |
|
|
||||||
| CLI telemetry `--format json` schema aligned with MCP tool format | Update parsers consuming `--format json` from telemetry commands. |
|
|
||||||
| `ASPIREEXTENSION001` JS diagnostic ID renamed to `ASPIREJAVASCRIPT001` | Update `#pragma warning disable` and any code-search rules. |
|
|
||||||
| Docker Swarm `UpdateConfig` property types changed | Update generated/hand-written Swarm overrides. |
|
|
||||||
| `aspire init` no longer wires up the AppHost | Follow with `aspireify` (in-plugin sibling skill or project-local copy). |
|
|
||||||
| In-dashboard GitHub Copilot UI removed | Replaced by `aspire agent init`-driven agentic flow (works with Copilot, Claude, Cursor, any MCP/skill agent). |
|
|
||||||
|
|
||||||
## TypeScript `withEnvironment` migration
|
|
||||||
|
|
||||||
Per-kind helpers are still generated for backward compatibility but marked `@deprecated` and
|
|
||||||
slated for removal. Replace every call site with the unified API.
|
|
||||||
|
|
||||||
| Old method (deprecated) | Replacement |
|
|
||||||
|------------------------------------------------------|--------------------------------------|
|
|
||||||
| `withEnvironmentExpression(name, expr)` | `withEnvironment(name, expr)` |
|
|
||||||
| `withEnvironmentEndpoint(name, endpoint)` | `withEnvironment(name, endpoint)` |
|
|
||||||
| `withEnvironmentParameter(name, param)` | `withEnvironment(name, param)` |
|
|
||||||
| `withEnvironmentConnectionString(name, resource)` | `withEnvironment(name, resource)` |
|
|
||||||
| `withEnvironmentFromOutput(name, output)` | `withEnvironment(name, output)` |
|
|
||||||
| `withEnvironmentFromKeyVaultSecret(name, secret)` | `withEnvironment(name, secret)` |
|
|
||||||
|
|
||||||
The unified `withEnvironment(name, value)` accepts any of: a plain `string`,
|
|
||||||
`ReferenceExpression`, `EndpointReference`, parameter builder, connection string resource
|
|
||||||
builder, or `IExpressionValue` — one method handles every value kind.
|
|
||||||
|
|
||||||
## Dashboard MCP migration
|
|
||||||
|
|
||||||
The Aspire dashboard no longer hosts an MCP server, and the
|
|
||||||
`ASPIRE_DASHBOARD_MCP_ENDPOINT_URL` environment variable has been removed. AI coding agents
|
|
||||||
now connect to your Aspire app through an **AppHost-level MCP server** plus skills wired up by
|
|
||||||
`aspire agent init`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Replace any prior dashboard-MCP setup with:
|
|
||||||
aspire agent init
|
|
||||||
```
|
|
||||||
|
|
||||||
Detection covers GitHub Copilot, Claude, Cursor, and any other agent that supports skills or
|
|
||||||
MCP. The previous in-dashboard GitHub Copilot UI has been removed in favor of this flow.
|
|
||||||
|
|
||||||
If a script or environment file still sets `ASPIRE_DASHBOARD_MCP_ENDPOINT_URL`, delete it.
|
|
||||||
|
|
||||||
## `aspire init` no longer wires the AppHost
|
|
||||||
|
|
||||||
In 13.3, `aspire init` drops a skeleton (`aspire.config.json` + AppHost stub) and installs the
|
|
||||||
`aspireify` agent skill, but does **not** wire resources, projects, or integrations on its own.
|
|
||||||
|
|
||||||
Workflow:
|
|
||||||
|
|
||||||
1. `aspire init --language csharp|typescript --non-interactive`
|
|
||||||
2. Hand off to the `aspireify` skill (in-plugin sibling or project-local
|
|
||||||
`.agents/skills/aspireify/SKILL.md`) to scan the repo, propose a resource graph, edit the
|
|
||||||
AppHost, wire `Aspire.ServiceDefaults` + OTel, and validate via `aspire start`.
|
|
||||||
|
|
||||||
Skills shipped in this plugin:
|
|
||||||
|
|
||||||
- `aspire-init` — owns the skeleton drop and template choice.
|
|
||||||
- `aspireify` — owns the wiring step after the skeleton lands.
|
|
||||||
|
|
||||||
## Migration from Aspire 13.2 to 13.3
|
|
||||||
|
|
||||||
Mirror of the upstream checklist plus the items above. Run through each step before
|
|
||||||
recommending Aspire-related changes against an existing repo.
|
|
||||||
|
|
||||||
1. **Update the CLI** — `aspire update --self`.
|
|
||||||
2. **Update your projects** — `aspire update` from the repo root (modifies project package
|
|
||||||
references; get user approval before running in CI).
|
|
||||||
3. **Audit `--log-level` usage** in CI/CD pipelines and rename to `--pipeline-log-level`.
|
|
||||||
4. **Search for breaking-change identifiers** in AppHost code:
|
|
||||||
- `NameOutput` → `NameOutputReference`
|
|
||||||
- `AddAndPublishPromptAgent` → `AddPromptAgent`
|
|
||||||
- `AksSkuTier` → delete (defaults to Free)
|
|
||||||
- `OtlpEndpointEnvironmentVariableName` → delete
|
|
||||||
- `ASPIREEXTENSION001` → `ASPIREJAVASCRIPT001`
|
|
||||||
5. **Replace `dotnet new aspire-py-starter`** with `aspire new aspire-py-starter`.
|
|
||||||
6. **Rerun `aspire agent init`** if you previously relied on the dashboard MCP server or its
|
|
||||||
`ASPIRE_DASHBOARD_MCP_ENDPOINT_URL` env var.
|
|
||||||
7. **Re-pin Node versions** in Dockerfiles if you were relying on `package.json`
|
|
||||||
`engines.node` for base-image selection.
|
|
||||||
8. **Rewire AppHost via `aspireify`** if the repo went through `aspire init` on 13.3 — the
|
|
||||||
skeleton is unwired by design.
|
|
||||||
9. **Replace deprecated TS `withEnvironment*` helpers** with the unified
|
|
||||||
`withEnvironment(name, value)` (see [migration table](#typescript-withenvironment-migration)).
|
|
||||||
10. **Update Kubernetes Ingress / Gateway `using` directives** if your AppHost references the
|
|
||||||
moved types directly.
|
|
||||||
11. **Update consumers of `--format json`** from CLI telemetry commands to the new
|
|
||||||
MCP-aligned schema.
|
|
||||||
12. **Update Docker Swarm overrides** for the new `UpdateConfig` property types.
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AFrameArchitectureSample.ServiceDefaults\AFrameArchitectureSample.ServiceDefaults.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
@ApiService_HostAddress = http://localhost:5324
|
|
||||||
|
|
||||||
GET {{ApiService_HostAddress}}/weatherforecast/
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add service defaults & Aspire client integrations.
|
|
||||||
builder.AddServiceDefaults();
|
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddProblemDetails();
|
|
||||||
|
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
|
||||||
builder.Services.AddOpenApi();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
app.UseExceptionHandler();
|
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.MapOpenApi();
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
|
|
||||||
|
|
||||||
app.MapGet("/", () => "API service is running. Navigate to /weatherforecast to see sample data.");
|
|
||||||
|
|
||||||
app.MapGet("/weatherforecast", () =>
|
|
||||||
{
|
|
||||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
|
||||||
new WeatherForecast
|
|
||||||
(
|
|
||||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
|
||||||
Random.Shared.Next(-20, 55),
|
|
||||||
summaries[Random.Shared.Next(summaries.Length)]
|
|
||||||
))
|
|
||||||
.ToArray();
|
|
||||||
return forecast;
|
|
||||||
})
|
|
||||||
.WithName("GetWeatherForecast");
|
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|
||||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
|
||||||
{
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": false,
|
|
||||||
"applicationUrl": "http://localhost:5324",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": false,
|
|
||||||
"applicationUrl": "https://localhost:7537;http://localhost:5324",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
<Project Sdk="Aspire.AppHost.Sdk/13.4.2">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<UserSecretsId>dd566541-a93f-49d5-9124-476847addf36</UserSecretsId>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AFrameArchitectureSample.ApiService\AFrameArchitectureSample.ApiService.csproj" />
|
|
||||||
<ProjectReference Include="..\AFrameArchitectureSample.Web\AFrameArchitectureSample.Web.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Aspire.Hosting.Redis" Version="13.4.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
var builder = DistributedApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
var cache = builder.AddRedis("cache");
|
|
||||||
|
|
||||||
var apiService = builder.AddProject<Projects.AFrameArchitectureSample_ApiService>("apiservice")
|
|
||||||
.WithHttpHealthCheck("/health");
|
|
||||||
|
|
||||||
builder.AddProject<Projects.AFrameArchitectureSample_Web>("webfrontend")
|
|
||||||
.WithExternalHttpEndpoints()
|
|
||||||
.WithHttpHealthCheck("/health")
|
|
||||||
.WithReference(cache)
|
|
||||||
.WaitFor(cache)
|
|
||||||
.WithReference(apiService)
|
|
||||||
.WaitFor(apiService);
|
|
||||||
|
|
||||||
builder.Build().Run();
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "https://aframearchitecturesample.dev.localhost:17291;http://aframearchitecturesample.dev.localhost:15244",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
|
||||||
"DOTNET_ENVIRONMENT": "Development",
|
|
||||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21079",
|
|
||||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22091"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "http://aframearchitecturesample.dev.localhost:15244",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
|
||||||
"DOTNET_ENVIRONMENT": "Development",
|
|
||||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19000",
|
|
||||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20296"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning",
|
|
||||||
"Aspire.Hosting.Dcp": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsAspireSharedProject>true</IsAspireSharedProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.6.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.6.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
-127
@@ -1,127 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.ServiceDiscovery;
|
|
||||||
using OpenTelemetry;
|
|
||||||
using OpenTelemetry.Metrics;
|
|
||||||
using OpenTelemetry.Trace;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
|
||||||
// This project should be referenced by each service project in your solution.
|
|
||||||
// To learn more about using this project, see https://aka.ms/aspire/service-defaults
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
private const string HealthEndpointPath = "/health";
|
|
||||||
private const string AlivenessEndpointPath = "/alive";
|
|
||||||
|
|
||||||
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.ConfigureOpenTelemetry();
|
|
||||||
|
|
||||||
builder.AddDefaultHealthChecks();
|
|
||||||
|
|
||||||
builder.Services.AddServiceDiscovery();
|
|
||||||
|
|
||||||
builder.Services.ConfigureHttpClientDefaults(http =>
|
|
||||||
{
|
|
||||||
// Turn on resilience by default
|
|
||||||
http.AddStandardResilienceHandler();
|
|
||||||
|
|
||||||
// Turn on service discovery by default
|
|
||||||
http.AddServiceDiscovery();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Uncomment the following to restrict the allowed schemes for service discovery.
|
|
||||||
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
|
|
||||||
// {
|
|
||||||
// options.AllowedSchemes = ["https"];
|
|
||||||
// });
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.Logging.AddOpenTelemetry(logging =>
|
|
||||||
{
|
|
||||||
logging.IncludeFormattedMessage = true;
|
|
||||||
logging.IncludeScopes = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddOpenTelemetry()
|
|
||||||
.WithMetrics(metrics =>
|
|
||||||
{
|
|
||||||
metrics.AddAspNetCoreInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation()
|
|
||||||
.AddRuntimeInstrumentation();
|
|
||||||
})
|
|
||||||
.WithTracing(tracing =>
|
|
||||||
{
|
|
||||||
tracing.AddSource(builder.Environment.ApplicationName)
|
|
||||||
.AddAspNetCoreInstrumentation(tracing =>
|
|
||||||
// Exclude health check requests from tracing
|
|
||||||
tracing.Filter = context =>
|
|
||||||
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
|
|
||||||
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
|
|
||||||
)
|
|
||||||
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
|
||||||
//.AddGrpcClientInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation();
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.AddOpenTelemetryExporters();
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
|
|
||||||
|
|
||||||
if (useOtlpExporter)
|
|
||||||
{
|
|
||||||
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
|
||||||
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
|
||||||
//{
|
|
||||||
// builder.Services.AddOpenTelemetry()
|
|
||||||
// .UseAzureMonitor();
|
|
||||||
//}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.Services.AddHealthChecks()
|
|
||||||
// Add a default liveness check to ensure app is responsive
|
|
||||||
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
|
||||||
{
|
|
||||||
// Adding health checks endpoints to applications in non-development environments has security implications.
|
|
||||||
// See https://aka.ms/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
// All health checks must pass for app to be considered ready to accept traffic after starting
|
|
||||||
app.MapHealthChecks(HealthEndpointPath);
|
|
||||||
|
|
||||||
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
|
||||||
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
|
|
||||||
{
|
|
||||||
Predicate = r => r.Tags.Contains("live")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<IsTestProject>true</IsTestProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Aspire.Hosting.Testing" Version="13.4.2" />
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
|
||||||
<PackageReference Include="xunit.v3" Version="3.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AFrameArchitectureSample.AppHost\AFrameArchitectureSample.AppHost.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="System.Net" />
|
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
|
||||||
<Using Include="Aspire.Hosting.ApplicationModel" />
|
|
||||||
<Using Include="Aspire.Hosting.Testing" />
|
|
||||||
<Using Include="Xunit" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace AFrameArchitectureSample.Tests;
|
|
||||||
|
|
||||||
public class WebTests
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetWebResourceRootReturnsOkStatusCode()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var cancellationToken = TestContext.Current.CancellationToken;
|
|
||||||
|
|
||||||
var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AFrameArchitectureSample_AppHost>(cancellationToken);
|
|
||||||
appHost.Services.AddLogging(logging =>
|
|
||||||
{
|
|
||||||
logging.SetMinimumLevel(LogLevel.Debug);
|
|
||||||
// Override the logging filters from the app's configuration
|
|
||||||
logging.AddFilter(appHost.Environment.ApplicationName, LogLevel.Debug);
|
|
||||||
logging.AddFilter("Aspire.", LogLevel.Debug);
|
|
||||||
// To output logs to the xUnit.net ITestOutputHelper, consider adding a package from https://www.nuget.org/packages?q=xunit+logging
|
|
||||||
});
|
|
||||||
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
|
|
||||||
{
|
|
||||||
clientBuilder.AddStandardResilienceHandler();
|
|
||||||
});
|
|
||||||
|
|
||||||
await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var httpClient = app.CreateHttpClient("webfrontend");
|
|
||||||
await app.ResourceNotifications.WaitForResourceHealthyAsync("webfrontend", cancellationToken).WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
var response = await httpClient.GetAsync("/", cancellationToken);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\AFrameArchitectureSample.ServiceDefaults\AFrameArchitectureSample.ServiceDefaults.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Aspire.StackExchange.Redis.OutputCaching" Version="13.4.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<base href="/" />
|
|
||||||
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
|
|
||||||
<link rel="stylesheet" href="@Assets["app.css"]" />
|
|
||||||
<link rel="stylesheet" href="@Assets["AFrameArchitectureSample.Web.styles.css"]" />
|
|
||||||
<ImportMap />
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
|
||||||
<HeadOutlet />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<Routes />
|
|
||||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
@inherits LayoutComponentBase
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div class="sidebar">
|
|
||||||
<NavMenu />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="top-row px-4">
|
|
||||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<article class="content px-4">
|
|
||||||
@Body
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="blazor-error-ui">
|
|
||||||
An unhandled error has occurred.
|
|
||||||
<a href="" class="reload">Reload</a>
|
|
||||||
<a class="dismiss">🗙</a>
|
|
||||||
</div>
|
|
||||||
-96
@@ -1,96 +0,0 @@
|
|||||||
.page {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
border-bottom: 1px solid #d6d5d5;
|
|
||||||
justify-content: flex-end;
|
|
||||||
height: 3.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:first-child {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640.98px) {
|
|
||||||
.top-row {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.page {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 250px;
|
|
||||||
height: 100vh;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row.auth ::deep a:first-child {
|
|
||||||
flex: 1;
|
|
||||||
text-align: right;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row, article {
|
|
||||||
padding-left: 2rem !important;
|
|
||||||
padding-right: 1.5rem !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#blazor-error-ui {
|
|
||||||
background: lightyellow;
|
|
||||||
bottom: 0;
|
|
||||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
display: none;
|
|
||||||
left: 0;
|
|
||||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#blazor-error-ui .dismiss {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
right: 0.75rem;
|
|
||||||
top: 0.5rem;
|
|
||||||
}
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
<div class="top-row ps-3 navbar navbar-dark">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" href="">AFrameArchitectureSample</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
|
||||||
|
|
||||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
|
||||||
<span class="bi bi-house-door-fill" aria-hidden="true"></span> Home
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="counter">
|
|
||||||
<span class="bi bi-plus-square-fill" aria-hidden="true"></span> Counter
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="weather">
|
|
||||||
<span class="bi bi-list-nested" aria-hidden="true"></span> Weather
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
-102
@@ -1,102 +0,0 @@
|
|||||||
.navbar-toggler {
|
|
||||||
appearance: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 3.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
color: white;
|
|
||||||
position: absolute;
|
|
||||||
top: 0.5rem;
|
|
||||||
right: 1rem;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler:checked {
|
|
||||||
background-color: rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
min-height: 3.5rem;
|
|
||||||
background-color: rgba(0,0,0,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
top: -1px;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-house-door-fill {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-plus-square-fill {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-list-nested {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:first-of-type {
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:last-of-type {
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a {
|
|
||||||
color: #d7d7d7;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 3rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a.active {
|
|
||||||
background-color: rgba(255,255,255,0.37);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a:hover {
|
|
||||||
background-color: rgba(255,255,255,0.1);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-scrollable {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler:checked ~ .nav-scrollable {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.navbar-toggler {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-scrollable {
|
|
||||||
/* Never collapse the sidebar for wide screens */
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
/* Allow sidebar to scroll for tall menus */
|
|
||||||
height: calc(100vh - 3.5rem);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
@rendermode InteractiveServer
|
|
||||||
|
|
||||||
<PageTitle>Counter</PageTitle>
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
@page "/Error"
|
|
||||||
@using System.Diagnostics
|
|
||||||
|
|
||||||
<PageTitle>Error</PageTitle>
|
|
||||||
|
|
||||||
<h1 class="text-danger">Error.</h1>
|
|
||||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
|
||||||
|
|
||||||
@if (ShowRequestId)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
<strong>Request ID:</strong> <code>@requestId</code>
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h3>Development Mode</h3>
|
|
||||||
<p>
|
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
|
||||||
It can result in displaying sensitive information from exceptions to end users.
|
|
||||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
|
||||||
and restarting the app.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
@code{
|
|
||||||
[CascadingParameter]
|
|
||||||
public HttpContext? HttpContext { get; set; }
|
|
||||||
|
|
||||||
private string? requestId;
|
|
||||||
private bool ShowRequestId => !string.IsNullOrEmpty(requestId);
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
@page "/"
|
|
||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
|
||||||
|
|
||||||
<h1>Hello, world!</h1>
|
|
||||||
|
|
||||||
Welcome to your new app.
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
@page "/weather"
|
|
||||||
@attribute [StreamRendering(true)]
|
|
||||||
@attribute [OutputCache(Duration = 5)]
|
|
||||||
|
|
||||||
@inject WeatherApiClient WeatherApi
|
|
||||||
|
|
||||||
<PageTitle>Weather</PageTitle>
|
|
||||||
|
|
||||||
<h1>Weather</h1>
|
|
||||||
|
|
||||||
<p>This component demonstrates showing data loaded from a backend API service.</p>
|
|
||||||
|
|
||||||
@if (forecasts == null)
|
|
||||||
{
|
|
||||||
<p><em>Loading...</em></p>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
|
||||||
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
|
||||||
<th>Summary</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var forecast in forecasts)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@forecast.Date.ToShortDateString()</td>
|
|
||||||
<td>@forecast.TemperatureC</td>
|
|
||||||
<td>@forecast.TemperatureF</td>
|
|
||||||
<td>@forecast.Summary</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private WeatherForecast[]? forecasts;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
forecasts = await WeatherApi.GetWeatherAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
<Router AppAssembly="typeof(Program).Assembly">
|
|
||||||
<Found Context="routeData">
|
|
||||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
|
||||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
|
||||||
</Found>
|
|
||||||
</Router>
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
@using System.Net.Http
|
|
||||||
@using System.Net.Http.Json
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
|
||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
|
||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
|
||||||
@using Microsoft.AspNetCore.OutputCaching
|
|
||||||
@using Microsoft.JSInterop
|
|
||||||
@using AFrameArchitectureSample.Web
|
|
||||||
@using AFrameArchitectureSample.Web.Components
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using AFrameArchitectureSample.Web;
|
|
||||||
using AFrameArchitectureSample.Web.Components;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add service defaults & Aspire client integrations.
|
|
||||||
builder.AddServiceDefaults();
|
|
||||||
builder.AddRedisOutputCache("cache");
|
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddRazorComponents()
|
|
||||||
.AddInteractiveServerComponents();
|
|
||||||
|
|
||||||
builder.Services.AddHttpClient<WeatherApiClient>(client =>
|
|
||||||
{
|
|
||||||
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
|
|
||||||
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
|
|
||||||
client.BaseAddress = new("https+http://apiservice");
|
|
||||||
});
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseAntiforgery();
|
|
||||||
|
|
||||||
app.UseOutputCache();
|
|
||||||
|
|
||||||
app.MapStaticAssets();
|
|
||||||
|
|
||||||
app.MapRazorComponents<App>()
|
|
||||||
.AddInteractiveServerRenderMode();
|
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "http://localhost:5201",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "https://localhost:7150;http://localhost:5201",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
namespace AFrameArchitectureSample.Web;
|
|
||||||
|
|
||||||
public class WeatherApiClient(HttpClient httpClient)
|
|
||||||
{
|
|
||||||
public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
List<WeatherForecast>? forecasts = null;
|
|
||||||
|
|
||||||
await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast", cancellationToken))
|
|
||||||
{
|
|
||||||
if (forecasts?.Count >= maxItems)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (forecast is not null)
|
|
||||||
{
|
|
||||||
forecasts ??= [];
|
|
||||||
forecasts.Add(forecast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return forecasts?.ToArray() ?? [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
|
||||||
{
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
html, body {
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .btn-link {
|
|
||||||
color: #006bb7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #1b6ec2;
|
|
||||||
border-color: #1861ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
|
||||||
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding-top: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.valid.modified:not([type=checkbox]) {
|
|
||||||
outline: 1px solid #26b050;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invalid {
|
|
||||||
outline: 1px solid #e52010;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation-message {
|
|
||||||
color: #e52010;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blazor-error-boundary {
|
|
||||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
|
||||||
padding: 1rem 1rem 1rem 3.7rem;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blazor-error-boundary::after {
|
|
||||||
content: "An error has occurred."
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
|
|
||||||
color: var(--bs-secondary-color);
|
|
||||||
text-align: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,48 +0,0 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.8.0.0
|
|
||||||
MinimumVisualStudioVersion = 17.8.0.0
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AFrameArchitectureSample.AppHost", "AFrameArchitectureSample.AppHost\AFrameArchitectureSample.AppHost.csproj", "{7EF0B53F-A956-4A86-8E31-F1AFE0D96E5C}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AFrameArchitectureSample.ServiceDefaults", "AFrameArchitectureSample.ServiceDefaults\AFrameArchitectureSample.ServiceDefaults.csproj", "{04EE0BB2-581A-4D9E-8A40-744FD474FCCA}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AFrameArchitectureSample.ApiService", "AFrameArchitectureSample.ApiService\AFrameArchitectureSample.ApiService.csproj", "{06DA36FA-A6DC-4D85-B036-5EA8BA724D40}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AFrameArchitectureSample.Tests", "AFrameArchitectureSample.Tests\AFrameArchitectureSample.Tests.csproj", "{6EA36642-9CF1-4496-A88A-6D0F9C25EBD1}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AFrameArchitectureSample.Web", "AFrameArchitectureSample.Web\AFrameArchitectureSample.Web.csproj", "{1F32D131-5531-462D-9D91-FA9BA845FC36}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{7EF0B53F-A956-4A86-8E31-F1AFE0D96E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7EF0B53F-A956-4A86-8E31-F1AFE0D96E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7EF0B53F-A956-4A86-8E31-F1AFE0D96E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7EF0B53F-A956-4A86-8E31-F1AFE0D96E5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{04EE0BB2-581A-4D9E-8A40-744FD474FCCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{04EE0BB2-581A-4D9E-8A40-744FD474FCCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{04EE0BB2-581A-4D9E-8A40-744FD474FCCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{04EE0BB2-581A-4D9E-8A40-744FD474FCCA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{06DA36FA-A6DC-4D85-B036-5EA8BA724D40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{06DA36FA-A6DC-4D85-B036-5EA8BA724D40}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{06DA36FA-A6DC-4D85-B036-5EA8BA724D40}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{06DA36FA-A6DC-4D85-B036-5EA8BA724D40}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6EA36642-9CF1-4496-A88A-6D0F9C25EBD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6EA36642-9CF1-4496-A88A-6D0F9C25EBD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6EA36642-9CF1-4496-A88A-6D0F9C25EBD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6EA36642-9CF1-4496-A88A-6D0F9C25EBD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{1F32D131-5531-462D-9D91-FA9BA845FC36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{1F32D131-5531-462D-9D91-FA9BA845FC36}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{1F32D131-5531-462D-9D91-FA9BA845FC36}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{1F32D131-5531-462D-9D91-FA9BA845FC36}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {A998A2A1-1B17-4D8F-B8FA-34EEF295271F}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"appHost": {
|
|
||||||
"path": "AFrameArchitectureSample.AppHost/AFrameArchitectureSample.AppHost.csproj"
|
|
||||||
},
|
|
||||||
"channel": "stable"
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<packageSources>
|
|
||||||
<clear />
|
|
||||||
<add key="https://api.nuget.org/v3/index.json" value="https://api.nuget.org/v3/index.json" />
|
|
||||||
</packageSources>
|
|
||||||
<packageSourceMapping>
|
|
||||||
<packageSource key="https://api.nuget.org/v3/index.json">
|
|
||||||
<package pattern="*" />
|
|
||||||
</packageSource>
|
|
||||||
</packageSourceMapping>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
bin/
|
|
||||||
obj/
|
|
||||||
/packages/
|
|
||||||
riderModule.iml
|
|
||||||
/_ReSharper.Caches/
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider ignored files
|
|
||||||
/modules.xml
|
|
||||||
/contentModel.xml
|
|
||||||
/.idea.WolverineTutorial.iml
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Ignored default folder with query files
|
|
||||||
/queries/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
|
||||||
</project>
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders />
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add service defaults & Aspire client integrations.
|
|
||||||
builder.AddServiceDefaults();
|
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddProblemDetails();
|
|
||||||
|
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
|
||||||
builder.Services.AddOpenApi();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
app.UseExceptionHandler();
|
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.MapOpenApi();
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] summaries =
|
|
||||||
["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
|
|
||||||
|
|
||||||
app.MapGet("/", () => "API service is running. Navigate to /weatherforecast to see sample data.");
|
|
||||||
|
|
||||||
app.MapGet("/weatherforecast", () =>
|
|
||||||
{
|
|
||||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
|
||||||
new WeatherForecast
|
|
||||||
(
|
|
||||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
|
||||||
Random.Shared.Next(-20, 55),
|
|
||||||
summaries[Random.Shared.Next(summaries.Length)]
|
|
||||||
))
|
|
||||||
.ToArray();
|
|
||||||
return forecast;
|
|
||||||
})
|
|
||||||
.WithName("GetWeatherForecast");
|
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|
||||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
|
||||||
{
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": false,
|
|
||||||
"applicationUrl": "http://localhost:5361",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": false,
|
|
||||||
"applicationUrl": "https://localhost:7529;http://localhost:5361",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\WolverineTutorial.ServiceDefaults\WolverineTutorial.ServiceDefaults.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="3.7.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
@ApiService_HostAddress = http://localhost:5361
|
|
||||||
|
|
||||||
GET {{ApiService_HostAddress}}/weatherforecast/
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
var builder = DistributedApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
var apiService = builder.AddProject<Projects.WolverineTutorial_ApiService>("apiservice")
|
|
||||||
.WithHttpHealthCheck("/health");
|
|
||||||
|
|
||||||
builder.AddProject<Projects.WolverineTutorial_Web>("webfrontend")
|
|
||||||
.WithExternalHttpEndpoints()
|
|
||||||
.WithHttpHealthCheck("/health")
|
|
||||||
.WithReference(apiService)
|
|
||||||
.WaitFor(apiService);
|
|
||||||
|
|
||||||
builder.Build().Run();
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "https://localhost:17175;http://localhost:15029",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
|
||||||
"DOTNET_ENVIRONMENT": "Development",
|
|
||||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21208",
|
|
||||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22219"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "http://localhost:15029",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
|
||||||
"DOTNET_ENVIRONMENT": "Development",
|
|
||||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19152",
|
|
||||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20087"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
<Project Sdk="Aspire.AppHost.Sdk/13.3.0">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<UserSecretsId>fc6d5624-216d-4606-903f-47d032655ad2</UserSecretsId>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\WolverineTutorial.ApiService\WolverineTutorial.ApiService.csproj" />
|
|
||||||
<ProjectReference Include="..\WolverineTutorial.Web\WolverineTutorial.Web.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.3" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.35.1" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.81.1">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="JsonPatch.Net" Version="5.0.2" />
|
|
||||||
<PackageReference Include="MessagePack" Version="3.1.7" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Primitives" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.NET.StringTools" Version="18.7.1" />
|
|
||||||
<PackageReference Include="ModelContextProtocol" Version="1.4.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.16.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.16.0" />
|
|
||||||
<PackageReference Include="Polly.Core" Version="8.7.0" />
|
|
||||||
<PackageReference Include="StreamJsonRpc" Version="2.25.25" />
|
|
||||||
<PackageReference Include="System.IO.Hashing" Version="10.0.9" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning",
|
|
||||||
"Aspire.Hosting.Dcp": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.ServiceDiscovery;
|
|
||||||
using OpenTelemetry;
|
|
||||||
using OpenTelemetry.Metrics;
|
|
||||||
using OpenTelemetry.Trace;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
|
||||||
// This project should be referenced by each service project in your solution.
|
|
||||||
// To learn more about using this project, see https://aka.ms/aspire/service-defaults
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
private const string HealthEndpointPath = "/health";
|
|
||||||
private const string AlivenessEndpointPath = "/alive";
|
|
||||||
|
|
||||||
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.ConfigureOpenTelemetry();
|
|
||||||
|
|
||||||
builder.AddDefaultHealthChecks();
|
|
||||||
|
|
||||||
builder.Services.AddServiceDiscovery();
|
|
||||||
|
|
||||||
builder.Services.ConfigureHttpClientDefaults(http =>
|
|
||||||
{
|
|
||||||
// Turn on resilience by default
|
|
||||||
http.AddStandardResilienceHandler();
|
|
||||||
|
|
||||||
// Turn on service discovery by default
|
|
||||||
http.AddServiceDiscovery();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Uncomment the following to restrict the allowed schemes for service discovery.
|
|
||||||
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
|
|
||||||
// {
|
|
||||||
// options.AllowedSchemes = ["https"];
|
|
||||||
// });
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
|
|
||||||
where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.Logging.AddOpenTelemetry(logging =>
|
|
||||||
{
|
|
||||||
logging.IncludeFormattedMessage = true;
|
|
||||||
logging.IncludeScopes = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddOpenTelemetry()
|
|
||||||
.WithMetrics(metrics =>
|
|
||||||
{
|
|
||||||
metrics.AddAspNetCoreInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation()
|
|
||||||
.AddRuntimeInstrumentation();
|
|
||||||
})
|
|
||||||
.WithTracing(tracing =>
|
|
||||||
{
|
|
||||||
tracing.AddSource(builder.Environment.ApplicationName)
|
|
||||||
.AddAspNetCoreInstrumentation(tracing =>
|
|
||||||
// Exclude health check requests from tracing
|
|
||||||
tracing.Filter = context =>
|
|
||||||
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
|
|
||||||
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
|
|
||||||
)
|
|
||||||
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
|
||||||
//.AddGrpcClientInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation();
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.AddOpenTelemetryExporters();
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder)
|
|
||||||
where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
|
|
||||||
|
|
||||||
if (useOtlpExporter)
|
|
||||||
{
|
|
||||||
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
|
||||||
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
|
||||||
//{
|
|
||||||
// builder.Services.AddOpenTelemetry()
|
|
||||||
// .UseAzureMonitor();
|
|
||||||
//}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)
|
|
||||||
where TBuilder : IHostApplicationBuilder
|
|
||||||
{
|
|
||||||
builder.Services.AddHealthChecks()
|
|
||||||
// Add a default liveness check to ensure app is responsive
|
|
||||||
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
|
||||||
{
|
|
||||||
// Adding health checks endpoints to applications in non-development environments has security implications.
|
|
||||||
// See https://aka.ms/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
// All health checks must pass for app to be considered ready to accept traffic after starting
|
|
||||||
app.MapHealthChecks(HealthEndpointPath);
|
|
||||||
|
|
||||||
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
|
||||||
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
|
|
||||||
{
|
|
||||||
Predicate = r => r.Tags.Contains("live")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsAspireSharedProject>true</IsAspireSharedProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.7.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.7.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.16.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.16.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace WolverineTutorial.Tests;
|
|
||||||
|
|
||||||
public class WebTests
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetWebResourceRootReturnsOkStatusCode()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var cancellationToken = new CancellationTokenSource(DefaultTimeout).Token;
|
|
||||||
|
|
||||||
var appHost =
|
|
||||||
await DistributedApplicationTestingBuilder.CreateAsync<Projects.WolverineTutorial_AppHost>(
|
|
||||||
cancellationToken);
|
|
||||||
appHost.Services.AddLogging(logging =>
|
|
||||||
{
|
|
||||||
logging.SetMinimumLevel(LogLevel.Debug);
|
|
||||||
// Override the logging filters from the app's configuration
|
|
||||||
logging.AddFilter(appHost.Environment.ApplicationName, LogLevel.Debug);
|
|
||||||
logging.AddFilter("Aspire.", LogLevel.Debug);
|
|
||||||
// To output logs to the xUnit.net ITestOutputHelper, consider adding a package from https://www.nuget.org/packages?q=xunit+logging
|
|
||||||
});
|
|
||||||
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
|
|
||||||
{
|
|
||||||
clientBuilder.AddStandardResilienceHandler();
|
|
||||||
});
|
|
||||||
|
|
||||||
await using var app = await appHost.BuildAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
await app.StartAsync(cancellationToken).WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var httpClient = app.CreateHttpClient("webfrontend");
|
|
||||||
await app.ResourceNotifications.WaitForResourceHealthyAsync("webfrontend", cancellationToken)
|
|
||||||
.WaitAsync(DefaultTimeout, cancellationToken);
|
|
||||||
var response = await httpClient.GetAsync("/", cancellationToken);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<IsTestProject>true</IsTestProject>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Aspire.Hosting.Testing" Version="13.4.3" />
|
|
||||||
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.35.1" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.81.1">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="JsonPatch.Net" Version="5.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.AutoActivation" Version="10.7.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.7.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Primitives" Version="10.0.9" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
|
||||||
<PackageReference Include="ModelContextProtocol" Version="1.4.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.16.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.16.0" />
|
|
||||||
<PackageReference Include="Polly.Core" Version="8.7.0" />
|
|
||||||
<PackageReference Include="Polly.Extensions" Version="8.7.0" />
|
|
||||||
<PackageReference Include="StreamJsonRpc" Version="2.25.25" />
|
|
||||||
<PackageReference Include="System.IO.Hashing" Version="10.0.9" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="xunit.v3" Version="3.2.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\WolverineTutorial.AppHost\WolverineTutorial.AppHost.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="System.Net" />
|
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
|
||||||
<Using Include="Aspire.Hosting.ApplicationModel" />
|
|
||||||
<Using Include="Aspire.Hosting.Testing" />
|
|
||||||
<Using Include="Xunit" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
||||||
<base href="/"/>
|
|
||||||
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]"/>
|
|
||||||
<link rel="stylesheet" href="@Assets["app.css"]"/>
|
|
||||||
<link rel="stylesheet" href="@Assets["WolverineTutorial.Web.styles.css"]"/>
|
|
||||||
<ImportMap/>
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
|
||||||
<HeadOutlet/>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<Routes/>
|
|
||||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
@inherits LayoutComponentBase
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div class="sidebar">
|
|
||||||
<NavMenu/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="top-row px-4">
|
|
||||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<article class="content px-4">
|
|
||||||
@Body
|
|
||||||
</article>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="blazor-error-ui">
|
|
||||||
An unhandled error has occurred.
|
|
||||||
<a href="" class="reload">Reload</a>
|
|
||||||
<a class="dismiss">🗙</a>
|
|
||||||
</div>
|
|
||||||
-96
@@ -1,96 +0,0 @@
|
|||||||
.page {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
border-bottom: 1px solid #d6d5d5;
|
|
||||||
justify-content: flex-end;
|
|
||||||
height: 3.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a:first-child {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640.98px) {
|
|
||||||
.top-row {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.page {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 250px;
|
|
||||||
height: 100vh;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row.auth ::deep a:first-child {
|
|
||||||
flex: 1;
|
|
||||||
text-align: right;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row, article {
|
|
||||||
padding-left: 2rem !important;
|
|
||||||
padding-right: 1.5rem !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#blazor-error-ui {
|
|
||||||
background: lightyellow;
|
|
||||||
bottom: 0;
|
|
||||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
display: none;
|
|
||||||
left: 0;
|
|
||||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#blazor-error-ui .dismiss {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
right: 0.75rem;
|
|
||||||
top: 0.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<div class="top-row ps-3 navbar navbar-dark">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" href="">WolverineTutorial</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler"/>
|
|
||||||
|
|
||||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
|
||||||
<nav class="nav flex-column">
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
|
||||||
<span class="bi bi-house-door-fill" aria-hidden="true"></span> Home
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="counter">
|
|
||||||
<span class="bi bi-plus-square-fill" aria-hidden="true"></span> Counter
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="weather">
|
|
||||||
<span class="bi bi-list-nested" aria-hidden="true"></span> Weather
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
-102
@@ -1,102 +0,0 @@
|
|||||||
.navbar-toggler {
|
|
||||||
appearance: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 3.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
color: white;
|
|
||||||
position: absolute;
|
|
||||||
top: 0.5rem;
|
|
||||||
right: 1rem;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler:checked {
|
|
||||||
background-color: rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-row {
|
|
||||||
min-height: 3.5rem;
|
|
||||||
background-color: rgba(0,0,0,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
top: -1px;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-house-door-fill {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-plus-square-fill {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-list-nested {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:first-of-type {
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:last-of-type {
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a {
|
|
||||||
color: #d7d7d7;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 3rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a.active {
|
|
||||||
background-color: rgba(255,255,255,0.37);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item ::deep a:hover {
|
|
||||||
background-color: rgba(255,255,255,0.1);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-scrollable {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler:checked ~ .nav-scrollable {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.navbar-toggler {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-scrollable {
|
|
||||||
/* Never collapse the sidebar for wide screens */
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
/* Allow sidebar to scroll for tall menus */
|
|
||||||
height: calc(100vh - 3.5rem);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
@rendermode InteractiveServer
|
|
||||||
|
|
||||||
<PageTitle>Counter</PageTitle>
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
@page "/Error"
|
|
||||||
@using System.Diagnostics
|
|
||||||
|
|
||||||
<PageTitle>Error</PageTitle>
|
|
||||||
|
|
||||||
<h1 class="text-danger">Error.</h1>
|
|
||||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
|
||||||
|
|
||||||
@if (ShowRequestId)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
<strong>Request ID:</strong> <code>@requestId</code>
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h3>Development Mode</h3>
|
|
||||||
<p>
|
|
||||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
|
||||||
It can result in displaying sensitive information from exceptions to end users.
|
|
||||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
|
||||||
and restarting the app.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
@code{
|
|
||||||
[CascadingParameter] public HttpContext? HttpContext { get; set; }
|
|
||||||
|
|
||||||
private string? requestId;
|
|
||||||
private bool ShowRequestId => !string.IsNullOrEmpty(requestId);
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
@page "/"
|
|
||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
|
||||||
|
|
||||||
<h1>Hello, world!</h1>
|
|
||||||
|
|
||||||
Welcome to your new app.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
@page "/weather"
|
|
||||||
@attribute [StreamRendering(true)]
|
|
||||||
@attribute [OutputCache(Duration = 5)]
|
|
||||||
|
|
||||||
@inject WeatherApiClient WeatherApi
|
|
||||||
|
|
||||||
<PageTitle>Weather</PageTitle>
|
|
||||||
|
|
||||||
<h1>Weather</h1>
|
|
||||||
|
|
||||||
<p>This component demonstrates showing data loaded from a backend API service.</p>
|
|
||||||
|
|
||||||
@if (forecasts == null)
|
|
||||||
{
|
|
||||||
<p>
|
|
||||||
<em>Loading...</em>
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
|
||||||
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
|
||||||
<th>Summary</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var forecast in forecasts)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@forecast.Date.ToShortDateString()</td>
|
|
||||||
<td>@forecast.TemperatureC</td>
|
|
||||||
<td>@forecast.TemperatureF</td>
|
|
||||||
<td>@forecast.Summary</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private WeatherForecast[]? forecasts;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
forecasts = await WeatherApi.GetWeatherAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<Router AppAssembly="typeof(Program).Assembly">
|
|
||||||
<Found Context="routeData">
|
|
||||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
|
|
||||||
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
|
||||||
</Found>
|
|
||||||
</Router>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
@using System.Net.Http
|
|
||||||
@using System.Net.Http.Json
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
|
||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
|
||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
|
||||||
@using Microsoft.AspNetCore.OutputCaching
|
|
||||||
@using Microsoft.JSInterop
|
|
||||||
@using WolverineTutorial.Web
|
|
||||||
@using WolverineTutorial.Web.Components
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using WolverineTutorial.Web;
|
|
||||||
using WolverineTutorial.Web.Components;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
// Add service defaults & Aspire client integrations.
|
|
||||||
builder.AddServiceDefaults();
|
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddRazorComponents()
|
|
||||||
.AddInteractiveServerComponents();
|
|
||||||
|
|
||||||
builder.Services.AddOutputCache();
|
|
||||||
|
|
||||||
builder.Services.AddHttpClient<WeatherApiClient>(client =>
|
|
||||||
{
|
|
||||||
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
|
|
||||||
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
|
|
||||||
client.BaseAddress = new("https+http://apiservice");
|
|
||||||
});
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseAntiforgery();
|
|
||||||
|
|
||||||
app.UseOutputCache();
|
|
||||||
|
|
||||||
app.MapStaticAssets();
|
|
||||||
|
|
||||||
app.MapRazorComponents<App>()
|
|
||||||
.AddInteractiveServerRenderMode();
|
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "http://localhost:5229",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"https": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"applicationUrl": "https://localhost:7064;http://localhost:5229",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace WolverineTutorial.Web;
|
|
||||||
|
|
||||||
public class WeatherApiClient(HttpClient httpClient)
|
|
||||||
{
|
|
||||||
public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
List<WeatherForecast>? forecasts = null;
|
|
||||||
|
|
||||||
await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast",
|
|
||||||
cancellationToken))
|
|
||||||
{
|
|
||||||
if (forecasts?.Count >= maxItems)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forecast is not null)
|
|
||||||
{
|
|
||||||
forecasts ??= [];
|
|
||||||
forecasts.Add(forecast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return forecasts?.ToArray() ?? [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
|
||||||
{
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\WolverineTutorial.ServiceDefaults\WolverineTutorial.ServiceDefaults.csproj"/>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App.Internal.Assets" Version="10.0.9" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
html, body {
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .btn-link {
|
|
||||||
color: #006bb7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #1b6ec2;
|
|
||||||
border-color: #1861ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
|
||||||
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding-top: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.valid.modified:not([type=checkbox]) {
|
|
||||||
outline: 1px solid #26b050;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invalid {
|
|
||||||
outline: 1px solid #e52290;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation-message {
|
|
||||||
color: #e52290;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blazor-error-boundary {
|
|
||||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
|
||||||
padding: 1rem 1rem 1rem 3.7rem;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blazor-error-boundary::after {
|
|
||||||
content: "An error has occurred."
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
|
|
||||||
color: var(--bs-secondary-color);
|
|
||||||
text-align: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user