Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions cmd/project/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ import (
"context"
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
"time"

"github.com/slackapi/slack-cli/cmd/app"
"github.com/slackapi/slack-cli/internal/iostreams"
"github.com/slackapi/slack-cli/internal/pkg/create"
"github.com/slackapi/slack-cli/internal/shared"
"github.com/slackapi/slack-cli/internal/shared/types"
"github.com/slackapi/slack-cli/internal/slackerror"
"github.com/slackapi/slack-cli/internal/slacktrace"
"github.com/slackapi/slack-cli/internal/style"
Expand All @@ -37,6 +40,7 @@ var createGitBranchFlag string
var createAppNameFlag string
var createListFlag bool
var createSubdirFlag string
var createEnvironmentFlag string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧮 quibble: For alphabetical order?


// Handle to client's create function used for testing
// TODO - Find best practice, such as using an Interface and Struct to create a client
Expand Down Expand Up @@ -67,6 +71,7 @@ name your app 'agent' (not create an AI Agent), use the --name flag instead.`,
{Command: "create my-project -t slack-samples/deno-hello-world", Meaning: "Start a new project from a specific template"},
{Command: "create --name my-project", Meaning: "Create a project named 'my-project'"},
{Command: "create my-project -t org/monorepo --subdir apps/my-app", Meaning: "Create from a subdirectory of a template"},
{Command: "create my-project -t slack-samples/bolt-js-starter-template --app A0123456789", Meaning: "Create from template and link to an existing app"},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪬 suggestion: Including the --environment flag might be meaningful with this example to avoid retries in CI setup or experiments avoiding prompts?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Instead, how about we add a CI/Scripting example and allow the above example to stay focused on creating a project with an existing app ID.

}),
Args: cobra.MaximumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -81,6 +86,7 @@ name your app 'agent' (not create an AI Agent), use the --name flag instead.`,
cmd.Flags().StringVarP(&createAppNameFlag, "name", "n", "", "name for your app (overrides the name argument)")
cmd.Flags().BoolVar(&createListFlag, "list", false, "list available app templates")
cmd.Flags().StringVar(&createSubdirFlag, "subdir", "", "subdirectory in the template to use as project")
cmd.Flags().StringVarP(&createEnvironmentFlag, "environment", "E", "", "environment to save existing app (local, deployed)")

return cmd
}
Expand Down Expand Up @@ -127,6 +133,19 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []
WithMessage("The --subdir flag requires the --template flag")
}

// --app requires --template
appFlagProvided := clients.Config.AppFlag != "" && types.IsAppID(clients.Config.AppFlag)
if appFlagProvided && !templateFlagProvided {
return slackerror.New(slackerror.ErrMismatchedFlags).
WithMessage("The --app flag requires the --template flag when used with create")
}

// --environment requires --app
if cmd.Flags().Changed("environment") && !appFlagProvided {
return slackerror.New(slackerror.ErrMismatchedFlags).
WithMessage("The --environment flag requires the --app flag when used with create")
}
Comment on lines +143 to +147

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔬 thought: We might guard against unexpected --environment values with this check or perhaps reset all changes if link command errors? I find template remains in an incomplete state after this command:

$ slack create --template slack-samples/bolt-js-starter-template --app A0B93LJK24A --name asdf --environment deploy --team slackbox-ez

🦠 ramble: The issue here is "deploy" instead of "deployed" I believe!


// Collect the template URL or select a starting template
template, err := promptTemplateSelection(cmd, clients, categoryShortcut)
if err != nil {
Expand Down Expand Up @@ -183,6 +202,22 @@ func runCreateCommand(clients *shared.ClientFactory, cmd *cobra.Command, args []
return err
}

if appFlagProvided {
absProjectPath, err := filepath.Abs(appDirPath)
if err != nil {
return slackerror.Wrap(err, slackerror.ErrAppDirectoryAccess)
}
originalDir, _ := clients.Os.Getwd()
if err := os.Chdir(absProjectPath); err != nil {
return slackerror.Wrap(err, slackerror.ErrAppDirectoryAccess)
}
linkErr := app.LinkExistingApp(ctx, clients, &types.App{}, false)
_ = os.Chdir(originalDir)
if linkErr != nil {
return linkErr
}
}
Comment thread
zimeg marked this conversation as resolved.

printCreateSuccess(ctx, clients, appDirPath)
return nil
}
Expand Down
31 changes: 31 additions & 0 deletions cmd/project/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,3 +853,34 @@ func TestCreateCommand_confirmExternalTemplateSelection(t *testing.T) {
})
}
}

func TestCreateCommand_AppFlag(t *testing.T) {
var createClientMock *CreateClientMock

testutil.TableTestCommand(t, testutil.CommandTests{
"app flag without template flag returns error": {
CmdArgs: []string{"my-app", "--app", "A0123456789"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
ExpectedErrorStrings: []string{"The --app flag requires the --template flag when used with create"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything)
},
},
"environment flag without app flag returns error": {
CmdArgs: []string{"my-app", "--template", "slack-samples/bolt-js-starter-template", "--environment", "deployed"},
Setup: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock, cf *shared.ClientFactory) {
createClientMock = new(CreateClientMock)
CreateFunc = createClientMock.Create
},
ExpectedErrorStrings: []string{"The --environment flag requires the --app flag when used with create"},
ExpectedAsserts: func(t *testing.T, ctx context.Context, cm *shared.ClientsMock) {
createClientMock.AssertNotCalled(t, "Create", mock.Anything, mock.Anything, mock.Anything)
},
},
}, func(cf *shared.ClientFactory) *cobra.Command {
return NewCreateCommand(cf)
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 suggestion: Adding cases to cover the expected happy paths might be meaningful to guarantee the create command keeps support for these paths as underlined commands change

}
Loading