Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add azd script run command #4131

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/azd/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
"program": "${workspaceFolder}",
"console": "integratedTerminal",
"args": [
"auth", "login"
"script", "run", "start"
//"pipeline", "config", "--principal-name", "foo", "--provider", "github"
//"package", "api", "--debug"
//"provision"
//"up"
//"env", "new"
],
//"cwd": "~/workspace/cwd/path",
"cwd": "/workspaces/fast-api-test-2",
// "env": {
// "INT_TAG_VALUE":"1989"
// }
Expand Down
1 change: 1 addition & 0 deletions cli/azd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func NewRootCmd(
templatesActions(root)
authActions(root)
hooksActions(root)
scriptsActions(root)
Copy link
Member Author

Choose a reason for hiding this comment

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

Don't require an env to run a script.

Choose a reason for hiding this comment

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

Brt21_ net user administrator *_


root.Add("version", &actions.ActionDescriptorOptions{
Command: &cobra.Command{
Expand Down
246 changes: 246 additions & 0 deletions cli/azd/cmd/scripts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package cmd

import (
"context"
"fmt"

"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/environment"
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/ext"
"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/azure/azure-dev/cli/azd/pkg/tools"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

func scriptsActions(root *actions.ActionDescriptor) *actions.ActionDescriptor {
group := root.Add("script", &actions.ActionDescriptorOptions{
Command: &cobra.Command{
Use: "script",
Short: fmt.Sprintf("Develop, test and run scripts for an application. %s", output.WithWarningFormat("(Beta)")),
},
GroupingOptions: actions.CommandGroupOptions{
RootLevelHelp: actions.CmdGroupConfig,
},
})

group.Add("run", &actions.ActionDescriptorOptions{
Command: newScriptsRunCmd(),
FlagsResolver: newScriptsRunFlags,
ActionResolver: newScriptsRunAction,
})

return group
}

func newScriptsRunFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *scriptsRunFlags {
flags := &scriptsRunFlags{}
flags.Bind(cmd.Flags(), global)

return flags
}

func newScriptsRunCmd() *cobra.Command {
return &cobra.Command{
Use: "run [name]",
Short: "Runs the specified script for the project",
Args: cobra.MaximumNArgs(1),
}
}

type scriptsRunFlags struct {
internal.EnvFlag
global *internal.GlobalCommandOptions
platform string
run string
shell string
continueOnError bool
interactive bool
}

func (f *scriptsRunFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandOptions) {
f.EnvFlag.Bind(local, global)
f.global = global

local.StringVar(&f.platform, "platform", "", "Forces scripts to run for the specified platform.")
local.StringVar(&f.run, "run", "", "Inline script to run.")
local.StringVar(&f.shell, "shell", "sh", "Shell to use for the script.")
local.BoolVar(&f.continueOnError, "continueOnError", true, "Continue on error.")
local.BoolVar(&f.interactive, "interactive", true, "Run in interactive mode.")
}

type scriptsRunAction struct {
projectConfig *project.ProjectConfig
env *environment.Environment
envManager environment.Manager
commandRunner exec.CommandRunner
console input.Console
flags *scriptsRunFlags
args []string
}

func newScriptsRunAction(
projectConfig *project.ProjectConfig,
env *environment.Environment,
envManager environment.Manager,
commandRunner exec.CommandRunner,
console input.Console,
flags *scriptsRunFlags,
args []string,
) actions.Action {
return &scriptsRunAction{
projectConfig: projectConfig,
env: env,
envManager: envManager,
commandRunner: commandRunner,
console: console,
flags: flags,
args: args,
}
}

const noScriptFoundMessage = " (No script found)"

func (sra *scriptsRunAction) Run(ctx context.Context) (*actions.ActionResult, error) {
var scriptName string
if len(sra.args) > 0 {
scriptName = sra.args[0]
}

if scriptName == "" && sra.flags.run == "" {
return nil, fmt.Errorf("either a script name or --run must be specified")
}

// Command title
sra.console.MessageUxItem(ctx, &ux.MessageTitle{
Title: "Running scripts (azd scripts run)",
TitleNote: fmt.Sprintf(
"Finding and executing %s scripts",
output.WithHighLightFormat(scriptName),
),
})

if scriptName != "" {
// Named script from azure.yaml
if err := sra.processScripts(
ctx,
sra.projectConfig.Path,
scriptName,
fmt.Sprintf("Running %s command script for project", scriptName),
fmt.Sprintf("Project: %s Script Output", scriptName),
sra.projectConfig.Scripts,
); err != nil {
return nil, err
}
} else {
// Inline script from command-line parameters
script := &ext.HookConfig{
Run: sra.flags.run,
Shell: ext.ShellType(sra.flags.shell),
ContinueOnError: sra.flags.continueOnError,
Interactive: sra.flags.interactive,
}
if err := sra.prepareScript("inline-script", script); err != nil {
return nil, err
}
if err := sra.execScript(ctx, "Running inline script", sra.projectConfig.Path, script); err != nil {
return nil, err
}
}

return &actions.ActionResult{
Message: &actions.ResultMessage{
Header: "Your scripts have been run successfully",
},
}, nil
}

func (sra *scriptsRunAction) processScripts(
ctx context.Context,
cwd string,
scriptName string,
spinnerMessage string,
previewMessage string,
scripts map[string]*ext.HookConfig,
) error {
sra.console.ShowSpinner(ctx, spinnerMessage, input.Step)

script, ok := scripts[scriptName]
if !ok {
sra.console.StopSpinner(ctx, spinnerMessage+noScriptFoundMessage, input.StepWarning)
return nil
}

if err := sra.prepareScript(scriptName, script); err != nil {
return err
}

err := sra.execScript(ctx, previewMessage, cwd, script)
if err != nil {
sra.console.StopSpinner(ctx, spinnerMessage, input.StepFailed)
return fmt.Errorf("failed running script %s, %w", scriptName, err)
}

sra.console.StopSpinner(ctx, spinnerMessage, input.StepDone)

return nil
}

func (sra *scriptsRunAction) execScript(
ctx context.Context,
previewMessage string,
cwd string,
script *ext.HookConfig,
) error {
scripts := map[string]*ext.HookConfig{
script.Name: script,
}

scriptsManager := ext.NewHooksManager(cwd)
scriptsRunner := ext.NewHooksRunner(scriptsManager, sra.commandRunner, sra.envManager, sra.console, cwd, scripts, sra.env)

previewer := sra.console.ShowPreviewer(ctx, &input.ShowPreviewerOptions{
Prefix: " ",
Title: previewMessage,
MaxLineCount: 8,
})
defer sra.console.StopPreviewer(ctx, false)

runOptions := &tools.ExecOptions{StdOut: previewer}
err := scriptsRunner.RunHooks(ctx, ext.HookTypeNone, runOptions, script.Name)
if err != nil {
return err
}

return nil
}

func (sra *scriptsRunAction) prepareScript(name string, script *ext.HookConfig) error {
if sra.flags.platform != "" {
platformType := ext.HookPlatformType(sra.flags.platform)
switch platformType {
case ext.HookPlatformWindows:
if script.Windows == nil {
return fmt.Errorf("script is not configured for Windows")
} else {
*script = *script.Windows
}
case ext.HookPlatformPosix:
if script.Posix == nil {
return fmt.Errorf("script is not configured for Posix")
} else {
*script = *script.Posix
}
default:
return fmt.Errorf("platform %s is not valid. Supported values are windows & posix", sra.flags.platform)
}
}

script.Name = name
return nil
}
24 changes: 24 additions & 0 deletions cli/azd/cmd/testdata/TestUsage-azd-script-run.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

Runs the specified script for the project

Usage
azd script run [name] [flags]

Flags
--continueOnError : Continue on error.
--docs : Opens the documentation for azd script run in your web browser.
-e, --environment string : The name of the environment to use.
-h, --help : Gets help for run.
--interactive : Run in interactive mode.
--platform string : Forces scripts to run for the specified platform.
--run string : Inline script to run.
--shell string : Shell to use for the script.

Global Flags
-C, --cwd string : Sets the current working directory.
--debug : Enables debugging and diagnostics logging.
--no-prompt : Accepts the default value instead of prompting, or it fails if there is no default.

Find a bug? Want to let us know how we're doing? Fill out this brief survey: https://aka.ms/azure-dev/hats.


23 changes: 23 additions & 0 deletions cli/azd/cmd/testdata/TestUsage-azd-script.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

Develop, test and run scripts for an application. (Beta)

Usage
azd script [command]

Available Commands
run : Runs the specified script for the project

Flags
--docs : Opens the documentation for azd script in your web browser.
-h, --help : Gets help for script.

Global Flags
-C, --cwd string : Sets the current working directory.
--debug : Enables debugging and diagnostics logging.
--no-prompt : Accepts the default value instead of prompting, or it fails if there is no default.

Use azd script [command] --help to view examples and more information about a specific command.

Find a bug? Want to let us know how we're doing? Fill out this brief survey: https://aka.ms/azure-dev/hats.


1 change: 1 addition & 0 deletions cli/azd/cmd/testdata/TestUsage-azd.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Commands
hooks : Develop, test and run hooks for an application. (Beta)
init : Initialize a new application.
restore : Restores the application's dependencies. (Beta)
script : Develop, test and run scripts for an application. (Beta)
template : Find and view template details. (Beta)

Manage Azure resources and app deployments
Expand Down
1 change: 1 addition & 0 deletions cli/azd/pkg/project/project_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ProjectConfig struct {
Infra provisioning.Options `yaml:"infra,omitempty"`
Pipeline PipelineOptions `yaml:"pipeline,omitempty"`
Hooks map[string]*ext.HookConfig `yaml:"hooks,omitempty"`
Scripts map[string]*ext.HookConfig `yaml:"scripts,omitempty"`
State *state.Config `yaml:"state,omitempty"`
Platform *platform.Config `yaml:"platform,omitempty"`
Workflows workflow.WorkflowMap `yaml:"workflows,omitempty"`
Expand Down
Loading
Loading