Skip to content

Commit e1106be

Browse files
committed
Add support for environment type
1 parent 0f2d680 commit e1106be

33 files changed

+821
-41
lines changed

cli/azd/cmd/container.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,19 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
177177
// semantics to follow.
178178
envValue, err := cmd.Flags().GetString(internal.EnvironmentNameFlagName)
179179
if err != nil {
180-
log.Printf("'%s'command asked for envFlag, but envFlag was not included in cmd.Flags().", cmd.CommandPath())
180+
log.Printf("'%s' command asked for envFlag, but envFlag was not included in cmd.Flags().", cmd.CommandPath())
181181
envValue = ""
182182
}
183183

184+
envTypeValue, err := cmd.Flags().GetString(internal.EnvironmentTypeFlagName)
185+
if err != nil {
186+
log.Printf(
187+
"'%s' command asked for envTypeFlag, but envTypeFlag was not included in cmd.Flags().",
188+
cmd.CommandPath(),
189+
)
190+
envTypeValue = ""
191+
}
192+
184193
if envValue == "" {
185194
// If no explicit environment flag was set, but one was provided
186195
// in the context, use that instead.
@@ -190,7 +199,10 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
190199
}
191200
}
192201

193-
return internal.EnvFlag{EnvironmentName: envValue}
202+
return internal.EnvFlag{
203+
EnvironmentName: envValue,
204+
EnvironmentType: envTypeValue,
205+
}
194206
})
195207

196208
container.MustRegisterSingleton(func(cmd *cobra.Command) CmdAnnotations {
@@ -229,9 +241,10 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
229241
}
230242

231243
environmentName := envFlags.EnvironmentName
244+
environmentType := envFlags.EnvironmentType
232245
var err error
233246

234-
env, err := envManager.LoadOrInitInteractive(ctx, environmentName)
247+
env, err := envManager.LoadOrInitInteractiveWithType(ctx, environmentName, environmentType)
235248
if err != nil {
236249
return nil, fmt.Errorf("loading environment: %w", err)
237250
}
@@ -382,14 +395,23 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
382395
func(
383396
ctx context.Context,
384397
lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext],
398+
envFlags internal.EnvFlag,
385399
) *lazy.Lazy[*project.ProjectConfig] {
386400
return lazy.NewLazy(func() (*project.ProjectConfig, error) {
387401
azdCtx, err := lazyAzdContext.GetValue()
388402
if err != nil {
389403
return nil, err
390404
}
391405

392-
projectConfig, err := project.Load(ctx, azdCtx.ProjectPath())
406+
// Use environment-specific project path when an environment is specified
407+
var projectPath string
408+
if envFlags.EnvironmentName != "" {
409+
projectPath = azdCtx.ProjectPathForEnvironment(envFlags.EnvironmentName)
410+
} else {
411+
projectPath = azdCtx.ProjectPath()
412+
}
413+
414+
projectConfig, err := project.Load(ctx, projectPath)
393415
if err != nil {
394416
return nil, err
395417
}
@@ -405,6 +427,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
405427
lazyProjectConfig *lazy.Lazy[*project.ProjectConfig],
406428
lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext],
407429
lazyLocalEnvStore *lazy.Lazy[environment.LocalDataStore],
430+
envFlags internal.EnvFlag,
408431
) (*cloud.Cloud, error) {
409432

410433
// Precedence for cloud configuration:
@@ -424,8 +447,15 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
424447
localEnvStore, _ := lazyLocalEnvStore.GetValue()
425448
if azdCtx, err := lazyAzdContext.GetValue(); err == nil {
426449
if azdCtx != nil && localEnvStore != nil {
427-
if defaultEnvName, err := azdCtx.GetDefaultEnvironmentName(); err == nil {
428-
if env, err := localEnvStore.Get(ctx, defaultEnvName); err == nil {
450+
var envName string
451+
if envFlags.EnvironmentName != "" {
452+
envName = envFlags.EnvironmentName
453+
} else if defaultEnvName, err := azdCtx.GetDefaultEnvironmentName(); err == nil {
454+
envName = defaultEnvName
455+
}
456+
457+
if envName != "" {
458+
if env, err := localEnvStore.Get(ctx, envName); err == nil {
429459
if cloudConfigurationNode, exists := env.Config.Get(cloud.ConfigPath); exists {
430460
if value, err := cloud.ParseCloudConfig(cloudConfigurationNode); err == nil {
431461
cloudConfig, err := cloud.NewCloud(value)
@@ -438,7 +468,7 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
438468
Suggestion: fmt.Sprintf(
439469
"Set the cloud configuration by editing the 'cloud' node in the config.json "+
440470
"file for the %s environment\n%s",
441-
defaultEnvName,
471+
envName,
442472
validClouds,
443473
),
444474
}

cli/azd/cmd/container_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/azure/azure-dev/cli/azd/pkg/ioc"
1212
"github.com/azure/azure-dev/cli/azd/pkg/lazy"
1313
"github.com/azure/azure-dev/cli/azd/pkg/project"
14+
"github.com/spf13/cobra"
1415
"github.com/stretchr/testify/require"
1516
)
1617

@@ -19,6 +20,13 @@ func Test_Lazy_Project_Config_Resolution(t *testing.T) {
1920
container := ioc.NewNestedContainer(nil)
2021
ioc.RegisterInstance(container, ctx)
2122

23+
// Register a mock cobra command for testing
24+
mockCmd := &cobra.Command{}
25+
mockCmd.SetContext(ctx)
26+
// Add the environment flag to avoid the "envFlag was not included in cmd.Flags()" error
27+
mockCmd.Flags().StringP("environment", "e", "", "Environment name")
28+
ioc.RegisterInstance(container, mockCmd)
29+
2230
registerCommonDependencies(container)
2331

2432
// Register the testing lazy component
@@ -88,6 +96,13 @@ func Test_Lazy_AzdContext_Resolution(t *testing.T) {
8896
container := ioc.NewNestedContainer(nil)
8997
ioc.RegisterInstance(container, ctx)
9098

99+
// Register a mock cobra command for testing
100+
mockCmd := &cobra.Command{}
101+
mockCmd.SetContext(ctx)
102+
// Add the environment flag to avoid the "envFlag was not included in cmd.Flags()" error
103+
mockCmd.Flags().StringP("environment", "e", "", "Environment name")
104+
ioc.RegisterInstance(container, mockCmd)
105+
91106
registerCommonDependencies(container)
92107

93108
// Register the testing lazy component

cli/azd/cmd/env.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"io"
1111
"os"
12+
"path/filepath"
1213
"slices"
1314
"strings"
1415
"time"
@@ -27,6 +28,7 @@ import (
2728
"github.com/azure/azure-dev/cli/azd/pkg/infra/provisioning/bicep"
2829
"github.com/azure/azure-dev/cli/azd/pkg/input"
2930
"github.com/azure/azure-dev/cli/azd/pkg/keyvault"
31+
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
3032
"github.com/azure/azure-dev/cli/azd/pkg/output"
3133
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
3234
"github.com/azure/azure-dev/cli/azd/pkg/project"
@@ -808,6 +810,7 @@ func (e *envListAction) Run(ctx context.Context) (*actions.ActionResult, error)
808810
type envNewFlags struct {
809811
subscription string
810812
location string
813+
envType string
811814
global *internal.GlobalCommandOptions
812815
}
813816

@@ -819,6 +822,7 @@ func (f *envNewFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandO
819822
"Name or ID of an Azure subscription to use for the new environment",
820823
)
821824
local.StringVarP(&f.location, "location", "l", "", "Azure location for the new environment")
825+
local.StringVarP(&f.envType, "type", "t", "", "The type of the environment (e.g., dev, test, prod). (Experimental)")
822826

823827
f.global = global
824828
}
@@ -870,17 +874,69 @@ func (en *envNewAction) Run(ctx context.Context) (*actions.ActionResult, error)
870874
environmentName = en.args[0]
871875
}
872876

877+
envType := en.flags.envType
873878
envSpec := environment.Spec{
874879
Name: environmentName,
875880
Subscription: en.flags.subscription,
876881
Location: en.flags.location,
882+
Type: envType,
877883
}
878884

879885
env, err := en.envManager.Create(ctx, envSpec)
880886
if err != nil {
881887
return nil, fmt.Errorf("creating new environment: %w", err)
882888
}
883889

890+
// Create environment-specific project file if environment type is specified
891+
if envType != "" {
892+
envProjectFileName := azdcontext.ProjectFileNameForEnvironmentType(envType)
893+
envProjectPath := filepath.Join(en.azdCtx.ProjectDirectory(), envProjectFileName)
894+
895+
if _, err := os.Stat(envProjectPath); os.IsNotExist(err) {
896+
// Check for current project file to copy from
897+
currentProjectFileName := en.azdCtx.ProjectFileName()
898+
currentProjectPath := en.azdCtx.ProjectPath()
899+
shouldCopy := false
900+
901+
if _, err := os.Stat(currentProjectPath); err == nil {
902+
// Prompt user if they want to copy content from azure.yaml
903+
shouldCopy, err = en.console.Confirm(ctx, input.ConsoleOptions{
904+
Message: fmt.Sprintf("Copy contents of %s to %s?", currentProjectFileName, envProjectFileName),
905+
DefaultValue: true,
906+
})
907+
if err != nil {
908+
return nil, fmt.Errorf("prompting to copy azure.yaml content: %w", err)
909+
}
910+
} else if !os.IsNotExist(err) {
911+
return nil, fmt.Errorf("checking for %s: %w", currentProjectFileName, err)
912+
}
913+
914+
if shouldCopy {
915+
content, err := os.ReadFile(currentProjectPath)
916+
if err != nil {
917+
return nil, fmt.Errorf("reading main project file: %w", err)
918+
}
919+
920+
if err := os.WriteFile(envProjectPath, content, osutil.PermissionFile); err != nil {
921+
return nil, fmt.Errorf("creating environment project file: %w", err)
922+
}
923+
924+
en.console.MessageUxItem(ctx, &ux.DoneMessage{
925+
Message: fmt.Sprintf("Created %s", envProjectFileName),
926+
})
927+
} else {
928+
_, err = project.New(ctx, envProjectPath, azdcontext.ProjectName(en.azdCtx.ProjectDirectory()))
929+
if err != nil {
930+
return nil, fmt.Errorf("creating empty project file: %w", err)
931+
}
932+
933+
en.console.MessageUxItem(ctx, &ux.DoneMessage{
934+
Message: fmt.Sprintf("Created empty %s", envProjectFileName),
935+
})
936+
}
937+
}
938+
}
939+
884940
envs, err := en.envManager.List(ctx)
885941
if err != nil {
886942
return nil, fmt.Errorf("listing environments: %w", err)

cli/azd/cmd/testdata/TestUsage-azd-deploy.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Usage
1010

1111
Flags
1212
--all : Deploys all services that are listed in azure.yaml
13+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
1314
-e, --environment string : The name of the environment to use.
1415
--from-package string : Deploys the packaged service located at the provided path. Supports zipped file packages (file path) or container images (image tag).
1516

cli/azd/cmd/testdata/TestUsage-azd-down.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Usage
55
azd down [flags]
66

77
Flags
8+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
89
-e, --environment string : The name of the environment to use.
910
--force : Does not require confirmation before it deletes resources.
1011
--purge : Does not require confirmation before it permanently deletes resources that are soft-deleted by default (for example, key vaults).

cli/azd/cmd/testdata/TestUsage-azd-env-get-value.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Usage
55
azd env get-value <keyName> [flags]
66

77
Flags
8+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
89
-e, --environment string : The name of the environment to use.
910

1011
Global Flags

cli/azd/cmd/testdata/TestUsage-azd-env-get-values.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Usage
55
azd env get-values [flags]
66

77
Flags
8+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
89
-e, --environment string : The name of the environment to use.
910

1011
Global Flags

cli/azd/cmd/testdata/TestUsage-azd-env-new.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Usage
77
Flags
88
-l, --location string : Azure location for the new environment
99
--subscription string : Name or ID of an Azure subscription to use for the new environment
10+
-t, --type string : The type of the environment (e.g., dev, test, prod). (Experimental)
1011

1112
Global Flags
1213
-C, --cwd string : Sets the current working directory.

cli/azd/cmd/testdata/TestUsage-azd-env-refresh.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Usage
55
azd env refresh <environment> [flags]
66

77
Flags
8+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
89
-e, --environment string : The name of the environment to use.
910
--hint string : Hint to help identify the environment to refresh
1011

cli/azd/cmd/testdata/TestUsage-azd-env-set-secret.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Usage
55
azd env set-secret <name> [flags]
66

77
Flags
8+
--env-type string : The type of the environment (e.g., dev, test, prod). (Experimental)
89
-e, --environment string : The name of the environment to use.
910

1011
Global Flags

0 commit comments

Comments
 (0)