diff --git a/admin/server/github.go b/admin/server/github.go
index e4adb0e518b..c6539f0327d 100644
--- a/admin/server/github.go
+++ b/admin/server/github.go
@@ -231,7 +231,7 @@ func (s *Server) registerGithubEndpoints(mux *http.ServeMux) {
observability.MuxHandle(inner, "/github/connect/callback", s.authenticator.HTTPMiddleware(middleware.Check(s.checkGithubRateLimit("/github/connect/callback"), http.HandlerFunc(s.githubConnectCallback))))
observability.MuxHandle(inner, "/github/auth/login", s.authenticator.HTTPMiddleware(middleware.Check(s.checkGithubRateLimit("github/auth/login"), http.HandlerFunc(s.githubAuthLogin))))
observability.MuxHandle(inner, "/github/auth/callback", s.authenticator.HTTPMiddleware(middleware.Check(s.checkGithubRateLimit("github/auth/callback"), http.HandlerFunc(s.githubAuthCallback))))
- observability.MuxHandle(inner, "/github/post-auth-redirect", s.authenticator.HTTPMiddleware(middleware.Check(s.checkGithubRateLimit("github/post-auth-redirect"), http.HandlerFunc(s.githubRepoStatus))))
+ observability.MuxHandle(inner, "/github/post-auth-redirect", s.authenticator.HTTPMiddleware(middleware.Check(s.checkGithubRateLimit("github/post-auth-redirect"), http.HandlerFunc(s.githubStatus))))
mux.Handle("/github/", observability.Middleware("admin", s.logger, inner))
}
@@ -597,9 +597,10 @@ func (s *Server) githubWebhook(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
-// githubRepoStatus is a http wrapper over [GetGithubRepoStatus]. It redirects to the grantAccessURL if there is no access.
+// githubStatus is a http wrapper over [GetGithubRepoStatus]/[GetGithubUserStatus] depending upon whether `remote` query is passed.
+// It redirects to the grantAccessURL if there is no access.
// It's implemented as a non-gRPC endpoint mounted directly on /github/post-auth-redirect.
-func (s *Server) githubRepoStatus(w http.ResponseWriter, r *http.Request) {
+func (s *Server) githubStatus(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Check the request is made by an authenticated user
claims := auth.GetClaims(ctx)
@@ -608,18 +609,36 @@ func (s *Server) githubRepoStatus(w http.ResponseWriter, r *http.Request) {
return
}
- resp, err := s.GetGithubRepoStatus(ctx, &adminv1.GetGithubRepoStatusRequest{GithubUrl: r.URL.Query().Get("remote")})
- if err != nil {
- http.Error(w, fmt.Sprintf("failed to fetch github repo status: %s", err), http.StatusInternalServerError)
- return
+ var (
+ hasAccess bool
+ grantAccessURL string
+ remote = r.URL.Query().Get("remote")
+ )
+
+ if remote == "" {
+ resp, err := s.GetGithubUserStatus(ctx, &adminv1.GetGithubUserStatusRequest{})
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to fetch user status: %s", err), http.StatusInternalServerError)
+ return
+ }
+ hasAccess = resp.HasAccess
+ grantAccessURL = resp.GrantAccessUrl
+ } else {
+ resp, err := s.GetGithubRepoStatus(ctx, &adminv1.GetGithubRepoStatusRequest{GithubUrl: remote})
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to fetch github repo status: %s", err), http.StatusInternalServerError)
+ return
+ }
+ hasAccess = resp.HasAccess
+ grantAccessURL = resp.GrantAccessUrl
}
- if resp.HasAccess {
+ if hasAccess {
http.Redirect(w, r, s.urls.githubConnectSuccess, http.StatusTemporaryRedirect)
return
}
- redirectURL, err := urlutil.WithQuery(s.urls.githubConnectUI, map[string]string{"redirect": resp.GrantAccessUrl})
+ redirectURL, err := urlutil.WithQuery(s.urls.githubConnectUI, map[string]string{"redirect": grantAccessURL})
if err != nil {
http.Error(w, fmt.Sprintf("failed to create redirect URL: %s", err), http.StatusInternalServerError)
return
diff --git a/cli/cmd/deploy/deploy.go b/cli/cmd/deploy/deploy.go
index 6b8f2c81105..81721fcfbf5 100644
--- a/cli/cmd/deploy/deploy.go
+++ b/cli/cmd/deploy/deploy.go
@@ -158,35 +158,19 @@ func DeployFlow(ctx context.Context, ch *cmdutil.Helper, opts *Options) error {
remote, githubURL, err = gitutil.ExtractGitRemote(localGitPath, opts.RemoteName, false)
if err != nil {
// It's not a valid remote for Github. We still navigate user to login and then ask user to chhose either to create repo manually or let rill create one for them.
+ silent := false
if !ch.IsAuthenticated() {
- err := loginWithTelemetry(ctx, ch, "")
+ err := loginWithTelemetryAndGithubRedirect(ctx, ch, "")
if err != nil {
- ch.PrintfWarn("Login failed with error: %s\n", err.Error())
+ return fmt.Errorf("Login failed with error: %s\n", err.Error())
}
- fmt.Println()
+ silent = true
}
if !errors.Is(err, gitutil.ErrGitRemoteNotFound) && !errors.Is(err, git.ErrRepositoryNotExists) {
return err
}
- ch.PrintfBold(`No git remote was found.
-
-Rill projects deploy continuously when you push changes to Github.
-Therefore, your project must be on Github before you deploy it to Rill.
-`)
-
- printer.ColorYellowBold.Print(`
-You can continue here and Rill can create a Github Repository for you or
-you can exit the command and create a repository manually.
-
-`)
- if !cmdutil.ConfirmPrompt("Do you want to continue?", "", true) {
- ch.PrintfBold(githubSetupMsg)
- return nil
- }
- // ideally this should be clubbed with login similar to main github flow to reduce switch between CLI and browser
- // but it is not possible right now since we need an input from user for the org where they want to create the repo.
- if err := createGithubRepoFlow(ctx, ch, localGitPath); err != nil {
+ if err := createGithubRepoFlow(ctx, ch, localGitPath, silent); err != nil {
return err
}
// In the rest of the flow we still check for the github access.
@@ -219,15 +203,7 @@ you can exit the command and create a repository manually.
silentGitFlow := false
if !ch.IsAuthenticated() {
silentGitFlow = true
- authURL := ch.AdminURL
- if strings.Contains(authURL, "http://localhost:9090") {
- authURL = "http://localhost:8080"
- }
- redirectURL, err := urlutil.WithQuery(urlutil.MustJoinURL(authURL, "/github/post-auth-redirect"), map[string]string{"remote": githubURL})
- if err != nil {
- return err
- }
- if err := loginWithTelemetry(ctx, ch, redirectURL); err != nil {
+ if err := loginWithTelemetryAndGithubRedirect(ctx, ch, githubURL); err != nil {
return err
}
}
@@ -364,6 +340,23 @@ you can exit the command and create a repository manually.
return nil
}
+func loginWithTelemetryAndGithubRedirect(ctx context.Context, ch *cmdutil.Helper, remote string) error {
+ authURL := ch.AdminURL
+ if strings.Contains(authURL, "http://localhost:9090") {
+ authURL = "http://localhost:8080"
+ }
+ var qry map[string]string
+ if remote != "" {
+ qry = map[string]string{"remote": remote}
+ }
+
+ redirectURL, err := urlutil.WithQuery(urlutil.MustJoinURL(authURL, "/github/post-auth-redirect"), qry)
+ if err != nil {
+ return err
+ }
+ return loginWithTelemetry(ctx, ch, redirectURL)
+}
+
func loginWithTelemetry(ctx context.Context, ch *cmdutil.Helper, redirectURL string) error {
ch.PrintfBold("Please log in or sign up for Rill. Opening browser...\n")
time.Sleep(2 * time.Second)
@@ -388,7 +381,7 @@ func loginWithTelemetry(ctx context.Context, ch *cmdutil.Helper, redirectURL str
return nil
}
-func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath string) error {
+func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath string, silent bool) error {
// Get the admin client
c, err := ch.Client()
if err != nil {
@@ -410,7 +403,9 @@ func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath
ch.Print("\t" + res.GrantAccessUrl + "\n\n")
// Open browser if possible
- _ = browser.Open(res.GrantAccessUrl)
+ if !silent {
+ _ = browser.Open(res.GrantAccessUrl)
+ }
}
}
@@ -438,12 +433,28 @@ func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath
// Emit success telemetry
ch.Telemetry(ctx).RecordBehavioralLegacy(activity.BehavioralEventGithubConnectedSuccess)
+ ch.PrintfBold(`No git remote was found.
+
+Rill projects deploy continuously when you push changes to Github.
+Therefore, your project must be on Github before you deploy it to Rill.
+ `)
+
+ ch.Print(`
+You can continue here and Rill can create a Github Repository for you or
+you can exit the command and create a repository manually.
+
+ `)
+ if !cmdutil.ConfirmPrompt("Do you want to continue?", "", true) {
+ ch.PrintfBold(githubSetupMsg)
+ return nil
+ }
+
repoOwner := pollRes.Account
if len(pollRes.Organizations) > 0 {
repoOwners := []string{pollRes.Account}
repoOwners = append(repoOwners, pollRes.Organizations...)
ch.Print("\nYou also have access to organization(s)\n\n")
- repoOwner = cmdutil.SelectPrompt("Please chhose where to create the repository", repoOwners, pollRes.Account)
+ repoOwner = cmdutil.SelectPrompt("Select Github account", repoOwners, pollRes.Account)
}
// create and verify
githubRepository, err := createGithubRepository(ctx, ch, pollRes, localGitPath, repoOwner)
@@ -451,6 +462,8 @@ func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath
return err
}
+ printer.ColorGreenBold.Printf("\nRepository %q created successfully\n\n", *githubRepository.Name)
+ ch.Print("Pushing local project to Github\n\n")
// init git repo
repo, err := git.PlainInit(localGitPath, false)
if err != nil {
@@ -492,7 +505,7 @@ func createGithubRepoFlow(ctx context.Context, ch *cmdutil.Helper, localGitPath
return fmt.Errorf("failed to push to remote %q : %w", *githubRepository.HTMLURL, err)
}
- printer.ColorGreenBold.Printf("\nRepository %q created successfully. Local changes pushed to remote.\n\n", *githubRepository.Name)
+ ch.Print("Local changes pushed to remote\n\n")
return nil
}
}
@@ -508,12 +521,11 @@ func createGithubRepository(ctx context.Context, ch *cmdutil.Helper, pollRes *ad
var githubRepo *github.Repository
var err error
- for i, tempRepoName := 1, repoName; i <= 10; i++ {
- githubRepo, _, err = githubClient.Repositories.Create(ctx, repoOwner, &github.Repository{Name: &tempRepoName, DefaultBranch: &defaultBranch})
+ for i := 1; i <= 10; i++ {
+ githubRepo, _, err = githubClient.Repositories.Create(ctx, repoOwner, &github.Repository{Name: &repoName, DefaultBranch: &defaultBranch})
if err == nil {
break
}
-
if strings.Contains(err.Error(), "authentication") || strings.Contains(err.Error(), "credentials") {
// The users who installed app before we started including repo:write permissions need to accept permissions
// and then only we can create repositories.
@@ -523,8 +535,12 @@ func createGithubRepository(ctx context.Context, ch *cmdutil.Helper, pollRes *ad
if !strings.Contains(err.Error(), "name already exists") {
return nil, fmt.Errorf("failed to create repository: %w", err)
}
- // there is a name conflict
- tempRepoName = repoName + fmt.Sprintf("_v%v", i)
+
+ ch.Printf("Repository name %q is already taken\n", repoName)
+ repoName = cmdutil.InputPrompt("Please provide alternate name", "")
+ }
+ if err != nil {
+ return nil, fmt.Errorf("failed to create repository: %w", err)
}
// the create repo API does not wait for repo creation to be fully processed on server. Need to verify by making a get call in a loop
@@ -539,7 +555,7 @@ func createGithubRepository(ctx context.Context, ch *cmdutil.Helper, pollRes *ad
select {
case <-pollCtx.Done():
return nil, pollCtx.Err()
- case <-time.After(5 * time.Second):
+ case <-time.After(2 * time.Second):
// Ready to check again.
}
_, _, err := githubClient.Repositories.Get(ctx, repoOwner, repoName)
diff --git a/cli/cmd/org/delete.go b/cli/cmd/org/delete.go
index 801dc45cc44..6713a03547d 100644
--- a/cli/cmd/org/delete.go
+++ b/cli/cmd/org/delete.go
@@ -55,11 +55,7 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
if !force {
fmt.Printf("Warn: Deleting the org %q will remove all metadata associated with the org\n", name)
msg := fmt.Sprintf("Type %q to confirm deletion", name)
- org, err := cmdutil.InputPrompt(msg, "")
- if err != nil {
- return err
- }
-
+ org := cmdutil.InputPrompt(msg, "")
if org != name {
return fmt.Errorf("Entered incorrect name: %q, expected value is %q", org, name)
}
diff --git a/cli/cmd/org/edit.go b/cli/cmd/org/edit.go
index c0342c68163..c508af767e3 100644
--- a/cli/cmd/org/edit.go
+++ b/cli/cmd/org/edit.go
@@ -61,10 +61,7 @@ func EditCmd(ch *cmdutil.Helper) *cobra.Command {
}
if promptFlagValues {
- description, err = cmdutil.InputPrompt("Enter the description", org.Description)
- if err != nil {
- return err
- }
+ description := cmdutil.InputPrompt("Enter the description", org.Description)
req.Description = &description
}
diff --git a/cli/cmd/project/delete.go b/cli/cmd/project/delete.go
index 612b475e896..1a443b06a41 100644
--- a/cli/cmd/project/delete.go
+++ b/cli/cmd/project/delete.go
@@ -41,11 +41,7 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
ch.PrintfWarn("Warn: Deleting the project %q will remove all metadata associated with the project\n", name)
msg := fmt.Sprintf("Type %q to confirm deletion", name)
- project, err := cmdutil.InputPrompt(msg, "")
- if err != nil {
- return err
- }
-
+ project := cmdutil.InputPrompt(msg, "")
if project != name {
return fmt.Errorf("Entered incorrect name : %q, expected value is %q", project, name)
}
diff --git a/cli/cmd/project/edit.go b/cli/cmd/project/edit.go
index 2149ce277c2..11654ed53f5 100644
--- a/cli/cmd/project/edit.go
+++ b/cli/cmd/project/edit.go
@@ -86,16 +86,10 @@ func EditCmd(ch *cmdutil.Helper) *cobra.Command {
}
proj := resp.Project
- description, err = cmdutil.InputPrompt("Enter the description", proj.Description)
- if err != nil {
- return err
- }
+ description = cmdutil.InputPrompt("Enter the description", proj.Description)
req.Description = &description
- prodBranch, err = cmdutil.InputPrompt("Enter the production branch", proj.ProdBranch)
- if err != nil {
- return err
- }
+ prodBranch = cmdutil.InputPrompt("Enter the production branch", proj.ProdBranch)
req.ProdBranch = &prodBranch
public = cmdutil.ConfirmPrompt("Make project public", "", proj.Public)
diff --git a/cli/pkg/cmdutil/prompt.go b/cli/pkg/cmdutil/prompt.go
index e174d7974ca..db9cdf86188 100644
--- a/cli/pkg/cmdutil/prompt.go
+++ b/cli/pkg/cmdutil/prompt.go
@@ -48,7 +48,7 @@ func ConfirmPrompt(msg, help string, def bool) bool {
return result
}
-func InputPrompt(msg, def string) (string, error) {
+func InputPrompt(msg, def string) string {
prompt := &survey.Input{
Message: msg,
Default: def,
@@ -56,9 +56,9 @@ func InputPrompt(msg, def string) (string, error) {
result := def
if err := survey.AskOne(prompt, &result); err != nil {
fmt.Printf("Prompt failed %v\n", err)
- return "", err
+ os.Exit(1)
}
- return strings.TrimSpace(result), nil
+ return strings.TrimSpace(result)
}
func StringPromptIfEmpty(input *string, msg string) {
@@ -109,11 +109,7 @@ func SetFlagsByInputPrompts(cmd cobra.Command, flags ...string) error {
val = fmt.Sprintf("%t", public)
default:
- val, err = InputPrompt(fmt.Sprintf("Enter the %s", f.Usage), f.DefValue)
- if err != nil {
- fmt.Println("error while input prompt, error:", err)
- return
- }
+ val = InputPrompt(fmt.Sprintf("Enter the %s", f.Usage), f.DefValue)
}
err = f.Value.Set(val)
diff --git a/web-admin/src/routes/-/github/connect/+page.svelte b/web-admin/src/routes/-/github/connect/+page.svelte
index aa9a9818307..bc308fec184 100644
--- a/web-admin/src/routes/-/github/connect/+page.svelte
+++ b/web-admin/src/routes/-/github/connect/+page.svelte
@@ -42,11 +42,13 @@