← back to Cli Printing Press
refactor(cli): split verify exec helpers (#321)
18cf50f130ea3876e4cf17a197ffd5e32b9c4caf · 2026-04-26 18:50:09 -0700 · Trevin Chow
Move verify build and subprocess helpers into runtime_exec.go so runtime.go stays focused on verification orchestration. Collapse runCLI onto runCLIWithOutput to keep command execution semantics in one place while preserving caller behavior.
Verification:
- go test ./internal/pipeline
- go test ./...
- go vet ./...
- golangci-lint run ./...
- scripts/golden.sh verify
- go build -o ./printing-press ./cmd/printing-press
Files touched
M internal/pipeline/runtime.goA internal/pipeline/runtime_exec.go
Diff
commit 18cf50f130ea3876e4cf17a197ffd5e32b9c4caf
Author: Trevin Chow <trevin@trevinchow.com>
Date: Sun Apr 26 18:50:09 2026 -0700
refactor(cli): split verify exec helpers (#321)
Move verify build and subprocess helpers into runtime_exec.go so runtime.go stays focused on verification orchestration. Collapse runCLI onto runCLIWithOutput to keep command execution semantics in one place while preserving caller behavior.
Verification:
- go test ./internal/pipeline
- go test ./...
- go vet ./...
- golangci-lint run ./...
- scripts/golden.sh verify
- go build -o ./printing-press ./cmd/printing-press
---
internal/pipeline/runtime.go | 102 --------------------------------------
internal/pipeline/runtime_exec.go | 101 +++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+), 102 deletions(-)
diff --git a/internal/pipeline/runtime.go b/internal/pipeline/runtime.go
index fd13c759..a84a91d3 100644
--- a/internal/pipeline/runtime.go
+++ b/internal/pipeline/runtime.go
@@ -11,7 +11,6 @@ import (
"path/filepath"
"regexp"
"slices"
- "sort"
"strings"
"time"
@@ -195,7 +194,6 @@ func RunVerify(cfg VerifyConfig) (*VerifyReport, error) {
report.Results = append(report.Results, result)
}
- // 8. Data pipeline test
report.DataPipeline, report.DataPipelineDetail = runDataPipelineTest(binaryPath, report.Mode, buildEnv)
report.Freshness = runFreshnessContractTest(cfg.Dir)
@@ -216,78 +214,6 @@ func RunVerify(cfg VerifyConfig) (*VerifyReport, error) {
return report, nil
}
-// buildCLI compiles the generated CLI and returns the binary path.
-func buildCLI(dir string) (string, error) {
- name := filepath.Base(dir)
- binaryPath, err := filepath.Abs(filepath.Join(dir, name))
- if err != nil {
- return "", fmt.Errorf("resolving binary path: %w", err)
- }
- cmdDir, err := findCLICommandDir(dir)
- if err != nil {
- return "", err
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
- defer cancel()
- cmd := exec.CommandContext(ctx, "go", "build", "-o", binaryPath, "./"+filepath.Base(cmdDir))
- cmd.Dir = filepath.Dir(cmdDir)
- cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
- if out, err := cmd.CombinedOutput(); err != nil {
- return "", fmt.Errorf("go build: %s\n%s", err, string(out))
- }
- return binaryPath, nil
-}
-
-func findCLICommandDir(dir string) (string, error) {
- name := filepath.Base(dir)
- apiName := naming.TrimCLISuffix(name)
- candidates := []string{
- filepath.Join(dir, "cmd", name),
- filepath.Join(dir, "cmd", naming.CLI(apiName)),
- filepath.Join(dir, "cmd", naming.LegacyCLI(apiName)),
- filepath.Join(dir, "cmd", apiName),
- }
-
- for _, candidate := range candidates {
- info, err := os.Stat(candidate)
- if err == nil && info.IsDir() {
- return candidate, nil
- }
- if err != nil && !os.IsNotExist(err) {
- return "", fmt.Errorf("stat %s: %w", candidate, err)
- }
- }
-
- entries, err := os.ReadDir(filepath.Join(dir, "cmd"))
- if err != nil {
- return "", fmt.Errorf("reading cmd directory: %w", err)
- }
-
- var cliEntries []string
- var dirEntries []string
- for _, entry := range entries {
- if !entry.IsDir() {
- continue
- }
- dirEntries = append(dirEntries, entry.Name())
- if naming.IsCLIDirName(entry.Name()) {
- cliEntries = append(cliEntries, entry.Name())
- }
- }
-
- sort.Strings(cliEntries)
- if len(cliEntries) == 1 {
- return filepath.Join(dir, "cmd", cliEntries[0]), nil
- }
-
- if len(dirEntries) == 1 {
- return filepath.Join(dir, "cmd", dirEntries[0]), nil
- }
-
- return "", fmt.Errorf("cannot find CLI cmd entry point in %s", dir)
-}
-
// discoverCommands finds all registered commands. It first tries to parse the
// binary's --help output for ground-truth command names. If that fails (binary
// missing, crash, timeout), it falls back to regex extraction from root.go with
@@ -812,34 +738,6 @@ func runDataPipelineTest(binary, mode string, envFn func() []string) (bool, stri
return true, fmt.Sprintf("PASS: %d domain tables created", len(tables))
}
-// runCLI executes the CLI binary with the given args and returns any error.
-func runCLI(binary string, args []string, env []string, timeout time.Duration) error {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
-
- cmd := exec.CommandContext(ctx, binary, args...)
- cmd.Env = env
- out, err := cmd.CombinedOutput()
- if err != nil {
- return fmt.Errorf("exit %v: %s", err, string(out))
- }
- return nil
-}
-
-// runCLIWithOutput executes the CLI binary and returns its combined output.
-func runCLIWithOutput(binary string, args []string, env []string, timeout time.Duration) ([]byte, error) {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
-
- cmd := exec.CommandContext(ctx, binary, args...)
- cmd.Env = env
- out, err := cmd.CombinedOutput()
- if err != nil {
- return out, fmt.Errorf("exit %v: %s", err, string(out))
- }
- return out, nil
-}
-
// parseSQLOutput extracts non-empty, non-header lines from sql command output.
func parseSQLOutput(out []byte) []string {
var tables []string
diff --git a/internal/pipeline/runtime_exec.go b/internal/pipeline/runtime_exec.go
new file mode 100644
index 00000000..a3cbd9db
--- /dev/null
+++ b/internal/pipeline/runtime_exec.go
@@ -0,0 +1,101 @@
+package pipeline
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "sort"
+ "time"
+
+ "github.com/mvanhorn/cli-printing-press/v2/internal/naming"
+)
+
+func buildCLI(dir string) (string, error) {
+ binaryPath, err := filepath.Abs(filepath.Join(dir, filepath.Base(dir)))
+ if err != nil {
+ return "", fmt.Errorf("resolving binary path: %w", err)
+ }
+ cmdDir, err := findCLICommandDir(dir)
+ if err != nil {
+ return "", err
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+ defer cancel()
+ cmd := exec.CommandContext(ctx, "go", "build", "-o", binaryPath, "./"+filepath.Base(cmdDir))
+ cmd.Dir = filepath.Dir(cmdDir)
+ cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ return "", fmt.Errorf("go build: %s\n%s", err, string(out))
+ }
+ return binaryPath, nil
+}
+
+func findCLICommandDir(dir string) (string, error) {
+ name := filepath.Base(dir)
+ apiName := naming.TrimCLISuffix(name)
+ candidates := []string{
+ filepath.Join(dir, "cmd", name),
+ filepath.Join(dir, "cmd", naming.CLI(apiName)),
+ filepath.Join(dir, "cmd", naming.LegacyCLI(apiName)),
+ filepath.Join(dir, "cmd", apiName),
+ }
+
+ for _, candidate := range candidates {
+ info, err := os.Stat(candidate)
+ if err == nil && info.IsDir() {
+ return candidate, nil
+ }
+ if err != nil && !os.IsNotExist(err) {
+ return "", fmt.Errorf("stat %s: %w", candidate, err)
+ }
+ }
+
+ entries, err := os.ReadDir(filepath.Join(dir, "cmd"))
+ if err != nil {
+ return "", fmt.Errorf("reading cmd directory: %w", err)
+ }
+
+ var cliEntries []string
+ var dirEntries []string
+ for _, entry := range entries {
+ if !entry.IsDir() {
+ continue
+ }
+ dirEntries = append(dirEntries, entry.Name())
+ if naming.IsCLIDirName(entry.Name()) {
+ cliEntries = append(cliEntries, entry.Name())
+ }
+ }
+
+ sort.Strings(cliEntries)
+ if len(cliEntries) == 1 {
+ return filepath.Join(dir, "cmd", cliEntries[0]), nil
+ }
+
+ if len(dirEntries) == 1 {
+ return filepath.Join(dir, "cmd", dirEntries[0]), nil
+ }
+
+ return "", fmt.Errorf("cannot find CLI cmd entry point in %s", dir)
+}
+
+func runCLI(binary string, args []string, env []string, timeout time.Duration) error {
+ _, err := runCLIWithOutput(binary, args, env, timeout)
+ return err
+}
+
+func runCLIWithOutput(binary string, args []string, env []string, timeout time.Duration) ([]byte, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+
+ cmd := exec.CommandContext(ctx, binary, args...)
+ cmd.Env = env
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return out, fmt.Errorf("exit %v: %s", err, string(out))
+ }
+ return out, nil
+}
← 7e733aa7 refactor(cli): split structural verify runtime (#320)
·
back to Cli Printing Press
·
refactor(cli): split verify command helpers (#322) b1001491 →