Skip to content

Commit 1cf75ce

Browse files
Centralize Foundry project reference wiring to address review feedback
Replace the bespoke AddProjectReference helper (which re-implemented the IResourceWithWaitSupport cast + WaitFor) with a shared internal WithProjectReference helper in ProjectBuilderExtension.cs. The public WithReference(project) overload now delegates to it, so the cast lives in exactly one place. AsHostedAgent calls the shared helper with an encoded connection name in both run and publish mode, preserving deploy-safe env var names and run/publish symmetry while keeping run-mode WaitFor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e631f29 commit 1cf75ce

2 files changed

Lines changed: 33 additions & 34 deletions

File tree

src/Aspire.Hosting.Foundry/HostedAgent/HostedAgentBuilderExtension.cs

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ private static void AddProjectReferenceForRunMode<T>(
134134
where T : IResourceWithEndpoints, IResourceWithEnvironment, IComputeResource
135135
{
136136
// Run mode waits for the project to provision so the agent's connection variables resolve before it starts.
137-
AddProjectReference(builder, project, waitForProject: true);
137+
// Use the encoded connection name so run and publish emit identical variable names (see EncodeProjectConnectionName).
138+
builder.WithProjectReference(project, connectionName: EncodeProjectConnectionName(project), waitFor: true);
138139

139140
// The default ACR is required for publish-time image push, but in run mode it adds noise to the dashboard.
140141
// When a hosted agent references a Foundry project for local execution, remove the default registry resource.
@@ -145,36 +146,14 @@ private static void AddProjectReferenceForRunMode<T>(
145146
}
146147
}
147148

148-
// Injects the Foundry project reference into the compute resource so the agent can reach the project
149-
// via the standard Aspire reference environment variables (ConnectionStrings__{name}, {NAME}_URI, ...).
150-
//
151-
// This must produce identical variable names in run and publish mode so an app that reads them locally
152-
// keeps working once deployed. The connection-string variable is named "ConnectionStrings__{connectionName}".
153-
// Foundry validates hosted agent environment variable names against ^[A-Za-z0-9_]+$ at deploy time (see
154-
// HostedAgentConfiguration), so a project name containing '-' — including the auto-generated default
155-
// "{resource.Name}-proj" — would otherwise emit "ConnectionStrings__{name}-proj" and fail deployment.
156-
// Encode the connection name up front so both modes stay symmetric and deploy-safe; for already-valid names
157-
// (e.g. "myproject") this is a no-op.
158-
//
159-
// We deliberately call the general WithReference overload (with an explicit connectionName) rather than the
160-
// Foundry-specific WithReference(project) overload: the latter ignores connectionName and always emits the
161-
// raw resource name. The Foundry overload also implicitly applies WaitFor(project); we reproduce that here
162-
// for run mode via waitForProject, but skip it in publish mode where waiting has no meaning.
163-
private static IResourceBuilder<T> AddProjectReference<T>(
164-
IResourceBuilder<T> builder,
165-
IResourceBuilder<AzureCognitiveServicesProjectResource> project,
166-
bool waitForProject)
167-
where T : IResourceWithEndpoints, IResourceWithEnvironment, IComputeResource
168-
{
169-
builder.WithReference(project, connectionName: EnvironmentVariableNameEncoder.Encode(project.Resource.Name));
170-
171-
if (waitForProject && builder is IResourceBuilder<IResourceWithWaitSupport> waitableBuilder)
172-
{
173-
waitableBuilder.WaitFor(project);
174-
}
175-
176-
return builder;
177-
}
149+
// Encodes the project's connection name so the resulting reference environment variables stay deploy-safe.
150+
// A project reference emits "ConnectionStrings__{connectionName}" (plus "{NAME}_URI", ...). Foundry validates
151+
// hosted agent environment variable names against ^[A-Za-z0-9_]+$ at deploy time (see HostedAgentConfiguration),
152+
// so a project name containing '-' — including the auto-generated default "{resource.Name}-proj" — would
153+
// otherwise emit "ConnectionStrings__{name}-proj" and fail deployment. Encoding up front keeps run and publish
154+
// mode symmetric and deploy-safe; for already-valid names (e.g. "myproject") this is a no-op.
155+
private static string EncodeProjectConnectionName(IResourceBuilder<AzureCognitiveServicesProjectResource> project)
156+
=> EnvironmentVariableNameEncoder.Encode(project.Resource.Name);
178157

179158
private static IResourceBuilder<AzureCognitiveServicesProjectResource> ResolveProjectBuilderForPublish<T>(IResourceBuilder<T> builder)
180159
where T : IResourceWithEndpoints, IResourceWithEnvironment, IComputeResource
@@ -375,7 +354,7 @@ private static void ConfigurePublishMode<T>(
375354
// needed during environment variable resolution in the deployment phase, and
376355
// mirrors the reference added in run mode so the variables match in both modes.
377356
// WaitFor is omitted in publish mode where it has no meaning.
378-
AddProjectReference(builder, project, waitForProject: false);
357+
builder.WithProjectReference(project, connectionName: EncodeProjectConnectionName(project), waitFor: false);
379358

380359
builder.ApplicationBuilder.AddResource(hostedAgent)
381360
.WithIconName("Agents")

src/Aspire.Hosting.Foundry/Project/ProjectBuilderExtension.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,33 @@ public static IResourceBuilder<TDestination> WithReference<TDestination>(this IR
6969
ArgumentNullException.ThrowIfNull(builder);
7070
ArgumentNullException.ThrowIfNull(project);
7171

72+
return WithProjectReference(builder, project, connectionName: null, waitFor: true);
73+
}
74+
75+
// Centralizes the wiring for referencing a Foundry project so callers never re-implement the standard
76+
// reference plus the IResourceWithWaitSupport cast + WaitFor. The public WithReference overload above
77+
// delegates here with the defaults (raw connection name, always wait).
78+
//
79+
// connectionName lets a caller override the default connection name. Hosted agents need this because
80+
// Foundry validates every hosted-agent environment variable name against ^[A-Za-z0-9_]+$ at deploy time
81+
// (see HostedAgentConfiguration), and a project name containing '-' would otherwise emit
82+
// "ConnectionStrings__{name}-proj" and fail deployment. waitFor is skipped in publish mode where waiting
83+
// has no meaning.
84+
internal static IResourceBuilder<TDestination> WithProjectReference<TDestination>(
85+
this IResourceBuilder<TDestination> builder,
86+
IResourceBuilder<AzureCognitiveServicesProjectResource> project,
87+
string? connectionName,
88+
bool waitFor)
89+
where TDestination : IResourceWithEnvironment
90+
{
7291
// Add standard references and environment variables
73-
ResourceBuilderExtensions.WithReference(builder, project);
92+
ResourceBuilderExtensions.WithReference(builder, project, connectionName);
7493

75-
if (builder is IResourceBuilder<IResourceWithWaitSupport> waitableBuilder)
94+
if (waitFor && builder is IResourceBuilder<IResourceWithWaitSupport> waitableBuilder)
7695
{
7796
waitableBuilder.WaitFor(project);
7897
}
98+
7999
return builder;
80100
}
81101

0 commit comments

Comments
 (0)