Skip to content

Commit 70b9cc4

Browse files
committed
[#8] Add -D flag for debugging from specific stage/chunk
The -D flag allows users to start debugging from: - A specific stage: -D stage2 - A specific chunk by ID: -D stage2/myChunkID - A specific chunk by index: -D stage2/0 - A specific file when running directories: -D myfile@stage2/chunk This enhances the existing -s flag by adding chunk-level granularity and enables targeted debugging workflows.
1 parent ac1e292 commit 70b9cc4

5 files changed

Lines changed: 305 additions & 25 deletions

File tree

README.md

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ Modes:
4545

4646
Execution Control:
4747
-i, --interactive Prompt to press enter between each chunk
48-
-s, --start-from string Start from a specific stage name
48+
-s, --start-from string Start from a specific stage (stage or file@stage)
49+
-B, --break-at string Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)
4950
-t, --timeout int The timeout in minutes for every executed command (default 10)
5051
-u, --update-files Update the chunk output section in the markdown files
5152
--ignore-breakpoints Ignore the breakpoints
@@ -81,12 +82,12 @@ go test ./...
8182
? github.com/arkmq-org/markdown-runner/pterm [no test files]
8283
? github.com/arkmq-org/markdown-runner/runnercontext [no test files]
8384
? github.com/arkmq-org/markdown-runner/view [no test files]
84-
ok github.com/arkmq-org/markdown-runner 0.005s
85-
ok github.com/arkmq-org/markdown-runner/chunk (cached)
85+
ok github.com/arkmq-org/markdown-runner 0.016s
86+
ok github.com/arkmq-org/markdown-runner/chunk 0.127s
8687
ok github.com/arkmq-org/markdown-runner/config (cached)
8788
ok github.com/arkmq-org/markdown-runner/parser (cached)
88-
ok github.com/arkmq-org/markdown-runner/runner (cached)
89-
ok github.com/arkmq-org/markdown-runner/stage (cached)
89+
ok github.com/arkmq-org/markdown-runner/runner 0.041s
90+
ok github.com/arkmq-org/markdown-runner/stage 0.126s
9091
```
9192

9293
Integration tests can be run with:
@@ -101,11 +102,13 @@ Integration tests can be run with:
101102
Running test #3: Test case parallel... ✅
102103
Running test #4: Schema error test (schema_error) should fail as expected... ✅
103104
Running test #5: Teardown test (teardown) should execute teardown... ✅
104-
Running test #6: Test case writer... ✅
105-
Running test #7: Recursive test... ✅
106-
Running test #8: Recursive test with file filter... ✅
105+
Running test #6: Verbose set -x test (verbose_set_x) should show command tracing with -v... ✅
106+
Running test #7: Non-verbose test (verbose_set_x) should not show command tracing without -v... ✅
107+
Running test #8: Test case writer... ✅
108+
Running test #9: Recursive test... ✅
109+
Running test #10: Recursive test with file filter... ✅
107110
\n--- Test Summary ---
108-
[32mAll 8 tests passed![0m
111+
[32mAll 10 tests passed![0m
109112
--- Cleaning up ---
110113
Cleanup complete.
111114
```
@@ -385,6 +388,78 @@ This works only if:
385388
Enters interactive mode when the chunk is started. Useful for debugging
386389
purposes. Better to use alongside `--verbose`
387390
391+
#### Debugging and Interactive Execution
392+
393+
The markdown-runner provides several ways to debug and interactively control the execution of your markdown files:
394+
395+
##### Using `--break-at` flag
396+
397+
The `--break-at` (`-B`) flag allows you to start debugging from a specific stage or even a specific chunk within a stage:
398+
399+
**Debug from a stage:**
400+
```bash
401+
markdown-runner --break-at stage2 myfile.md
402+
```
403+
404+
**Debug from a specific chunk by ID:**
405+
```bash
406+
markdown-runner --break-at stage2/myChunkID myfile.md
407+
```
408+
409+
**Debug from a specific chunk by index (0-based):**
410+
```bash
411+
markdown-runner --break-at stage2/0 myfile.md
412+
```
413+
414+
**Debug from a specific file when running a directory:**
415+
```bash
416+
markdown-runner --break-at myfile@stage2 mydirectory/
417+
```
418+
419+
When debugging is enabled, the runner will prompt you for each chunk:
420+
- `yes` - Execute this chunk and continue prompting for the next
421+
- `no` - Skip this chunk and continue prompting for the next
422+
- `all` - Execute this chunk and all remaining chunks without prompting
423+
- `cancel` - Stop execution immediately
424+
425+
**Examples:**
426+
427+
Given a markdown file with chunks:
428+
````markdown
429+
```bash {"stage":"setup", "id":"init", "label": "Initialize"}
430+
echo "Setting up..."
431+
```
432+
```shell markdown_runner
433+
Setting up...
434+
```
435+
436+
```bash {"stage":"setup", "label": "Configure"}
437+
echo "Configuring..."
438+
```
439+
```shell markdown_runner
440+
Configuring...
441+
```
442+
443+
```bash {"stage":"main", "id":"process", "label": "Process data"}
444+
echo "Processing..."
445+
```
446+
```shell markdown_runner
447+
Processing...
448+
```
449+
````
450+
451+
- `markdown-runner -B setup myfile.md` - Start debugging from the "setup" stage
452+
- `markdown-runner -B setup/init myfile.md` - Start debugging from the "init" chunk
453+
- `markdown-runner -B setup/1 myfile.md` - Start debugging from the second chunk in setup (index 1)
454+
- `markdown-runner -B main/process myfile.md` - Start debugging from the "process" chunk in main stage
455+
- `markdown-runner -B myfile@setup mydirectory/` - Start debugging from the "setup" stage in myfile.md only
456+
- `markdown-runner -B myfile@setup/init mydirectory/` - Start debugging from the "init" chunk in myfile.md only
457+
- `markdown-runner -s myfile@setup mydirectory/` - Start execution from the "setup" stage in myfile.md only
458+
459+
**Useful combinations:**
460+
- `markdown-runner -D stage2/0 --dry-run myfile.md` - See what would be executed without running it
461+
- `markdown-runner -B stage2/myChunk --verbose myfile.md` - Get detailed output during debugging
462+
388463
##### `"id":"someID"`
389464
390465
Give an ID to a chunk so that it can get referenced later on
@@ -674,17 +749,29 @@ go run main.go \
674749
```
675750
```shell markdown_runner
676751
677-
working command
678-
SUCCESS: working command
679-
failing command
680-
ERROR: stdout:
752+
753+
754+
working command
755+
756+
757+
SUCCESS: working command
758+
759+
760+
failing command
761+
762+
763+
ERROR: stdout:
681764
this has failed
682765
683766
stderr:
684767
685768
exit code:1
686-
Successful teardown
687-
SUCCESS: Successful teardown
769+
770+
771+
Successful teardown
772+
773+
774+
SUCCESS: Successful teardown
688775
exit status 1
689776
```
690777

config/config.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package config
55
import (
66
"fmt"
77
"os"
8+
"strings"
89

910
"github.com/pterm/pterm"
1011
"github.com/spf13/pflag"
@@ -23,6 +24,12 @@ type Config struct {
2324
Quiet bool
2425
Recursive bool
2526
StartFrom string
27+
StartFromFile string
28+
StartFromStage string
29+
DebugFrom string
30+
DebugFromFile string
31+
DebugFromStage string
32+
DebugFromChunk string
2633
MinutesToTimeout int
2734
UpdateFile bool
2835
Verbose bool
@@ -47,7 +54,8 @@ Modes:
4754
4855
Execution Control:
4956
-i, --interactive Prompt to press enter between each chunk
50-
-s, --start-from string Start from a specific stage name
57+
-s, --start-from string Start from a specific stage (stage or file@stage)
58+
-B, --break-at string Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)
5159
-t, --timeout int The timeout in minutes for every executed command (default 10)
5260
-u, --update-files Update the chunk output section in the markdown files
5361
--ignore-breakpoints Ignore the breakpoints
@@ -77,14 +85,60 @@ Help:
7785
pflag.BoolVarP(&cfg.NoStyling, "no-styling", "", false, "Disable spinners in CLI")
7886
pflag.BoolVarP(&cfg.Quiet, "quiet", "q", false, "Disable output")
7987
pflag.BoolVarP(&cfg.Recursive, "recursive", "r", false, "Search for markdown files recursively")
80-
pflag.StringVarP(&cfg.StartFrom, "start-from", "s", "", "Start from a specific stage name")
88+
pflag.StringVarP(&cfg.StartFrom, "start-from", "s", "", "Start from a specific stage (stage or file@stage)")
89+
pflag.StringVarP(&cfg.DebugFrom, "break-at", "B", "", "Start debugging from a specific stage or chunk (stage, stage/chunkID, or file@stage/chunkID)")
8190
pflag.IntVarP(&cfg.MinutesToTimeout, "timeout", "t", 10, "The timeout in minutes for every executed command")
8291
pflag.BoolVarP(&cfg.UpdateFile, "update-files", "u", false, "Update the chunk output section in the markdown files")
8392
pflag.BoolVarP(&cfg.Verbose, "verbose", "v", false, "Print more logs")
8493
pflag.StringVar(&cfg.View, "view", "default", "UI to be used, can be 'default' or 'ci'")
8594

8695
pflag.Parse()
8796

97+
// Parse start-from format: stage or file@stage
98+
if cfg.StartFrom != "" {
99+
// Check for file@stage format
100+
if strings.Contains(cfg.StartFrom, "@") {
101+
atParts := strings.Split(cfg.StartFrom, "@")
102+
if len(atParts) != 2 {
103+
pterm.Fatal.Println("Invalid start-from format. Use 'stage' or 'file@stage'.")
104+
}
105+
cfg.StartFromFile = atParts[0]
106+
cfg.StartFromStage = atParts[1]
107+
} else {
108+
cfg.StartFromStage = cfg.StartFrom
109+
}
110+
}
111+
112+
// Parse break-at format: stage, stage/chunkID, or file@stage/chunkID
113+
if cfg.DebugFrom != "" {
114+
var debugPart string
115+
116+
// Check for file@stage format
117+
if strings.Contains(cfg.DebugFrom, "@") {
118+
atParts := strings.Split(cfg.DebugFrom, "@")
119+
if len(atParts) != 2 {
120+
pterm.Fatal.Println("Invalid break-at format. Use 'stage', 'stage/chunkID', or 'file@stage/chunkID'.")
121+
}
122+
cfg.DebugFromFile = atParts[0]
123+
debugPart = atParts[1]
124+
} else {
125+
debugPart = cfg.DebugFrom
126+
}
127+
128+
// Parse stage/chunk part
129+
parts := strings.Split(debugPart, "/")
130+
if len(parts) == 1 {
131+
// Just stage name
132+
cfg.DebugFromStage = parts[0]
133+
} else if len(parts) == 2 {
134+
// Stage and chunk ID
135+
cfg.DebugFromStage = parts[0]
136+
cfg.DebugFromChunk = parts[1]
137+
} else {
138+
pterm.Fatal.Println("Invalid break-at format. Use 'stage', 'stage/chunkID', or 'file@stage/chunkID'.")
139+
}
140+
}
141+
88142
if len(pflag.Args()) > 1 {
89143
pterm.Fatal.Println("Too many positional arguments, please specify only one directory.")
90144
}

config/config_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func TestNewConfig(t *testing.T) {
1919
recursive bool
2020
timeout int
2121
startFrom string
22+
debugFrom string
2223
markdownDir string
2324
filter string
2425
ignoreBreakpoints bool
@@ -29,13 +30,14 @@ func TestNewConfig(t *testing.T) {
2930
}{
3031
{
3132
name: "long-form flags",
32-
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"},
33+
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"},
3334
dryRun: true,
3435
interactive: true,
3536
verbose: true,
3637
recursive: true,
3738
timeout: 5,
3839
startFrom: "stage2",
40+
debugFrom: "stage3",
3941
markdownDir: "/tmp",
4042
filter: "test.md",
4143
ignoreBreakpoints: true,
@@ -53,6 +55,7 @@ func TestNewConfig(t *testing.T) {
5355
recursive: true,
5456
timeout: 5,
5557
startFrom: "stage2",
58+
debugFrom: "",
5659
markdownDir: "/tmp",
5760
filter: "test.md",
5861
ignoreBreakpoints: false,
@@ -80,6 +83,7 @@ func TestNewConfig(t *testing.T) {
8083
assert.Equal(t, tc.recursive, cfg.Recursive)
8184
assert.Equal(t, tc.timeout, cfg.MinutesToTimeout)
8285
assert.Equal(t, tc.startFrom, cfg.StartFrom)
86+
assert.Equal(t, tc.debugFrom, cfg.DebugFrom)
8387
assert.Equal(t, tc.markdownDir, cfg.MarkdownDir)
8488
assert.Equal(t, tc.filter, cfg.Filter)
8589
assert.Equal(t, tc.ignoreBreakpoints, cfg.IgnoreBreakpoints)
@@ -107,6 +111,7 @@ func TestNewConfig(t *testing.T) {
107111
assert.False(t, cfg.Verbose, "Expected Verbose to be false by default")
108112
assert.Equal(t, 10, cfg.MinutesToTimeout, "Expected MinutesToTimeout to be 10 by default")
109113
assert.Equal(t, "", cfg.StartFrom, "Expected StartFrom to be empty by default")
114+
assert.Equal(t, "", cfg.DebugFrom, "Expected DebugFrom to be empty by default")
110115
assert.Equal(t, "./", cfg.MarkdownDir, "Expected MarkdownDir to be './' by default")
111116
assert.Equal(t, "", cfg.Filter, "Expected filter to be empty by default")
112117
assert.False(t, cfg.IgnoreBreakpoints, "Expected IgnoreBreakpoints to be false by default")

0 commit comments

Comments
 (0)