Skip to content
Merged
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
73 changes: 73 additions & 0 deletions cli/azd/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -263,6 +264,8 @@ func (i *initAction) Run(ctx context.Context) (*actions.ActionResult, error) {
if existingProject {
// only initialize environment when no mode is set explicitly
initTypeSelect = initEnvironment
} else if i.console.IsNoPromptMode() {
return nil, &initModeRequiredError{}
} else {
// Prompt for init type for new projects
initTypeSelect, err = promptInitType(i.console, ctx, i.featuresManager, i.configManager)
Expand Down Expand Up @@ -880,3 +883,73 @@ func getCmdInitHelpFooter(*cobra.Command) string {
),
})
}

// initModeRequiredError is returned when azd init requires interactive prompts for initialization mode
// but --no-prompt is set.
type initModeRequiredError struct{}

func (e *initModeRequiredError) Error() string {
return "initialization mode required when --no-prompt is set"
}

func (e *initModeRequiredError) ToString(currentIndentation string) string {
var buf strings.Builder
separator := "──────────────────────────────────────────────────────────────"

buf.WriteString(separator + "\n")
buf.WriteString("Init cannot continue (interactive prompts disabled)\n")
buf.WriteString(separator + "\n\n")

buf.WriteString("Choose one:\n\n")

buf.WriteString(" • Minimal (no template)\n")
buf.WriteString(" Creates required azd project files in the current directory.\n")
buf.WriteString(" azd init --minimal\n\n")

buf.WriteString(" • From template\n")
buf.WriteString(" Creates a new project from an azd template.\n")
buf.WriteString(" azd template list\n")
buf.WriteString(" azd init --template <template-id> --environment <environment>\n\n")

buf.WriteString("Environment name must be globally unique (for example: myapp-dev).\n")

return buf.String()
}

func (e *initModeRequiredError) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Code string `json:"code"`
Message string `json:"message"`
Details struct {
Type string `json:"type"`
Options []initModeRequiredErrorOptions `json:"options"`
} `json:"details"`
}{
Code: "initModeRequired",
Message: "Init cannot continue (interactive prompts disabled)",
Details: struct {
Type string `json:"type"`
Options []initModeRequiredErrorOptions `json:"options"`
}{
Type: "initModeRequired",
Options: []initModeRequiredErrorOptions{
{
Name: "minimal",
Description: "Creates required azd project files in the current directory.",
Command: "azd init --minimal",
},
{
Name: "template",
Description: "Creates a new project from an azd template.",
Command: "azd init --template <template-id> --environment <environment>",
},
},
},
})
}

type initModeRequiredErrorOptions struct {
Name string `json:"name"`
Description string `json:"description"`
Command string `json:"command"`
}
65 changes: 65 additions & 0 deletions cli/azd/cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cmd

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -68,6 +69,70 @@ func runActionSafe(ctx context.Context, action *initAction) (retErr error) {
return err
}

func TestInitNoPromptRequiresMode(t *testing.T) {
t.Run("ReturnsInitNoPromptErrorWhenNoMode", func(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
mockContext.Console.SetNoPromptMode(true)

flags := &initFlags{
global: &internal.GlobalCommandOptions{NoPrompt: true},
}

action := setupInitAction(t, mockContext, flags)

result, err := action.Run(*mockContext.Context)
require.Error(t, err)
require.Nil(t, result)

var noPromptErr *initModeRequiredError
require.ErrorAs(t, err, &noPromptErr)

output := noPromptErr.ToString("")
require.Contains(t, output, "Init cannot continue (interactive prompts disabled)")
require.Contains(t, output, "azd init --minimal")
require.Contains(t, output, "azd init --template")
})

t.Run("DoesNotErrorWhenMinimalFlagSet", func(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
mockContext.Console.SetNoPromptMode(true)

Comment thread
weikanglim marked this conversation as resolved.
flags := &initFlags{
minimal: true,
global: &internal.GlobalCommandOptions{NoPrompt: true},
}

action := setupInitAction(t, mockContext, flags)

err := runActionSafe(*mockContext.Context, action)
if err != nil {
var noPromptErr *initModeRequiredError
require.False(t, errors.As(err, &noPromptErr),
"should not return InitNoPromptError when --minimal is set")
}
})
Comment thread
weikanglim marked this conversation as resolved.

t.Run("DoesNotErrorWhenTemplateAndEnvironmentProvided", func(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
mockContext.Console.SetNoPromptMode(true)

flags := &initFlags{
templatePath: "owner/repo",
global: &internal.GlobalCommandOptions{NoPrompt: true},
}
flags.EnvironmentName = "myenv"

action := setupInitAction(t, mockContext, flags)

err := runActionSafe(*mockContext.Context, action)
if err != nil {
var noPromptErr *initModeRequiredError
require.False(t, errors.As(err, &noPromptErr),
"should not return InitNoPromptError when --template and --environment are both set")
}
})
}

func TestInitFailFastMissingEnvNonInteractive(t *testing.T) {
t.Run("FailsWhenNoPromptWithTemplateAndNoEnv", func(t *testing.T) {
mockContext := mocks.NewMockContext(context.Background())
Expand Down
8 changes: 8 additions & 0 deletions cli/azd/cmd/middleware/ux.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ func (m *UxMiddleware) Run(ctx context.Context, next NextFn) (*actions.ActionRes
}

m.console.Message(ctx, errMessage)

// Print out additional text for errors that have it.
var uxItemErr ux.UxItem
if errors.As(err, &uxItemErr) {
m.console.Message(ctx, "")
m.console.MessageUxItem(ctx, uxItemErr)
return actionResult, err
}
}

if actionResult != nil && actionResult.Message != nil {
Comment thread
weikanglim marked this conversation as resolved.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2356,6 +2356,11 @@ func (p *BicepProvider) ensureParameters(
}{key: key, param: param})
}

// If in no-prompt mode and there are missing parameters, return an error with all missing inputs
if len(parameterPrompts) > 0 && p.console.IsNoPromptMode() {
return nil, p.buildMissingInputsError(parameterPrompts, parametersResult.envMapping)
}

Comment thread
weikanglim marked this conversation as resolved.
if len(parameterPrompts) > 0 {
if p.console.SupportsPromptDialog() {

Expand Down
Loading
Loading