cli: apply --wait-timeout before the first clone-probe token mint · Entire

cli: apply --wait-timeout before the first clone-probe token mint

2ebd0d8main·

toothbrush·1mo ago·2 files·+36 added/-7 removed

waitForMirrorClone minted its first repo-scoped token before arming the wait deadline. That was tolerable when the mint was a single STS exchange, but it now spans cluster discovery and a possible login refresh — a slow auth path could block well past a small --wait-timeout before the timer even started. Arm the deadline first so the authorization phase consumes the user's wait budget.

The mint goes through a package seam so the regression test can pin a hung auth path against a 50ms timeout.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Sessions

be119056ae83View transcript

[?
Auth Refactor: Eliminate Static FallbacksClaude Code·Fable 5.{1m}·2 steps](/content/gh/entireio/cli/session/e6146684-ebfa-4f57-beff-34194cac8c2a#timeline-be119056ae83/index.html)

Changes

2

161 unmodified lines

162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    6 unmodified lines

178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    182
    196
    197
    198
    199
    1 unmodified line

201
    202
    203
    190
    191
    192
    193
    194
    195
    204
    205
    206

161 unmodified lines

return true, NewSilentError(fmt.Errorf("mirror %s is suspended", mirrorID))

// mintRepoToken mints the repo-scoped pull token for the clone probe.
// Package var so tests can substitute a slow or failing auth path.
var mintRepoToken = auth.RepoScopedToken

// waitForMirrorClone blocks until the mirror at /gh/<owner>/<repo> on
// clusterHost advertises a resolvable HEAD (the initial GitHub→EntireDB
// clone has landed) or the deadline expires. It probes the data plane's
// rather than minting once up front. Re-mints are floored at
// minReauthInterval so a flapping STS can't be amplified into a re-mint
// every probeInterval ticks.
//
// The deadline is applied before the first mint: minting now spans cluster
// discovery and a possible login refresh, so the authorization phase must
// consume the user's wait budget, not run before it.
func waitForMirrorClone(ctx context.Context, out io.Writer, clusterHost, owner, repo string, timeout time.Duration) error {
    if timeout > 0 {
        var cancel context.CancelFunc
        ctx, cancel = context.WithTimeout(ctx, timeout)
        defer cancel()
    }

repoSlug := "/gh/" + owner + "/" + repo
    checkURL := fmt.Sprintf("https://%s%s/info/refs?service=git-upload-pack", clusterHost, repoSlug)

mintToken := func() (string, error) {
        return auth.RepoScopedToken(ctx, clusterHost, repoSlug, "pull")
        return mintRepoToken(ctx, clusterHost, repoSlug, "pull")
    }
    token, err := mintToken()
    if err != nil {
    1 unmodified line
    }
    lastMint := time.Now()

if timeout > 0 {
        var cancel context.CancelFunc
        ctx, cancel = context.WithTimeout(ctx, timeout)
        defer cancel()
    }

ticker := time.NewTicker(probeInterval)
    defer ticker.Stop()

Mcmd/entire/cli/repo_mirror_probe.go+15/-7

4 unmodified lines

5
6
7
8
9
10
11
498 unmodified lines

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532

4 unmodified lines

"context"
    "errors"
    "fmt"
    "io"
    "net"
    "net/http"
    "net/http/httptest"
498 unmodified lines

require.NoError(t, err)
    return &http.Request{URL: u}
}

// TestWaitForMirrorClone_TimeoutBoundsAuthorization pins that --wait-timeout
// covers the initial token mint, not just the probe loop: minting spans
// cluster discovery and a possible login refresh, so a hung auth path must
// be cut off by the user's wait budget.
//
// Not parallel: swaps the package-level mintRepoToken seam.
func TestWaitForMirrorClone_TimeoutBoundsAuthorization(t *testing.T) {
    prev := mintRepoToken
    mintRepoToken = func(ctx context.Context, _, _, _ string) (string, error) {
        <-ctx.Done() // hang until the wait deadline fires
        return "", ctx.Err()
    }
    t.Cleanup(func() { mintRepoToken = prev })

start := time.Now()
    err := waitForMirrorClone(context.Background(), io.Discard, "cluster.example", "o", "r", 50*time.Millisecond)
    require.ErrorIs(t, err, context.DeadlineExceeded)
    require.Less(t, time.Since(start), 10*time.Second, "first mint must be bounded by the wait timeout")
}

Mcmd/entire/cli/repo_mirror_test.go+21