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
21 changes: 1 addition & 20 deletions pkg/cli/interactive.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package cli

import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"slices"
"strings"

Expand All @@ -17,9 +15,6 @@ import (

var interactiveLog = logger.New("cli:interactive")

// workflowNameRegex validates workflow names contain only alphanumeric characters, hyphens, and underscores
var workflowNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)

// commonWorkflowNames contains common workflow name patterns for autocomplete suggestions
var commonWorkflowNames = []string{
"issue-triage",
Expand All @@ -34,12 +29,6 @@ var commonWorkflowNames = []string{
"documentation-check",
}

// isValidWorkflowName checks if the provided workflow name contains only valid characters.
// Returns false for empty strings (which should be checked separately for a more specific error message).
func isValidWorkflowName(name string) bool {
return workflowNameRegex.MatchString(name)
}

// isAccessibleMode detects if accessibility mode should be enabled based on environment variables
func isAccessibleMode() bool {
return os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") != ""
Expand Down Expand Up @@ -128,15 +117,7 @@ func (b *InteractiveWorkflowBuilder) promptForWorkflowName() error {
Description("Enter a descriptive name for your workflow (e.g., 'issue-triage', 'code-review-helper')").
Suggestions(commonWorkflowNames).
Value(&b.WorkflowName).
Validate(func(s string) error {
if s == "" {
return errors.New("workflow name cannot be empty")
}
if !isValidWorkflowName(s) {
return errors.New("workflow name must contain only alphanumeric characters, hyphens, and underscores")
}
return nil
}),
Validate(ValidateWorkflowName),
),
).WithAccessible(isAccessibleMode())

Expand Down
79 changes: 40 additions & 39 deletions pkg/cli/interactive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,69 +6,70 @@ import (
"testing"
)

func TestIsValidWorkflowName(t *testing.T) {
func TestValidateWorkflowName_Integration(t *testing.T) {
tests := []struct {
name string
input string
expected bool
name string
input string
expectError bool
}{
{
name: "valid simple name",
input: "my-workflow",
expected: true,
name: "valid simple name",
input: "my-workflow",
expectError: false,
},
{
name: "valid with underscores",
input: "my_workflow",
expected: true,
name: "valid with underscores",
input: "my_workflow",
expectError: false,
},
{
name: "valid alphanumeric",
input: "workflow123",
expected: true,
name: "valid alphanumeric",
input: "workflow123",
expectError: false,
},
{
name: "valid mixed",
input: "my-workflow_v2",
expected: true,
name: "valid mixed",
input: "my-workflow_v2",
expectError: false,
},
{
name: "invalid with spaces",
input: "my workflow",
expected: false,
name: "invalid with spaces",
input: "my workflow",
expectError: true,
},
{
name: "invalid with special chars",
input: "my@workflow!",
expected: false,
name: "invalid with special chars",
input: "my@workflow!",
expectError: true,
},
{
name: "invalid with dots",
input: "my.workflow",
expected: false,
name: "invalid with dots",
input: "my.workflow",
expectError: true,
},
{
name: "invalid with slashes",
input: "my/workflow",
expected: false,
name: "invalid with slashes",
input: "my/workflow",
expectError: true,
},
{
name: "empty string",
input: "",
expected: false,
name: "empty string",
input: "",
expectError: true,
},
{
name: "valid uppercase",
input: "MyWorkflow",
expected: true,
name: "valid uppercase",
input: "MyWorkflow",
expectError: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isValidWorkflowName(tt.input)
if result != tt.expected {
t.Errorf("isValidWorkflowName(%q) = %v, want %v", tt.input, result, tt.expected)
err := ValidateWorkflowName(tt.input)
hasError := err != nil
if hasError != tt.expectError {
t.Errorf("ValidateWorkflowName(%q) error = %v, expectError %v", tt.input, err, tt.expectError)
}
})
}
Expand All @@ -81,8 +82,8 @@ func TestCommonWorkflowNamesAreValid(t *testing.T) {
}

for _, name := range commonWorkflowNames {
if !isValidWorkflowName(name) {
t.Errorf("commonWorkflowNames contains invalid workflow name: %q", name)
if err := ValidateWorkflowName(name); err != nil {
t.Errorf("commonWorkflowNames contains invalid workflow name: %q (error: %v)", name, err)
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions pkg/cli/validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cli

import (
"errors"
"regexp"
)

// workflowNameRegex validates workflow names contain only alphanumeric characters, hyphens, and underscores
var workflowNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)

// ValidateWorkflowName checks if the provided workflow name is valid.
// It ensures the name is not empty and contains only alphanumeric characters, hyphens, and underscores.
func ValidateWorkflowName(s string) error {
if s == "" {
return errors.New("workflow name cannot be empty")
}
if !workflowNameRegex.MatchString(s) {
return errors.New("workflow name must contain only alphanumeric characters, hyphens, and underscores")
}
return nil
}
Loading