diff --git a/e2e-scripts/init.bash b/e2e-scripts/init.bash index 674ed69..2bfa882 100644 --- a/e2e-scripts/init.bash +++ b/e2e-scripts/init.bash @@ -15,5 +15,5 @@ echo ' hello() { echo "Hello world !" } -' > ~/.config/wo/api/functions/functions.bash +' > ~/.config/wo/workspaces/api/functions/functions.bash } diff --git a/e2e-scripts/init.fish b/e2e-scripts/init.fish index ce0cf56..c770f6d 100644 --- a/e2e-scripts/init.fish +++ b/e2e-scripts/init.fish @@ -12,5 +12,5 @@ function create_function function hello -d "Hello world function" echo "Hello world !" end -' >~/.config/wo/api/functions/functions.fish +' >~/.config/wo/workspaces/api/functions/functions.fish end diff --git a/e2e-scripts/init.sh b/e2e-scripts/init.sh index e61485c..69eb2ea 100644 --- a/e2e-scripts/init.sh +++ b/e2e-scripts/init.sh @@ -13,5 +13,5 @@ echo ' hello() { echo "Hello world !" } -' > ~/.config/wo/api/functions/functions.sh +' > ~/.config/wo/workspaces/api/functions/functions.sh } diff --git a/e2e-scripts/init.zsh b/e2e-scripts/init.zsh index d5bf929..80b730a 100644 --- a/e2e-scripts/init.zsh +++ b/e2e-scripts/init.zsh @@ -13,5 +13,5 @@ echo ' hello() { echo "Hello world !" } -' > ~/.config/wo/api/functions/functions.zsh +' > ~/.config/wo/workspaces/api/functions/functions.zsh } diff --git a/internal/cmd/interface.go b/internal/cmd/interface.go index f87d54b..0879657 100644 --- a/internal/cmd/interface.go +++ b/internal/cmd/interface.go @@ -19,6 +19,7 @@ type workspaceManager interface { SetConfig(string, map[string]string) error GetSupportedApps() []string GetConfigDir() string + Migrate() error } type completionManager interface { diff --git a/internal/cmd/migrate.go b/internal/cmd/migrate.go new file mode 100644 index 0000000..9754532 --- /dev/null +++ b/internal/cmd/migrate.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +func newMigrateCmd(workspaceManager workspaceManager) *cobra.Command { + return &cobra.Command{ + Use: "migrate", + Short: "Migrate the existing config", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + err := workspaceManager.Migrate() + if err != nil { + return err + } + cmd.Printf(regularStyle.Render("Config migrated")) + return nil + }, + } +} diff --git a/internal/cmd/mock_workspaceManager_test.go b/internal/cmd/mock_workspaceManager_test.go index 18f089c..647ddb6 100644 --- a/internal/cmd/mock_workspaceManager_test.go +++ b/internal/cmd/mock_workspaceManager_test.go @@ -228,6 +228,24 @@ func (_m *mockWorkspaceManager) List() ([]workspace.Workspace, error) { return r0, r1 } +// Migrate provides a mock function with given fields: +func (_m *mockWorkspaceManager) Migrate() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Migrate") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Remove provides a mock function with given fields: _a0 func (_m *mockWorkspaceManager) Remove(_a0 string) error { ret := _m.Called(_a0) diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 75f5c43..9e7a263 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -101,6 +101,7 @@ func newRootCmd() *cobra.Command { rootCmd.AddCommand(newRunCmd(w, funcCompMgr)) rootCmd.AddCommand(newShowCmd(w, wksCompMgr)) rootCmd.AddCommand(newVersionCmd()) + rootCmd.AddCommand(newMigrateCmd(w)) return rootCmd } diff --git a/internal/workspace/workspace.go b/internal/workspace/workspace.go index 7827420..df7c1ac 100644 --- a/internal/workspace/workspace.go +++ b/internal/workspace/workspace.go @@ -21,6 +21,7 @@ import ( const ( defaultConfigDir = ".config/wo" envVariablePrefix = "WO" + defaultEnv = "default" ) const ( @@ -116,7 +117,7 @@ func (s WorkspaceManager) BuildAliases(prefix string) ([]string, error) { func (s WorkspaceManager) List() ([]Workspace, error) { workspaces := []Workspace{} - entries, err := os.ReadDir(s.configDir) + entries, err := os.ReadDir(s.getWorkspacesDir()) if os.IsNotExist(err) { return workspaces, nil } @@ -156,7 +157,7 @@ func (s WorkspaceManager) Create(name string, path string) error { if err != nil { return err } - err = s.createFile(s.resolveEnvFile(name, "default")) + err = s.createFile(s.resolveEnvFile(name, defaultEnv)) if err != nil { return err } @@ -285,6 +286,45 @@ func (s WorkspaceManager) CreateEnvVariableStatement(name string, value string) return "" } +func (s WorkspaceManager) Migrate() error { + stat, err := os.Stat(fmt.Sprintf("%s/workspaces", s.configDir)) + if stat != nil { + return nil + } + if err != nil { + return err + } + entries, err := os.ReadDir(s.configDir) + if os.IsNotExist(err) { + return err + } + err = os.MkdirAll(s.getWorkspacesDir(), 0o777) + if err != nil { + return err + } + for _, e := range entries { + if !e.IsDir() { + continue + } + if strings.HasPrefix(e.Name(), ".") { + continue + } + err := os.Rename(fmt.Sprintf("%s/%s", s.configDir, e.Name()), fmt.Sprintf("%s/%s", s.getWorkspacesDir(), e.Name())) + if err != nil { + return err + } + err = s.createWorkspaceFolder(e.Name()) + if err != nil { + return err + } + err = s.createFile(s.resolveEnvFile(e.Name(), defaultEnv)) + if err != nil { + return err + } + } + return nil +} + func (s WorkspaceManager) appendLoadStatement(name string, env string, functionAndArgs []string) []string { data := []string{} data = append(data, s.CreateEnvVariableStatement(fmt.Sprintf("%s_NAME", envVariablePrefix), name)) @@ -369,7 +409,7 @@ func (s WorkspaceManager) getExtension() string { } func (s WorkspaceManager) createConfigFolder() error { - err := errors.Join(os.MkdirAll(s.configDir, 0o777), os.WriteFile(s.resolveGitignoreFile(), []byte("*/envs/*\n"), 0o666)) + err := errors.Join(os.MkdirAll(s.configDir, 0o777), os.WriteFile(s.resolveGitignoreFile(), []byte("**/envs/**\n"), 0o666)) if err != nil { return nil } @@ -393,13 +433,18 @@ func (s WorkspaceManager) createConfigFolder() error { func (s WorkspaceManager) createWorkspaceFolder(name string) error { return errors.Join( + os.MkdirAll(s.getWorkspaceDir(name), 0o777), os.MkdirAll(s.getWorkspaceFunctionsDir(name), 0o777), os.MkdirAll(s.getWorkspaceEnvsDir(name), 0o777), ) } +func (s WorkspaceManager) getWorkspacesDir() string { + return fmt.Sprintf("%s/workspaces", s.configDir) +} + func (s WorkspaceManager) getWorkspaceDir(name string) string { - return fmt.Sprintf("%s/%s", s.configDir, name) + return fmt.Sprintf("%s/%s", s.getWorkspacesDir(), name) } func (s WorkspaceManager) getWorkspaceFunctionsDir(name string) string { diff --git a/internal/workspace/workspace_test.go b/internal/workspace/workspace_test.go index 1ec29a1..022f977 100644 --- a/internal/workspace/workspace_test.go +++ b/internal/workspace/workspace_test.go @@ -118,9 +118,9 @@ func TestList(t *testing.T) { assert.NoError(t, err) assert.Len(t, ws, 3) assert.Equal(t, []Workspace{ - {Name: "api", Functions: Functions{file: fmt.Sprintf("%s/api/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/api/envs/default.bash", config.getPath(t))}, {Name: "dev", file: fmt.Sprintf("%s/api/envs/dev.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/api", config.getPath(t))}, - {Name: "db", Functions: Functions{file: fmt.Sprintf("%s/db/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/db/envs/default.bash", config.getPath(t))}, {Name: "staging", file: fmt.Sprintf("%s/db/envs/staging.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/db", config.getPath(t))}, - {Name: "front", Functions: Functions{file: fmt.Sprintf("%s/front/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/front/envs/default.bash", config.getPath(t))}, {Name: "prod", file: fmt.Sprintf("%s/front/envs/prod.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/front", config.getPath(t))}, + {Name: "api", Functions: Functions{file: fmt.Sprintf("%s/workspaces/api/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/workspaces/api/envs/default.bash", config.getPath(t))}, {Name: "dev", file: fmt.Sprintf("%s/workspaces/api/envs/dev.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/workspaces/api", config.getPath(t))}, + {Name: "db", Functions: Functions{file: fmt.Sprintf("%s/workspaces/db/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/workspaces/db/envs/default.bash", config.getPath(t))}, {Name: "staging", file: fmt.Sprintf("%s/workspaces/db/envs/staging.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/workspaces/db", config.getPath(t))}, + {Name: "front", Functions: Functions{file: fmt.Sprintf("%s/workspaces/front/functions/functions.bash", config.getPath(t)), Functions: []Function{}}, Envs: []Env{{Name: "default", file: fmt.Sprintf("%s/workspaces/front/envs/default.bash", config.getPath(t))}, {Name: "prod", file: fmt.Sprintf("%s/workspaces/front/envs/prod.bash", config.getPath(t))}}, Config: map[string]string{"app": "bash", "path": project.getPath(t)}, dir: fmt.Sprintf("%s/workspaces/front", config.getPath(t))}, }, ws) }, }, @@ -162,7 +162,7 @@ func TestGet(t *testing.T) { err = w.CreateEnv("front", "prod") assert.NoError(t, err) - functionPath := config.getPath(t) + "/front/functions/functions.bash" + functionPath := config.getPath(t) + "/workspaces/front/functions/functions.bash" assert.NoError(t, os.WriteFile(functionPath, []byte(` # A function 1 test_func1() { @@ -182,7 +182,7 @@ test_func2() { Workspace{ Name: "front", Functions: Functions{ - file: fmt.Sprintf("%s/front/functions/functions.bash", config.getPath(t)), + file: fmt.Sprintf("%s/workspaces/front/functions/functions.bash", config.getPath(t)), Functions: []Function{ { Name: "test_func1", @@ -197,18 +197,18 @@ test_func2() { Envs: []Env{ { Name: "default", - file: fmt.Sprintf("%s/front/envs/default.bash", config.getPath(t)), + file: fmt.Sprintf("%s/workspaces/front/envs/default.bash", config.getPath(t)), }, { Name: "prod", - file: fmt.Sprintf("%s/front/envs/prod.bash", config.getPath(t)), + file: fmt.Sprintf("%s/workspaces/front/envs/prod.bash", config.getPath(t)), }, }, Config: map[string]string{ "app": "bash", "path": project.getPath(t), }, - dir: fmt.Sprintf("%s/front", config.getPath(t)), + dir: fmt.Sprintf("%s/workspaces/front", config.getPath(t)), }, w) }, }, @@ -260,17 +260,17 @@ func TestCreate(t *testing.T) { assert.NoError(t, err) b, err = os.ReadFile(path + "/.gitignore") assert.NoError(t, err) - assert.Equal(t, "*/envs/*\n", string(b)) - envFile, err := os.Stat(path + "/test/envs/default.bash") + assert.Equal(t, "**/envs/**\n", string(b)) + envFile, err := os.Stat(path + "/workspaces/test/envs/default.bash") assert.NoError(t, err) assert.Equal(t, "default.bash", envFile.Name()) - functionFile, err := os.Stat(path + "/test/functions/functions.bash") + functionFile, err := os.Stat(path + "/workspaces/test/functions/functions.bash") assert.NoError(t, err) assert.Equal(t, "functions.bash", functionFile.Name()) - configFile, err := os.Stat(path + "/test/config.toml") + configFile, err := os.Stat(path + "/workspaces/test/config.toml") assert.NoError(t, err) assert.Equal(t, "config.toml", configFile.Name()) - b, err = os.ReadFile(path + "/test/config.toml") + b, err = os.ReadFile(path + "/workspaces/test/config.toml") assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("app = 'bash'\npath = '%s'\n", project.getPath(t)), string(b)) }, @@ -327,13 +327,13 @@ func TestCreateEnv(t *testing.T) { func(t *testing.T, err error) { assert.NoError(t, err) path := config.getPath(t) - defaultEnvFile, err := os.Stat(path + "/test/envs/default.bash") + defaultEnvFile, err := os.Stat(path + "/workspaces/test/envs/default.bash") assert.NoError(t, err) assert.Equal(t, "default.bash", defaultEnvFile.Name()) - prodEnvFile, err := os.Stat(path + "/test/envs/prod.bash") + prodEnvFile, err := os.Stat(path + "/workspaces/test/envs/prod.bash") assert.NoError(t, err) assert.Equal(t, "prod.bash", prodEnvFile.Name()) - functionFile, err := os.Stat(path + "/test/functions/functions.bash") + functionFile, err := os.Stat(path + "/workspaces/test/functions/functions.bash") assert.NoError(t, err) assert.Equal(t, "functions.bash", functionFile.Name()) }, @@ -372,12 +372,12 @@ func TestEdit(t *testing.T) { { "Edit workspace", func(t *testing.T, w WorkspaceManager, exec *MockCommander) { - exec.On("command", "", "-c", fmt.Sprintf("emacs %s/test/functions/functions.bash", config.getPath(t))).Return(nil) + exec.On("command", "", "-c", fmt.Sprintf("emacs %s/workspaces/test/functions/functions.bash", config.getPath(t))).Return(nil) err := w.Create("test", project.getPath(t)) assert.NoError(t, err) }, func(t *testing.T) { - f, err := os.Stat(fmt.Sprintf("%s/test/envs/default.bash", config.getPath(t))) + f, err := os.Stat(fmt.Sprintf("%s/workspaces/test/envs/default.bash", config.getPath(t))) assert.NoError(t, err) assert.Equal(t, "default.bash", f.Name()) }, @@ -410,14 +410,14 @@ func TestEditEnv(t *testing.T) { "Edit default workspace", "default", func(t *testing.T, exec *MockCommander) { - exec.On("command", "", "-c", fmt.Sprintf("emacs %s/test/envs/default.bash", config.getPath(t))).Return(nil) + exec.On("command", "", "-c", fmt.Sprintf("emacs %s/workspaces/test/envs/default.bash", config.getPath(t))).Return(nil) }, }, { "Edit prod workspace", "prod", func(t *testing.T, exec *MockCommander) { - exec.On("command", "", "-c", fmt.Sprintf("emacs %s/test/envs/prod.bash", config.getPath(t))).Return(nil) + exec.On("command", "", "-c", fmt.Sprintf("emacs %s/workspaces/test/envs/prod.bash", config.getPath(t))).Return(nil) }, }, } @@ -455,14 +455,14 @@ func TestRunFunction(t *testing.T) { "default", "/bin/bash", func(t *testing.T, exec *MockCommander) { - functionPath := config.getPath(t) + "/test/functions/functions.bash" + functionPath := config.getPath(t) + "/workspaces/test/functions/functions.bash" assert.NoError(t, os.WriteFile(functionPath, []byte(` run-db() { } `), 0o777)) - exec.On("command", project.getPath(t), "-c", fmt.Sprintf("export WO_NAME=test && export WO_ENV=default && source %s/test/envs/default.bash && source %s/test/functions/functions.bash && run-db", config.getPath(t), config.getPath(t))).Return(nil) + exec.On("command", project.getPath(t), "-c", fmt.Sprintf("export WO_NAME=test && export WO_ENV=default && source %s/workspaces/test/envs/default.bash && source %s/workspaces/test/functions/functions.bash && run-db", config.getPath(t), config.getPath(t))).Return(nil) }, }, { @@ -471,13 +471,13 @@ run-db() { "default", "/bin/fish", func(t *testing.T, exec *MockCommander) { - functionPath := config.getPath(t) + "/test/functions/functions.fish" + functionPath := config.getPath(t) + "/workspaces/test/functions/functions.fish" assert.NoError(t, os.WriteFile(functionPath, []byte(` function run-db end `), 0o777)) - exec.On("command", project.getPath(t), "-C", "set -x -g WO_NAME test", "-C", "set -x -g WO_ENV default", "-C", fmt.Sprintf("source %s/test/envs/default.fish", config.getPath(t)), "-C", fmt.Sprintf("source %s/test/functions/functions.fish", config.getPath(t)), "-c", "run-db").Return(nil) + exec.On("command", project.getPath(t), "-C", "set -x -g WO_NAME test", "-C", "set -x -g WO_ENV default", "-C", fmt.Sprintf("source %s/workspaces/test/envs/default.fish", config.getPath(t)), "-C", fmt.Sprintf("source %s/workspaces/test/functions/functions.fish", config.getPath(t)), "-c", "run-db").Return(nil) }, }, { @@ -486,14 +486,14 @@ end "prod", "/bin/bash", func(t *testing.T, exec *MockCommander) { - functionPath := config.getPath(t) + "/test/functions/functions.bash" + functionPath := config.getPath(t) + "/workspaces/test/functions/functions.bash" assert.NoError(t, os.WriteFile(functionPath, []byte(` run-db() { } `), 0o777)) - exec.On("command", project.getPath(t), "-c", fmt.Sprintf("export WO_NAME=test && export WO_ENV=prod && source %s/test/envs/prod.bash && source %s/test/functions/functions.bash && run-db watch", config.getPath(t), config.getPath(t))).Return(nil) + exec.On("command", project.getPath(t), "-c", fmt.Sprintf("export WO_NAME=test && export WO_ENV=prod && source %s/workspaces/test/envs/prod.bash && source %s/workspaces/test/functions/functions.bash && run-db watch", config.getPath(t), config.getPath(t))).Return(nil) }, }, { @@ -502,12 +502,12 @@ run-db() { "prod", "/bin/fish", func(t *testing.T, exec *MockCommander) { - functionPath := config.getPath(t) + "/test/functions/functions.fish" + functionPath := config.getPath(t) + "/workspaces/test/functions/functions.fish" assert.NoError(t, os.WriteFile(functionPath, []byte(` function run-db end `), 0o777)) - exec.On("command", project.getPath(t), "-C", "set -x -g WO_NAME test", "-C", "set -x -g WO_ENV prod", "-C", fmt.Sprintf("source %s/test/envs/prod.fish", config.getPath(t)), "-C", fmt.Sprintf("source %s/test/functions/functions.fish", config.getPath(t)), "-c", "run-db watch").Return(nil) + exec.On("command", project.getPath(t), "-C", "set -x -g WO_NAME test", "-C", "set -x -g WO_ENV prod", "-C", fmt.Sprintf("source %s/workspaces/test/envs/prod.fish", config.getPath(t)), "-C", fmt.Sprintf("source %s/workspaces/test/functions/functions.fish", config.getPath(t)), "-c", "run-db watch").Return(nil) }, }, } @@ -550,18 +550,18 @@ func TestRemove(t *testing.T) { func(t *testing.T, e error) { assert.NoError(t, e) path := config.getPath(t) - _, err := os.Stat(path + "/test/envs/prod.bash") + _, err := os.Stat(path + "/workspaces/test/envs/prod.bash") assert.True(t, os.IsNotExist(err)) - _, err = os.Stat(path + "/test/envs/dev.bash") + _, err = os.Stat(path + "/workspaces/test/envs/dev.bash") assert.True(t, os.IsNotExist(err)) - _, err = os.Stat(path + "/test/functions/functions.bash") + _, err = os.Stat(path + "/workspaces/test/functions/functions.bash") assert.True(t, os.IsNotExist(err)) - _, err = os.Stat(path + "/test/config.toml") + _, err = os.Stat(path + "/workspaces/test/config.toml") assert.True(t, os.IsNotExist(err)) - _, err = os.Stat(path + "/front/functions/functions.bash") + _, err = os.Stat(path + "/workspaces/front/functions/functions.bash") assert.NoError(t, err) - _, err = os.Stat(path + "/front/envs/dev.bash") + _, err = os.Stat(path + "/workspaces/front/envs/dev.bash") assert.NoError(t, err) }, }, @@ -604,7 +604,7 @@ func TestSetConfig(t *testing.T) { "/tmp", func(t *testing.T, err error) { assert.NoError(t, err) - b, err := os.ReadFile(fmt.Sprintf("%s/%s", config.getPath(t), "test/config.toml")) + b, err := os.ReadFile(fmt.Sprintf("%s/%s", config.getPath(t), "workspaces/test/config.toml")) assert.NoError(t, err) assert.Equal(t, []byte("app = 'bash'\npath = '/tmp'\n"), b) },