Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(turborepo): Port login without sso team #3372

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7bba2a5
Implemented RepoConfig and first part of login flow
NicholasLYang Jan 18, 2023
54c8539
First attempt at one-shot server logic
NicholasLYang Jan 18, 2023
b3e4bbe
Replaced complex type with OnceCell
NicholasLYang Jan 18, 2023
4a9cdfb
More work on login. Implementing APIClient
NicholasLYang Jan 20, 2023
0d5c1e4
Got User fetching hooked up to login
NicholasLYang Jan 23, 2023
3d736ff
Refining login code.
NicholasLYang Jan 23, 2023
2a969d8
Added retrying
NicholasLYang Jan 23, 2023
542da28
Ended up implementing my own retry mechanism
NicholasLYang Jan 24, 2023
088d4df
Merge
NicholasLYang Jan 24, 2023
4631280
Cleaning up post-merge
NicholasLYang Jan 24, 2023
26adc03
Merge branch 'main' into nicholasyang/login-without-sso-team
NicholasLYang Jan 24, 2023
15af971
fix lockfile
NicholasLYang Jan 24, 2023
03d4d5a
CI fixes
NicholasLYang Jan 24, 2023
1d0275c
Removed old Go code
NicholasLYang Jan 24, 2023
a0af43e
Fixing tests
NicholasLYang Jan 24, 2023
ffa5165
Merge branch 'main' into nicholasyang/login-without-sso-team
NicholasLYang Jan 25, 2023
de8b489
Clippy
NicholasLYang Jan 25, 2023
b0b79d4
PR feedback
NicholasLYang Jan 26, 2023
b1cde38
Merge branch 'main' into nicholasyang/login-without-sso-team
NicholasLYang Jan 26, 2023
8817cb9
Use base instead of initializing config directly
NicholasLYang Jan 26, 2023
1d8bcc5
Added back the test run logic
NicholasLYang Jan 26, 2023
ea3ea09
PR feedback
NicholasLYang Jan 26, 2023
2277961
Added back spinner and Apple Terminal fallback
NicholasLYang Jan 26, 2023
718c297
Add comment
NicholasLYang Jan 26, 2023
f8972e3
Lockfile fix
NicholasLYang Jan 26, 2023
c5cf5a2
Debugging
NicholasLYang Jan 27, 2023
0e0e04b
Fix integration tests
NicholasLYang Jan 27, 2023
35e3648
Reverting debug addition
NicholasLYang Jan 27, 2023
e6d61b4
Merge branch 'main' into nicholasyang/login-without-sso-team
NicholasLYang Jan 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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