Skip to content

pass optional context to pipe (#225)#244

Open
billvamva wants to merge 1 commit into
bitfield:masterfrom
billvamva:master
Open

pass optional context to pipe (#225)#244
billvamva wants to merge 1 commit into
bitfield:masterfrom
billvamva:master

Conversation

@billvamva

@billvamva billvamva commented Jul 6, 2026

Copy link
Copy Markdown

Following the discussion: #225, I added a simple WithContext closure and function to support timeouts/cancellations/deadlines. This is a straightforward implementation as exec.CommandContext and http.NewRequestWithContext return an error when the signal is received and that is cascaded by the existing implementation. I encountered this gap while migrating some test-suite setup scripts to use this library, and thought this would be an easy addition.

1. Show that the lack of the feature is a significant problem for a significant number of users.

Exec and ExecForEach are two of the most commonly used methods in the library. Any caller invoking an external command in a program that has a timeout or deadline requirement (a web server handler, a CI runner, a daemon, any program with a --timeout flag) cannot use script.Exec because there is no way to cancel it. It's a common pattern of having request scoped timeouts and an overall timeout.

2. Propose a feature that addresses the problem in a satisfactory way.

Add a single method WithContext(ctx context.Context) *Pipe . Store the context on the Pipe struct. Use it in Exec and ExecForEach via exec.CommandContext, and in Do/Get/Post via req.WithContext. Default to context.Background() in NewPipe() so no existing code changes behaviour.

3. Include a runnable example program that shows how the proposed syntax or API would be used to solve the problem for a realistic use case.

Honestly unsure what's expected here. The use case I have encountered was during a DB setup for a component testing suite, with a non deterministic rebalancing step that should be complete before proceeding. The code snippet is not runnable as is but should convey the issue encountered.

    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
    defer cancel()

    // Start the database container. Without context this hangs if docker stalls.
    if err := script.NewPipe().WithContext(ctx).
        Exec("docker compose up -d database").Wait(); err != nil {
        return err
    }

    if err := script.Echo(`nodes=node1,node2`).WithContext(ctx).
        Post("<host>/controller/rebalance").Wait(); err != nil {
        return err
    }

    // Poll until rebalance is complete. Each Post call in the loop
    for {
        status, err := script.Echo(`{}`).WithContext(ctx).
            Post("<host>/rebalanceProgress").String()
        if err != nil {
            return err // should return if overall context is exceeded
        }
        time.Sleep(2 * time.Second)
    }

    if err := script.Echo(`user=app&roles=readwrite`).WithContext(ctx).
        Post("http://localhost:8091/settings/auth").Wait(); err != nil {
        return err
    }


If any step exceeds the overall deadline, ctx is cancelled, the subprocess is killed, and the error propagates through the pipe's error mechanism.

4. Show that this is significantly better than any existing solution without the feature.

The only existing solution is to not use script.Exec and use raw os/exec.Command instead or using NewRequestWithContext and Do which limits the API.

@billvamva billvamva changed the title pass optional context to pipe pass optional context to pipe (#225) Jul 6, 2026
@bitfield

bitfield commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Thanks, @billvamva! I'll take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants