From 41ea8ffc008280633dd7335659b16288e3c7f181 Mon Sep 17 00:00:00 2001 From: jfeodor <98314775+jfeodor@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:33:35 +0100 Subject: [PATCH] fix(cli): deploy apps within project directories (#54) Properly handles the `deploy` command `--project-dir` flag to allow deploying an app that exists within a project directory. Also: * Fixes some linter issues related to format printing * Adds improved traceability to the requests sent to the server by adding operation names --- cmd/deploy/cmd.go | 2 +- cmd/deploy/deploy.go | 56 ++++++++++++++++++++++------ cmd/legacy/push/build_events.go | 2 +- cmd/login/login.go | 2 +- internal/app/app_create.go | 6 ++- internal/app/app_delete.go | 6 ++- internal/app/app_deploy.go | 18 ++++++--- internal/app/app_deploy_logs.go | 3 +- internal/app/app_list.go | 4 +- internal/app/app_read.go | 6 ++- internal/app/current_app_version.go | 6 ++- internal/app/deploy_events.go | 4 +- internal/app/version_create.go | 10 ++--- internal/app/version_download_url.go | 10 +++-- internal/app/version_upload_url.go | 6 ++- 15 files changed, 99 insertions(+), 42 deletions(-) diff --git a/cmd/deploy/cmd.go b/cmd/deploy/cmd.go index 86da62e6..7f5ca916 100644 --- a/cmd/deploy/cmd.go +++ b/cmd/deploy/cmd.go @@ -44,7 +44,7 @@ var ( appSlug string verbose bool appDir string = "." - projectDir string = "." + projectDir string = "" message string version string follow bool diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 2d9e5fff..df136891 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -55,6 +55,12 @@ type DeployInput struct { } func Deploy(ctx context.Context, apps AppService, input DeployInput) error { + appRelativePath, err := findAppRelativePath(input) + if err != nil { + output.PrintError("Project directory %q must be a parent of app directory %q", "", input.ProjectDir, input.AppDir) + return err + } + manifest, secrets, err := loadAppConfiguration(input) if err != nil { return err @@ -69,12 +75,13 @@ func Deploy(ctx context.Context, apps AppService, input DeployInput) error { if err != nil { return err } + defer os.Remove(archive.Name()) if err := uploadAppArchive(ctx, apps, archive, appVersionOutput.AppVersionID); err != nil { return err } - if err := deployApp(ctx, appVersionOutput, secrets, apps, input); err != nil { + if err := deployApp(ctx, appVersionOutput, secrets, apps, input, appRelativePath); err != nil { return err } @@ -107,6 +114,29 @@ func Deploy(ctx context.Context, apps AppService, input DeployInput) error { return nil } +func findAppRelativePath(input DeployInput) (string, error) { + if input.ProjectDir == "" { + return "", nil + } + + absProjectDir, err := filepath.Abs(input.ProjectDir) + if err != nil { + return "", err + } + + absAppDir, err := filepath.Abs(input.AppDir) + if err != nil { + return "", err + } + + rel, err := filepath.Rel(absProjectDir, absAppDir) + if err != nil || strings.HasPrefix(rel, "..") { + return "", err + } + + return filepath.ToSlash(rel), nil +} + func loadAppConfiguration(input DeployInput) (*manifest.Manifest, map[string]string, error) { task := output.StartTask("Loading app configuration") m, err := manifest.Load(filepath.Join(input.AppDir, manifest.ManifestFileName)) @@ -135,25 +165,26 @@ func loadAppConfiguration(input DeployInput) (*manifest.Manifest, map[string]str } func createAppArchive(input DeployInput, manifest *manifest.Manifest) (*os.File, error) { - task := output.StartTask("Creating app archive") - tarSrcDir := input.AppDir + srcPath := input.AppDir if input.ProjectDir != "" { - tarSrcDir = input.ProjectDir + srcPath = input.ProjectDir } - tarPath := path.Join(tarSrcDir, ".tmp_app_archive.tar") - if err := archive.TarCreate(tarSrcDir, tarPath, manifest.Exclude); err != nil { + task := output.StartTask("Creating app archive") + archivePath := path.Join(srcPath, ".tmp_app_archive.tar") + + if err := archive.TarCreate(srcPath, archivePath, manifest.Exclude); err != nil { task.Error() output.PrintErrorDetails("Error archiving app source", err) return nil, err } - defer os.Remove(tarPath) - archive, err := os.Open(tarPath) + archive, err := os.Open(archivePath) if err != nil { task.Error() - output.PrintErrorDetails("Error archiving app source", err) + output.PrintErrorDetails("Error creating app source archive", err) + os.Remove(archivePath) // nolint: errcheck return nil, err } @@ -318,13 +349,16 @@ func printAppSourceUploadErr(appSourceUploadErr *app.AppSourceUploadError) { ) } -func deployApp(ctx context.Context, appVersionOutput app.CreateAppVersionOutput, secrets map[string]string, apps AppService, input DeployInput) error { +func deployApp(ctx context.Context, appVersionOutput app.CreateAppVersionOutput, secrets map[string]string, apps AppService, input DeployInput, appRelativePath string) error { task := output.StartTask("Deploying app") - deployAppInput := app.DeployAppInput{AppVersionID: appVersionOutput.AppVersionID, Secrets: secrets} + + deployAppInput := app.DeployAppInput{AppVersionID: appVersionOutput.AppVersionID, Secrets: secrets, AppRelativePath: appRelativePath} deployAppOutput, err := apps.DeployApp(ctx, deployAppInput) if err != nil { task.Error() output.PrintErrorDetails("Error deploying app", err) + + return err } appDeploymentStatusEventUpdater := statusUpdater{verbose: input.Verbose, task: task} diff --git a/cmd/legacy/push/build_events.go b/cmd/legacy/push/build_events.go index 8227f5ca..c32b6aee 100644 --- a/cmd/legacy/push/build_events.go +++ b/cmd/legacy/push/build_events.go @@ -85,7 +85,7 @@ func buildEventSubscription(client subscriptionClient, w io.Writer, buildID stri } if sub.BuildEvents.Typename == "BuildEventFailure" { - fmt.Fprintf(w, sub.BuildEvents.Failure.Result) + fmt.Fprint(w, sub.BuildEvents.Failure.Result) return errors.New(sub.BuildEvents.Failure.Result) } diff --git a/cmd/login/login.go b/cmd/login/login.go index 3ded3fc1..689ea97f 100644 --- a/cmd/login/login.go +++ b/cmd/login/login.go @@ -65,7 +65,7 @@ func login(a auth.Authenticator, ctx context.Context) (*auth.User, error) { } if err := a.OpenURL(state.VerificationURI); err != nil { - fmt.Printf( + fmt.Println( "The browser could not be opened automatically, please go to this site to continue\n" + "the log-in: " + state.VerificationURI, ) diff --git a/internal/app/app_create.go b/internal/app/app_create.go index 4e28d052..e9ada171 100644 --- a/internal/app/app_create.go +++ b/internal/app/app_create.go @@ -2,6 +2,8 @@ package app import ( "context" + + "github.com/hasura/go-graphql-client" ) type CreateAppInput struct { @@ -16,7 +18,7 @@ type CreateAppOutput struct { } const appCreateText = ` -mutation AppCreate($orgSlug: String!, $appSlug: String!, $displayName: String!, $description: String!) { +mutation CLIAppCreate($orgSlug: String!, $appSlug: String!, $displayName: String!, $description: String!) { appCreate(organizationSlug: $orgSlug, appData: {appSlug: $appSlug, displayName: $displayName, description: $description}) { id } @@ -38,7 +40,7 @@ func (s *Service) Create(ctx context.Context, input CreateAppInput) (CreateAppOu "displayName": input.DisplayName, "description": input.Description, } - err := s.client.Exec(ctx, appCreateText, &resp, variables) + err := s.client.Exec(ctx, appCreateText, &resp, variables, graphql.OperationName("CLIAppCreate")) if err != nil { return CreateAppOutput{}, convertErrors(err) } diff --git a/internal/app/app_delete.go b/internal/app/app_delete.go index d6a2bffe..c4498ee3 100644 --- a/internal/app/app_delete.go +++ b/internal/app/app_delete.go @@ -2,10 +2,12 @@ package app import ( "context" + + "github.com/hasura/go-graphql-client" ) const deleteMutation string = ` - mutation DeleteApp($orgSlug: String!, $appSlug: String!) { + mutation CLIAppDelete($orgSlug: String!, $appSlug: String!) { appDelete(input: {organizationSlug: $orgSlug, appSlug: $appSlug}) { __typename } @@ -27,7 +29,7 @@ func (s *Service) Delete(ctx context.Context, input DeleteAppInput) error { resp := deleteAppResponse{} vars := map[string]any{"orgSlug": input.OrganizationSlug, "appSlug": input.AppSlug} - err := s.client.Exec(ctx, deleteMutation, &resp, vars) + err := s.client.Exec(ctx, deleteMutation, &resp, vars, graphql.OperationName("CLIAppDelete")) if err != nil { return convertErrors(err) } diff --git a/internal/app/app_deploy.go b/internal/app/app_deploy.go index 35c2abb3..fbdbeea2 100644 --- a/internal/app/app_deploy.go +++ b/internal/app/app_deploy.go @@ -3,12 +3,14 @@ package app import ( "context" + "github.com/hasura/go-graphql-client" "numerous.com/cli/internal/gql/secret" ) type DeployAppInput struct { - AppVersionID string - Secrets map[string]string + AppVersionID string + AppRelativePath string + Secrets map[string]string } type DeployAppOutput struct { @@ -16,8 +18,8 @@ type DeployAppOutput struct { } const appDeployText = ` -mutation AppDeploy($appVersionID: ID!, $secrets: [AppSecret!]) { - appDeploy(appVersionID: $appVersionID, input: {secrets: $secrets}) { +mutation CLIAppDeploy($appVersionID: ID!, $secrets: [AppSecret!], $appRelativePath: String!) { + appDeploy(appVersionID: $appVersionID, input: {appRelativePath: $appRelativePath, secrets: $secrets}) { id } } @@ -32,9 +34,13 @@ type appDeployResponse struct { func (s *Service) DeployApp(ctx context.Context, input DeployAppInput) (DeployAppOutput, error) { var resp appDeployResponse convertedSecrets := secret.AppSecretsFromMap(input.Secrets) - variables := map[string]any{"appVersionID": input.AppVersionID, "secrets": convertedSecrets} + variables := map[string]any{ + "appVersionID": input.AppVersionID, + "secrets": convertedSecrets, + "appRelativePath": input.AppRelativePath, + } - err := s.client.Exec(ctx, appDeployText, &resp, variables) + err := s.client.Exec(ctx, appDeployText, &resp, variables, graphql.OperationName("CLIAppDeploy")) if err != nil { return DeployAppOutput{}, convertErrors(err) } diff --git a/internal/app/app_deploy_logs.go b/internal/app/app_deploy_logs.go index ef74b4af..e95d910b 100644 --- a/internal/app/app_deploy_logs.go +++ b/internal/app/app_deploy_logs.go @@ -5,6 +5,7 @@ import ( "numerous.com/cli/internal/appident" + "github.com/hasura/go-graphql-client" "github.com/hasura/go-graphql-client/pkg/jsonutil" ) @@ -40,7 +41,7 @@ func (s *Service) AppDeployLogs(ai appident.AppIdentifier) (chan AppDeployLogEnt vars := make(map[string]any) vars["orgSlug"] = ai.OrganizationSlug vars["appSlug"] = ai.AppSlug - _, err := s.subscription.Subscribe(&AppDeployLogsSubscription{}, vars, handler) + _, err := s.subscription.Subscribe(&AppDeployLogsSubscription{}, vars, handler, graphql.OperationName("CLIAppDeployLogs")) if err != nil { return nil, err } diff --git a/internal/app/app_list.go b/internal/app/app_list.go index 24c98dfc..2fb661fb 100644 --- a/internal/app/app_list.go +++ b/internal/app/app_list.go @@ -4,6 +4,8 @@ import ( "context" "errors" "time" + + "github.com/hasura/go-graphql-client" ) type ListApp struct { @@ -62,7 +64,7 @@ var ( func (s *Service) List(ctx context.Context, organizationSlug string) ([]ListApp, error) { var q QueryOrganizationApps - if err := s.client.Query(ctx, &q, map[string]interface{}{"organizationSlug": organizationSlug}); err != nil { + if err := s.client.Query(ctx, &q, map[string]interface{}{"organizationSlug": organizationSlug}, graphql.OperationName("CLIAppList")); err != nil { return nil, convertErrors(err) } diff --git a/internal/app/app_read.go b/internal/app/app_read.go index ce4bae1a..0c11466e 100644 --- a/internal/app/app_read.go +++ b/internal/app/app_read.go @@ -2,6 +2,8 @@ package app import ( "context" + + "github.com/hasura/go-graphql-client" ) type ReadAppInput struct { @@ -14,7 +16,7 @@ type ReadAppOutput struct { } const queryAppText = ` -query App($orgSlug: String!, $appSlug: String!) { +query CLIAppRead($orgSlug: String!, $appSlug: String!) { app(organizationSlug: $orgSlug, appSlug: $appSlug) { id } @@ -31,7 +33,7 @@ func (s *Service) ReadApp(ctx context.Context, input ReadAppInput) (ReadAppOutpu var resp appResponse variables := map[string]any{"orgSlug": input.OrganizationSlug, "appSlug": input.AppSlug} - err := s.client.Exec(ctx, queryAppText, &resp, variables) + err := s.client.Exec(ctx, queryAppText, &resp, variables, graphql.OperationName("CLIAppRead")) if err == nil { return ReadAppOutput{AppID: resp.App.ID}, nil } diff --git a/internal/app/current_app_version.go b/internal/app/current_app_version.go index 021217d0..2cb24003 100644 --- a/internal/app/current_app_version.go +++ b/internal/app/current_app_version.go @@ -3,6 +3,8 @@ package app import ( "context" "errors" + + "github.com/hasura/go-graphql-client" ) var ErrNotDeployed = errors.New("app is not deployed") @@ -17,7 +19,7 @@ type CurrentAppVersionOutput struct { } const queryCurrentAppVersionText = ` -query App($orgSlug: String!, $appSlug: String!) { +query CLIReadCurrentAppVersion($orgSlug: String!, $appSlug: String!) { app(organizationSlug: $orgSlug, appSlug: $appSlug) { defaultDeployment { current { @@ -46,7 +48,7 @@ func (s *Service) CurrentAppVersion(ctx context.Context, input CurrentAppVersion var resp currentAppVersionResponse variables := map[string]any{"orgSlug": input.OrganizationSlug, "appSlug": input.AppSlug} - err := s.client.Exec(ctx, queryCurrentAppVersionText, &resp, variables) + err := s.client.Exec(ctx, queryCurrentAppVersionText, &resp, variables, graphql.OperationName("CLIReadCurrentAppVersion")) if err != nil { return CurrentAppVersionOutput{}, convertErrors(err) } diff --git a/internal/app/deploy_events.go b/internal/app/deploy_events.go index ea36376a..660cdd46 100644 --- a/internal/app/deploy_events.go +++ b/internal/app/deploy_events.go @@ -81,9 +81,9 @@ func (s *Service) DeployEvents(ctx context.Context, input DeployEventsInput) err return nil } - _, err := s.subscription.Subscribe(&DeployEventsSubscription{}, variables, handler) + _, err := s.subscription.Subscribe(&DeployEventsSubscription{}, variables, handler, graphql.OperationName("CLIAppDeployEvents")) if err != nil { - return nil + return err } err = s.subscription.Run() diff --git a/internal/app/version_create.go b/internal/app/version_create.go index 7f1dec11..026ecf37 100644 --- a/internal/app/version_create.go +++ b/internal/app/version_create.go @@ -2,6 +2,8 @@ package app import ( "context" + + "github.com/hasura/go-graphql-client" ) type CreateAppVersionInput struct { @@ -17,7 +19,7 @@ type CreateAppVersionOutput struct { } const appVersionCreateText = ` -mutation AppVersionCreate($appID: ID!, $version: String, $message: String!) { +mutation CLIAppVersionCreate($appID: ID!, $version: String, $message: String!) { appVersionCreate(appID: $appID, input: {version: $version, message: $message}) { id } @@ -42,12 +44,10 @@ func (s *Service) CreateVersion(ctx context.Context, input CreateAppVersionInput variables["version"] = input.Version } - err := s.client.Exec(ctx, appVersionCreateText, &resp, variables) + err := s.client.Exec(ctx, appVersionCreateText, &resp, variables, graphql.OperationName("CLIAppVersionCreate")) if err != nil { return CreateAppVersionOutput{}, err } - return CreateAppVersionOutput{ - AppVersionID: resp.AppVersionCreate.ID, - }, nil + return CreateAppVersionOutput{AppVersionID: resp.AppVersionCreate.ID}, nil } diff --git a/internal/app/version_download_url.go b/internal/app/version_download_url.go index fcdb0be1..2ae83332 100644 --- a/internal/app/version_download_url.go +++ b/internal/app/version_download_url.go @@ -1,6 +1,10 @@ package app -import "context" +import ( + "context" + + "github.com/hasura/go-graphql-client" +) type AppVersionDownloadURLInput struct { AppVersionID string @@ -11,7 +15,7 @@ type AppVersionDownloadURLOutput struct { } const appVersionDownloadURLText = ` -mutation AppVersionDownloadURL($appVersionID: ID!) { +mutation CLIAppVersionDownloadURL($appVersionID: ID!) { appVersionDownloadURL(appVersionID: $appVersionID) { url } @@ -30,7 +34,7 @@ func (s *Service) AppVersionDownloadURL(ctx context.Context, input AppVersionDow variables := map[string]any{ "appVersionID": input.AppVersionID, } - err := s.client.Exec(ctx, appVersionDownloadURLText, &resp, variables) + err := s.client.Exec(ctx, appVersionDownloadURLText, &resp, variables, graphql.OperationName("CLIAppVersionDownloadURL")) if err != nil { return AppVersionDownloadURLOutput{}, convertErrors(err) } diff --git a/internal/app/version_upload_url.go b/internal/app/version_upload_url.go index 87cf6810..00b8ee80 100644 --- a/internal/app/version_upload_url.go +++ b/internal/app/version_upload_url.go @@ -2,6 +2,8 @@ package app import ( "context" + + "github.com/hasura/go-graphql-client" ) type AppVersionUploadURLInput struct { @@ -13,7 +15,7 @@ type AppVersionUploadURLOutput struct { } const appVersionUploadURLText = ` -mutation AppVersionUploadURL($appVersionID: ID!) { +mutation CLIAppVersionUploadURL($appVersionID: ID!) { appVersionUploadURL(appVersionID: $appVersionID) { url } @@ -32,7 +34,7 @@ func (s *Service) AppVersionUploadURL(ctx context.Context, input AppVersionUploa variables := map[string]any{ "appVersionID": input.AppVersionID, } - err := s.client.Exec(ctx, appVersionUploadURLText, &resp, variables) + err := s.client.Exec(ctx, appVersionUploadURLText, &resp, variables, graphql.OperationName("CLIAppVersionUploadURL")) if err != nil { return AppVersionUploadURLOutput{}, err }