Utils Package API
The pkg/utils
package provides utility functions for command execution with advanced features like timeout control, streaming output, and batch operations.
Executor
The executor component provides flexible command execution capabilities with support for various execution modes and advanced features.
NewExecutor
func NewExecutor() *Executor
Creates a new command executor with default configuration.
Example:
executor := utils.NewExecutor()
Basic Execution
ExecuteSimple
func (e *Executor) ExecuteSimple(ctx context.Context, command string, args ...string) (*ExecuteResult, error)
Executes a command with basic configuration and returns the result.
Parameters:
ctx
(context.Context): Context for cancellation and timeoutcommand
(string): Command to executeargs
(...string): Command arguments
Returns:
*ExecuteResult
: Execution resulterror
: Error if execution fails
Example:
ctx := context.Background()
result, err := executor.ExecuteSimple(ctx, "npm", "--version")
if err != nil {
log.Fatal(err)
}
fmt.Printf("npm version: %s\n", strings.TrimSpace(result.Stdout))
fmt.Printf("Exit code: %d\n", result.ExitCode)
fmt.Printf("Duration: %v\n", result.Duration)
ExecuteWithInput
func (e *Executor) ExecuteWithInput(ctx context.Context, input string, command string, args ...string) (*ExecuteResult, error)
Executes a command with input data provided to stdin.
Parameters:
ctx
(context.Context): Context for cancellation and timeoutinput
(string): Input data to send to command's stdincommand
(string): Command to executeargs
(...string): Command arguments
Example:
ctx := context.Background()
input := "console.log('Hello, World!');"
result, err := executor.ExecuteWithInput(ctx, input, "node", "-e", "-")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Output: %s\n", result.Stdout)
Advanced Execution
Execute
func (e *Executor) Execute(ctx context.Context, options ExecuteOptions) (*ExecuteResult, error)
Executes a command with advanced configuration options.
Parameters:
ctx
(context.Context): Context for cancellation and timeoutoptions
(ExecuteOptions): Execution configuration
Returns:
*ExecuteResult
: Execution resulterror
: Error if execution fails
ExecuteOptions
type ExecuteOptions struct {
Command string `json:"command"`
Args []string `json:"args,omitempty"`
WorkingDir string `json:"working_dir,omitempty"`
Env map[string]string `json:"env,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
Input string `json:"input,omitempty"`
CaptureOutput bool `json:"capture_output"`
StreamOutput bool `json:"stream_output"`
OutputCallback func(string) `json:"-"`
}
Example:
ctx := context.Background()
options := utils.ExecuteOptions{
Command: "npm",
Args: []string{"install", "lodash"},
WorkingDir: "/path/to/project",
Timeout: 5 * time.Minute,
CaptureOutput: true,
StreamOutput: true,
Env: map[string]string{
"NODE_ENV": "development",
},
OutputCallback: func(output string) {
fmt.Printf("npm: %s", output)
},
}
result, err := executor.Execute(ctx, options)
if err != nil {
log.Fatal(err)
}
if result.Success {
fmt.Println("Package installed successfully")
} else {
fmt.Printf("Installation failed: %s\n", result.Stderr)
}
Streaming Execution
ExecuteStream
func (e *Executor) ExecuteStream(ctx context.Context, callback func(string), command string, args ...string) (*ExecuteResult, error)
Executes a command with real-time output streaming.
Parameters:
ctx
(context.Context): Context for cancellation and timeoutcallback
(func(string)): Function called for each line of outputcommand
(string): Command to executeargs
(...string): Command arguments
Example:
ctx := context.Background()
callback := func(output string) {
fmt.Printf("Real-time output: %s", output)
}
result, err := executor.ExecuteStream(ctx, callback, "npm", "install", "--verbose")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Command completed with exit code: %d\n", result.ExitCode)
Execution Results
ExecuteResult
type ExecuteResult struct {
Success bool `json:"success"`
ExitCode int `json:"exit_code"`
Stdout string `json:"stdout,omitempty"`
Stderr string `json:"stderr,omitempty"`
Duration time.Duration `json:"duration"`
Cancelled bool `json:"cancelled"`
Error error `json:"-"`
}
Fields:
Success
: Whether the command executed successfully (exit code 0)ExitCode
: Command exit codeStdout
: Standard output contentStderr
: Standard error contentDuration
: Execution durationCancelled
: Whether execution was cancelledError
: Execution error (if any)
Batch Execution
NewBatchExecutor
func NewBatchExecutor(maxConcurrency int) *BatchExecutor
Creates a new batch executor with specified maximum concurrency.
Parameters:
maxConcurrency
(int): Maximum number of concurrent executions
ExecuteBatch
func (be *BatchExecutor) ExecuteBatch(ctx context.Context, options BatchOptions) (*BatchResult, error)
Executes multiple commands concurrently with specified options.
Parameters:
ctx
(context.Context): Context for cancellation and timeoutoptions
(BatchOptions): Batch execution configuration
BatchOptions
type BatchOptions struct {
Commands []ExecuteOptions `json:"commands"`
StopOnError bool `json:"stop_on_error"`
MaxConcurrency int `json:"max_concurrency,omitempty"`
}
BatchResult
type BatchResult struct {
Results []*ExecuteResult `json:"results"`
Success bool `json:"success"`
TotalTime time.Duration `json:"total_time"`
FailedCount int `json:"failed_count"`
}
Example:
batchExecutor := utils.NewBatchExecutor(3)
ctx := context.Background()
commands := []utils.ExecuteOptions{
{
Command: "npm",
Args: []string{"install", "lodash"},
WorkingDir: "/project1",
CaptureOutput: true,
},
{
Command: "npm",
Args: []string{"install", "axios"},
WorkingDir: "/project2",
CaptureOutput: true,
},
{
Command: "npm",
Args: []string{"test"},
WorkingDir: "/project3",
CaptureOutput: true,
},
}
options := utils.BatchOptions{
Commands: commands,
StopOnError: false,
MaxConcurrency: 2,
}
result, err := batchExecutor.ExecuteBatch(ctx, options)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Batch execution completed in %v\n", result.TotalTime)
fmt.Printf("Success: %v, Failed: %d/%d\n", result.Success, result.FailedCount, len(result.Results))
for i, res := range result.Results {
if res.Success {
fmt.Printf("Command %d: SUCCESS\n", i+1)
} else {
fmt.Printf("Command %d: FAILED (exit code: %d)\n", i+1, res.ExitCode)
}
}
Configuration
SetDefaultTimeout
func (e *Executor) SetDefaultTimeout(timeout time.Duration)
Sets the default timeout for command execution.
SetDefaultWorkingDir
func (e *Executor) SetDefaultWorkingDir(dir string)
Sets the default working directory for command execution.
SetDefaultEnv
func (e *Executor) SetDefaultEnv(env map[string]string)
Sets the default environment variables for command execution.
Example:
executor := utils.NewExecutor()
// Configure defaults
executor.SetDefaultTimeout(30 * time.Second)
executor.SetDefaultWorkingDir("/home/user/projects")
executor.SetDefaultEnv(map[string]string{
"NODE_ENV": "production",
"PATH": "/usr/local/bin:/usr/bin:/bin",
})
// These defaults will be used when not specified in ExecuteOptions
Process Management
KillProcess
func (e *Executor) KillProcess(cmd *exec.Cmd) error
Kills a running process.
Parameters:
cmd
(*exec.Cmd): Command process to kill
Example:
// This is typically used internally, but can be called manually if needed
err := executor.KillProcess(cmd)
if err != nil {
log.Printf("Failed to kill process: %v", err)
}
Error Handling
The utils package provides specific error constants:
var (
ErrCommandTimeout = errors.New("command execution timeout")
ErrCommandFailed = errors.New("command execution failed")
ErrInvalidCommand = errors.New("invalid command")
)
Example:
result, err := executor.ExecuteSimple(ctx, "invalid-command")
if err != nil {
if errors.Is(err, utils.ErrCommandTimeout) {
fmt.Println("Command timed out")
} else if errors.Is(err, utils.ErrCommandFailed) {
fmt.Println("Command failed")
} else if errors.Is(err, utils.ErrInvalidCommand) {
fmt.Println("Invalid command")
} else {
fmt.Printf("Other error: %v\n", err)
}
}
Best Practices
- Use contexts: Always pass appropriate contexts for timeout and cancellation control
- Handle timeouts: Set reasonable timeouts for long-running commands
- Stream output: Use streaming for commands with significant output
- Batch operations: Use batch executor for multiple related commands
- Error handling: Check specific error types for better error handling
- Working directories: Set appropriate working directories for project-specific commands
- Environment variables: Configure environment variables as needed
- Resource cleanup: Ensure proper cleanup of resources and processes
Integration Example
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/scagogogo/go-npm-sdk/pkg/utils"
)
func main() {
executor := utils.NewExecutor()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// Configure executor
executor.SetDefaultTimeout(30 * time.Second)
executor.SetDefaultWorkingDir("/path/to/project")
// Execute npm install with streaming output
options := utils.ExecuteOptions{
Command: "npm",
Args: []string{"install"},
CaptureOutput: true,
StreamOutput: true,
OutputCallback: func(output string) {
fmt.Printf("npm: %s", output)
},
}
result, err := executor.Execute(ctx, options)
if err != nil {
log.Fatal(err)
}
if result.Success {
fmt.Printf("Installation completed in %v\n", result.Duration)
} else {
fmt.Printf("Installation failed: %s\n", result.Stderr)
}
}