@@ -76,8 +76,12 @@ func (s profileStatus) anyNeeded() bool {
7676 return s .configNeeded || s .credsNeeded
7777}
7878
79- // checkProfileStatus determines which AWS profile files need to be written or updated.
80- func checkProfileStatus (configPath , credsPath , resolvedHost string ) (profileStatus , error ) {
79+ // CheckProfileStatus determines which AWS profile files need to be written or updated.
80+ func CheckProfileStatus (resolvedHost string ) (profileStatus , error ) {
81+ configPath , credsPath , err := awsPaths ()
82+ if err != nil {
83+ return profileStatus {}, err
84+ }
8185 configNeeded , err := configNeedsWrite (configPath , resolvedHost )
8286 if err != nil {
8387 return profileStatus {}, err
@@ -184,40 +188,84 @@ func writeCredsProfile(credsPath string) error {
184188 return upsertSection (credsPath , credsSectionName , credentialsDefaults ())
185189}
186190
187- // Setup checks for the localstack AWS profile and prompts to create or update it if needed.
188- // resolvedHost must be a host:port string (e.g. "localhost.localstack.cloud:4566").
189- // In non-interactive mode, emits a note instead of prompting.
190- func Setup (ctx context.Context , sink output.Sink , interactive bool , resolvedHost string ) error {
191+ func emitMissingProfileNote (sink output.Sink ) {
192+ output .EmitNote (sink , "LocalStack AWS profile is incomplete. Run 'lstk setup aws'." )
193+ }
194+
195+ // checkProfileSetup returns both the profile status (which files need writing) and presence (which files exist).
196+ // This avoids loading the same files twice by combining needsProfileSetup and profilePresence.
197+ func checkProfileSetup (resolvedHost string ) (profileStatus , bool , bool , error ) {
191198 configPath , credsPath , err := awsPaths ()
192199 if err != nil {
193- output .EmitWarning (sink , fmt .Sprintf ("could not determine AWS config paths: %v" , err ))
194- return nil
200+ return profileStatus {}, false , false , err
201+ }
202+
203+ status , err := CheckProfileStatus (resolvedHost )
204+ if err != nil {
205+ return profileStatus {}, false , false , err
206+ }
207+
208+ configOK , err := sectionExists (configPath , configSectionName )
209+ if err != nil {
210+ return profileStatus {}, false , false , err
211+ }
212+ credsOK , err := sectionExists (credsPath , credsSectionName )
213+ if err != nil {
214+ return profileStatus {}, false , false , err
195215 }
196216
197- status , err := checkProfileStatus (configPath , credsPath , resolvedHost )
217+ return status , configOK , credsOK , nil
218+ }
219+
220+ // EnsureProfile checks for the LocalStack AWS profile and either emits a note when it is incomplete
221+ // or triggers the interactive setup flow.
222+ // resolvedHost must be a host:port string (e.g. "localhost.localstack.cloud:4566").
223+ func EnsureProfile (ctx context.Context , sink output.Sink , interactive bool , resolvedHost string ) error {
224+ status , configOK , credsOK , err := checkProfileSetup (resolvedHost )
198225 if err != nil {
199226 output .EmitWarning (sink , fmt .Sprintf ("could not check AWS profile: %v" , err ))
200227 return nil
201228 }
202229 if ! status .anyNeeded () {
203230 return nil
204231 }
232+ if interactive && ! configOK && ! credsOK {
233+ return Setup (ctx , sink , resolvedHost , status )
234+ }
235+
236+ emitMissingProfileNote (sink )
237+ return nil
238+ }
239+
240+ // Setup checks for the localstack AWS profile and prompts to create or update it if needed.
241+ // resolvedHost must be a host:port string (e.g. "localhost.localstack.cloud:4566").
242+ // status is passed in from EnsureProfile to avoid re-checking the profile status.
243+ func Setup (ctx context.Context , sink output.Sink , resolvedHost string , status profileStatus ) error {
244+ if ! status .anyNeeded () {
245+ output .EmitNote (sink , "LocalStack AWS profile is already configured." )
246+ return nil
247+ }
205248
206- if ! interactive {
207- output .EmitNote (sink , fmt .Sprintf ("No complete LocalStack AWS profile found. Run lstk interactively to configure one, or add a [profile %s] section to ~/.aws/config manually." , profileName ))
249+ configPath , credsPath , err := awsPaths ()
250+ if err != nil {
251+ output .EmitWarning (sink , fmt .Sprintf ("could not determine AWS config paths: %v" , err ))
208252 return nil
209253 }
210254
211255 responseCh := make (chan output.InputResponse , 1 )
212256 output .EmitUserInputRequest (sink , output.UserInputRequestEvent {
213- Prompt : "Configure AWS profile in ~/.aws/ ?" ,
257+ Prompt : "Set up a LocalStack profile for AWS CLI and SDKs in ~/.aws?" ,
214258 Options : []output.InputOption {{Key : "y" , Label : "Y" }, {Key : "n" , Label : "n" }},
215259 ResponseCh : responseCh ,
216260 })
217261
218262 select {
219263 case resp := <- responseCh :
220- if resp .Cancelled || resp .SelectedKey == "n" {
264+ if resp .Cancelled {
265+ return nil
266+ }
267+ if resp .SelectedKey == "n" {
268+ output .EmitNote (sink , "Skipped adding LocalStack AWS profile." )
221269 return nil
222270 }
223271 if status .configNeeded {
@@ -232,12 +280,16 @@ func Setup(ctx context.Context, sink output.Sink, interactive bool, resolvedHost
232280 return nil
233281 }
234282 }
235- output .EmitSuccess (sink , "AWS profile successfully configured" )
236- output .EmitNote (sink , fmt .Sprintf ("Try: aws s3 mb s3://test --profile %s" , profileName ))
283+ if status .configNeeded && status .credsNeeded {
284+ output .EmitSuccess (sink , "Created LocalStack profile in ~/.aws" )
285+ } else if status .configNeeded {
286+ output .EmitSuccess (sink , "Created LocalStack profile in ~/.aws/config" )
287+ } else {
288+ output .EmitSuccess (sink , "Updated LocalStack credentials in ~/.aws/credentials" )
289+ }
237290 case <- ctx .Done ():
238291 return ctx .Err ()
239292 }
240293
241294 return nil
242295}
243-
0 commit comments