| 
9 | 9 | using Aspire.Hosting.ApplicationModel;  | 
10 | 10 | using Aspire.Hosting.Azure;  | 
11 | 11 | using Aspire.Hosting.Pipelines;  | 
12 |  | -using Aspire.Hosting.Publishing;  | 
13 | 12 | using Azure.Core;  | 
14 | 13 | using Azure.Identity;  | 
15 | 14 | using Azure.Provisioning;  | 
@@ -57,150 +56,139 @@ await DeployProjectToAppServiceAsync(  | 
57 | 56 |     }  | 
58 | 57 | 
 
  | 
59 | 58 |     private static async Task DeployProjectToAppServiceAsync(  | 
60 |  | -        DeployingContext context,  | 
 | 59 | +        PipelineStepContext context,  | 
61 | 60 |         ProjectResource projectResource,  | 
62 | 61 |         AzureAppServiceWebSiteResource websiteResource,  | 
63 | 62 |         AzureAppServiceEnvironmentResource appServiceEnvironment,  | 
64 | 63 |         CancellationToken cancellationToken)  | 
65 | 64 |     {  | 
66 |  | -        var stepName = $"deploy-{projectResource.Name}";  | 
67 |  | -        var step = await context.ActivityReporter.CreateStepAsync(stepName, cancellationToken).ConfigureAwait(false);  | 
 | 65 | +        var projectMetadata = projectResource.GetProjectMetadata();  | 
 | 66 | +        var projectPath = projectMetadata.ProjectPath;  | 
68 | 67 | 
 
  | 
69 |  | -        await using (step.ConfigureAwait(false))  | 
 | 68 | +        var publishTask = await context.ReportingStep.CreateTaskAsync($"Publishing {projectResource.Name}", cancellationToken).ConfigureAwait(false);  | 
 | 69 | +        await using (publishTask.ConfigureAwait(false))  | 
70 | 70 |         {  | 
71 |  | -            var projectMetadata = projectResource.GetProjectMetadata();  | 
72 |  | -            var projectPath = projectMetadata.ProjectPath;  | 
 | 71 | +            var publishDir = Path.Combine(Path.GetTempPath(), $"aspire-publish-{Guid.NewGuid()}");  | 
 | 72 | +            Directory.CreateDirectory(publishDir);  | 
73 | 73 | 
 
  | 
74 |  | -            var publishTask = await step.CreateTaskAsync($"Publishing {projectResource.Name}", cancellationToken).ConfigureAwait(false);  | 
75 |  | -            await using (publishTask.ConfigureAwait(false))  | 
 | 74 | +            try  | 
76 | 75 |             {  | 
77 |  | -                var publishDir = Path.Combine(Path.GetTempPath(), $"aspire-publish-{Guid.NewGuid()}");  | 
78 |  | -                Directory.CreateDirectory(publishDir);  | 
 | 76 | +                var publishProcess = Process.Start(new ProcessStartInfo  | 
 | 77 | +                {  | 
 | 78 | +                    FileName = "dotnet",  | 
 | 79 | +                    Arguments = $"publish \"{projectPath}\" -c Release -o \"{publishDir}\"",  | 
 | 80 | +                    RedirectStandardOutput = true,  | 
 | 81 | +                    RedirectStandardError = true,  | 
 | 82 | +                    UseShellExecute = false,  | 
 | 83 | +                    CreateNoWindow = true  | 
 | 84 | +                });  | 
 | 85 | + | 
 | 86 | +                if (publishProcess == null)  | 
 | 87 | +                {  | 
 | 88 | +                    await publishTask.CompleteAsync(  | 
 | 89 | +                        "Failed to start dotnet publish",  | 
 | 90 | +                        CompletionState.CompletedWithError,  | 
 | 91 | +                        cancellationToken).ConfigureAwait(false);  | 
 | 92 | +                    return;  | 
 | 93 | +                }  | 
79 | 94 | 
 
  | 
80 |  | -                try  | 
 | 95 | +                await publishProcess.WaitForExitAsync(cancellationToken).ConfigureAwait(false);  | 
 | 96 | + | 
 | 97 | +                if (publishProcess.ExitCode != 0)  | 
81 | 98 |                 {  | 
82 |  | -                    var publishProcess = Process.Start(new ProcessStartInfo  | 
83 |  | -                    {  | 
84 |  | -                        FileName = "dotnet",  | 
85 |  | -                        Arguments = $"publish \"{projectPath}\" -c Release -o \"{publishDir}\"",  | 
86 |  | -                        RedirectStandardOutput = true,  | 
87 |  | -                        RedirectStandardError = true,  | 
88 |  | -                        UseShellExecute = false,  | 
89 |  | -                        CreateNoWindow = true  | 
90 |  | -                    });  | 
91 |  | - | 
92 |  | -                    if (publishProcess == null)  | 
93 |  | -                    {  | 
94 |  | -                        await publishTask.CompleteAsync(  | 
95 |  | -                            "Failed to start dotnet publish",  | 
96 |  | -                            CompletionState.CompletedWithError,  | 
97 |  | -                            cancellationToken).ConfigureAwait(false);  | 
98 |  | -                        return;  | 
99 |  | -                    }  | 
 | 99 | +                    var error = await publishProcess.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false);  | 
 | 100 | +                    await publishTask.CompleteAsync(  | 
 | 101 | +                        $"Publish failed: {error}",  | 
 | 102 | +                        CompletionState.CompletedWithError,  | 
 | 103 | +                        cancellationToken).ConfigureAwait(false);  | 
 | 104 | +                    return;  | 
 | 105 | +                }  | 
100 | 106 | 
 
  | 
101 |  | -                    await publishProcess.WaitForExitAsync(cancellationToken).ConfigureAwait(false);  | 
 | 107 | +                await publishTask.CompleteAsync(  | 
 | 108 | +                    "Publish completed",  | 
 | 109 | +                    CompletionState.Completed,  | 
 | 110 | +                    cancellationToken).ConfigureAwait(false);  | 
102 | 111 | 
 
  | 
103 |  | -                    if (publishProcess.ExitCode != 0)  | 
104 |  | -                    {  | 
105 |  | -                        var error = await publishProcess.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false);  | 
106 |  | -                        await publishTask.CompleteAsync(  | 
107 |  | -                            $"Publish failed: {error}",  | 
108 |  | -                            CompletionState.CompletedWithError,  | 
109 |  | -                            cancellationToken).ConfigureAwait(false);  | 
110 |  | -                        return;  | 
111 |  | -                    }  | 
 | 112 | +                var zipTask = await context.ReportingStep.CreateTaskAsync($"Creating deployment package", cancellationToken).ConfigureAwait(false);  | 
 | 113 | +                await using (zipTask.ConfigureAwait(false))  | 
 | 114 | +                {  | 
 | 115 | +                    var zipPath = Path.Combine(Path.GetTempPath(), $"aspire-deploy-{Guid.NewGuid()}.zip");  | 
112 | 116 | 
 
  | 
113 |  | -                    await publishTask.CompleteAsync(  | 
114 |  | -                        "Publish completed",  | 
 | 117 | +                    ZipFile.CreateFromDirectory(publishDir, zipPath);  | 
 | 118 | + | 
 | 119 | +                    await zipTask.CompleteAsync(  | 
 | 120 | +                        "Deployment package created",  | 
115 | 121 |                         CompletionState.Completed,  | 
116 | 122 |                         cancellationToken).ConfigureAwait(false);  | 
117 | 123 | 
 
  | 
118 |  | -                    var zipTask = await step.CreateTaskAsync($"Creating deployment package", cancellationToken).ConfigureAwait(false);  | 
119 |  | -                    await using (zipTask.ConfigureAwait(false))  | 
 | 124 | +                    var uploadTask = await context.ReportingStep.CreateTaskAsync($"Uploading to {projectResource.Name}", cancellationToken).ConfigureAwait(false);  | 
 | 125 | +                    await using (uploadTask.ConfigureAwait(false))  | 
120 | 126 |                     {  | 
121 |  | -                        var zipPath = Path.Combine(Path.GetTempPath(), $"aspire-deploy-{Guid.NewGuid()}.zip");  | 
 | 127 | +                        try  | 
 | 128 | +                        {  | 
 | 129 | +                            var siteName = websiteResource.Outputs[$"{Infrastructure.NormalizeBicepIdentifier(websiteResource.Name)}_name"]?.ToString();  | 
 | 130 | +                            if (string.IsNullOrEmpty(siteName))  | 
 | 131 | +                            {  | 
 | 132 | +                                siteName = appServiceEnvironment.Outputs["name"]?.ToString();  | 
 | 133 | +                            }  | 
 | 134 | + | 
 | 135 | +                            if (string.IsNullOrEmpty(siteName))  | 
 | 136 | +                            {  | 
 | 137 | +                                await uploadTask.CompleteAsync(  | 
 | 138 | +                                    "Could not determine website name",  | 
 | 139 | +                                    CompletionState.CompletedWithError,  | 
 | 140 | +                                    cancellationToken).ConfigureAwait(false);  | 
 | 141 | +                                return;  | 
 | 142 | +                            }  | 
122 | 143 | 
 
  | 
123 |  | -                        ZipFile.CreateFromDirectory(publishDir, zipPath);  | 
 | 144 | +                            var credential = new AzureCliCredential();  | 
 | 145 | +                            var tokenRequestContext = new TokenRequestContext(["https://management.azure.com/.default"]);  | 
 | 146 | +                            var accessToken = await credential.GetTokenAsync(tokenRequestContext, cancellationToken).ConfigureAwait(false);  | 
124 | 147 | 
 
  | 
125 |  | -                        await zipTask.CompleteAsync(  | 
126 |  | -                            "Deployment package created",  | 
127 |  | -                            CompletionState.Completed,  | 
128 |  | -                            cancellationToken).ConfigureAwait(false);  | 
 | 148 | +                            var kuduUrl = $"https://{siteName}.scm.azurewebsites.net/api/zipdeploy";  | 
129 | 149 | 
 
  | 
130 |  | -                        var uploadTask = await step.CreateTaskAsync($"Uploading to {projectResource.Name}", cancellationToken).ConfigureAwait(false);  | 
131 |  | -                        await using (uploadTask.ConfigureAwait(false))  | 
132 |  | -                        {  | 
133 |  | -                            try  | 
134 |  | -                            {  | 
135 |  | -                                var siteName = websiteResource.Outputs[$"{Infrastructure.NormalizeBicepIdentifier(websiteResource.Name)}_name"]?.ToString();  | 
136 |  | -                                if (string.IsNullOrEmpty(siteName))  | 
137 |  | -                                {  | 
138 |  | -                                    siteName = appServiceEnvironment.Outputs["name"]?.ToString();  | 
139 |  | -                                }  | 
140 |  | - | 
141 |  | -                                if (string.IsNullOrEmpty(siteName))  | 
142 |  | -                                {  | 
143 |  | -                                    await uploadTask.CompleteAsync(  | 
144 |  | -                                        "Could not determine website name",  | 
145 |  | -                                        CompletionState.CompletedWithError,  | 
146 |  | -                                        cancellationToken).ConfigureAwait(false);  | 
147 |  | -                                    return;  | 
148 |  | -                                }  | 
149 |  | - | 
150 |  | -                                var credential = new AzureCliCredential();  | 
151 |  | -                                var tokenRequestContext = new TokenRequestContext(["https://management.azure.com/.default"]);  | 
152 |  | -                                var accessToken = await credential.GetTokenAsync(tokenRequestContext, cancellationToken).ConfigureAwait(false);  | 
153 |  | - | 
154 |  | -                                var kuduUrl = $"https://{siteName}.scm.azurewebsites.net/api/zipdeploy";  | 
155 |  | - | 
156 |  | -                                using var httpClient = new HttpClient();  | 
157 |  | -                                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token);  | 
158 |  | -                                httpClient.Timeout = TimeSpan.FromMinutes(30);  | 
159 |  | - | 
160 |  | -                                await using var zipStream = File.OpenRead(zipPath);  | 
161 |  | -                                using var content = new StreamContent(zipStream);  | 
162 |  | -                                content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");  | 
163 |  | - | 
164 |  | -                                var response = await httpClient.PostAsync(kuduUrl, content, cancellationToken).ConfigureAwait(false);  | 
165 |  | - | 
166 |  | -                                if (!response.IsSuccessStatusCode)  | 
167 |  | -                                {  | 
168 |  | -                                    var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);  | 
169 |  | -                                    await uploadTask.CompleteAsync(  | 
170 |  | -                                        $"Upload failed: {response.StatusCode} - {errorContent}",  | 
171 |  | -                                        CompletionState.CompletedWithError,  | 
172 |  | -                                        cancellationToken).ConfigureAwait(false);  | 
173 |  | -                                    return;  | 
174 |  | -                                }  | 
 | 150 | +                            using var httpClient = new HttpClient();  | 
 | 151 | +                            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token);  | 
 | 152 | +                            httpClient.Timeout = TimeSpan.FromMinutes(30);  | 
 | 153 | + | 
 | 154 | +                            await using var zipStream = File.OpenRead(zipPath);  | 
 | 155 | +                            using var content = new StreamContent(zipStream);  | 
 | 156 | +                            content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");  | 
 | 157 | + | 
 | 158 | +                            var response = await httpClient.PostAsync(kuduUrl, content, cancellationToken).ConfigureAwait(false);  | 
175 | 159 | 
 
  | 
 | 160 | +                            if (!response.IsSuccessStatusCode)  | 
 | 161 | +                            {  | 
 | 162 | +                                var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);  | 
176 | 163 |                                 await uploadTask.CompleteAsync(  | 
177 |  | -                                    "Upload completed successfully",  | 
178 |  | -                                    CompletionState.Completed,  | 
 | 164 | +                                    $"Upload failed: {response.StatusCode} - {errorContent}",  | 
 | 165 | +                                    CompletionState.CompletedWithError,  | 
179 | 166 |                                     cancellationToken).ConfigureAwait(false);  | 
 | 167 | +                                return;  | 
180 | 168 |                             }  | 
181 |  | -                            finally  | 
 | 169 | + | 
 | 170 | +                            await uploadTask.CompleteAsync(  | 
 | 171 | +                                "Upload completed successfully",  | 
 | 172 | +                                CompletionState.Completed,  | 
 | 173 | +                                cancellationToken).ConfigureAwait(false);  | 
 | 174 | +                        }  | 
 | 175 | +                        finally  | 
 | 176 | +                        {  | 
 | 177 | +                            if (File.Exists(zipPath))  | 
182 | 178 |                             {  | 
183 |  | -                                if (File.Exists(zipPath))  | 
184 |  | -                                {  | 
185 |  | -                                    File.Delete(zipPath);  | 
186 |  | -                                }  | 
 | 179 | +                                File.Delete(zipPath);  | 
187 | 180 |                             }  | 
188 | 181 |                         }  | 
189 | 182 |                     }  | 
190 | 183 |                 }  | 
191 |  | -                finally  | 
 | 184 | +            }  | 
 | 185 | +            finally  | 
 | 186 | +            {  | 
 | 187 | +                if (Directory.Exists(publishDir))  | 
192 | 188 |                 {  | 
193 |  | -                    if (Directory.Exists(publishDir))  | 
194 |  | -                    {  | 
195 |  | -                        Directory.Delete(publishDir, recursive: true);  | 
196 |  | -                    }  | 
 | 189 | +                    Directory.Delete(publishDir, recursive: true);  | 
197 | 190 |                 }  | 
198 | 191 |             }  | 
199 |  | - | 
200 |  | -            await step.CompleteAsync(  | 
201 |  | -                "Deployment completed",  | 
202 |  | -                CompletionState.Completed,  | 
203 |  | -                cancellationToken).ConfigureAwait(false);  | 
204 | 192 |         }  | 
205 | 193 |     }  | 
206 | 194 | }  | 
0 commit comments