Skip to content

Commit

Permalink
feat: add cardinal subcommand to start/stop/restart/purge (#9)
Browse files Browse the repository at this point in the history
Co-authored-by: Tyler <[email protected]>
  • Loading branch information
smsunarto and technicallyty authored Oct 25, 2023
1 parent 1ef5889 commit 702bd0a
Show file tree
Hide file tree
Showing 33 changed files with 401 additions and 116 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Parameters

Licensor: Argus World Labs, Inc.

Licensed Work: World Engine
Licensed Work: World CLI
The Licensed Work is (c) 2023 Argus World Labs, Inc.

Additional Use Grant:
Expand Down
24 changes: 24 additions & 0 deletions cmd/cardinal/cardinal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cardinal

import (
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/tea/style"
)

func init() {
// Register subcommands - `world cardinal [subcommand]`
BaseCmd.AddCommand(createCmd, startCmd, restartCmd, purgeCmd, stopCmd)
}

var BaseCmd = &cobra.Command{
Use: "cardinal",
Short: "Manage your Cardinal game shard project",
Long: style.CLIHeader("World CLI — CARDINAL", "Manage your Cardinal game shard project"),
GroupID: "Core",
Run: func(cmd *cobra.Command, args []string) {
if err := cmd.Help(); err != nil {
log.Fatal().Err(err).Msg("Failed to execute cardinal command")
}
},
}
47 changes: 27 additions & 20 deletions cmd/create.go → cmd/cardinal/create.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package cmd
package cardinal

import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"io"
"pkg.world.dev/world-cli/cmd/action"
"pkg.world.dev/world-cli/cmd/component/steps"
"pkg.world.dev/world-cli/cmd/style"
"pkg.world.dev/world-cli/common/tea_cmd"
"pkg.world.dev/world-cli/tea/component/steps"
"pkg.world.dev/world-cli/tea/style"
"strings"
)

const TemplateGitUrl = "https://github.com/Argus-Labs/starter-game-template.git"

var CreateDeps = []action.Dependency{
action.GitDependency,
var CreateDeps = []tea_cmd.Dependency{
tea_cmd.GitDependency,
}

/////////////////
// Cobra Setup //
/////////////////

func init() {
rootCmd.AddCommand(createCmd)
}

var createCmd = &cobra.Command{
Use: "create",
Short: "Creates a newModel game shard from scratch.",
Long: `Creates a World Engine game shard based on https://github.com/Argus-Labs/starter-game-template`,
Use: "create [directory_name]",
Short: "Create a World Engine game shard from scratch",
Long: `Create a World Engine game shard based on https://github.com/Argus-Labs/starter-game-template.
If [directory_name] is set, it will automatically clone the starter project into that directory.
Otherwise, it will prompt you to enter a directory name.`,
Args: cobra.MaximumNArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
p := tea.NewProgram(NewWorldCreateModel(args))
if _, err := p.Run(); err != nil {
Expand All @@ -47,7 +46,7 @@ type WorldCreateModel struct {
steps steps.Model
projectNameInput textinput.Model
args []string
depStatus []action.DependencyStatus
depStatus []tea_cmd.DependencyStatus
depStatusErr error
err error
}
Expand Down Expand Up @@ -85,22 +84,23 @@ func NewWorldCreateModel(args []string) WorldCreateModel {
func (m WorldCreateModel) Init() tea.Cmd {
// If the project name was passed in as an argument, skip the 1st step
if m.projectNameInput.Value() != "" {
return tea.Sequence(action.CheckDependenciesCmd(CreateDeps), textinput.Blink, m.steps.StartCmd(), m.steps.CompleteStepCmd(nil))
return tea.Sequence(tea_cmd.CheckDependenciesCmd(CreateDeps), textinput.Blink, m.steps.StartCmd(), m.steps.CompleteStepCmd(nil))
}
return tea.Sequence(action.CheckDependenciesCmd(CreateDeps), textinput.Blink, m.steps.StartCmd())
return tea.Sequence(tea_cmd.CheckDependenciesCmd(CreateDeps), textinput.Blink, m.steps.StartCmd())
}

// Update handles incoming events and updates the model accordingly
func (m WorldCreateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case action.CheckDependenciesMsg:
case tea_cmd.CheckDependenciesMsg:
m.depStatus = msg.DepStatus
m.depStatusErr = msg.Err
if msg.Err != nil {
return m, tea.Quit
} else {
return m, nil
}

case tea.KeyMsg:
switch msg.Type {
case tea.KeyEnter:
Expand All @@ -112,30 +112,36 @@ func (m WorldCreateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyCtrlC:
return m, tea.Quit
}

case NewLogMsg:
m.logs = append(m.logs, msg.Log)
return m, nil

case steps.SignalStepStartedMsg:
// If step 1 is started, dispatch the git clone command
if msg.Index == 1 {
return m, tea.Sequence(
NewLogCmd(style.ChevronIcon.Render()+"Cloning starter-game-template..."),
action.GitCloneCmd(TemplateGitUrl, m.projectNameInput.Value(), "Initial commit from World CLI"),
tea_cmd.GitCloneCmd(TemplateGitUrl, m.projectNameInput.Value(), "Initial commit from World CLI"),
)
}
return m, nil

case steps.SignalStepCompletedMsg:
// If step 1 is completed, log success message
if msg.Index == 1 {
return m, NewLogCmd(style.ChevronIcon.Render() + "Successfully created a newModel game shard based on starter-game-template!")
}

case steps.SignalStepErrorMsg:
// Log error, then quit
return m, tea.Sequence(NewLogCmd(style.CrossIcon.Render()+"Error: "+msg.Err.Error()), tea.Quit)

case steps.SignalAllStepCompletedMsg:
// All done, quit
return m, tea.Quit
case action.GitCloneFinishMsg:

case tea_cmd.GitCloneFinishMsg:
// If there is an error, log stderr then mark step as failed
if msg.Err != nil {
stderrBytes, err := io.ReadAll(msg.ErrBuf)
Expand All @@ -149,6 +155,7 @@ func (m WorldCreateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

// Otherwise, mark step as completed
return m, m.steps.CompleteStepCmd(nil)

default:
var cmd tea.Cmd
m.steps, cmd = m.steps.Update(msg)
Expand All @@ -163,7 +170,7 @@ func (m WorldCreateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// View renders the UI based on the data in the WorldCreateModel
func (m WorldCreateModel) View() string {
if m.depStatusErr != nil {
return action.PrettyPrintMissingDependency(m.depStatus)
return tea_cmd.PrettyPrintMissingDependency(m.depStatus)
}

output := ""
Expand Down
21 changes: 6 additions & 15 deletions cmd/dev.go → cmd/cardinal/dev.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
package cmd
package cardinal

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/cmd/component"
"pkg.world.dev/world-cli/utils"
"pkg.world.dev/world-cli/common"
"pkg.world.dev/world-cli/tea/component"
)

/////////////////
// Cobra Setup //
/////////////////

func init() {
rootCmd.AddCommand(devCmd)
}

var devCmd = &cobra.Command{
Use: "dev",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Short: "TODO",
Long: `TODO`,
RunE: func(cmd *cobra.Command, args []string) error {
//total width/height doesn't matter here as soon as you put it into the bubbletea framework everything will resize to fit window.
lowerLeftBox := component.NewServerStatusApp()
lowerLeftBoxInfo := component.CreateBoxInfo(lowerLeftBox, 50, 30, component.WithBorder)
triLayout := component.BuildTriLayoutHorizontal(0, 0, nil, lowerLeftBoxInfo, nil)
_, _, _, err := utils.RunShellCommandReturnBuffers("cd cardinal && go run .", 1024)
_, _, _, err := common.RunShellCommandReturnBuffers("cd cardinal && go run .", 1024)
if err != nil {
return err
}
Expand Down
25 changes: 25 additions & 0 deletions cmd/cardinal/purge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cardinal

import (
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/common/tea_cmd"
)

/////////////////
// Cobra Setup //
/////////////////

var purgeCmd = &cobra.Command{
Use: "purge",
Short: "Stop and reset the state of your Cardinal game shard",
Long: `Stop and reset the state of your Cardinal game shard.
This command stop all Docker services and remove all Docker volumes.`,
RunE: func(cmd *cobra.Command, args []string) error {
err := tea_cmd.DockerPurge()
if err != nil {
return err
}

return nil
},
}
31 changes: 31 additions & 0 deletions cmd/cardinal/restart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cardinal

import (
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/common/tea_cmd"
)

/////////////////
// Cobra Setup //
/////////////////

var restartCmd = &cobra.Command{
Use: "restart",
Short: "Restart your Cardinal game shard stack",
Long: `Restart your Cardinal game shard stack.
This will restart the following Docker services:
- Cardinal (Core game logic)
- Nakama (Relay)`,
RunE: func(cmd *cobra.Command, args []string) error {
err := tea_cmd.DockerRestart(true, []tea_cmd.DockerService{
tea_cmd.DockerServiceCardinal,
tea_cmd.DockerServiceNakama,
})
if err != nil {
return err
}

return nil
},
}
77 changes: 77 additions & 0 deletions cmd/cardinal/start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cardinal

import (
"fmt"
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/common/tea_cmd"
)

/////////////////
// Cobra Setup //
/////////////////

func init() {
startCmd.Flags().Bool("build", true, "Rebuild the Docker images before starting")
startCmd.Flags().Bool("debug", false, "Enable debug mode")
startCmd.Flags().String("mode", "", "Run with special mode [detach/integration-test]")
}

var startCmd = &cobra.Command{
Use: "start",
Short: "Start your Cardinal game shard stack",
Long: `Start your Cardinal game shard stack.
This will start the following Docker services:
- Cardinal (Core game logic)
- Nakama (Relay)`,
RunE: func(cmd *cobra.Command, args []string) error {
buildFlag, err := cmd.Flags().GetBool("build")
if err != nil {
return err
}

debugFlag, err := cmd.Flags().GetBool("debug")
if err != nil {
return err
}

modeFlag, err := cmd.Flags().GetString("mode")
if err != nil {
return err
}

// Don't allow running with special mode and debug mode
if modeFlag != "" && debugFlag {
return fmt.Errorf("cannot run with special mode and debug mode at the same time")
}

switch modeFlag {
case "":
if debugFlag {
err = tea_cmd.DockerStartDebug()
if err != nil {
return err
}
} else {
err = tea_cmd.DockerStart(buildFlag, []tea_cmd.DockerService{tea_cmd.DockerServiceCardinal, tea_cmd.DockerServiceNakama})
if err != nil {
return err
}
}
case "detach":
err = tea_cmd.DockerStartDetach()
if err != nil {
return err
}
case "integration-test":
err = tea_cmd.DockerStartTest()
if err != nil {
return err
}
default:
return fmt.Errorf("unknown mode %s", modeFlag)
}

return nil
},
}
34 changes: 34 additions & 0 deletions cmd/cardinal/stop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cardinal

import (
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/common/tea_cmd"
)

/////////////////
// Cobra Setup //
/////////////////

var stopCmd = &cobra.Command{
Use: "stop",
Short: "Stop your Cardinal game shard stack",
Long: `Stop your Cardinal game shard stack.
This will stop the following Docker services:
- Cardinal (Core game logic)
- Nakama (Relay)`,
RunE: func(cmd *cobra.Command, args []string) error {
err := tea_cmd.DockerStop([]tea_cmd.DockerService{
tea_cmd.DockerServiceCardinal,
tea_cmd.DockerServiceNakama,
tea_cmd.DockerServicePostgres,
tea_cmd.DockerServiceRedis,
tea_cmd.DockerServiceTestsuite,
})
if err != nil {
return err
}

return nil
},
}
Loading

0 comments on commit 702bd0a

Please sign in to comment.