diff --git a/examples/frontmatter/skipPrompts/README.md b/examples/frontmatter/skipPrompts/README.md new file mode 100644 index 000000000..3d6ae0598 --- /dev/null +++ b/examples/frontmatter/skipPrompts/README.md @@ -0,0 +1,8 @@ +--- +skipPrompts: true +--- + +```sh { name=skip-prompts-sample } +$ export ENV="" +$ echo "The content of ENV is ${ENV}" +``` diff --git a/internal/cmd/run.go b/internal/cmd/run.go index 85154448c..0d39c6989 100644 --- a/internal/cmd/run.go +++ b/internal/cmd/run.go @@ -95,13 +95,18 @@ func runCmd() *cobra.Command { for _, fileBlock := range blocks { block := fileBlock.Block - if category == "" || block.ExcludeFromRunAll() { + if runAll && block.ExcludeFromRunAll() { continue } - containsCategory := slices.Contains(strings.Split(block.Category(), ","), category) - if !containsCategory { - continue + if category != "" { + if block.ExcludeFromRunAll() { + continue + } + + if !slices.Contains(strings.Split(block.Category(), ","), category) { + continue + } } runBlocks = append(runBlocks, fileBlock) @@ -183,6 +188,14 @@ func runCmd() *cobra.Command { if err != nil { return err } + + for _, block := range runBlocks { + if block.GetFrontmatter().SkipPrompts { + skipPrompts = true + break + } + } + if (skipPromptsExplicitly || isTerminal(os.Stdout.Fd())) && !skipPrompts { err = promptEnvVars(cmd, sessionEnvs, runBlocks...) if err != nil { diff --git a/internal/cmd/tui.go b/internal/cmd/tui.go index b15964548..f0db8e15b 100644 --- a/internal/cmd/tui.go +++ b/internal/cmd/tui.go @@ -155,9 +155,11 @@ func tuiCmd() *cobra.Command { runBlock := result.block.Clone() - err = promptEnvVars(cmd, sessionEnvs, runBlock) - if err != nil { - return err + if !runBlock.GetFrontmatter().SkipPrompts { + err = promptEnvVars(cmd, sessionEnvs, runBlock) + if err != nil { + return err + } } err = inRawMode(func() error { diff --git a/main_test.go b/main_test.go index 17abab6c4..0b52924b7 100644 --- a/main_test.go +++ b/main_test.go @@ -3,10 +3,19 @@ package main import ( + "bytes" + "context" "os" + "os/exec" + "regexp" + "strings" + "syscall" "testing" + "time" + "github.com/creack/pty" "github.com/rogpeppe/go-internal/testscript" + "github.com/stretchr/testify/assert" ) func TestMain(m *testing.M) { @@ -24,3 +33,51 @@ func TestRunme(t *testing.T) { Dir: "testdata/script", }) } + +func TestRunmeCategories(t *testing.T) { + testscript.Run(t, testscript.Params{ + Dir: "testdata/categories", + }) +} + +func TestRunmeRunAll(t *testing.T) { + testscript.Run(t, testscript.Params{ + Dir: "testdata/runall", + }) +} + +func TestSkipPromptsWithinAPty(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _ = os.Setenv("RUNME_VERBOSE", "false") + defer os.Unsetenv("RUNME_VERBOSE") + + cmd := exec.Command("go", "run", ".", "run", "skip-prompts-sample", "--chdir", "./examples/frontmatter/skipPrompts") + ptmx, err := pty.StartWithAttrs(cmd, &pty.Winsize{Rows: 25, Cols: 80}, &syscall.SysProcAttr{}) + if err != nil { + t.Fatalf("Could not start command with pty: %s", err) + } + defer ptmx.Close() + + buf := new(bytes.Buffer) + _, _ = buf.ReadFrom(ptmx) + + if ctx.Err() == context.DeadlineExceeded { + t.Fatalf("Command timed out") + return + } + + assert.Nil(t, err, "Command execution failed") + + expected := "The content of ENV is " + current := buf.String() + current = RemoveAnsiCodes(current) + current = strings.TrimSpace(current) + + assert.Equal(t, expected, current, "Output does not match") +} + +func RemoveAnsiCodes(str string) string { + re := regexp.MustCompile(`\x1b\[.*?[a-zA-Z]|\x1b\].*?\x1b\\`) + return re.ReplaceAllString(str, "") +} diff --git a/testdata/categories/basic.txtar b/testdata/categories/basic.txtar new file mode 100644 index 000000000..2bbcdec7d --- /dev/null +++ b/testdata/categories/basic.txtar @@ -0,0 +1,42 @@ +env SHELL=/bin/bash +exec runme run --all --category=foo --filename=CATEGORIES.md +cmp stdout foo-bar-list.txt +! stderr . + +env SHELL=/bin/bash +exec runme run --all --category=bar --filename=CATEGORIES.md +cmp stdout bar-list.txt +! stderr . + +-- CATEGORIES.md -- + +```bash { name=set-env category=foo } +$ export ENV="foo!" +``` + +```bash { name=print-foo category=foo } +$ stty -opost +$ echo "$ENV" +``` + +```bash { name=print-bar category=foo,bar } +$ stty -opost +$ echo "bar!" +``` + +```bash { name=excluded category=foo,bar excludeFromRunAll=true } +$ stty -opost +$ echo "excluded!" +``` + +-- foo-bar-list.txt -- + ► Running task set-env... + ► ✓ Task set-env exited with code 0 + ► Running task print-foo... +foo! + ► ✓ Task print-foo exited with code 0 + ► Running task print-bar... +bar! + ► ✓ Task print-bar exited with code 0 +-- bar-list.txt -- +bar! diff --git a/testdata/runall/basic.txtar b/testdata/runall/basic.txtar new file mode 100644 index 000000000..82659f1f9 --- /dev/null +++ b/testdata/runall/basic.txtar @@ -0,0 +1,54 @@ +env SHELL=/bin/bash +exec runme run --all --filename=README.md +cmp stdout all.txt +! stderr . + +env SHELL=/bin/bash +exec runme run foo-command +cmp stdout skip.txt +! stderr . + +-- all.txt -- + ► Running task set-env... + ► ✓ Task set-env exited with code 0 + ► Running task print-foo... +foo! + ► ✓ Task print-foo exited with code 0 + ► Running task print-bar... +bar! + ► ✓ Task print-bar exited with code 0 +-- skip.txt -- +foo-command +-- README.md -- +--- +skipPrompts: true +--- + +```bash { name=set-env category=foo interactive=true } +$ export ENV="foo!" +``` + +```bash { name=print-foo category=foo interactive=true } +$ stty -opost +$ echo "$ENV" +``` + +```bash { name=print-bar category=foo,bar interactive=true } +$ stty -opost +$ echo "bar!" +``` + +```bash { name=excluded category=foo,bar interactive=true excludeFromRunAll=true } +$ stty -opost +$ echo "excluded!" +``` +-- SKIP.md -- +--- +skipPrompts: true +--- + +```sh { name=foo-command category=c1 interactive=true} +$ stty -opost +export BAR="foo-command" +echo $BAR +```