diff --git a/cmd/dotenv/model.go b/cmd/dotenv/model.go index 659f7b9c..f944b2fb 100644 --- a/cmd/dotenv/model.go +++ b/cmd/dotenv/model.go @@ -16,10 +16,12 @@ package dotenv import ( "fmt" + "os" "os/exec" "path/filepath" "slices" "strings" + "time" "github.com/charmbracelet/bubbles/textarea" tea "github.com/charmbracelet/bubbletea" @@ -28,6 +30,19 @@ import ( "github.com/spf13/viper" ) +func timingEnabled() bool { + return os.Getenv("GITHUB_ACTIONS") != "" || os.Getenv("DR_TIMING") != "" +} + +func timingf(format string, args ...any) { + if !timingEnabled() { + return + } + + tp := time.Now().UTC().Format(time.RFC3339) + fmt.Fprintf(os.Stderr, "[%s] TIMING dotenv: %s\n", tp, fmt.Sprintf(format, args...)) +} + const ( // Key bindings keyQuit = "enter" @@ -158,11 +173,15 @@ func (m Model) loadPrompts() tea.Cmd { return func() tea.Msg { currentDir := filepath.Dir(m.DotenvFile) + start := time.Now() + userPrompts, err := envbuilder.GatherUserPrompts(currentDir, m.variables) if err != nil { return errMsg{err} } + timingf("loadPrompts dir=%s prompts=%d duration=%s", currentDir, len(userPrompts), time.Since(start)) + return promptsLoadedMsg{userPrompts} } } @@ -341,13 +360,24 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint: cyclop } case promptFinishedMsg: if m.currentPromptIndex < len(m.prompts) { + promptEnv := m.prompts[m.currentPromptIndex].Env + start := time.Now() + values := m.currentPrompt.Values m.prompts[m.currentPromptIndex].Value = strings.Join(values, ",") m.prompts[m.currentPromptIndex].Commented = false m.currentPromptIndex++ - - return m.moveToNextPrompt() + model, cmd := m.moveToNextPrompt() + timingf( + "advance from=%s index=%d/%d duration=%s", + promptEnv, + m.currentPromptIndex, + len(m.prompts), + time.Since(start), + ) + + return model, cmd } m.screen = listScreen diff --git a/internal/envbuilder/builder.go b/internal/envbuilder/builder.go index 8c9446cc..ef8d0f55 100644 --- a/internal/envbuilder/builder.go +++ b/internal/envbuilder/builder.go @@ -21,6 +21,7 @@ import ( "slices" "strconv" "strings" + "time" "github.com/datarobot/cli/internal/log" "gopkg.in/yaml.v3" @@ -143,6 +144,8 @@ func (up UserPrompt) ShouldAsk() bool { } func GatherUserPrompts(rootDir string, variables Variables) ([]UserPrompt, error) { + start := time.Now() + yamlFiles, err := Discover(rootDir, 5) if err != nil { return nil, fmt.Errorf("Failed to discover task yaml files: %w", err) @@ -168,6 +171,14 @@ func GatherUserPrompts(rootDir string, variables Variables) ([]UserPrompt, error allPrompts = promptsWithValues(allPrompts, variables) allPrompts = DetermineRequiredSections(allPrompts) + timingf( + "GatherUserPrompts root=%s files=%d prompts=%d duration=%s", + rootDir, + len(yamlFiles), + len(allPrompts), + time.Since(start), + ) + return allPrompts, nil } diff --git a/internal/envbuilder/discovery.go b/internal/envbuilder/discovery.go index c678d9b3..14113315 100644 --- a/internal/envbuilder/discovery.go +++ b/internal/envbuilder/discovery.go @@ -20,6 +20,7 @@ import ( "path/filepath" "sort" "strings" + "time" "github.com/datarobot/cli/internal/log" ) @@ -50,9 +51,15 @@ func Discover(root string, maxDepth int) ([]string, error) { // findComponents looks for the *.{yaml,yml} files in subdirectories (e.g. which are app framework components) of the given .datarobot directory, // and returns discovered components func findComponents(root string, maxDepth int) ([]string, error) { + start := time.Now() + walked := 0 + globMatches := 0 + var includes []string err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + walked++ + if err != nil { log.Debug(err) return nil @@ -85,6 +92,8 @@ func findComponents(root string, maxDepth int) ([]string, error) { return nil } + globMatches += len(matches) + includes = append(includes, matches...) return nil @@ -95,5 +104,15 @@ func findComponents(root string, maxDepth int) ([]string, error) { return includes[i] < includes[j] }) + timingf( + "Discover root=%s maxDepth=%d walked=%d matches=%d includes=%d duration=%s", + root, + maxDepth, + walked, + globMatches, + len(includes), + time.Since(start), + ) + return includes, err } diff --git a/internal/envbuilder/timing.go b/internal/envbuilder/timing.go new file mode 100644 index 00000000..e6985611 --- /dev/null +++ b/internal/envbuilder/timing.go @@ -0,0 +1,34 @@ +// Copyright 2025 DataRobot, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package envbuilder + +import ( + "fmt" + "os" + "time" +) + +func timingEnabled() bool { + return os.Getenv("GITHUB_ACTIONS") != "" || os.Getenv("DR_TIMING") != "" +} + +func timingf(format string, args ...any) { + if !timingEnabled() { + return + } + + tp := time.Now().UTC().Format(time.RFC3339) + fmt.Fprintf(os.Stderr, "[%s] TIMING envbuilder: %s\n", tp, fmt.Sprintf(format, args...)) +}