Skip to content

Allow pipeline steps to depend on deployment-target steps materialized during before-start - #21511

Open
David Fowler (davidfowl) wants to merge 1 commit into
mainfrom
aspireagent/fix-20301-before-start-step-validation
Open

David Fowler (davidfowl) wants to merge 1 commit into
mainfrom
aspireagent/fix-20301-before-start-step-validation

Conversation

@davidfowl

Copy link
Copy Markdown
Collaborator

Description

A pipeline step that depends on a per-resource deployment-target provisioning step (for example provision-myjob-containerapp from PublishAsAzureContainerAppJob()) fails at startup with:

System.InvalidOperationException: Step 'start-myjob' depends on unknown step 'provision-myjob-containerapp'
   at DistributedApplicationPipeline.ValidateSteps(...)
   at DistributedApplicationPipeline.ResolveStepsAsync(...)
   at DistributedApplicationPipeline.ExecuteStepSequentiallyAsync(...)
   at DistributedApplication.ExecuteBeforeStartHooksAsync(...)

Root cause

ExecuteBeforeStartHooksAsync runs the before-start step through ExecuteStepSequentiallyAsync, which called ResolveStepsAsync and therefore validated every DependsOn/RequiredBy reference in the whole pipeline. But per-resource provisioning steps such as provision-{name}-containerapp come from deployment targets that are only materialized while before-start runs (by prepare-azure-container-apps-{env}). So a user step unrelated to before-start (it is required by deploy) was rejected before the step it references could exist. Depending on an aggregate step like provision-azure-bicep-resources worked only because that step exists up front.

Fix

  • ExecuteStepSequentiallyAsync resolves steps without full-graph reference validation (duplicate names are still rejected; RequiredBy edges to unresolved steps are skipped during normalization), then validates every DependsOn/RequiredBy reference of the steps it is about to execute (the target's transitive closure). Misconfigured steps that participate in before-start still fail fast with the same messages.
  • ExecuteAsync (publish/deploy/--step) is unchanged and still validates the full graph — by then the deployment targets exist, so the dependency on provision-myjob-containerapp resolves and is ordered correctly.

Regression evidence

Test Before fix After fix
DistributedApplicationPipelineTests.ExecuteStepSequentiallyAsync_StepOutsideTargetClosureDependsOnStepMaterializedByTarget_Succeeds (core repro: a step factory that only yields provision-compute after a before-start step runs) ❌ 1/1 failed: Step 'start-job' depends on unknown step 'provision-compute' ✅ 5/5 runs passed
AzureContainerAppEnvironmentExtensionsTests.PipelineStepCanDependOnContainerAppJobProvisioningStep (the issue's AppHost: ACA env + PublishAsAzureContainerAppJob() + Pipeline.AddStep(dependsOn: "provision-myjob-containerapp", requiredBy: deploy), published end to end) ❌ 1/1 failed: Step 'start-myjob' depends on unknown step 'provision-myjob-containerapp' ✅ 3/3 runs passed
New guards: unknown DependsOn / RequiredBy inside the executed closure still throw ✅ ✅

Broader local runs with the fix (Linux):

  • Aspire.Hosting.Tests Pipelines + PipelineExecutor + ComputeEnvironmentValidation + PythonAppDeployPipeline tests: 239/239 passed.
  • Aspire.Hosting.Azure.Tests AzureContainerAppEnvironmentExtensions + AzureContainerApps + AzureDeployer + AzureAppService tests: 246/246 passed.

Fixes #20301

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No

🤖 Posted by AspireAgent (AI Agent)

Steps that depend on deployment-target provisioning steps (for example
provision-{name}-containerapp) failed with 'depends on unknown step'
because before-start validated the full step graph before the deployment
targets it materializes existed. Before-start now validates references for
the steps it executes; ExecuteAsync still validates the full graph.

Fixes #20301

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21511

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21511"

@github-actions github-actions Bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Oct 31, 2026
@aspire-repo-bot
aspire-repo-bot Bot requested a balanced review from Copilot October 31, 2026 01:02
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

52 / 100 PR test projects · 4 PR jobs, from 3 changed files.

Selected PR test projects (52 / 100)

Aspire.Cli.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.EntraId.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Provisioning.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.Java.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Rust.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests, Aspire.Templates.Tests

Selected PR jobs (4)

cli-starter-validation, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 44 of the 52 selected test projects come from a single change — src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs.

🔧 src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs (changed source)
→ 44 via the project graph

show 44

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.EntraId.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Provisioning.Tests (3 hops), Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.Java.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Rust.Tests (2 hops), Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Testing.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/Pipelines/DistributedApplicationPipelineTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Tests
→ 3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

📦 affected project Aspire.Hosting.Dotnet
→ 2 tests: Aspire.Cli.EndToEnd.Tests, Aspire.Templates.Tests

🧪 tests/Aspire.Hosting.Azure.Tests/AzureContainerAppEnvironmentExtensionsTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Azure.Tests
→ 1 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests

📦 affected project Aspire.Hosting
→ 1 test: Aspire.Cli.EndToEnd.Tests

Job reasons

Job Triggered by
cli-starter-validation affected project Aspire.Hosting.Dotnet
extension-e2e • src/Aspire.Hosting/Pipelines/DistributedApplicationPipeline.cs
• affected project Aspire.Hosting
polyglot • affected project Aspire.Hosting
• affected project Aspire.Hosting.Azure.Provisioning.KeyVault
typescript-api-compat affected project Aspire.Hosting

Selection computed for commit 1d79cbe.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues were identified for the change allowing references to provisioning steps created during before-start.

No open issues 🎉

What changed in this PR

Fixes #20301 by allowing pipeline steps to reference deployment-target provisioning steps that are created during before-start, while preserving validation of steps needed for startup.

Changes:

  • Limits sequential execution’s reference validation to the target step and its transitive dependencies.
  • Preserves duplicate-name checks and full-graph validation for normal pipeline execution.
  • Adds core regression tests and an Azure Container App Job publishing test.
File Description
tests/​Aspire.Hosting.Tests/​Pipelines/​DistributedApplicationPipelineTests.cs Tests deferred step creation and rejection of unknown references within the executed dependency closure.
tests/​Aspire.Hosting.Azure.Tests/​AzureContainerAppEnvironmentExtensionsTests.cs Tests publishing with a custom step that depends on Container App Job provisioning.
src/​Aspire.Hosting/​Pipelines/​DistributedApplicationPipeline.cs Separates name and reference validation and defers unrelated reference checks during sequential execution.

Review effort: Balanced

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@davidfowl
David Fowler (davidfowl) marked this pull request as ready for review October 31, 2026 02:20

Copy link
Copy Markdown
Collaborator Author

CI green on exact head 1d79cbe (338 pass / 14 skipped). Attempt-1 failures were unrelated: Hosting-6 (windows) runner lost communication (infra); Hosting.MongoDB (ubuntu) failed in attempts 1–2 with the pre-existing InterruptedDueToReplStateChange replica-set init race (also seen on main run 40992075134), passed on attempt 3.

🤖 Posted by AspireAgent (AI Agent)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to use Pipeline API to add step dependent on dynamically generated resource provisioning step

2 participants