Skip to content

Commit

Permalink
feat: add ability to create new custom provider stack
Browse files Browse the repository at this point in the history
  • Loading branch information
davemooreuws committed Apr 8, 2024
1 parent f45d217 commit e477887
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 23 deletions.
6 changes: 6 additions & 0 deletions pkg/project/stack/custom.config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The provider to use and it's published version
# See building custom provider docs:
# https://nitric.io/docs/reference/providers/custom/building-custom-provider#packaging-your-provider
provider: custom/%[email protected]
# Add your additional keys and config below
# region:
7 changes: 6 additions & 1 deletion pkg/project/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ var azureConfigTemplate string
//go:embed gcp.config.yaml
var gcpConfigTemplate string

//go:embed custom.config.yaml
var customConfigTemplate string

var fileNameRegex = regexp.MustCompile(`(?i)^nitric\.(\S+)\.ya?ml$`)

func IsValidFileName(stackName string) bool {
return fileNameRegex.MatchString(stackName)
}

func NewStackFile(fs afero.Fs, providerName string, stackName string, dir string) (string, error) {
func NewStackFile(fs afero.Fs, providerName string, stackName string, dir string, customProviderName string) (string, error) {
if dir == "" {
dir = "./"
}
Expand All @@ -62,6 +65,8 @@ func NewStackFile(fs afero.Fs, providerName string, stackName string, dir string
template = gcpConfigTemplate
case "azure":
template = azureConfigTemplate
case "custom":
template = fmt.Sprintf(customConfigTemplate, customProviderName)
}

fileName := StackFileName(stackName)
Expand Down
82 changes: 60 additions & 22 deletions pkg/view/tui/commands/stack/new/stack_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,22 @@ type NewStackStatus int
const (
NameInput NewStackStatus = iota
ProviderInput
CustomProviderInput
Pending
Done
Error
)

// Model - represents the state of the new stack creation operation
type Model struct {
namePrompt textprompt.TextPrompt
providerPrompt listprompt.ListPrompt
spinner spinner.Model
status NewStackStatus
provider string
projectConfig *project.ProjectConfiguration
nonInteractive bool
namePrompt textprompt.TextPrompt
providerPrompt listprompt.ListPrompt
customProviderNamePrompt textprompt.TextPrompt
spinner spinner.Model
status NewStackStatus
provider string
projectConfig *project.ProjectConfiguration
nonInteractive bool

newStackFilePath string

Expand All @@ -81,6 +83,11 @@ func (m Model) ProviderName() string {
return m.providerPrompt.Choice()
}

// customProviderName - returns the custom provider name entered by the user
func (m Model) customProviderName() string {
return m.customProviderNamePrompt.Value()
}

// Init initializes the model, used by Bubbletea
func (m Model) Init() tea.Cmd {
if m.err != nil {
Expand Down Expand Up @@ -124,6 +131,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.namePrompt.Blur()

m.status = ProviderInput
} else if msg.ID == m.customProviderNamePrompt.ID {
m.customProviderNamePrompt.Blur()

m.status = Pending

return m, m.createStack()
}

return m, nil
Expand All @@ -140,10 +153,20 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.providerPrompt.Choice() != "" {
m.provider = m.providerPrompt.Choice()

if m.provider == "custom" {
m.status = CustomProviderInput

m.customProviderNamePrompt.Focus()

return m, cmd
}

m.status = Pending

return m, m.createStack()
}
case CustomProviderInput:
m.customProviderNamePrompt, cmd = m.customProviderNamePrompt.UpdateTextPrompt(msg)
case Pending:
m.spinner, cmd = m.spinner.Update(msg)
case Done:
Expand Down Expand Up @@ -193,6 +216,11 @@ func (m Model) View() string {
if m.status >= ProviderInput {
v.Addln(m.providerPrompt.View())
}

// Custom provider input
if m.status >= CustomProviderInput && m.provider == "custom" {
v.Addln(m.customProviderNamePrompt.View())
}
}

// Creating Status
Expand Down Expand Up @@ -266,12 +294,13 @@ func stackNameExistsValidator(projectDir string) validation.StringValidator {
}

const (
Aws = "aws"
Azure = "azure"
Gcp = "gcp"
Aws = "aws"
Azure = "azure"
Gcp = "gcp"
Custom = "custom"
)

var availableProviders = []string{Aws, Gcp, Azure}
var availableProviders = []string{Aws, Gcp, Azure, Custom}

func New(fs afero.Fs, args Args) Model {
// Load and update the project name in the template's nitric.yaml
Expand Down Expand Up @@ -300,6 +329,14 @@ func New(fs afero.Fs, args Args) Model {
Items: list.StringsToListItems(availableProviders),
})

customProviderNamePrompt := textprompt.NewTextPrompt("customProviderName", textprompt.TextPromptArgs{
Prompt: "What should we name this provider?",
Tag: "name",
Validator: nameValidator,
Placeholder: "extension",
InFlightValidator: nameInFlightValidator,
})

s := spinner.New()
s.Spinner = spinner.Dot
s.Style = spinnerStyle
Expand All @@ -316,9 +353,9 @@ func New(fs afero.Fs, args Args) Model {
}

if args.ProviderName != "" {
if !lo.Contains([]string{"aws", "azure", "gcp"}, args.ProviderName) {
if !lo.Contains([]string{"aws", "azure", "gcp", "custom"}, args.ProviderName) {
return Model{
err: fmt.Errorf("cloud name is not valid, must be aws, azure or gcp"),
err: fmt.Errorf("cloud name is not valid, must be aws, azure, gcp or custom"),
}
}

Expand All @@ -335,14 +372,15 @@ func New(fs afero.Fs, args Args) Model {
}

return Model{
fs: fs,
namePrompt: namePrompt,
providerPrompt: providerPrompt,
nonInteractive: isNonInteractive,
status: stackStatus,
projectConfig: projectConfig,
spinner: s,
err: nil,
fs: fs,
namePrompt: namePrompt,
providerPrompt: providerPrompt,
customProviderNamePrompt: customProviderNamePrompt,
nonInteractive: isNonInteractive,
status: stackStatus,
projectConfig: projectConfig,
spinner: s,
err: nil,
}
}

Expand All @@ -354,7 +392,7 @@ type stackCreateResultMsg struct {
// createStack returns a command that will create the stack on disk using the inputs gathered
func (m Model) createStack() tea.Cmd {
return func() tea.Msg {
filePath, err := stack.NewStackFile(m.fs, m.provider, m.StackName(), "")
filePath, err := stack.NewStackFile(m.fs, m.provider, m.StackName(), "", m.customProviderName())

return stackCreateResultMsg{
err: err,
Expand Down

0 comments on commit e477887

Please sign in to comment.