Go API

Embed Shai inside Go applications for programmatic sandbox control.

Installation

1go get github.com/colony-2/shai/pkg/shai

Quick Start

 1package main
 2
 3import (
 4    "context"
 5    "log"
 6
 7    shai "github.com/colony-2/shai/pkg/shai"
 8)
 9
10func main() {
11    ctx := context.Background()
12
13    // Load config from workspace
14    cfg, err := shai.LoadSandboxConfig("/path/to/workspace",
15        shai.WithReadWritePaths([]string{"src/components"}),
16        shai.WithResourceSets([]string{"frontend-dev"}),
17    )
18    if err != nil {
19        log.Fatal(err)
20    }
21
22    // Set command to run
23    cfg.PostSetupExec = &shai.SandboxExec{
24        Command: []string{"npm", "test"},
25        Workdir: "/src",
26        UseTTY:  false,
27    }
28
29    // Create and run sandbox
30    sandbox, err := shai.NewSandbox(cfg)
31    if err != nil {
32        log.Fatal(err)
33    }
34    defer sandbox.Close()
35
36    if err := sandbox.Run(ctx); err != nil {
37        log.Fatal(err)
38    }
39}

Core Types

SandboxConfig

Configuration for the sandbox:

 1type SandboxConfig struct {
 2    WorkspacePath   string              // Path to workspace
 3    ConfigPath      string              // Path to .shai/config.yaml
 4    ReadWritePaths  []string            // Paths to mount as writable
 5    ResourceSets    []string            // Additional resource sets
 6    TemplateVars    map[string]string   // Template variables
 7    PostSetupExec   *SandboxExec        // Command to run
 8    ImageOverride   string              // Override image
 9    UserOverride    string              // Override user
10    Verbose         bool                // Enable verbose output
11}

SandboxExec

Command execution specification:

1type SandboxExec struct {
2    Command []string          // Command and args
3    Env     map[string]string // Additional env vars
4    Workdir string            // Working directory
5    UseTTY  bool              // Allocate TTY
6}

Sandbox Interface

1type Sandbox interface {
2    Run(ctx context.Context) error        // Run and wait
3    Start(ctx context.Context) (SandboxSession, error)  // Start without waiting
4    Close() error                          // Cleanup
5}

SandboxSession

Long-running sandbox session:

1type SandboxSession interface {
2    ContainerID() string               // Get container ID
3    Wait() error                       // Wait for completion
4    Stop(timeout time.Duration) error  // Stop gracefully
5    Close() error                      // Cleanup
6}

Common Patterns

Running Tests

 1cfg, _ := shai.LoadSandboxConfig(workspacePath,
 2    shai.WithReadWritePaths([]string{"coverage"}),
 3)
 4
 5cfg.PostSetupExec = &shai.SandboxExec{
 6    Command: []string{"go", "test", "./..."},
 7    UseTTY:  false,
 8}
 9
10sandbox, _ := shai.NewSandbox(cfg)
11defer sandbox.Close()
12
13if err := sandbox.Run(ctx); err != nil {
14    // Tests failed
15}

Long-Running Process

 1sandbox, _ := shai.NewSandbox(cfg)
 2defer sandbox.Close()
 3
 4// Start without waiting
 5session, err := sandbox.Start(ctx)
 6if err != nil {
 7    log.Fatal(err)
 8}
 9defer session.Close()
10
11log.Printf("Container: %s", session.ContainerID())
12
13// Do other work...
14
15// Wait for completion
16if err := session.Wait(); err != nil {
17    log.Fatal(err)
18}

Multiple Sandboxes

 1// Run multiple sandboxes concurrently
 2var wg sync.WaitGroup
 3
 4for _, component := range components {
 5    wg.Add(1)
 6    go func(comp string) {
 7        defer wg.Done()
 8
 9        cfg, _ := shai.LoadSandboxConfig(workspacePath,
10            shai.WithReadWritePaths([]string{comp}),
11        )
12
13        sandbox, _ := shai.NewSandbox(cfg)
14        defer sandbox.Close()
15
16        sandbox.Run(ctx)
17    }(component)
18}
19
20wg.Wait()

With Template Variables

1cfg, _ := shai.LoadSandboxConfig(workspacePath,
2    shai.WithTemplateVars(map[string]string{
3        "ENV":    "staging",
4        "REGION": "us-east-1",
5    }),
6)

Error Handling

 1cfg, err := shai.LoadSandboxConfig(workspacePath)
 2if err != nil {
 3    // Config loading failed
 4    // - Config file invalid
 5    // - Template vars missing
 6    // - Workspace doesn't exist
 7    log.Fatal(err)
 8}
 9
10sandbox, err := shai.NewSandbox(cfg)
11if err != nil {
12    // Sandbox creation failed
13    // - Docker not available
14    // - Image pull failed
15    // - Invalid configuration
16    log.Fatal(err)
17}
18
19if err := sandbox.Run(ctx); err != nil {
20    // Execution failed
21    // - Command failed
22    // - Container crashed
23    // - Network error
24    log.Fatal(err)
25}

Testing with Shai

Use Shai in integration tests:

 1func TestMyApp(t *testing.T) {
 2    ctx := context.Background()
 3
 4    cfg, err := shai.LoadSandboxConfig(".",
 5        shai.WithReadWritePaths([]string{"tmp"}),
 6    )
 7    require.NoError(t, err)
 8
 9    cfg.PostSetupExec = &shai.SandboxExec{
10        Command: []string{"./test-script.sh"},
11    }
12
13    sandbox, err := shai.NewSandbox(cfg)
14    require.NoError(t, err)
15    defer sandbox.Close()
16
17    err = sandbox.Run(ctx)
18    assert.NoError(t, err)
19}

Advanced: Custom Logging

1import "os"
2
3cfg.StdoutWriter = os.Stdout
4cfg.StderrWriter = os.Stderr
5cfg.Verbose = true
6
7sandbox, _ := shai.NewSandbox(cfg)
8// All output goes to configured writers

See Also