Skip to content

Commit

Permalink
fix(cli): deploy apps within project directories (#54)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jfeodor authored Nov 25, 2024
1 parent 4db1ffd commit 41ea8ff
Show file tree
Hide file tree
Showing 15 changed files with 99 additions and 42 deletions.
2 changes: 1 addition & 1 deletion cmd/deploy/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var (
appSlug string
verbose bool
appDir string = "."
projectDir string = "."
projectDir string = ""
message string
version string
follow bool
Expand Down
56 changes: 45 additions & 11 deletions cmd/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion cmd/legacy/push/build_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down
6 changes: 4 additions & 2 deletions internal/app/app_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package app

import (
"context"

"github.com/hasura/go-graphql-client"
)

type CreateAppInput struct {
Expand All @@ -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
}
Expand All @@ -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)
}
Expand Down
6 changes: 4 additions & 2 deletions internal/app/app_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
}
Expand Down
18 changes: 12 additions & 6 deletions internal/app/app_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ 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 {
DeploymentVersionID string
}

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
}
}
Expand All @@ -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)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/app/app_deploy_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"numerous.com/cli/internal/appident"

"github.com/hasura/go-graphql-client"
"github.com/hasura/go-graphql-client/pkg/jsonutil"
)

Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion internal/app/app_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"time"

"github.com/hasura/go-graphql-client"
)

type ListApp struct {
Expand Down Expand Up @@ -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)
}

Expand Down
6 changes: 4 additions & 2 deletions internal/app/app_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package app

import (
"context"

"github.com/hasura/go-graphql-client"
)

type ReadAppInput struct {
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
6 changes: 4 additions & 2 deletions internal/app/current_app_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package app
import (
"context"
"errors"

"github.com/hasura/go-graphql-client"
)

var ErrNotDeployed = errors.New("app is not deployed")
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/app/deploy_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
10 changes: 5 additions & 5 deletions internal/app/version_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package app

import (
"context"

"github.com/hasura/go-graphql-client"
)

type CreateAppVersionInput struct {
Expand All @@ -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
}
Expand All @@ -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
}
10 changes: 7 additions & 3 deletions internal/app/version_download_url.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package app

import "context"
import (
"context"

"github.com/hasura/go-graphql-client"
)

type AppVersionDownloadURLInput struct {
AppVersionID string
Expand All @@ -11,7 +15,7 @@ type AppVersionDownloadURLOutput struct {
}

const appVersionDownloadURLText = `
mutation AppVersionDownloadURL($appVersionID: ID!) {
mutation CLIAppVersionDownloadURL($appVersionID: ID!) {
appVersionDownloadURL(appVersionID: $appVersionID) {
url
}
Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit 41ea8ff

Please sign in to comment.