Skip to content

Commit c14778d

Browse files
authored
Fixes issue where project level events not invoked from extensions (#5964)
* Invoke project events during commands * fixes lint issues
1 parent 1f4ff07 commit c14778d

File tree

7 files changed

+394
-295
lines changed

7 files changed

+394
-295
lines changed

cli/azd/cmd/build.go

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -162,44 +162,57 @@ func (ba *buildAction) Run(ctx context.Context) (*actions.ActionResult, error) {
162162
return nil, err
163163
}
164164

165-
buildResults := map[string]*project.ServiceBuildResult{}
166165
stableServices, err := ba.importManager.ServiceStable(ctx, ba.projectConfig)
167166
if err != nil {
168167
return nil, err
169168
}
170169

171-
for _, svc := range stableServices {
172-
stepMessage := fmt.Sprintf("Building service %s", svc.Name)
173-
ba.console.ShowSpinner(ctx, stepMessage, input.Step)
174-
175-
// Skip this service if both cases are true:
176-
// 1. The user specified a service name
177-
// 2. This service is not the one the user specified
178-
if targetServiceName != "" && targetServiceName != svc.Name {
179-
ba.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
180-
continue
181-
}
170+
projectEventArgs := project.ProjectLifecycleEventArgs{
171+
Project: ba.projectConfig,
172+
}
182173

183-
buildResult, err := async.RunWithProgress(
184-
func(buildProgress project.ServiceProgress) {
185-
progressMessage := fmt.Sprintf("Building service %s (%s)", svc.Name, buildProgress.Message)
186-
ba.console.ShowSpinner(ctx, progressMessage, input.Step)
187-
},
188-
func(progress *async.Progress[project.ServiceProgress]) (*project.ServiceBuildResult, error) {
189-
return ba.serviceManager.Build(ctx, svc, nil, progress)
190-
},
191-
)
174+
buildResults := map[string]*project.ServiceBuildResult{}
192175

193-
if err != nil {
194-
ba.console.StopSpinner(ctx, stepMessage, input.StepFailed)
195-
return nil, err
176+
err = ba.projectConfig.Invoke(ctx, project.ProjectEventBuild, projectEventArgs, func() error {
177+
for _, svc := range stableServices {
178+
stepMessage := fmt.Sprintf("Building service %s", svc.Name)
179+
ba.console.ShowSpinner(ctx, stepMessage, input.Step)
180+
181+
// Skip this service if both cases are true:
182+
// 1. The user specified a service name
183+
// 2. This service is not the one the user specified
184+
if targetServiceName != "" && targetServiceName != svc.Name {
185+
ba.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
186+
continue
187+
}
188+
189+
buildResult, err := async.RunWithProgress(
190+
func(buildProgress project.ServiceProgress) {
191+
progressMessage := fmt.Sprintf("Building service %s (%s)", svc.Name, buildProgress.Message)
192+
ba.console.ShowSpinner(ctx, progressMessage, input.Step)
193+
},
194+
func(progress *async.Progress[project.ServiceProgress]) (*project.ServiceBuildResult, error) {
195+
return ba.serviceManager.Build(ctx, svc, nil, progress)
196+
},
197+
)
198+
199+
if err != nil {
200+
ba.console.StopSpinner(ctx, stepMessage, input.StepFailed)
201+
return err
202+
}
203+
204+
ba.console.StopSpinner(ctx, stepMessage, input.StepDone)
205+
buildResults[svc.Name] = buildResult
206+
207+
// report build outputs
208+
ba.console.MessageUxItem(ctx, buildResult.Artifacts)
196209
}
197210

198-
ba.console.StopSpinner(ctx, stepMessage, input.StepDone)
199-
buildResults[svc.Name] = buildResult
211+
return nil
212+
})
200213

201-
// report build outputs
202-
ba.console.MessageUxItem(ctx, buildResult.Artifacts)
214+
if err != nil {
215+
return nil, err
203216
}
204217

205218
if ba.formatter.Kind() == output.JsonFormat {

cli/azd/cmd/package.go

Lines changed: 79 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -142,81 +142,95 @@ func (pa *packageAction) Run(ctx context.Context) (*actions.ActionResult, error)
142142
return nil, err
143143
}
144144

145-
packageResults := map[string]*project.ServicePackageResult{}
146-
147145
serviceTable, err := pa.importManager.ServiceStable(ctx, pa.projectConfig)
148146
if err != nil {
149147
return nil, err
150148
}
149+
151150
serviceCount := len(serviceTable)
152-
for index, svc := range serviceTable {
153-
// TODO(ellismg): We need to figure out what packaging an containerized dotnet app means. For now, just skip it.
154-
// We "package" the app during deploy when we call `dotnet publish /p:PublishProfile=DefaultContainer` to build
155-
// and push the container image.
156-
//
157-
// Doing this skip here means that during `azd up` we don't show output like:
158-
// /* cSpell:disable */
159-
//
160-
// Packaging services (azd package)
161-
//
162-
// (✓) Done: Packaging service basketservice
163-
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd472091284
164-
//
165-
// (✓) Done: Packaging service catalogservice
166-
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd2265185954
167-
//
168-
// (✓) Done: Packaging service frontend
169-
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd2956031596
170-
//
171-
// /* cSpell:enable */
172-
// Which is nice - since the above is not the package that we publish (instead it's the raw output of
173-
// `dotnet publish`, as if you were going to run on App Service.).
174-
//
175-
// With .NET 8, we'll be able to build just the container image, by setting ContainerArchiveOutputPath
176-
// as a property when we run `dotnet publish`. If we set this to the filepath of a tgz (doesn't need to exist)
177-
// the the action will just produce a container image and save it to that tgz, as `docker save` would have. It will
178-
// not push the container image.
179-
//
180-
// It's probably right for us to think about "package" for a containerized application as meaning "produce the tgz"
181-
// of the image, as would be done by `docker save` and then do this for both DotNetContainerAppTargets and
182-
// ContainerAppTargets.
183-
if svc.Host == project.DotNetContainerAppTarget {
184-
continue
185-
}
186151

187-
stepMessage := fmt.Sprintf("Packaging service %s", svc.Name)
188-
pa.console.ShowSpinner(ctx, stepMessage, input.Step)
152+
projectEventArgs := project.ProjectLifecycleEventArgs{
153+
Project: pa.projectConfig,
154+
}
189155

190-
// Skip this service if both cases are true:
191-
// 1. The user specified a service name
192-
// 2. This service is not the one the user specified
193-
if targetServiceName != "" && targetServiceName != svc.Name {
194-
pa.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
195-
continue
196-
}
156+
packageResults := map[string]*project.ServicePackageResult{}
197157

198-
options := &project.PackageOptions{OutputPath: pa.flags.outputPath}
199-
packageResult, err := async.RunWithProgress(
200-
func(packageProgress project.ServiceProgress) {
201-
progressMessage := fmt.Sprintf("Packaging service %s (%s)", svc.Name, packageProgress.Message)
202-
pa.console.ShowSpinner(ctx, progressMessage, input.Step)
203-
},
204-
func(progress *async.Progress[project.ServiceProgress]) (*project.ServicePackageResult, error) {
205-
return pa.serviceManager.Package(ctx, svc, nil, progress, options)
206-
},
207-
)
208-
pa.console.StopSpinner(ctx, stepMessage, input.GetStepResultFormat(err))
209-
210-
if err != nil {
211-
return nil, err
212-
}
213-
packageResults[svc.Name] = packageResult
158+
err = pa.projectConfig.Invoke(ctx, project.ProjectEventPackage, projectEventArgs, func() error {
159+
for index, svc := range serviceTable {
160+
// TODO(ellismg): We need to figure out what packaging an containerized dotnet app means. For now, just skip it.
161+
// We "package" the app during deploy when we call `dotnet publish /p:PublishProfile=DefaultContainer` to build
162+
// and push the container image.
163+
//
164+
// Doing this skip here means that during `azd up` we don't show output like:
165+
// /* cSpell:disable */
166+
//
167+
// Packaging services (azd package)
168+
//
169+
// (✓) Done: Packaging service basketservice
170+
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd472091284
171+
//
172+
// (✓) Done: Packaging service catalogservice
173+
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd2265185954
174+
//
175+
// (✓) Done: Packaging service frontend
176+
// - Package Output: /var/folders/6n/sxbj12js5ksg6ztn0kslqp400000gn/T/azd2956031596
177+
//
178+
// /* cSpell:enable */
179+
// Which is nice - since the above is not the package that we publish (instead it's the raw output of
180+
// `dotnet publish`, as if you were going to run on App Service.).
181+
//
182+
// With .NET 8, we'll be able to build just the container image, by setting ContainerArchiveOutputPath
183+
// as a property when we run `dotnet publish`. If we set this to the filepath of a tgz (doesn't need to exist)
184+
// the the action will just produce a container image and save it to that tgz, as `docker save` would have.
185+
// It will not push the container image.
186+
//
187+
// It's probably right for us to think about "package" for a containerized application as meaning
188+
// "produce the tgz" of the image, as would be done by `docker save` and then do this for both
189+
// DotNetContainerAppTargets and ContainerAppTargets.
190+
if svc.Host == project.DotNetContainerAppTarget {
191+
continue
192+
}
193+
194+
stepMessage := fmt.Sprintf("Packaging service %s", svc.Name)
195+
pa.console.ShowSpinner(ctx, stepMessage, input.Step)
214196

215-
// report package output
216-
pa.console.MessageUxItem(ctx, packageResult.Artifacts)
217-
if index < serviceCount-1 {
218-
pa.console.Message(ctx, "")
197+
// Skip this service if both cases are true:
198+
// 1. The user specified a service name
199+
// 2. This service is not the one the user specified
200+
if targetServiceName != "" && targetServiceName != svc.Name {
201+
pa.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
202+
continue
203+
}
204+
205+
options := &project.PackageOptions{OutputPath: pa.flags.outputPath}
206+
packageResult, err := async.RunWithProgress(
207+
func(packageProgress project.ServiceProgress) {
208+
progressMessage := fmt.Sprintf("Packaging service %s (%s)", svc.Name, packageProgress.Message)
209+
pa.console.ShowSpinner(ctx, progressMessage, input.Step)
210+
},
211+
func(progress *async.Progress[project.ServiceProgress]) (*project.ServicePackageResult, error) {
212+
return pa.serviceManager.Package(ctx, svc, nil, progress, options)
213+
},
214+
)
215+
pa.console.StopSpinner(ctx, stepMessage, input.GetStepResultFormat(err))
216+
217+
if err != nil {
218+
return err
219+
}
220+
packageResults[svc.Name] = packageResult
221+
222+
// report package output
223+
pa.console.MessageUxItem(ctx, packageResult.Artifacts)
224+
if index < serviceCount-1 {
225+
pa.console.Message(ctx, "")
226+
}
219227
}
228+
229+
return nil
230+
})
231+
232+
if err != nil {
233+
return nil, err
220234
}
221235

222236
if pa.formatter.Kind() == output.JsonFormat {

cli/azd/cmd/restore.go

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -154,44 +154,60 @@ func (ra *restoreAction) Run(ctx context.Context) (*actions.ActionResult, error)
154154
return nil, err
155155
}
156156

157-
restoreResults := map[string]*project.ServiceRestoreResult{}
158157
stableServices, err := ra.importManager.ServiceStable(ctx, ra.projectConfig)
159158
if err != nil {
160159
return nil, err
161160
}
162161

163-
for _, svc := range stableServices {
164-
stepMessage := fmt.Sprintf("Restoring service %s", svc.Name)
165-
ra.console.ShowSpinner(ctx, stepMessage, input.Step)
162+
projectEventArgs := project.ProjectLifecycleEventArgs{
163+
Project: ra.projectConfig,
164+
}
166165

167-
// Skip this service if both cases are true:
168-
// 1. The user specified a service name
169-
// 2. This service is not the one the user specified
170-
if targetServiceName != "" && targetServiceName != svc.Name {
171-
ra.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
172-
continue
173-
}
166+
restoreResults := map[string]*project.ServiceRestoreResult{}
174167

175-
// Initialize service context for restore operation
176-
serviceContext := &project.ServiceContext{}
177-
178-
restoreResult, err := async.RunWithProgress(
179-
func(buildProgress project.ServiceProgress) {
180-
progressMessage := fmt.Sprintf("Building service %s (%s)", svc.Name, buildProgress.Message)
181-
ra.console.ShowSpinner(ctx, progressMessage, input.Step)
182-
},
183-
func(progress *async.Progress[project.ServiceProgress]) (*project.ServiceRestoreResult, error) {
184-
return ra.serviceManager.Restore(ctx, svc, serviceContext, progress)
185-
},
186-
)
187-
188-
if err != nil {
189-
ra.console.StopSpinner(ctx, stepMessage, input.StepFailed)
190-
return nil, err
168+
err = ra.projectConfig.Invoke(ctx, project.ProjectEventRestore, projectEventArgs, func() error {
169+
for _, svc := range stableServices {
170+
stepMessage := fmt.Sprintf("Restoring service %s", svc.Name)
171+
ra.console.ShowSpinner(ctx, stepMessage, input.Step)
172+
173+
// Skip this service if both cases are true:
174+
// 1. The user specified a service name
175+
// 2. This service is not the one the user specified
176+
if targetServiceName != "" && targetServiceName != svc.Name {
177+
ra.console.StopSpinner(ctx, stepMessage, input.StepSkipped)
178+
continue
179+
}
180+
181+
// Initialize service context for restore operation
182+
serviceContext := &project.ServiceContext{}
183+
184+
restoreResult, err := async.RunWithProgress(
185+
func(buildProgress project.ServiceProgress) {
186+
progressMessage := fmt.Sprintf("Building service %s (%s)", svc.Name, buildProgress.Message)
187+
ra.console.ShowSpinner(ctx, progressMessage, input.Step)
188+
},
189+
func(progress *async.Progress[project.ServiceProgress]) (*project.ServiceRestoreResult, error) {
190+
return ra.serviceManager.Restore(ctx, svc, serviceContext, progress)
191+
},
192+
)
193+
194+
if err != nil {
195+
ra.console.StopSpinner(ctx, stepMessage, input.StepFailed)
196+
return err
197+
}
198+
199+
ra.console.StopSpinner(ctx, stepMessage, input.StepDone)
200+
restoreResults[svc.Name] = restoreResult
201+
202+
// report restore output
203+
ra.console.MessageUxItem(ctx, restoreResult.Artifacts)
191204
}
192205

193-
ra.console.StopSpinner(ctx, stepMessage, input.StepDone)
194-
restoreResults[svc.Name] = restoreResult
206+
return nil
207+
})
208+
209+
if err != nil {
210+
return nil, err
195211
}
196212

197213
if ra.formatter.Kind() == output.JsonFormat {

cli/azd/extensions/microsoft.azd.demo/internal/cmd/listen.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,43 @@ func newListenCommand() *cobra.Command {
4141

4242
return nil
4343
}).
44+
WithProjectEventHandler("predeploy", func(ctx context.Context, args *azdext.ProjectEventArgs) error {
45+
for i := 1; i <= 20; i++ {
46+
fmt.Printf("%d. Doing important predeploy project work in extension...\n", i)
47+
time.Sleep(250 * time.Millisecond)
48+
}
49+
50+
return nil
51+
}).
52+
WithProjectEventHandler("postdeploy", func(ctx context.Context, args *azdext.ProjectEventArgs) error {
53+
for i := 1; i <= 20; i++ {
54+
fmt.Printf("%d. Doing important postdeploy project work in extension...\n", i)
55+
time.Sleep(250 * time.Millisecond)
56+
}
57+
58+
return nil
59+
}).
4460
WithServiceEventHandler("prepackage", func(ctx context.Context, args *azdext.ServiceEventArgs) error {
4561
for i := 1; i <= 20; i++ {
46-
fmt.Printf("%d. Doing important work in extension...\n", i)
62+
fmt.Printf("%d. Doing important prepackage service work in extension...\n", i)
63+
time.Sleep(250 * time.Millisecond)
64+
}
65+
66+
return nil
67+
}, &azdext.ServerEventOptions{
68+
// Optionally filter your subscription by service host and/or language
69+
Host: "containerapp",
70+
}).
71+
WithServiceEventHandler("postpackage", func(ctx context.Context, args *azdext.ServiceEventArgs) error {
72+
for i := 1; i <= 20; i++ {
73+
fmt.Printf("%d. Doing important postpackage service work in extension...\n", i)
4774
time.Sleep(250 * time.Millisecond)
4875
}
4976

5077
return nil
5178
}, &azdext.ServerEventOptions{
5279
// Optionally filter your subscription by service host and/or language
53-
Host: "containerapp",
54-
Language: "python",
80+
Host: "containerapp",
5581
})
5682

5783
// Start listening for events

0 commit comments

Comments
 (0)