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
117 changes: 102 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ Modes:

Execution Control:
-i, --interactive Prompt to press enter between each chunk
-s, --start-from string Start from a specific stage name
-s, --start-from string Start from a specific stage (stage or file@stage)
-B, --break-at string Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)
-t, --timeout int The timeout in minutes for every executed command (default 10)
-u, --update-files Update the chunk output section in the markdown files
--ignore-breakpoints Ignore the breakpoints
Expand Down Expand Up @@ -81,12 +82,12 @@ go test ./...
? github.com/arkmq-org/markdown-runner/pterm [no test files]
? github.com/arkmq-org/markdown-runner/runnercontext [no test files]
? github.com/arkmq-org/markdown-runner/view [no test files]
ok github.com/arkmq-org/markdown-runner 0.005s
ok github.com/arkmq-org/markdown-runner/chunk (cached)
ok github.com/arkmq-org/markdown-runner 0.016s
ok github.com/arkmq-org/markdown-runner/chunk 0.127s
ok github.com/arkmq-org/markdown-runner/config (cached)
ok github.com/arkmq-org/markdown-runner/parser (cached)
ok github.com/arkmq-org/markdown-runner/runner (cached)
ok github.com/arkmq-org/markdown-runner/stage (cached)
ok github.com/arkmq-org/markdown-runner/runner 0.041s
ok github.com/arkmq-org/markdown-runner/stage 0.126s
```

Integration tests can be run with:
Expand All @@ -101,11 +102,13 @@ Integration tests can be run with:
Running test #3: Test case parallel... ✅
Running test #4: Schema error test (schema_error) should fail as expected... ✅
Running test #5: Teardown test (teardown) should execute teardown... ✅
Running test #6: Test case writer... ✅
Running test #7: Recursive test... ✅
Running test #8: Recursive test with file filter... ✅
Running test #6: Verbose set -x test (verbose_set_x) should show command tracing with -v... ✅
Running test #7: Non-verbose test (verbose_set_x) should not show command tracing without -v... ✅
Running test #8: Test case writer... ✅
Running test #9: Recursive test... ✅
Running test #10: Recursive test with file filter... ✅
\n--- Test Summary ---
All 8 tests passed!
All 10 tests passed!
--- Cleaning up ---
Cleanup complete.
```
Expand Down Expand Up @@ -385,6 +388,78 @@ This works only if:
Enters interactive mode when the chunk is started. Useful for debugging
purposes. Better to use alongside `--verbose`

#### Debugging and Interactive Execution

The markdown-runner provides several ways to debug and interactively control the execution of your markdown files:

##### Using `--break-at` flag

The `--break-at` (`-B`) flag allows you to start debugging from a specific stage or even a specific chunk within a stage:

**Debug from a stage:**
```bash
markdown-runner --break-at stage2 myfile.md
```

**Debug from a specific chunk by ID:**
```bash
markdown-runner --break-at stage2/myChunkID myfile.md
```

**Debug from a specific chunk by index (0-based):**
```bash
markdown-runner --break-at stage2/0 myfile.md
```

**Debug from a specific file when running a directory:**
```bash
markdown-runner --break-at myfile@stage2 mydirectory/
```

When debugging is enabled, the runner will prompt you for each chunk:
- `yes` - Execute this chunk and continue prompting for the next
- `no` - Skip this chunk and continue prompting for the next
- `all` - Execute this chunk and all remaining chunks without prompting
- `cancel` - Stop execution immediately

**Examples:**

Given a markdown file with chunks:
````markdown
```bash {"stage":"setup", "id":"init", "label": "Initialize"}
echo "Setting up..."
```
```shell markdown_runner
Setting up...
```

```bash {"stage":"setup", "label": "Configure"}
echo "Configuring..."
```
```shell markdown_runner
Configuring...
```

```bash {"stage":"main", "id":"process", "label": "Process data"}
echo "Processing..."
```
```shell markdown_runner
Processing...
```
````

- `markdown-runner -B setup myfile.md` - Start debugging from the "setup" stage
- `markdown-runner -B setup/init myfile.md` - Start debugging from the "init" chunk
- `markdown-runner -B setup/1 myfile.md` - Start debugging from the second chunk in setup (index 1)
- `markdown-runner -B main/process myfile.md` - Start debugging from the "process" chunk in main stage
- `markdown-runner -B myfile@setup mydirectory/` - Start debugging from the "setup" stage in myfile.md only
- `markdown-runner -B myfile@setup/init mydirectory/` - Start debugging from the "init" chunk in myfile.md only
- `markdown-runner -s myfile@setup mydirectory/` - Start execution from the "setup" stage in myfile.md only

**Useful combinations:**
- `markdown-runner -D stage2/0 --dry-run myfile.md` - See what would be executed without running it
- `markdown-runner -B stage2/myChunk --verbose myfile.md` - Get detailed output during debugging

##### `"id":"someID"`

Give an ID to a chunk so that it can get referenced later on
Expand Down Expand Up @@ -674,17 +749,29 @@ go run main.go \
```
```shell markdown_runner

working command
SUCCESS: working command
failing command
ERROR: stdout:


working command


SUCCESS: working command


failing command


ERROR: stdout:
this has failed

stderr:

exit code:1
Successful teardown
SUCCESS: Successful teardown


Successful teardown


SUCCESS: Successful teardown
exit status 1
```

Expand Down
58 changes: 56 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package config
import (
"fmt"
"os"
"strings"

"github.com/pterm/pterm"
"github.com/spf13/pflag"
Expand All @@ -23,6 +24,12 @@ type Config struct {
Quiet bool
Recursive bool
StartFrom string
StartFromFile string
StartFromStage string
DebugFrom string
DebugFromFile string
DebugFromStage string
DebugFromChunk string
MinutesToTimeout int
UpdateFile bool
Verbose bool
Expand All @@ -47,7 +54,8 @@ Modes:

Execution Control:
-i, --interactive Prompt to press enter between each chunk
-s, --start-from string Start from a specific stage name
-s, --start-from string Start from a specific stage (stage or file@stage)
-B, --break-at string Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)
-t, --timeout int The timeout in minutes for every executed command (default 10)
-u, --update-files Update the chunk output section in the markdown files
--ignore-breakpoints Ignore the breakpoints
Expand Down Expand Up @@ -77,14 +85,60 @@ Help:
pflag.BoolVarP(&cfg.NoStyling, "no-styling", "", false, "Disable spinners in CLI")
pflag.BoolVarP(&cfg.Quiet, "quiet", "q", false, "Disable output")
pflag.BoolVarP(&cfg.Recursive, "recursive", "r", false, "Search for markdown files recursively")
pflag.StringVarP(&cfg.StartFrom, "start-from", "s", "", "Start from a specific stage name")
pflag.StringVarP(&cfg.StartFrom, "start-from", "s", "", "Start from a specific stage (stage or file@stage)")
pflag.StringVarP(&cfg.DebugFrom, "break-at", "B", "", "Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)")
pflag.IntVarP(&cfg.MinutesToTimeout, "timeout", "t", 10, "The timeout in minutes for every executed command")
pflag.BoolVarP(&cfg.UpdateFile, "update-files", "u", false, "Update the chunk output section in the markdown files")
pflag.BoolVarP(&cfg.Verbose, "verbose", "v", false, "Print more logs")
pflag.StringVar(&cfg.View, "view", "default", "UI to be used, can be 'default' or 'ci'")

pflag.Parse()

// Parse start-from format: stage or file@stage
if cfg.StartFrom != "" {
// Check for file@stage format
if strings.Contains(cfg.StartFrom, "@") {
atParts := strings.Split(cfg.StartFrom, "@")
if len(atParts) != 2 {
pterm.Fatal.Println("Invalid start-from format. Use 'stage' or 'file@stage'.")
}
cfg.StartFromFile = atParts[0]
cfg.StartFromStage = atParts[1]
} else {
cfg.StartFromStage = cfg.StartFrom
}
}

// Parse break-at format: stage, stage/chunkID, or file@stage/chunkID
if cfg.DebugFrom != "" {
var debugPart string

// Check for file@stage format
if strings.Contains(cfg.DebugFrom, "@") {
atParts := strings.Split(cfg.DebugFrom, "@")
if len(atParts) != 2 {
pterm.Fatal.Println("Invalid break-at format. Use 'stage', 'stage/chunkID', or 'file@stage/chunkID'.")
}
cfg.DebugFromFile = atParts[0]
debugPart = atParts[1]
} else {
debugPart = cfg.DebugFrom
}

// Parse stage/chunk part
parts := strings.Split(debugPart, "/")
if len(parts) == 1 {
// Just stage name
cfg.DebugFromStage = parts[0]
} else if len(parts) == 2 {
// Stage and chunk ID
cfg.DebugFromStage = parts[0]
cfg.DebugFromChunk = parts[1]
} else {
pterm.Fatal.Println("Invalid break-at format. Use 'stage', 'stage/chunkID', or 'file@stage/chunkID'.")
}
}

if len(pflag.Args()) > 1 {
pterm.Fatal.Println("Too many positional arguments, please specify only one directory.")
}
Expand Down
7 changes: 6 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func TestNewConfig(t *testing.T) {
recursive bool
timeout int
startFrom string
debugFrom string
markdownDir string
filter string
ignoreBreakpoints bool
Expand All @@ -29,13 +30,14 @@ func TestNewConfig(t *testing.T) {
}{
{
name: "long-form flags",
args: []string{"cmd", "--dry-run", "--interactive=true", "--verbose", "--recursive", "--timeout=5", "--start-from=stage2", "--filter=test.md", "--ignore-breakpoints", "--update-files", "--list", "--no-styling", "--quiet", "/tmp"},
args: []string{"cmd", "--dry-run", "--interactive=true", "--verbose", "--recursive", "--timeout=5", "--start-from=stage2", "--break-at=stage3", "--filter=test.md", "--ignore-breakpoints", "--update-files", "--list", "--no-styling", "--quiet", "/tmp"},
dryRun: true,
interactive: true,
verbose: true,
recursive: true,
timeout: 5,
startFrom: "stage2",
debugFrom: "stage3",
markdownDir: "/tmp",
filter: "test.md",
ignoreBreakpoints: true,
Expand All @@ -53,6 +55,7 @@ func TestNewConfig(t *testing.T) {
recursive: true,
timeout: 5,
startFrom: "stage2",
debugFrom: "",
markdownDir: "/tmp",
filter: "test.md",
ignoreBreakpoints: false,
Expand Down Expand Up @@ -80,6 +83,7 @@ func TestNewConfig(t *testing.T) {
assert.Equal(t, tc.recursive, cfg.Recursive)
assert.Equal(t, tc.timeout, cfg.MinutesToTimeout)
assert.Equal(t, tc.startFrom, cfg.StartFrom)
assert.Equal(t, tc.debugFrom, cfg.DebugFrom)
assert.Equal(t, tc.markdownDir, cfg.MarkdownDir)
assert.Equal(t, tc.filter, cfg.Filter)
assert.Equal(t, tc.ignoreBreakpoints, cfg.IgnoreBreakpoints)
Expand Down Expand Up @@ -107,6 +111,7 @@ func TestNewConfig(t *testing.T) {
assert.False(t, cfg.Verbose, "Expected Verbose to be false by default")
assert.Equal(t, 10, cfg.MinutesToTimeout, "Expected MinutesToTimeout to be 10 by default")
assert.Equal(t, "", cfg.StartFrom, "Expected StartFrom to be empty by default")
assert.Equal(t, "", cfg.DebugFrom, "Expected DebugFrom to be empty by default")
assert.Equal(t, "./", cfg.MarkdownDir, "Expected MarkdownDir to be './' by default")
assert.Equal(t, "", cfg.Filter, "Expected filter to be empty by default")
assert.False(t, cfg.IgnoreBreakpoints, "Expected IgnoreBreakpoints to be false by default")
Expand Down
Loading