github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/internal/codespaces/codespaces.go (about) 1 package codespaces 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/cenkalti/backoff/v4" 10 "github.com/ungtb10d/cli/v2/internal/codespaces/api" 11 "github.com/ungtb10d/cli/v2/pkg/liveshare" 12 ) 13 14 func connectionReady(codespace *api.Codespace) bool { 15 return codespace.Connection.SessionID != "" && 16 codespace.Connection.SessionToken != "" && 17 codespace.Connection.RelayEndpoint != "" && 18 codespace.Connection.RelaySAS != "" && 19 codespace.State == api.CodespaceStateAvailable 20 } 21 22 type apiClient interface { 23 GetCodespace(ctx context.Context, name string, includeConnection bool) (*api.Codespace, error) 24 StartCodespace(ctx context.Context, name string) error 25 } 26 27 type progressIndicator interface { 28 StartProgressIndicatorWithLabel(s string) 29 StopProgressIndicator() 30 } 31 32 type logger interface { 33 Println(v ...interface{}) 34 Printf(f string, v ...interface{}) 35 } 36 37 // ConnectToLiveshare waits for a Codespace to become running, 38 // and connects to it using a Live Share session. 39 func ConnectToLiveshare(ctx context.Context, progress progressIndicator, sessionLogger logger, apiClient apiClient, codespace *api.Codespace) (sess *liveshare.Session, err error) { 40 if codespace.State != api.CodespaceStateAvailable { 41 progress.StartProgressIndicatorWithLabel("Starting codespace") 42 defer progress.StopProgressIndicator() 43 if err := apiClient.StartCodespace(ctx, codespace.Name); err != nil { 44 return nil, fmt.Errorf("error starting codespace: %w", err) 45 } 46 } 47 expBackoff := backoff.NewExponentialBackOff() 48 49 expBackoff.Multiplier = 1.1 50 expBackoff.MaxInterval = 10 * time.Second 51 expBackoff.MaxElapsedTime = 5 * time.Minute 52 53 for retries := 0; !connectionReady(codespace); retries++ { 54 if retries > 1 { 55 duration := expBackoff.NextBackOff() 56 time.Sleep(duration) 57 } 58 59 if expBackoff.GetElapsedTime() >= expBackoff.MaxElapsedTime { 60 return nil, errors.New("timed out while waiting for the codespace to start") 61 } 62 63 codespace, err = apiClient.GetCodespace(ctx, codespace.Name, true) 64 if err != nil { 65 return nil, fmt.Errorf("error getting codespace: %w", err) 66 } 67 } 68 69 progress.StartProgressIndicatorWithLabel("Connecting to codespace") 70 defer progress.StopProgressIndicator() 71 72 return liveshare.Connect(ctx, liveshare.Options{ 73 ClientName: "gh", 74 SessionID: codespace.Connection.SessionID, 75 SessionToken: codespace.Connection.SessionToken, 76 RelaySAS: codespace.Connection.RelaySAS, 77 RelayEndpoint: codespace.Connection.RelayEndpoint, 78 HostPublicKeys: codespace.Connection.HostPublicKeys, 79 Logger: sessionLogger, 80 }) 81 }