Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add world cardinal dev command #10

Merged
merged 7 commits into from
Oct 31, 2023
Merged
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ world-cli
.DS_Store

# Jetbrains
.idea
.idea

# Test
/starter-game
4 changes: 3 additions & 1 deletion cmd/cardinal/cardinal.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (

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

// BaseCmd is the base command for the cardinal subcommand
// Usage: `world cardinal`
var BaseCmd = &cobra.Command{
Use: "cardinal",
Short: "Manage your Cardinal game shard project",
Expand Down
10 changes: 3 additions & 7 deletions cmd/cardinal/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
"io"
"pkg.world.dev/world-cli/common/tea_cmd"
"pkg.world.dev/world-cli/tea/component/steps"
"pkg.world.dev/world-cli/tea/style"
Expand All @@ -21,6 +20,8 @@ var CreateDeps = []tea_cmd.Dependency{
// Cobra Setup //
/////////////////

// createCmd creates a new World Engine project based on starter-game-template
// Usage: `world cardinal create [directory_name]`
var createCmd = &cobra.Command{
Use: "create [directory_name]",
Short: "Create a World Engine game shard from scratch",
Expand Down Expand Up @@ -144,12 +145,7 @@ func (m WorldCreateModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
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)
if err != nil {
m.logs = append(m.logs, style.CrossIcon.Render()+"Error occurred while reading stderr")
} else {
m.logs = append(m.logs, style.CrossIcon.Render()+string(stderrBytes))
}
m.logs = append(m.logs, style.CrossIcon.Render()+msg.Err.Error())
return m, m.steps.CompleteStepCmd(msg.Err)
}

Expand Down
131 changes: 120 additions & 11 deletions cmd/cardinal/dev.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,143 @@
package cardinal

import (
tea "github.com/charmbracelet/bubbletea"
"fmt"
"github.com/magefile/mage/sh"
"github.com/spf13/cobra"
"pkg.world.dev/world-cli/common"
"pkg.world.dev/world-cli/tea/component"
"os"
"os/exec"
"os/signal"
"pkg.world.dev/world-cli/tea/style"
"syscall"
)

const (
CardinalPort = "3333"
RedisPort = "6379"
WebdisPort = "7379"
)

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

// devCmd runs Cardinal in development mode
// Usage: `world cardinal dev`
var devCmd = &cobra.Command{
Use: "dev",
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 := common.RunShellCommandReturnBuffers("cd cardinal && go run .", 1024)
err := os.Chdir("cardinal")
if err != nil {
return err
}

// Run Redis container
err = sh.Run("docker", "run", "-d", "-p", fmt.Sprintf("%s:%s", RedisPort, RedisPort), "-e", "LOCAL_REDIS=true", "--name", "cardinal-dev-redis", "redis")
if err != nil {
return err
}
p := tea.NewProgram(triLayout, tea.WithAltScreen())
_, err = p.Run()

// Run Webdis container - this provides a REST wrapper around Redis
err = sh.Run("docker", "run", "-d", "-p", fmt.Sprintf("%s:%s", WebdisPort, WebdisPort), "--link", "cardinal-dev-redis:redis", "--name", "cardinal-dev-webdis", "anapsix/webdis")
if err != nil {
return err
}
return nil

// Run Cardinal
execCmd, err := runCardinal()
if err != nil {
return err
}

// Create a channel to receive termination signals
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

// Create a channel to receive errors from the command
cmdErr := make(chan error, 1)

go func() {
err := execCmd.Wait()
cmdErr <- err
}()

select {
case <-signalCh:
// Shutdown signal received, attempt to gracefully stop the command
errCleanup := cleanup()
if errCleanup != nil {
return errCleanup
}

err = execCmd.Process.Signal(syscall.SIGTERM)
if err != nil {
return err
}

return nil

case err := <-cmdErr:
fmt.Println(err)
errCleanup := cleanup()
if errCleanup != nil {
return errCleanup
}
return nil
}
},
}

// runCardinal runs cardinal in dev mode.
// We run cardinal without docker to make it easier to debug and skip the docker image build step
func runCardinal() (*exec.Cmd, error) {
fmt.Print(style.CLIHeader("Cardinal", "Running Cardinal in dev mode"), "\n")
fmt.Println(style.BoldText.Render("Press Ctrl+C to stop"))
fmt.Println()
fmt.Println(fmt.Sprintf("Redis: localhost:%s", RedisPort))
fmt.Println(fmt.Sprintf("Webdis: localhost:%s", WebdisPort))
fmt.Println(fmt.Sprintf("Cardinal: localhost:%s", CardinalPort))
fmt.Println()

env := map[string]string{
"REDIS_MODE": "normal",
"CARDINAL_PORT": CardinalPort,
"REDIS_ADDR": fmt.Sprintf("localhost:%s", RedisPort),
"DEPLOY_MODE": "development",
}

cmd := exec.Command("go", "run", ".")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
for k, v := range env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}

err := cmd.Start()
if err != nil {
return cmd, err
}

return cmd, nil
}

// cleanup stops and removes the Redis and Webdis containers
func cleanup() error {
err := sh.Run("docker", "rm", "-f", "cardinal-dev-redis")
if err != nil {
fmt.Println("Failed to delete Redis container automatically")
fmt.Println("Please delete it manually with `docker rm -f cardinal-dev-redis`")
return err
}

err = sh.Run("docker", "rm", "-f", "cardinal-dev-webdis")
if err != nil {
fmt.Println("Failed to delete Webdis container automatically")
fmt.Println("Please delete it manually with `docker rm -f cardinal-dev-webdis`")
return err
}

return nil
}
2 changes: 2 additions & 0 deletions cmd/cardinal/purge.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
// Cobra Setup //
/////////////////

// purgeCmd stops and resets the state of your Cardinal game shard
// Usage: `world cardinal purge`
var purgeCmd = &cobra.Command{
Use: "purge",
Short: "Stop and reset the state of your Cardinal game shard",
Expand Down
2 changes: 2 additions & 0 deletions cmd/cardinal/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
// Cobra Setup //
/////////////////

// restartCmd restarts your Cardinal game shard stack
// Usage: `world cardinal restart`
var restartCmd = &cobra.Command{
Use: "restart",
Short: "Restart your Cardinal game shard stack",
Expand Down
2 changes: 2 additions & 0 deletions cmd/cardinal/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func init() {
startCmd.Flags().String("mode", "", "Run with special mode [detach/integration-test]")
}

// startCmd starts your Cardinal game shard stack
// Usage: `world cardinal start`
var startCmd = &cobra.Command{
Use: "start",
Short: "Start your Cardinal game shard stack",
Expand Down
2 changes: 2 additions & 0 deletions cmd/cardinal/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
// Cobra Setup //
/////////////////

// stopCmd stops your Cardinal game shard stack
// Usage: `world cardinal stop`
var stopCmd = &cobra.Command{
Use: "stop",
Short: "Stop your Cardinal game shard stack",
Expand Down
2 changes: 2 additions & 0 deletions cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ var DoctorDeps = []tea_cmd.Dependency{
// Cobra Setup //
/////////////////

// doctorCmd checks that required dependencies are installed
// Usage: `world doctor`
var doctorCmd = &cobra.Command{
Use: "doctor",
Short: "Check that required dependencies are installed",
Expand Down
4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func init() {
rootCmd.AddCommand(cardinal.BaseCmd)
}

// rootCmd represents the base command when called without any subcommands
// rootCmd represents the base command
// Usage: `world`
var rootCmd = &cobra.Command{
Use: "world",
Short: "A swiss army knife for World Engine projects",
Expand All @@ -35,6 +36,5 @@ var rootCmd = &cobra.Command{
func Execute() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout})
if err := rootCmd.Execute(); err != nil {
log.Fatal().Err(err).Msg("Failed to execute root command")
}
}
2 changes: 2 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/spf13/cobra"
)

// versionCmd print the version number of World CLI
// Usage: `world version`
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of World CLI",
Expand Down
20 changes: 10 additions & 10 deletions common/tea_cmd/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@

// DockerBuild builds all docker images
func DockerBuild() error {
if err := prepareDirs("cardinal", "nakama"); err != nil {
if err := prepareDirs("cardinal"); err != nil {

Check warning on line 89 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L89

Added line #L89 was not covered by tests
return err
}
if err := sh.RunV("docker", "compose", "build"); err != nil {
if err := sh.Run("docker", "compose", "build"); err != nil {

Check warning on line 92 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L92

Added line #L92 was not covered by tests
return err
}
return nil
Expand All @@ -100,7 +100,7 @@
if services == nil {
return fmt.Errorf("no service names provided")
}
if err := prepareDirs("cardinal", "nakama"); err != nil {
if err := prepareDirs("cardinal"); err != nil {

Check warning on line 103 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L103

Added line #L103 was not covered by tests
return err
}
if build {
Expand All @@ -120,10 +120,10 @@
if err := DockerPurge(); err != nil {
return err
}
if err := prepareDirs("testsuite", "cardinal", "nakama"); err != nil {
if err := prepareDirs("testsuite", "cardinal"); err != nil {

Check warning on line 123 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L123

Added line #L123 was not covered by tests
return err
}
if err := sh.RunV("docker", "compose", "up", "--build", "--abort-on-container-exit", "--exit-code-from", "testsuite", "--attach", "testsuite"); err != nil {
if err := sh.Run("docker", "compose", "up", "--build", "--abort-on-container-exit", "--exit-code-from", "testsuite", "--attach", "testsuite"); err != nil {

Check warning on line 126 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L126

Added line #L126 was not covered by tests
return err
}
return nil
Expand All @@ -132,21 +132,21 @@
// DockerStartDebug starts Nakama and Cardinal in debug mode with Cardinal debugger listening on port 40000
// Note: Cardinal server will not run until a debugger is attached port 40000
func DockerStartDebug() error {
if err := prepareDirs("cardinal", "nakama"); err != nil {
if err := prepareDirs("cardinal"); err != nil {

Check warning on line 135 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L135

Added line #L135 was not covered by tests
return err
}
if err := sh.RunV("docker", "compose", "-f", "docker-compose-debug.yml", "up", "--build", "cardinal", "nakama"); err != nil {
if err := sh.Run("docker", "compose", "-f", "docker-compose-debug.yml", "up", "--build", "cardinal", "nakama"); err != nil {

Check warning on line 138 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L138

Added line #L138 was not covered by tests
return err
}
return nil
}

// DockerStartDetach starts Nakama and Cardinal with detach and wait-timeout 60s (useful for CI workflow)
func DockerStartDetach() error {
if err := prepareDirs("cardinal", "nakama"); err != nil {
if err := prepareDirs("cardinal"); err != nil {

Check warning on line 146 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L146

Added line #L146 was not covered by tests
return err
}
if err := sh.RunV("docker", "compose", "up", "--detach", "--wait", "--wait-timeout", "60"); err != nil {
if err := sh.Run("docker", "compose", "up", "--detach", "--wait", "--wait-timeout", "60"); err != nil {

Check warning on line 149 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L149

Added line #L149 was not covered by tests
return err
}
return nil
Expand Down Expand Up @@ -187,7 +187,7 @@
// DockerPurge stops and deletes all docker containers and data volumes
// This will completely wipe the state, if you only want to stop the containers, use DockerStop
func DockerPurge() error {
return sh.RunV("docker", "compose", "down", "--volumes")
return sh.Run("docker", "compose", "down", "--volumes")

Check warning on line 190 in common/tea_cmd/docker.go

View check run for this annotation

Codecov / codecov/patch

common/tea_cmd/docker.go#L190

Added line #L190 was not covered by tests
}

// dockerArgs converts a string of docker args and slice of DockerService to a single slice of strings.
Expand Down
Loading