Skip to content

Commit

Permalink
Merge branch 'main' into adityahegde/connect-to-github-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
AdityaHegde committed Jul 30, 2024
2 parents 0c17b50 + 9096522 commit f31a127
Show file tree
Hide file tree
Showing 71 changed files with 1,368 additions and 499 deletions.
16 changes: 14 additions & 2 deletions cli/pkg/cmdutil/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"context"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"

"github.com/rilldata/rill/admin/client"
"github.com/rilldata/rill/admin/database"
"github.com/rilldata/rill/cli/pkg/dotrill"
"github.com/rilldata/rill/cli/pkg/dotrillcloud"
"github.com/rilldata/rill/cli/pkg/gitutil"
Expand Down Expand Up @@ -274,9 +276,19 @@ func (h *Helper) InferProjectName(ctx context.Context, org, path string) (string
Id: rc.ProjectID,
})
if err != nil {
return "", err
// unset if project doesnt exist
if errors.Is(err, database.ErrNotFound) {
err = dotrillcloud.Delete(path, h.AdminURL)
if err != nil {
return "", err
}
}
// do not error here.
// this could be because the locally saved project id might not be available when project is deleted outside the project path
// or the current user might not have access
} else {
return proj.Project.Name, nil
}
return proj.Project.Name, nil
}

// Verify projectPath is a Git repo with remote on Github
Expand Down
201 changes: 127 additions & 74 deletions cli/pkg/local/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
localv1 "github.com/rilldata/rill/proto/gen/rill/local/v1"
"github.com/rilldata/rill/proto/gen/rill/local/v1/localv1connect"
"github.com/rilldata/rill/runtime/compilers/rillv1"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -144,8 +145,59 @@ func (s *Server) DeployValidation(ctx context.Context, r *connect.Request[localv
return nil, err
}
var deployedProjectID string
var project *adminv1.Project
if rc != nil {
deployedProjectID = rc.ProjectID

proj, err := c.GetProjectByID(ctx, &adminv1.GetProjectByIDRequest{
Id: deployedProjectID,
})
if err != nil {
// unset if project doesnt exist
if errors.Is(err, database.ErrNotFound) {
err = dotrillcloud.Delete(s.app.ProjectPath, s.app.adminURL)
if err != nil {
return nil, err
}
}
deployedProjectID = ""
} else {
project = proj.Project
}
}

// get rill user orgs
resp, err := c.ListOrganizations(ctx, &adminv1.ListOrganizationsRequest{})
if err != nil {
return nil, err
}

userOrgs := make([]string, 0, len(resp.Organizations))
for _, org := range resp.Organizations {
userOrgs = append(userOrgs, org.Name)
}
// TODO if len(userOrgs) > 0 then check if any project in these orgs already deploys from ghUrl

isGithubRepo := false
githubRemoteFound := false
repoAccess := false
var remote *gitutil.Remote
var ghURL string

if project == nil || project.GithubUrl == "" {
// check if project is a git repo only if there is no project already, or it is connected to github
remote, ghURL, err = gitutil.ExtractGitRemote(s.app.ProjectPath, "", false)
if err != nil {
if errors.Is(err, gitutil.ErrGitRemoteNotFound) {
isGithubRepo = true
} else if !errors.Is(err, git.ErrRepositoryNotExists) {
// do not return error if repo doesn't exist
return nil, err
}
} else {
isGithubRepo = true
githubRemoteFound = true
}
}

userStatus, err := c.GetGithubUserStatus(ctx, &adminv1.GetGithubUserStatusRequest{})
Expand All @@ -163,30 +215,30 @@ func (s *Server) DeployValidation(ctx context.Context, r *connect.Request[localv
GithubUserName: "",
GithubUserPermission: adminv1.GithubPermission_GITHUB_PERMISSION_UNSPECIFIED,
GithubOrganizationPermissions: nil,
IsGithubRepo: false,
IsGithubRemoteFound: false,
IsGithubRepo: isGithubRepo,
IsGithubRemoteFound: githubRemoteFound,
IsGithubRepoAccessGranted: false,
GithubUrl: "",
HasUncommittedChanges: nil,
RillOrgExistsAsGithubUserName: false,
RillUserOrgs: nil,
RillUserOrgs: userOrgs,
LocalProjectName: localProjectName,
DeployedProjectId: deployedProjectID,
}), nil
}

isGithubRepo := true
githubRemoteFound := true
repoAccess := false
// check if project is a git repo
remote, ghURL, err := gitutil.ExtractGitRemote(s.app.ProjectPath, "", false)
if err != nil {
if errors.Is(err, git.ErrRepositoryNotExists) {
isGithubRepo = false
} else if errors.Is(err, gitutil.ErrGitRemoteNotFound) {
githubRemoteFound = false
rillOrgExistsAsGitUserName := false
// if user does not any have orgs, check if any orgs exist as github username as we suggest this as org name
if len(userOrgs) == 0 {
_, err = c.GetOrganization(ctx, &adminv1.GetOrganizationRequest{
Name: userStatus.Account,
})
if err != nil {
if !errors.Is(err, database.ErrNotFound) {
return nil, err
}
} else {
return nil, err
rillOrgExistsAsGitUserName = true
}
}

Expand Down Expand Up @@ -225,41 +277,14 @@ func (s *Server) DeployValidation(ctx context.Context, r *connect.Request[localv
IsGithubRepoAccessGranted: repoAccess,
GithubUrl: "",
HasUncommittedChanges: hasUncommittedChanges,
RillOrgExistsAsGithubUserName: false,
RillUserOrgs: nil,
RillOrgExistsAsGithubUserName: rillOrgExistsAsGitUserName,
RillUserOrgs: userOrgs,
LocalProjectName: localProjectName,
DeployedProjectId: deployedProjectID,
}), nil
}
}

// get rill user orgs
resp, err := c.ListOrganizations(ctx, &adminv1.ListOrganizationsRequest{})
if err != nil {
return nil, err
}

userOrgs := make([]string, 0, len(resp.Organizations))
for _, org := range resp.Organizations {
userOrgs = append(userOrgs, org.Name)
}
// TODO if len(userOrgs) > 0 then check if any project in these orgs already deploys from ghUrl

rillOrgExistsAsGitUserName := false
// if user does not any have orgs, check if any orgs exist as github username as we suggest this as org name
if len(userOrgs) == 0 {
_, err = c.GetOrganization(ctx, &adminv1.GetOrganizationRequest{
Name: userStatus.Account,
})
if err != nil {
if !errors.Is(err, database.ErrNotFound) {
return nil, err
}
} else {
rillOrgExistsAsGitUserName = true
}
}

return connect.NewResponse(&localv1.DeployValidationResponse{
IsAuthenticated: true,
LoginUrl: loginURL,
Expand Down Expand Up @@ -434,6 +459,26 @@ func (s *Server) DeployProject(ctx context.Context, r *connect.Request[localv1.D
return nil, err
}

// check if rill org exists
_, err = c.GetOrganization(ctx, &adminv1.GetOrganizationRequest{
Name: r.Msg.Org,
})
if err != nil {
st, ok := status.FromError(err)
if ok && st.Code() == codes.NotFound {
// create org if not exists
_, err = c.CreateOrganization(ctx, &adminv1.CreateOrganizationRequest{
Name: r.Msg.Org,
Description: "Auto created by Rill",
})
if err != nil {
return nil, err
}
} else {
return nil, err
}
}

var projRequest *adminv1.CreateProjectRequest
if r.Msg.Upload { // upload repo to rill managed storage instead of github
repo, release, err := s.app.Runtime.Repo(ctx, s.app.Instance.ID)
Expand Down Expand Up @@ -513,26 +558,6 @@ func (s *Server) DeployProject(ctx context.Context, r *connect.Request[localv1.D
}
}

// check if rill org exists
_, err = c.GetOrganization(ctx, &adminv1.GetOrganizationRequest{
Name: r.Msg.Org,
})
if err != nil {
st, ok := status.FromError(err)
if ok && st.Code() == codes.NotFound {
// create org if not exists
_, err = c.CreateOrganization(ctx, &adminv1.CreateOrganizationRequest{
Name: r.Msg.Org,
Description: "Auto created by Rill",
})
if err != nil {
return nil, err
}
} else {
return nil, err
}
}

// create project
suffix := 0
var projResp *adminv1.CreateProjectResponse
Expand All @@ -557,6 +582,27 @@ func (s *Server) DeployProject(ctx context.Context, r *connect.Request[localv1.D
return nil, err
}

// Parse .env and push it as variables
repo, instanceID, err := cmdutil.RepoForProjectPath(s.app.ProjectPath)
if err != nil {
return nil, err
}
parser, err := rillv1.Parse(ctx, repo, instanceID, "prod", "duckdb")
if err != nil {
return nil, fmt.Errorf("failed to parse project: %w", err)
}
if parser.RillYAML == nil {
return nil, fmt.Errorf("not a valid Rill project (missing a rill.yaml file)")
}
_, err = c.UpdateProjectVariables(ctx, &adminv1.UpdateProjectVariablesRequest{
OrganizationName: r.Msg.Org,
Name: r.Msg.ProjectName,
Variables: parser.DotEnv,
})
if err != nil {
return nil, err
}

return connect.NewResponse(&localv1.DeployProjectResponse{
DeployId: projResp.Project.ProdDeploymentId,
Org: projResp.Project.OrgName,
Expand All @@ -575,31 +621,38 @@ func (s *Server) RedeployProject(ctx context.Context, r *connect.Request[localv1
return nil, err
}

projResp, err := c.GetProjectByID(ctx, &adminv1.GetProjectByIDRequest{
Id: r.Msg.ProjectId,
})
if err != nil {
return nil, err
}

if r.Msg.Reupload {
repo, release, err := s.app.Runtime.Repo(ctx, s.app.Instance.ID)
if err != nil {
return nil, err
}
defer release()

projResp, err := c.GetProjectByID(ctx, &adminv1.GetProjectByIDRequest{
Id: r.Msg.ProjectId,
})
if err != nil {
return nil, err
}

assetID, err := cmdutil.UploadRepo(ctx, repo, s.app.ch, projResp.Project.OrgName, projResp.Project.Name)
if err != nil {
return nil, err
}
_, err = c.UpdateProject(ctx, &adminv1.UpdateProjectRequest{ArchiveAssetId: &assetID})
_, err = c.UpdateProject(ctx, &adminv1.UpdateProjectRequest{
ArchiveAssetId: &assetID,
OrganizationName: projResp.Project.OrgName,
Name: projResp.Project.Name,
})
if err != nil {
return nil, err
}
}

// TODO : Add other update project fields
return connect.NewResponse(&localv1.RedeployProjectResponse{}), nil
return connect.NewResponse(&localv1.RedeployProjectResponse{
FrontendUrl: projResp.Project.FrontendUrl,
}), nil
}

func (s *Server) GetCurrentUser(ctx context.Context, r *connect.Request[localv1.GetCurrentUserRequest]) (*connect.Response[localv1.GetCurrentUserResponse], error) {
Expand Down Expand Up @@ -697,13 +750,13 @@ func (s *Server) authCallbackHandler() http.Handler {
// Exchange the code for an access token
token, err := authenticator.ExchangeCodeForToken(code)
if err != nil {
http.Error(w, "failed to exchange code for token", http.StatusInternalServerError)
http.Error(w, fmt.Sprintf("failed to exchange code for token: %s", err), http.StatusInternalServerError)
return
}
// save token and redirect back to url provided by caller when initiating auth flow
err = dotrill.SetAccessToken(token)
if err != nil {
http.Error(w, "failed to save access token", http.StatusInternalServerError)
http.Error(w, fmt.Sprintf("failed to save access token: %s", err), http.StatusInternalServerError)
return
}
s.app.ch.AdminTokenDefault = token
Expand Down
5 changes: 4 additions & 1 deletion cli/pkg/pkce/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"net/url"
Expand Down Expand Up @@ -96,7 +97,9 @@ func (a *Authenticator) ExchangeCodeForToken(code string) (string, error) {

// Check if the response is an error
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode)
// read body to get the error message
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("unexpected status code: %d, status: %s, body: %s", resp.StatusCode, resp.Status, string(body))
}

tokenResponse := &oauth.TokenResponse{}
Expand Down
Loading

0 comments on commit f31a127

Please sign in to comment.