Skip to content

Commit

Permalink
feat(turborepo): Layered Config (#5796)
Browse files Browse the repository at this point in the history
This PR enables existing configuration settings to be stored in any of
"global", "local", or "shared" locations.
- Removes the config crate.
- Sets up configuration layering per our requirements.
- Does not make any behavior changes as to where link/unlink and
login/logout store the team and token values.

The primary value enabled here is that we can set `teamId` inside of
`turbo.json` and omit the `link` step, and make login aware of which
team scope it needs to auth to, eliminating the need for `--sso-team` in
most cases.

Closes TURBO-1252

---------

Co-authored-by: Nathan Hammond <Nathan Hammond>
  • Loading branch information
nathanhammond committed Oct 10, 2023
1 parent fa4ae6a commit a6d3adf
Show file tree
Hide file tree
Showing 29 changed files with 1,045 additions and 1,297 deletions.
68 changes: 0 additions & 68 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions cli/internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ var ErrNoCachesEnabled = errors.New("no caches are enabled")
// Opts holds configuration options for the cache
// TODO(gsoltis): further refactor this into fs cache opts and http cache opts
type Opts struct {
OverrideDir string
SkipRemote bool
SkipFilesystem bool
Workers int
RemoteCacheOpts fs.RemoteCacheOptions
OverrideDir string
SkipRemote bool
SkipFilesystem bool
Workers int
Signature bool
}

// resolveCacheDir calculates the location turbo should use to cache artifacts,
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/cache/cache_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func newHTTPCache(opts Opts, client client, recorder analytics.Recorder, repoRoo
// TODO(Gaspar): this should use RemoteCacheOptions.TeamId once we start
// enforcing team restrictions for repositories.
teamID: client.GetTeamID(),
enabled: opts.RemoteCacheOpts.Signature,
enabled: opts.Signature,
},
}
}
8 changes: 2 additions & 6 deletions cli/internal/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,7 @@ func TestNew(t *testing.T) {
args: args{
opts: Opts{
SkipFilesystem: true,
RemoteCacheOpts: fs.RemoteCacheOptions{
Signature: true,
},
Signature: true,
},
recorder: &nullRecorder{},
onCacheRemoved: func(Cache, error) {},
Expand All @@ -277,9 +275,7 @@ func TestNew(t *testing.T) {
name: "With both configured, new returns an fsCache and httpCache",
args: args{
opts: Opts{
RemoteCacheOpts: fs.RemoteCacheOptions{
Signature: true,
},
Signature: true,
},
recorder: &nullRecorder{},
onCacheRemoved: func(Cache, error) {},
Expand Down
2 changes: 2 additions & 0 deletions cli/internal/cmdutil/cmdutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ func (h *Helper) GetCmdBase(executionState *turbostate.ExecutionState) (*CmdBase
UIFactory: uiFactory,
Logger: logger,
RepoRoot: repoRoot,
Config: executionState.Config,
APIClient: apiClient,
SpacesAPIClient: spacesClient,
TurboVersion: h.TurboVersion,
Expand All @@ -186,6 +187,7 @@ type CmdBase struct {
UIFactory ui.Factory
Logger hclog.Logger
RepoRoot turbopath.AbsoluteSystemPath
Config turbostate.Config
APIClient *client.APIClient
SpacesAPIClient *client.APIClient
TurboVersion string
Expand Down
7 changes: 3 additions & 4 deletions cli/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ func (r *run) run(ctx gocontext.Context, targets []string, executionState *turbo
return err
}

// TODO: these values come from a config file, hopefully viper can help us merge these
r.opts.cacheOpts.RemoteCacheOpts = turboJSON.RemoteCacheOptions
r.opts.cacheOpts.Signature = r.base.Config.Signature

// If a spaceID wasn't passed as a flag, read it from the turbo.json config.
// If that is not set either, we'll still end up with a blank string.
Expand Down Expand Up @@ -447,8 +446,8 @@ func (r *run) initAnalyticsClient(ctx gocontext.Context) analytics.Client {

// After we know if its _possible_ to enable remote cache, check the config
// and dsiable it if wanted.
if !r.opts.cacheOpts.RemoteCacheOpts.Enabled {
r.opts.cacheOpts.SkipRemote = true
if r.base.Config.Enabled != nil {
r.opts.cacheOpts.SkipRemote = !*r.base.Config.Enabled
}

analyticsClient := analytics.NewClient(ctx, analyticsSink, r.base.Logger.Named("analytics"))
Expand Down
14 changes: 14 additions & 0 deletions cli/internal/turbostate/turbostate.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ type TaskHashTracker struct {

// ExecutionState is the entire state of a turbo execution that is passed from the Rust shim.
type ExecutionState struct {
Config Config `json:"config"`
APIClientConfig APIClientConfig `json:"api_client_config"`
SpacesAPIClientConfig APIClientConfig `json:"spaces_api_client_config"`
PackageManager string `json:"package_manager"`
Expand All @@ -106,6 +107,19 @@ type ExecutionState struct {
TaskHashTracker *TaskHashTracker `json:"task_hash_tracker"`
}

// Config holds the resolved configuration from the combination of all sources.
type Config struct {
APIURL string `json:"apiUrl"`
LoginURL string `json:"loginUrl"`
TeamSlug string `json:"teamSlug"`
TeamID string `json:"teamId"`
Token string `json:"token"`
Signature bool `json:"signature"`
Preflight bool `json:"preflight"`
Timeout uint64 `json:"timeout"`
Enabled *bool `json:"enabled"`
}

// APIClientConfig holds the authentication and endpoint details for the API client
type APIClientConfig struct {
Token string `json:"token"`
Expand Down
2 changes: 2 additions & 0 deletions crates/turborepo-api-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub enum Error {
ReqwestError(#[from] reqwest::Error),
#[error("skipping HTTP Request, too many failures have occurred.\nLast error: {0}")]
TooManyFailures(#[from] Box<reqwest::Error>),
#[error("Unable to set up TLS.")]
TlsError(reqwest::Error),
#[error("Error parsing header: {0}")]
InvalidHeader(#[from] ToStrError),
#[error("Error parsing URL: {0}")]
Expand Down
7 changes: 4 additions & 3 deletions crates/turborepo-api-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,14 @@ impl APIClient {
version: &str,
use_preflight: bool,
) -> Result<Self> {
let client = if timeout != 0 {
let builder_result = if timeout != 0 {
reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(timeout))
.build()?
.build()
} else {
reqwest::Client::builder().build()?
reqwest::Client::builder().build()
};
let client = builder_result.map_err(Error::TlsError)?;

let user_agent = format!(
"turbo {} {} {} {}",
Expand Down
6 changes: 6 additions & 0 deletions crates/turborepo-cache/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,9 @@ pub struct RemoteCacheOpts {
team_id: String,
signature: bool,
}

impl RemoteCacheOpts {
pub fn new(team_id: String, signature: bool) -> Self {
Self { team_id, signature }
}
}
1 change: 0 additions & 1 deletion crates/turborepo-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ chrono = { workspace = true, features = ["serde"] }
clap = { workspace = true, features = ["derive", "env"] }
clap_complete = { workspace = true }
command-group = { version = "2.1.0", features = ["with-tokio"] }
config = "0.13"
console = { workspace = true }
ctrlc = { version = "3.4.0", features = ["termination"] }
dialoguer = { workspace = true, features = ["fuzzy-select"] }
Expand Down
18 changes: 9 additions & 9 deletions crates/turborepo-lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ pub async fn run(
}
#[allow(unused_variables)]
Command::Daemon { command, idle_time } => {
let base = CommandBase::new(cli_args.clone(), repo_root, version, ui)?;
let base = CommandBase::new(cli_args.clone(), repo_root, version, ui);

match command {
Some(command) => daemon::daemon_client(command, &base).await,
Expand Down Expand Up @@ -740,7 +740,7 @@ pub async fn run(
}
Command::Info { workspace } => {
let workspace = workspace.clone();
let mut base = CommandBase::new(cli_args, repo_root, version, ui)?;
let mut base = CommandBase::new(cli_args, repo_root, version, ui);
info::run(&mut base, workspace.as_deref())?;

Ok(Payload::Rust(Ok(0)))
Expand All @@ -756,7 +756,7 @@ pub async fn run(

let modify_gitignore = !*no_gitignore;
let to = *target;
let mut base = CommandBase::new(cli_args, repo_root, version, ui)?;
let mut base = CommandBase::new(cli_args, repo_root, version, ui);

if let Err(err) = link::link(&mut base, modify_gitignore, to).await {
error!("error: {}", err.to_string())
Expand All @@ -765,7 +765,7 @@ pub async fn run(
Ok(Payload::Rust(Ok(0)))
}
Command::Logout { .. } => {
let mut base = CommandBase::new(cli_args, repo_root, version, ui)?;
let mut base = CommandBase::new(cli_args, repo_root, version, ui);
logout::logout(&mut base)?;

Ok(Payload::Rust(Ok(0)))
Expand All @@ -778,7 +778,7 @@ pub async fn run(

let sso_team = sso_team.clone();

let mut base = CommandBase::new(cli_args, repo_root, version, ui)?;
let mut base = CommandBase::new(cli_args, repo_root, version, ui);

if let Some(sso_team) = sso_team {
login::sso_login(&mut base, &sso_team).await?;
Expand All @@ -795,7 +795,7 @@ pub async fn run(
}

let from = *target;
let mut base = CommandBase::new(cli_args, repo_root, version, ui)?;
let mut base = CommandBase::new(cli_args, repo_root, version, ui);

unlink::unlink(&mut base, from)?;

Expand All @@ -808,7 +808,7 @@ pub async fn run(
if args.tasks.is_empty() {
return Err(anyhow!("at least one task must be specified"));
}
let base = CommandBase::new(cli_args.clone(), repo_root, version, ui)?;
let base = CommandBase::new(cli_args.clone(), repo_root, version, ui);

if args.experimental_rust_codepath {
use crate::commands::run;
Expand All @@ -826,7 +826,7 @@ pub async fn run(
if args.tasks.is_empty() {
return Err(anyhow!("at least one task must be specified"));
}
let base = CommandBase::new(cli_args, repo_root, version, ui)?;
let base = CommandBase::new(cli_args, repo_root, version, ui);
Ok(Payload::Go(Box::new(base)))
}
Command::Prune {
Expand All @@ -842,7 +842,7 @@ pub async fn run(
.unwrap_or_default();
let docker = *docker;
let output_dir = output_dir.clone();
let base = CommandBase::new(cli_args, repo_root, version, ui)?;
let base = CommandBase::new(cli_args, repo_root, version, ui);
prune::prune(&base, &scope, docker, &output_dir)?;
Ok(Payload::Rust(Ok(0)))
}
Expand Down
Loading

0 comments on commit a6d3adf

Please sign in to comment.