Skip to content

Commit

Permalink
feature(turborepo): Port login without sso team (vercel#3372)
Browse files Browse the repository at this point in the history
  • Loading branch information
NicholasLYang authored Jan 27, 2023
1 parent 3a3035a commit c1e2408
Show file tree
Hide file tree
Showing 16 changed files with 1,143 additions and 613 deletions.
1,227 changes: 735 additions & 492 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions cli/integration_tests/bad_flag.t
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ Setup

Bad flag should print misuse text
$ ${TURBO} --bad-flag
ERROR Found argument '--bad-flag' which wasn't expected, or isn't valid in this context
ERROR unexpected argument '--bad-flag' found

If you tried to supply '--bad-flag' as a value rather than a flag, use '-- --bad-flag'
note: to pass '--bad-flag' as a value, use '-- --bad-flag'

Usage: turbo [OPTIONS] [COMMAND]

For more information try '--help'
For more information, try '--help'.

[1]

Bad flag with an implied run command should display run flags
$ ${TURBO} build --bad-flag
ERROR Found argument '--bad-flag' which wasn't expected, or isn't valid in this context
ERROR unexpected argument '--bad-flag' found

If you tried to supply '--bad-flag' as a value rather than a flag, use '-- --bad-flag'
note: to pass '--bad-flag' as a value, use '-- --bad-flag'

Usage: turbo <--cache-dir <CACHE_DIR>|--cache-workers <CACHE_WORKERS>|--concurrency <CONCURRENCY>|--continue|--dry-run [<DRY_RUN>]|--single-package|--filter <FILTER>|--force|--global-deps <GLOBAL_DEPS>|--graph [<GRAPH>]|--ignore <IGNORE>|--include-dependencies|--no-cache|--no-daemon|--no-deps|--output-logs <OUTPUT_LOGS>|--only|--parallel|--pkg-inference-root <PKG_INFERENCE_ROOT>|--profile <PROFILE>|--remote-only|--scope <SCOPE>|--since <SINCE>|TASKS|PASS_THROUGH_ARGS>

For more information try '--help'
For more information, try '--help'.

[1]
4 changes: 1 addition & 3 deletions cli/integration_tests/basic_monorepo/infer_pkg.t
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ Run a dry run
Run a dry run in a directory
$ cd packages/util
$ ${TURBO} build --dry=json | jq .packages
[
"util"
]
[]

Ensure we don't infer packages if --cwd is supplied
$ ${TURBO} build --cwd=../.. --dry=json | jq .packages
Expand Down
4 changes: 2 additions & 2 deletions cli/integration_tests/basic_monorepo/verbosity.t
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ Verbosity level 2

Make sure users can only use one verbosity flag
$ ${TURBO} build -v --verbosity=1
ERROR The argument '-v...' cannot be used with '--verbosity <COUNT>'
ERROR the argument '-v...' cannot be used with '--verbosity <COUNT>'

Usage: turbo [OPTIONS] [COMMAND]

For more information try '--help'
For more information, try '--help'.

[1]
2 changes: 1 addition & 1 deletion cli/integration_tests/no_args.t
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Make sure exit code is 2 when no args are passed
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--cache-dir <CACHE_DIR> Override the filesystem cache directory
Expand Down
12 changes: 6 additions & 6 deletions cli/integration_tests/turbo_help.t
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Test help flag
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--cache-dir <CACHE_DIR> Override the filesystem cache directory
Expand Down Expand Up @@ -94,7 +94,7 @@ Test help flag
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--cache-dir <CACHE_DIR> Override the filesystem cache directory
Expand Down Expand Up @@ -141,7 +141,7 @@ Test help flag for link command
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--single-package Run turbo in single-package mode
Expand All @@ -167,7 +167,7 @@ Test help flag for unlink command
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--single-package Run turbo in single-package mode
Expand All @@ -194,7 +194,7 @@ Test help flag for login command
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--single-package Run turbo in single-package mode
Expand All @@ -220,7 +220,7 @@ Test help flag for logout command
--token <TOKEN> Set the auth token for API calls
--trace <TRACE> Specify a file to save a pprof trace
--verbosity <COUNT> Verbosity level
-h, --help Print help information
-h, --help Print help

Run Arguments:
--single-package Run turbo in single-package mode
88 changes: 14 additions & 74 deletions cli/internal/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const defaultHostname = "127.0.0.1"
const defaultPort = 9789
const defaultSSOProvider = "SAML/OIDC Single Sign-On"

// ExecuteLogin executes the `login` command.
// ExecuteLogin executes the `login` command for SSO Teams (regular login is implemented in Rust).
func ExecuteLogin(ctx context.Context, helper *cmdutil.Helper, args *turbostate.ParsedArgsFromRust) error {
base, err := helper.GetCmdBase(args)
if err != nil {
Expand All @@ -34,34 +34,26 @@ func ExecuteLogin(ctx context.Context, helper *cmdutil.Helper, args *turbostate.
return nil
}

if args.Command.Login.SsoTeam != "" {
return errors.New("internal error: SSO login should be handled by Rust")
}

login := login{
base: base,
openURL: browser.OpenBrowser,
client: base.APIClient,
promptEnableCaching: promptEnableCaching,
}
if args.Command.Login.SsoTeam != "" {
err := login.loginSSO(ctx, args.Command.Login.SsoTeam)
if err != nil {
if errors.Is(err, errUserCanceled) || errors.Is(err, context.Canceled) {
base.UI.Info("Canceled. Turborepo not set up.")
} else if errors.Is(err, errTryAfterEnable) || errors.Is(err, errNeedCachingEnabled) || errors.Is(err, errOverage) {
base.UI.Info("Remote Caching not enabled. Please run 'turbo login' again after Remote Caching has been enabled")
} else {
base.LogError("SSO login failed: %v", err)
}
return err
}
} else {
err := login.run(ctx)
if err != nil {
if errors.Is(err, context.Canceled) {
base.UI.Info("Canceled. Turborepo not set up.")
} else {
base.LogError("login failed: %v", err)
}
return err
err = login.loginSSO(ctx, args.Command.Login.SsoTeam)
if err != nil {
if errors.Is(err, errUserCanceled) || errors.Is(err, context.Canceled) {
base.UI.Info("Canceled. Turborepo not set up.")
} else if errors.Is(err, errTryAfterEnable) || errors.Is(err, errNeedCachingEnabled) || errors.Is(err, errOverage) {
base.UI.Info("Remote Caching not enabled. Please run 'turbo login' again after Remote Caching has been enabled")
} else {
base.LogError("SSO login failed: %v", err)
}
return err
}
return nil
}
Expand Down Expand Up @@ -92,58 +84,6 @@ func (l *login) directUserToURL(url string) {
}
}

func (l *login) run(ctx context.Context) error {
loginURLBase := l.base.RepoConfig.LoginURL()
l.base.Logger.Debug(fmt.Sprintf("turbo v%v", l.base.TurboVersion))
l.base.Logger.Debug(fmt.Sprintf("api url: %v", l.base.RemoteConfig.APIURL))
l.base.Logger.Debug(fmt.Sprintf("login url: %v", loginURLBase))
redirectURL := fmt.Sprintf("http://%v:%v", defaultHostname, defaultPort)
loginURL := fmt.Sprintf("%v/turborepo/token?redirect_uri=%v", loginURLBase, redirectURL)

l.base.UI.Info(util.Sprintf(">>> Opening browser to %v", loginURL))

rootctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
defer cancel()

var query url.Values
oss, err := newOneShotServer(rootctx, func(w http.ResponseWriter, r *http.Request) {
query = r.URL.Query()
http.Redirect(w, r, loginURLBase+"/turborepo/success", http.StatusFound)
}, defaultPort)
if err != nil {
return errors.Wrap(err, "failed to start local server")
}

s := ui.NewSpinner(os.Stdout)
l.directUserToURL(loginURL)
s.Start("Waiting for your authorization...")
err = oss.Wait()
if err != nil {
return errors.Wrap(err, "failed to shut down local server")
}
// Stop the spinner before we return to ensure terminal is left in a good state
s.Stop("")

if err := l.base.UserConfig.SetToken(query.Get("token")); err != nil {
return err
}
rawToken := query.Get("token")
l.client.SetToken(rawToken)
userResponse, err := l.client.GetUser()
if err != nil {
return errors.Wrap(err, "could not get user information")
}
l.base.UI.Info("")
l.base.UI.Info(util.Sprintf("%s Turborepo CLI authorized for %s${RESET}", ui.Rainbow(">>> Success!"), userResponse.User.Email))
l.base.UI.Info("")
l.base.UI.Info(util.Sprintf("${CYAN}To connect to your Remote Cache. Run the following in the${RESET}"))
l.base.UI.Info(util.Sprintf("${CYAN}root of any turborepo:${RESET}"))
l.base.UI.Info("")
l.base.UI.Info(util.Sprintf(" ${BOLD}npx turbo link${RESET}"))
l.base.UI.Info("")
return nil
}

func (l *login) loginSSO(ctx context.Context, ssoTeam string) error {
redirectURL := fmt.Sprintf("http://%v:%v", defaultHostname, defaultPort)
query := make(url.Values)
Expand Down
25 changes: 0 additions & 25 deletions cli/internal/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,31 +135,6 @@ func newTest(t *testing.T, redirectedURL string) *testResult {
return tr
}

func Test_run(t *testing.T) {
ctx := context.Background()
test := newTest(t, "http://127.0.0.1:9789/?token=my-token")
login := test.getTestLogin()
err := login.run(ctx)
if err != nil {
t.Errorf("expected to succeed, got error %v", err)
}
if test.clientErr != nil {
t.Errorf("test client had error %v", test.clientErr)
}

expectedURL := "login-url/turborepo/token?redirect_uri=http://127.0.0.1:9789"
if test.openedURL != expectedURL {
t.Errorf("openedURL got %v, want %v", test.openedURL, expectedURL)
}

if test.userConfig.Token() != "my-token" {
t.Errorf("config token got %v, want my-token", test.userConfig.Token())
}
if test.client.setToken != "my-token" {
t.Errorf("user client token got %v, want my-token", test.client.setToken)
}
}

func Test_sso(t *testing.T) {
ctx := context.Background()
redirectParams := make(url.Values)
Expand Down
7 changes: 7 additions & 0 deletions crates/turborepo-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ tempfile = "3.3.0"
[dependencies]
anyhow = { version = "1.0.65", features = ["backtrace"] }
atty = "0.2"
axum = "0.6.2"
axum-server = "0.4.4"
chrono = "0.4.23"
clap = { version = "4.0.22", features = ["derive"] }
clap_complete = "4.0.6"
Expand All @@ -22,12 +24,17 @@ console = "0.15.5"
dirs-next = "2.0.0"
dunce = "1.0"
env_logger = "0.10.0"
indicatif = "0.17.3"
lazy_static = "1.4.0"
log = "0.4.17"
predicates = "2.1.1"
reqwest = { version = "0.11.14", features = ["json"] }
rustc_version_runtime = "0.2.1"
semver = "1.0"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.86"
serde_yaml = "0.8.26"
tiny-gradient = "0.1"
tokio = { version = "1.24.2", features = ["full"] }
turbo-updater = { path = "../turbo-updater" }
webbrowser = "0.8.4"
25 changes: 21 additions & 4 deletions crates/turborepo-lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use log::{debug, error};
use serde::Serialize;

use crate::{
commands::{bin, logout, CommandBase},
commands::{bin, login, logout, CommandBase},
get_version,
shim::{RepoMode, RepoState},
ui::UI,
Expand Down Expand Up @@ -376,7 +376,8 @@ pub struct RunArgs {
/// we use it here to modify clap's arguments.
///
/// returns: Result<Payload, Error>
pub fn run(repo_state: Option<RepoState>) -> Result<Payload> {
#[tokio::main]
pub async fn run(repo_state: Option<RepoState>) -> Result<Payload> {
let mut clap_args = Args::new()?;
// If there is no command, we set the command to `Command::Run` with
// `self.parsed_args.run_args` as arguments.
Expand Down Expand Up @@ -443,8 +444,24 @@ pub fn run(repo_state: Option<RepoState>) -> Result<Payload> {

Ok(Payload::Rust(Ok(0)))
}
Command::Login { .. }
| Command::Link { .. }
Command::Login { sso_team } => {
if clap_args.test_run {
println!("Login test run successful");
return Ok(Payload::Rust(Ok(0)));
}

// We haven't implemented sso_team yet so we delegate to Go
if sso_team.is_some() {
return Ok(Payload::Go(Box::new(clap_args)));
}

let base = CommandBase::new(clap_args, repo_root)?;

login::login(base).await?;

Ok(Payload::Rust(Ok(0)))
}
Command::Link { .. }
| Command::Unlink { .. }
| Command::Daemon { .. }
| Command::Prune { .. }
Expand Down
Loading

0 comments on commit c1e2408

Please sign in to comment.