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
150 changes: 144 additions & 6 deletions cmd/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/agentuity/go-common/logger"
cstr "github.com/agentuity/go-common/string"
"github.com/agentuity/go-common/tui"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/api/resource"
Expand Down Expand Up @@ -121,6 +122,63 @@ func ShowNewProjectImport(ctx context.Context, logger logger.Logger, cmd *cobra.
tui.ShowSuccess("Project imported successfully")
}

var envTemplateFileNames = []string{".env.example", ".env.template"}

func readPossibleEnvTemplateFiles(baseDir string) map[string][]env.EnvLineComment {
var results map[string][]env.EnvLineComment
keys := make(map[string]bool)
for _, file := range envTemplateFileNames {
filename := filepath.Join(baseDir, file)
if !util.Exists(filename) {
continue
}
efc, err := env.ParseEnvFileWithComments(filename)
if err == nil {
if results == nil {
results = make(map[string][]env.EnvLineComment)
}
for _, ev := range efc {
if _, ok := keys[ev.Key]; !ok {
if isAgentuityEnv.MatchString(ev.Key) {
continue
}
keys[ev.Key] = true
results[file] = append(results[file], ev)
}
}
}
}
return results
}
Comment on lines +127 to +152
Copy link
Contributor

@coderabbitai coderabbitai bot May 19, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Surface parse errors from template files instead of silently ignoring them

readPossibleEnvTemplateFiles swallows any parsing error coming from env.ParseEnvFileWithComments.
If a user mistypes a line in .env.example, the CLI will behave as if the variable is simply missing, giving no clue about the real problem.

Consider propagating or at least logging the error so the user can fix the malformed template.

-func readPossibleEnvTemplateFiles(baseDir string) map[string][]env.EnvLineComment {
-    var results map[string][]env.EnvLineComment
+// returns map[template-filename]vars and an optional error so callers can decide how to react
+func readPossibleEnvTemplateFiles(baseDir string) (map[string][]env.EnvLineComment, error) {
+    var results map[string][]env.EnvLineComment
     keys := make(map[string]bool)
@@
-        efc, err := env.ParseEnvFileWithComments(filename)
-        if err == nil {
+        efc, err := env.ParseEnvFileWithComments(filename)
+        if err != nil {
+            // bubble up – caller can choose to warn the user
+            return nil, fmt.Errorf("parsing %s: %w", filename, err)
+        }
+
         if results == nil {
             results = make(map[string][]env.EnvLineComment)
         }
@@
-    return results
+    return results, nil
 }

You will need to adjust the single call-site accordingly.
Fail-fast behaviour avoids silent mis-configuration.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cmd/cloud.go around lines 127 to 152, the function
readPossibleEnvTemplateFiles currently ignores errors from
env.ParseEnvFileWithComments, which hides parsing issues in env template files.
Modify the function to return an error along with the results map, propagate any
parsing errors instead of silently continuing, and update the call-site to
handle this error. This change will enable fail-fast behavior and prevent silent
misconfiguration by surfacing malformed template problems to the user.

Copy link
Contributor

Choose a reason for hiding this comment

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

He's got a valid point... happy path is covered

Copy link
Contributor

Choose a reason for hiding this comment

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

No description provided.

Copy link
Contributor

Choose a reason for hiding this comment

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

A summary of the edits CodeRabbit can apply:

Update readPossibleEnvTemplateFiles in cmd/cloud.go to return an error and propagate parsing failures.

Update the call site in cmd/cloud.go to handle the error returned by readPossibleEnvTemplateFiles.

A plan of each step that CodeRabbit will take in order to execute the edits for each file (with inline rough draft snippets):

✏️ cmd/cloud.go

Replace the function signature and add documentation comments. Overwrite lines 127–127 with:

// readPossibleEnvTemplateFiles reads and parses environment template files
// from the specified base directory.
func readPossibleEnvTemplateFiles(baseDir string) (map[string][]env.EnvLineComment, error) {

Inside the loop, replace the error check at lines 135–136 from:

efc, err := env.ParseEnvFileWithComments(filename)
if err == nil {

to:

efc, err := env.ParseEnvFileWithComments(filename)
if err != nil {
    return nil, fmt.Errorf("parsing %s: %w", filename, err)
}

Replace the final return at line 151 from:

return results

to:

return results, nil

Change the call at line 290 from:

templateEnvs := readPossibleEnvTemplateFiles(dir)

to:

templateEnvs, err := readPossibleEnvTemplateFiles(dir)

Immediately after, insert an error check:

if err != nil {
    errsystem.New(errsystem.ErrParseEnvironmentFile, err,
        errsystem.WithContextMessage("Error parsing environment template file")).ShowErrorAndExit()
}

A summary of the context that CodeRabbit is considering across the codebase:

We need to inspect the existing readPossibleEnvTemplateFiles definition—including its signature, body, and return—to determine the precise lines to update.

We need to locate the call site(s) of readPossibleEnvTemplateFiles in order to update them to handle the new error return.

  • ✅ Create PR with these edits
  • 📌 Create commit in current branch
  • 📋 Get copyable edits


func appendToEnvFile(envfile string, envs []env.EnvLineComment) ([]env.EnvLineComment, error) {
le, err := env.ParseEnvFileWithComments(envfile)
if err != nil {
return nil, err
}
var buf strings.Builder
for _, ev := range le {
if ev.Comment != "" {
buf.WriteString(fmt.Sprintf("# %s\n", ev.Comment))
}
buf.WriteString(fmt.Sprintf("%s=%s\n", ev.Key, ev.Raw))
}
for _, ev := range envs {
if ev.Comment != "" {
buf.WriteString(fmt.Sprintf("# %s\n", ev.Comment))
}
buf.WriteString(fmt.Sprintf("%s=%s\n", ev.Key, ev.Raw))
le = append(le, ev)
}
if err := os.WriteFile(envfile, []byte(buf.String()), 0644); err != nil {
return nil, err
}
return le, nil
}

var border = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).Padding(1).BorderForeground(lipgloss.AdaptiveColor{Light: "#999999", Dark: "#999999"})
var redDiff = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#990000", Dark: "#EE0000"})

var cloudDeployCmd = &cobra.Command{
Use: "deploy",
Short: "Deploy project to the cloud",
Expand Down Expand Up @@ -193,7 +251,7 @@ Examples:
}

var err error
var le []env.EnvLine
var le []env.EnvLineComment
var projectExists bool
var action func()

Expand Down Expand Up @@ -228,13 +286,80 @@ Examples:
envfilename := filepath.Join(dir, ".env")
if tui.HasTTY && util.Exists(envfilename) {

le, err = env.ParseEnvFile(envfilename)
// attempt to see if we have any template files
templateEnvs := readPossibleEnvTemplateFiles(dir)

le, err = env.ParseEnvFileWithComments(envfilename)
if err != nil {
errsystem.New(errsystem.ErrParseEnvironmentFile, err,
errsystem.WithContextMessage("Error parsing .env file")).ShowErrorAndExit()
}
envFile = &deployer.EnvFile{Filepath: envfilename, Env: le}

if len(templateEnvs) > 0 {
kvmap := make(map[string]env.EnvLineComment)
for _, ev := range le {
if isAgentuityEnv.MatchString(ev.Key) {
continue
}
kvmap[ev.Key] = ev
}
var osenv map[string]string
var addtoenvfile []env.EnvLineComment
// look to see if we have any template environment variables that are not in the .env file
for filename, evs := range templateEnvs {
for _, ev := range evs {
if _, ok := kvmap[ev.Key]; !ok {
isSecret := looksLikeSecret.MatchString(ev.Key)
if !isSecret && descriptionLookingLikeASecret(ev.Comment) {
isSecret = true
}
_ = filename
var content string
var para []string
para = append(para, tui.Warning("Missing Environment Variable\n"))
para = append(para, fmt.Sprintf("The variable %s was found in %s but not in your %s file:\n", tui.Bold(ev.Key), tui.Bold(filename), tui.Bold(".env")))
if ev.Comment != "" {
para = append(para, tui.Muted(fmt.Sprintf("# %s", ev.Comment)))
}
if isSecret {
para = append(para, redDiff.Render(fmt.Sprintf("+ %s=%s\n", ev.Key, cstr.Mask(ev.Val))))
} else {
para = append(para, redDiff.Render(fmt.Sprintf("+ %s=%s\n", ev.Key, ev.Val)))
}
content = lipgloss.JoinVertical(lipgloss.Left, para...)
fmt.Println(border.Render(content))
if !tui.Ask(logger, "Would you like to add it to your .env file?", true) {
fmt.Println()
tui.ShowWarning("cancelled")
continue
}
if osenv == nil {
osenv = loadOSEnv()
}
val := promptForEnv(logger, ev.Key, isSecret, nil, osenv, ev.Val, ev.Comment)
addtoenvfile = append(addtoenvfile, env.EnvLineComment{
EnvLine: env.EnvLine{
Key: ev.Key,
Val: val,
Raw: val,
},
Comment: ev.Comment,
})
}
}
}
if len(addtoenvfile) > 0 {
le, err = appendToEnvFile(envfilename, addtoenvfile)
if err != nil {
errsystem.New(errsystem.ErrParseEnvironmentFile, err,
errsystem.WithContextMessage("Error parsing .env file")).ShowErrorAndExit()
}
tui.ShowSuccess("added %s to your .env file", util.Pluralize(len(addtoenvfile), "environment variable", "environment variables"))
fmt.Println()
}
}

var foundkeys []string
for _, ev := range le {
if isAgentuityEnv.MatchString(ev.Key) {
Expand All @@ -250,15 +375,25 @@ Examples:
}
if len(foundkeys) > 0 {
var title string
var suffix string
switch {
case len(foundkeys) < 3 && len(foundkeys) > 1:
title = fmt.Sprintf("The environment variables %s from .env are not been set in the project. Would you like to add it?", strings.Join(foundkeys, ", "))
suffix = "it"
var colorized []string
for _, key := range foundkeys {
colorized = append(colorized, tui.Bold(key))
}
title = fmt.Sprintf("The environment variables %s from %s are not been set in the project.", strings.Join(colorized, ", "), tui.Bold(".env"))
case len(foundkeys) == 1:
title = fmt.Sprintf("The environment variable %s from .env has not been set in the project. Would you like to add it?", foundkeys[0])
suffix = "it"
title = fmt.Sprintf("The environment variable %s from %s has not been set in the project.", tui.Bold(foundkeys[0]), tui.Bold(".env"))
default:
title = fmt.Sprintf("There are %d environment variables from .env that are not set in the project. Would you like to add them?", len(foundkeys))
suffix = "them"
title = fmt.Sprintf("There are %d environment variables from %s that are not set in the project.", len(foundkeys), tui.Bold(".env"))
}
if !tui.Ask(logger, title, true) {
fmt.Println(title)
if !tui.Ask(logger, "Would you like to set "+suffix+"?", true) {
fmt.Println()
tui.ShowWarning("cancelled")
return
}
Expand All @@ -271,6 +406,8 @@ Examples:
errsystem.New(errsystem.ErrEnvironmentVariablesNotSet, err,
errsystem.WithContextMessage("Failed to set project environment variables")).ShowErrorAndExit()
}
fmt.Println()
fmt.Println()
projectData = pd // overwrite with the new version
switch {
case len(envs) > 0 && len(secrets) > 0:
Expand All @@ -284,6 +421,7 @@ Examples:
case len(secrets) > 1:
tui.ShowSuccess("Secrets added")
}
fmt.Println()
}
}

Expand Down
8 changes: 1 addition & 7 deletions cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -44,12 +43,7 @@ Examples:
logLevel := env.LogLevel(cmd)
apiUrl, appUrl, transportUrl := util.GetURLs(log)

signals := []os.Signal{os.Interrupt, syscall.SIGINT}
if runtime.GOOS != "windows" {
signals = append(signals, syscall.SIGTERM)
}

ctx, cancel := signal.NotifyContext(context.Background(), signals...)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer cancel()

apiKey, userId := util.EnsureLoggedIn(ctx, log, cmd)
Expand Down
42 changes: 33 additions & 9 deletions cmd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,31 @@ var (
isAgentuityEnv = regexp.MustCompile(`(?i)AGENTUITY_`)
)

func loadEnvFile(le []env.EnvLine, forceSecret bool) (map[string]string, map[string]string) {
func descriptionLookingLikeASecret(description string) bool {
if description == "" {
return false
}
val := strings.ToLower(description)
if strings.Contains(val, "secret") {
return true
}
if strings.Contains(val, "password") {
return true
}
if strings.Contains(val, "key") {
return true
}
return false
}

func loadEnvFile(le []env.EnvLineComment, forceSecret bool) (map[string]string, map[string]string) {
envs := make(map[string]string)
secrets := make(map[string]string)
for _, ev := range le {
if isAgentuityEnv.MatchString(ev.Key) {
continue
}
if looksLikeSecret.MatchString(ev.Key) || forceSecret {
if looksLikeSecret.MatchString(ev.Key) || forceSecret || descriptionLookingLikeASecret(ev.Comment) {
secrets[ev.Key] = ev.Val
} else {
envs[ev.Key] = ev.Val
Expand All @@ -57,25 +74,32 @@ func loadEnvFile(le []env.EnvLine, forceSecret bool) (map[string]string, map[str
return envs, secrets
}

func promptForEnv(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string) string {
func promptForEnv(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string, defaultValue string, placeholder string) string {
prompt := "Enter the value for " + key
var help string
var defaultValue string
var value string
if isSecret {
prompt = "Enter the secret value for " + key
if val, ok := localenv[key]; ok {
help = "Press enter to set as " + maxString(cstr.Mask(val), 30) + " from your .env file"
defaultValue = val
if defaultValue == "" {
defaultValue = val
}
} else if val, ok := osenv[key]; ok {
help = "Press enter to set as " + maxString(cstr.Mask(val), 30) + " from your environment"
defaultValue = val
if defaultValue == "" {
defaultValue = val
}
} else {
help = "Your input will be masked"
}
value = tui.Password(logger, prompt, help)
} else {
value = tui.Input(logger, prompt, help)
if placeholder == "" {
value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
} else {
value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
}
Comment on lines +77 to +102
Copy link
Contributor

@coderabbitai coderabbitai bot May 19, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhanced promptForEnv with better default handling and placeholder support

The function has been extended to:

  1. Accept defaultValue and placeholder parameters
  2. Only use values from local/OS environment if no explicit default is provided
  3. Support placeholders for non-secret inputs, improving the UX

There appears to be a small logic error with placeholder handling.

Lines 98-102 contain a logical error in the conditional block. If placeholder is provided (not empty), it uses help as the placeholder parameter instead of the actual placeholder. This should be fixed:

-		if placeholder == "" {
-			value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
-		} else {
-			value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
-		}
+		if placeholder == "" {
+			value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
+		} else {
+			value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
+		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func promptForEnv(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string, defaultValue string, placeholder string) string {
prompt := "Enter the value for " + key
var help string
var defaultValue string
var value string
if isSecret {
prompt = "Enter the secret value for " + key
if val, ok := localenv[key]; ok {
help = "Press enter to set as " + maxString(cstr.Mask(val), 30) + " from your .env file"
defaultValue = val
if defaultValue == "" {
defaultValue = val
}
} else if val, ok := osenv[key]; ok {
help = "Press enter to set as " + maxString(cstr.Mask(val), 30) + " from your environment"
defaultValue = val
if defaultValue == "" {
defaultValue = val
}
} else {
help = "Your input will be masked"
}
value = tui.Password(logger, prompt, help)
} else {
value = tui.Input(logger, prompt, help)
if placeholder == "" {
value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
} else {
value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
}
func promptForEnv(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string, defaultValue string, placeholder string) string {
prompt := "Enter the value for " + key
var help string
var value string
if isSecret {
// … secret‐input logic …
value = tui.Password(logger, prompt, help)
} else {
if placeholder == "" {
value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
} else {
value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
}
}
return value
}
🤖 Prompt for AI Agents
In cmd/env.go around lines 98 to 102, the conditional incorrectly uses the
variable 'help' as the placeholder argument when 'placeholder' is non-empty. To
fix this, update the else branch to pass the actual 'placeholder' variable to
tui.InputWithPlaceholder instead of 'help', ensuring the correct placeholder
text is shown for non-secret inputs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, good catch. This needs fixing

Copy link
Contributor

Choose a reason for hiding this comment

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

No description provided.

Copy link
Contributor

Choose a reason for hiding this comment

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

A summary of the edits CodeRabbit can apply:

Fix the placeholder handling logic in promptForEnv in cmd/env.go to correctly use help when placeholder is empty and placeholder when provided.

A plan of each step that CodeRabbit will take in order to execute the edits for each file (with inline rough draft snippets):

✏️ cmd/env.go

Open cmd/env.go and find the promptForEnv function around line 77.


Locate the non-secret input placeholder block (currently lines 100–103):

if placeholder == "" {
    value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
} else {
    value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
}

Replace lines 100–103 with the corrected logic so that help is used when placeholder is empty and placeholder when provided:

if placeholder == "" {
    value = tui.InputWithPlaceholder(logger, prompt, help, defaultValue)
} else {
    value = tui.InputWithPlaceholder(logger, prompt, placeholder, defaultValue)
}

Verify the indentation uses tabs to align with surrounding code, save cmd/env.go, then run the CLI in both placeholder-present and placeholder-empty scenarios to ensure the fix works.

A summary of the context that CodeRabbit is considering across the codebase:

Display the relevant lines around the InputWithPlaceholder calls to confirm the exact block to update.

  • ✅ Create PR with these edits
  • 📌 Create commit in current branch
  • 📋 Get copyable edits

}

if value == "" && defaultValue != "" {
Expand Down Expand Up @@ -141,7 +165,7 @@ Examples:
setFromFile, err := cmd.Flags().GetString("file")
if setFromFile != "" {
if sys.Exists(setFromFile) {
le, _ := env.ParseEnvFile(setFromFile)
le, _ := env.ParseEnvFileWithComments(setFromFile)
envs, secrets = loadEnvFile(le, forceSecret)
if len(envs) > 0 || len(secrets) > 0 {
hasSetFromFile = true
Expand Down Expand Up @@ -227,7 +251,7 @@ Examples:
}
}
if value == "" {
value = promptForEnv(logger, key, isSecret, localenv, osenv)
value = promptForEnv(logger, key, isSecret, localenv, osenv, "", "")
}
}
if key != "" && value != "" {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.24.2

require (
github.com/Masterminds/semver v1.5.0
github.com/agentuity/go-common v1.0.59
github.com/agentuity/go-common v1.0.60
github.com/agentuity/mcp-golang/v2 v2.0.2
github.com/bep/debounce v1.2.1
github.com/bmatcuk/doublestar/v4 v4.8.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/agentuity/go-common v1.0.59 h1:WOR35IDV6X7qSBr+E7ztF1PygENO305/nEKd+rDAX2A=
github.com/agentuity/go-common v1.0.59/go.mod h1:cy1EPYpZUkp3JSMgTb+Sa3sLnS7vQQupj/RwO4An6L4=
github.com/agentuity/go-common v1.0.60 h1:r9uLZrYnNasnVxsTGju7ktP9+A5D3j13ald8H3Z7AMQ=
github.com/agentuity/go-common v1.0.60/go.mod h1:cy1EPYpZUkp3JSMgTb+Sa3sLnS7vQQupj/RwO4An6L4=
github.com/agentuity/mcp-golang/v2 v2.0.2 h1:wZqS/aHWZsQoU/nd1E1/iMsVY2dywWT9+PFlf+3YJxo=
github.com/agentuity/mcp-golang/v2 v2.0.2/go.mod h1:U105tZXyTatxxOBlcObRgLb/ULvGgT2DJ1nq/8++P6Q=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
Expand Down
4 changes: 2 additions & 2 deletions internal/deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

type EnvFile struct {
Filepath string
Env []env.EnvLine
Env []env.EnvLineComment
}

func (e *EnvFile) Lookup(key string) (string, bool) {
Expand Down Expand Up @@ -41,7 +41,7 @@ type PromptHelpers struct {
// Ask will ask the user for input and return true (confirm) or false (no!)
Ask func(logger logger.Logger, title string, defaultValue bool) bool
// PromptForEnv is a helper for prompting the user to get a environment (or secret) value. You must do something with the result such as save it.
PromptForEnv func(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string) string
PromptForEnv func(logger logger.Logger, key string, isSecret bool, localenv map[string]string, osenv map[string]string, defaultValue string, placeholder string) string
}

type DeployPreflightCheckData struct {
Expand Down
Loading