diff --git a/cmd/turbo/main.go b/cmd/turbo/main.go index cab592df6cde0..24c170741eb17 100644 --- a/cmd/turbo/main.go +++ b/cmd/turbo/main.go @@ -60,7 +60,7 @@ func main() { c.HelpWriter = os.Stdout c.ErrorWriter = os.Stderr // Parse and validate cmd line flags and env vars - cf, err := config.ParseAndValidate(c.Args, ui) + cf, err := config.ParseAndValidate(c.Args, ui, turboVersion) if err != nil { ui.Error(fmt.Sprintf("%s %s", uiPkg.ERROR_PREFIX, color.RedString(err.Error()))) os.Exit(1) diff --git a/internal/client/client.go b/internal/client/client.go index 215a93f30532f..eebb08cf185ca 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "net/url" + "runtime" "strings" "time" @@ -17,8 +18,9 @@ import ( type ApiClient struct { // The api's base URL - baseUrl string - Token string + baseUrl string + Token string + turboVersion string // An http client HttpClient *retryablehttp.Client } @@ -28,9 +30,10 @@ func (api *ApiClient) SetToken(token string) { } // New creates a new ApiClient -func NewClient(baseUrl string, logger hclog.Logger) *ApiClient { +func NewClient(baseUrl string, logger hclog.Logger, turboVersion string) *ApiClient { return &ApiClient{ - baseUrl: baseUrl, + baseUrl: baseUrl, + turboVersion: turboVersion, HttpClient: &retryablehttp.Client{ HTTPClient: &http.Client{ Timeout: time.Duration(60 * time.Second), @@ -63,6 +66,10 @@ func (c *ApiClient) makeUrl(endpoint string) string { return fmt.Sprintf("%v%v", c.baseUrl, endpoint) } +func (c *ApiClient) UserAgent() string { + return fmt.Sprintf("turbo %v %v %v (%v)", c.turboVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH) +} + func (c *ApiClient) PutArtifact(hash string, teamId string, slug string, duration int, rawBody interface{}) error { params := url.Values{} if teamId != "" && strings.HasPrefix(teamId, "team_") { @@ -75,6 +82,7 @@ func (c *ApiClient) PutArtifact(hash string, teamId string, slug string, duratio req.Header.Set("Content-Type", "application/octet-stream") req.Header.Set("x-artifact-duration", fmt.Sprintf("%v", duration)) req.Header.Set("Authorization", "Bearer "+c.Token) + req.Header.Set("User-Agent", c.UserAgent()) if err != nil { return fmt.Errorf("[WARNING] Invalid cache URL: %w", err) } @@ -109,7 +117,7 @@ func (c *ApiClient) RequestDeviceToken() (*DeviceToken, error) { return nil, err } - req.Header.Set("User-Agent", "Turbo CLI") + req.Header.Set("User-Agent", c.UserAgent()) req.Header.Set("Content-Type", "application/json") resp, err := c.HttpClient.Do(req) @@ -265,7 +273,7 @@ func (c *ApiClient) GetTeams() (*TeamsResponse, error) { return nil, err } - req.Header.Set("User-Agent", "Turbo CLI") + req.Header.Set("User-Agent", c.UserAgent()) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+c.Token) resp, err := c.HttpClient.Do(req) @@ -309,7 +317,7 @@ func (c *ApiClient) GetUser() (*UserResponse, error) { return nil, err } - req.Header.Set("User-Agent", "Turbo CLI") + req.Header.Set("User-Agent", c.UserAgent()) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+c.Token) resp, err := c.HttpClient.Do(req) diff --git a/internal/config/config.go b/internal/config/config.go index ab3c74f5d0f59..81c7c51af637f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -40,8 +40,9 @@ type Config struct { ApiUrl string // Backend retryable http client ApiClient *client.ApiClient - - Cache *CacheConfig + // Turborepo CLI Version + TurboVersion string + Cache *CacheConfig } // CacheConfig @@ -57,7 +58,7 @@ type CacheConfig struct { // ParseAndValidate parses the cmd line flags / env vars, and verifies that all required // flags have been set. Users can pass in flags when calling a subcommand, or set env vars // with the prefix 'TURBO_'. If both values are set, the env var value will be used. -func ParseAndValidate(args []string, ui cli.Ui) (c *Config, err error) { +func ParseAndValidate(args []string, ui cli.Ui, turboVersion string) (c *Config, err error) { // Special check for ./turbo invocation without any args // Return the help message @@ -163,15 +164,16 @@ func ParseAndValidate(args []string, ui cli.Ui) (c *Config, err error) { Output: output, }) - apiClient := client.NewClient(partialConfig.ApiUrl, logger) + apiClient := client.NewClient(partialConfig.ApiUrl, logger, turboVersion) c = &Config{ - Logger: logger, - Token: partialConfig.Token, - TeamSlug: partialConfig.TeamSlug, - TeamId: partialConfig.TeamId, - ApiUrl: partialConfig.ApiUrl, - ApiClient: apiClient, + Logger: logger, + Token: partialConfig.Token, + TeamSlug: partialConfig.TeamSlug, + TeamId: partialConfig.TeamId, + ApiUrl: partialConfig.ApiUrl, + ApiClient: apiClient, + TurboVersion: turboVersion, Cache: &CacheConfig{ Workers: runtime.NumCPU() + 2, Dir: filepath.Join("node_modules", ".cache", "turbo"),