cli/azd/cmd/restore.go (197 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. package cmd import ( "context" "fmt" "io" "time" "github.com/azure/azure-dev/cli/azd/cmd/actions" "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/async" "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext" "github.com/azure/azure-dev/cli/azd/pkg/exec" "github.com/azure/azure-dev/cli/azd/pkg/input" "github.com/azure/azure-dev/cli/azd/pkg/output" "github.com/azure/azure-dev/cli/azd/pkg/output/ux" "github.com/azure/azure-dev/cli/azd/pkg/project" "github.com/spf13/cobra" "github.com/spf13/pflag" ) type restoreFlags struct { all bool global *internal.GlobalCommandOptions serviceName string internal.EnvFlag } func (r *restoreFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandOptions) { local.BoolVar( &r.all, "all", false, "Restores all services that are listed in "+azdcontext.ProjectFileName, ) local.StringVar( &r.serviceName, "service", "", //nolint:lll "Restores a specific service (when the string is unspecified, all services that are listed in the "+azdcontext.ProjectFileName+" file are restored).", ) //deprecate:flag hide --service _ = local.MarkHidden("service") } func newRestoreFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *restoreFlags { flags := &restoreFlags{} flags.Bind(cmd.Flags(), global) flags.EnvFlag.Bind(cmd.Flags(), global) flags.global = global return flags } func newRestoreCmd() *cobra.Command { cmd := &cobra.Command{ Use: "restore <service>", Short: fmt.Sprintf("Restores the application's dependencies. %s", output.WithWarningFormat("(Beta)")), } cmd.Args = cobra.MaximumNArgs(1) return cmd } type restoreAction struct { flags *restoreFlags args []string console input.Console formatter output.Formatter writer io.Writer azdCtx *azdcontext.AzdContext env *environment.Environment projectConfig *project.ProjectConfig projectManager project.ProjectManager importManager *project.ImportManager serviceManager project.ServiceManager commandRunner exec.CommandRunner } func newRestoreAction( flags *restoreFlags, args []string, console input.Console, formatter output.Formatter, writer io.Writer, azdCtx *azdcontext.AzdContext, env *environment.Environment, projectConfig *project.ProjectConfig, projectManager project.ProjectManager, serviceManager project.ServiceManager, commandRunner exec.CommandRunner, importManager *project.ImportManager, ) actions.Action { return &restoreAction{ flags: flags, args: args, console: console, formatter: formatter, writer: writer, azdCtx: azdCtx, projectConfig: projectConfig, projectManager: projectManager, serviceManager: serviceManager, env: env, commandRunner: commandRunner, importManager: importManager, } } type RestoreResult struct { Timestamp time.Time `json:"timestamp"` Services map[string]*project.ServiceRestoreResult `json:"services"` } func (ra *restoreAction) Run(ctx context.Context) (*actions.ActionResult, error) { // Command title ra.console.MessageUxItem(ctx, &ux.MessageTitle{ Title: "Restoring services (azd restore)", }) startTime := time.Now() serviceNameWarningCheck(ra.console, ra.flags.serviceName, "restore") targetServiceName := ra.flags.serviceName if len(ra.args) == 1 { targetServiceName = ra.args[0] } targetServiceName, err := getTargetServiceName( ctx, ra.projectManager, ra.importManager, ra.projectConfig, string(project.ServiceEventRestore), targetServiceName, ra.flags.all, ) if err != nil { return nil, err } if err := ra.projectManager.Initialize(ctx, ra.projectConfig); err != nil { return nil, err } if err := ra.projectManager.EnsureRestoreTools(ctx, ra.projectConfig, func(svc *project.ServiceConfig) bool { return targetServiceName == "" || svc.Name == targetServiceName }); err != nil { return nil, err } restoreResults := map[string]*project.ServiceRestoreResult{} stableServices, err := ra.importManager.ServiceStable(ctx, ra.projectConfig) if err != nil { return nil, err } for _, svc := range stableServices { stepMessage := fmt.Sprintf("Restoring service %s", svc.Name) ra.console.ShowSpinner(ctx, stepMessage, input.Step) // Skip this service if both cases are true: // 1. The user specified a service name // 2. This service is not the one the user specified if targetServiceName != "" && targetServiceName != svc.Name { ra.console.StopSpinner(ctx, stepMessage, input.StepSkipped) continue } restoreResult, err := async.RunWithProgress( func(buildProgress project.ServiceProgress) { progressMessage := fmt.Sprintf("Building service %s (%s)", svc.Name, buildProgress.Message) ra.console.ShowSpinner(ctx, progressMessage, input.Step) }, func(progress *async.Progress[project.ServiceProgress]) (*project.ServiceRestoreResult, error) { return ra.serviceManager.Restore(ctx, svc, progress) }, ) if err != nil { ra.console.StopSpinner(ctx, stepMessage, input.StepFailed) return nil, err } ra.console.StopSpinner(ctx, stepMessage, input.StepDone) restoreResults[svc.Name] = restoreResult } if ra.formatter.Kind() == output.JsonFormat { restoreResult := RestoreResult{ Timestamp: time.Now(), Services: restoreResults, } if fmtErr := ra.formatter.Format(restoreResult, ra.writer, nil); fmtErr != nil { return nil, fmt.Errorf("restore result could not be displayed: %w", fmtErr) } } return &actions.ActionResult{ Message: &actions.ResultMessage{ Header: fmt.Sprintf( "Your applications dependencies were restored in %s.", ux.DurationAsText(since(startTime))), }, }, nil } func getCmdRestoreHelpDescription(*cobra.Command) string { return generateCmdHelpDescription( fmt.Sprintf("Restore application dependencies. %s", output.WithWarningFormat("(Beta)")), []string{ formatHelpNote("Run this command to download and install all required dependencies so that you can build," + " run, and debug the application locally."), formatHelpNote(fmt.Sprintf("For the best local run and debug experience, go to %s to learn how "+ "to use the Visual Studio Code extension.", output.WithLinkFormat("https://aka.ms/azure-dev/vscode"), )), }) } func getCmdRestoreHelpFooter(*cobra.Command) string { return generateCmdHelpSamplesBlock(map[string]string{ "Downloads and installs all application dependencies.": output.WithHighLightFormat("azd restore"), "Downloads and installs a specific application service " + "dependency, Individual services are listed in your azure.yaml file.": fmt.Sprintf("%s %s", output.WithHighLightFormat("azd restore <service>"), output.WithWarningFormat("[Service name]")), }) }