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
12 changes: 10 additions & 2 deletions pkg/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"path/filepath"

"github.com/kortex-hub/kortex-cli/pkg/instances"
"github.com/kortex-hub/kortex-cli/pkg/runtime/fake"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -71,6 +72,13 @@ func (i *initCmd) preRun(cmd *cobra.Command, args []string) error {
if err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to create manager: %w", err))
}

// Register fake runtime (for testing)
// TODO: In production, register only the runtimes that are available/configured
if err := manager.RegisterRuntime(fake.New()); err != nil {
return outputErrorIfJSON(cmd, i.output, fmt.Errorf("failed to register fake runtime: %w", err))
}

i.manager = manager

// Get sources directory (default to current directory)
Expand Down Expand Up @@ -124,8 +132,8 @@ func (i *initCmd) run(cmd *cobra.Command, args []string) error {
return outputErrorIfJSON(cmd, i.output, err)
}

// Add the instance to the manager
addedInstance, err := i.manager.Add(instance)
// Add the instance to the manager with runtime
addedInstance, err := i.manager.Add(cmd.Context(), instance, "fake")
if err != nil {
return outputErrorIfJSON(cmd, i.output, err)
}
Expand Down
32 changes: 27 additions & 5 deletions pkg/cmd/workspace_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package cmd

import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
Expand All @@ -29,6 +30,7 @@ import (
api "github.com/kortex-hub/kortex-cli-api/cli/go"
"github.com/kortex-hub/kortex-cli/pkg/cmd/testutil"
"github.com/kortex-hub/kortex-cli/pkg/instances"
"github.com/kortex-hub/kortex-cli/pkg/runtime/fake"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -243,7 +245,12 @@ func TestWorkspaceListCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down Expand Up @@ -308,12 +315,17 @@ func TestWorkspaceListCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance 2: %v", err)
}

addedInstance1, err := manager.Add(instance1)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance1, err := manager.Add(context.Background(), instance1, "fake")
if err != nil {
t.Fatalf("Failed to add instance 1: %v", err)
}

addedInstance2, err := manager.Add(instance2)
addedInstance2, err := manager.Add(context.Background(), instance2, "fake")
if err != nil {
t.Fatalf("Failed to add instance 2: %v", err)
}
Expand Down Expand Up @@ -385,7 +397,12 @@ func TestWorkspaceListCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down Expand Up @@ -473,7 +490,12 @@ func TestWorkspaceListCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/cmd/workspace_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

api "github.com/kortex-hub/kortex-cli-api/cli/go"
"github.com/kortex-hub/kortex-cli/pkg/instances"
"github.com/kortex-hub/kortex-cli/pkg/runtime/fake"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -69,6 +70,13 @@ func (w *workspaceRemoveCmd) preRun(cmd *cobra.Command, args []string) error {
if err != nil {
return outputErrorIfJSON(cmd, w.output, fmt.Errorf("failed to create manager: %w", err))
}

// Register fake runtime (for testing)
// TODO: In production, register only the runtimes that are available/configured
if err := manager.RegisterRuntime(fake.New()); err != nil {
return outputErrorIfJSON(cmd, w.output, fmt.Errorf("failed to register fake runtime: %w", err))
}

w.manager = manager

return nil
Expand All @@ -77,7 +85,7 @@ func (w *workspaceRemoveCmd) preRun(cmd *cobra.Command, args []string) error {
// run executes the workspace remove command logic
func (w *workspaceRemoveCmd) run(cmd *cobra.Command, args []string) error {
// Delete the instance
err := w.manager.Delete(w.id)
err := w.manager.Delete(cmd.Context(), w.id)
if err != nil {
if errors.Is(err, instances.ErrInstanceNotFound) {
if w.output == "json" {
Expand Down
83 changes: 76 additions & 7 deletions pkg/cmd/workspace_remove_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package cmd

import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
Expand All @@ -29,6 +30,7 @@ import (
api "github.com/kortex-hub/kortex-cli-api/cli/go"
"github.com/kortex-hub/kortex-cli/pkg/cmd/testutil"
"github.com/kortex-hub/kortex-cli/pkg/instances"
"github.com/kortex-hub/kortex-cli/pkg/runtime/fake"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -220,6 +222,46 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
}
})

t.Run("creates manager from storage flag", func(t *testing.T) {
t.Parallel()

storageDir := t.TempDir()
sourcesDir := t.TempDir()

// Create a workspace first
manager, err := instances.NewManager(storageDir)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}

instance, err := instances.NewInstance(instances.NewInstanceParams{
SourceDir: sourcesDir,
ConfigDir: filepath.Join(sourcesDir, ".kortex"),
})
if err != nil {
t.Fatalf("Failed to create instance: %v", err)
}

// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}

// Now remove it
rootCmd := NewRootCmd()
rootCmd.SetArgs([]string{"workspace", "remove", addedInstance.GetID(), "--storage", storageDir})

err = rootCmd.Execute()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
})

t.Run("removes existing workspace by ID", func(t *testing.T) {
t.Parallel()

Expand All @@ -240,7 +282,12 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down Expand Up @@ -308,6 +355,7 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Run("removes only specified workspace when multiple exist", func(t *testing.T) {
t.Parallel()

ctx := context.Background()
storageDir := t.TempDir()
sourcesDir1 := t.TempDir()
sourcesDir2 := t.TempDir()
Expand All @@ -334,12 +382,17 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance 2: %v", err)
}

addedInstance1, err := manager.Add(instance1)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance1, err := manager.Add(ctx, instance1, "fake")
if err != nil {
t.Fatalf("Failed to add instance 1: %v", err)
}

addedInstance2, err := manager.Add(instance2)
addedInstance2, err := manager.Add(ctx, instance2, "fake")
if err != nil {
t.Fatalf("Failed to add instance 2: %v", err)
}
Expand Down Expand Up @@ -404,7 +457,12 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down Expand Up @@ -460,7 +518,12 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance: %v", err)
}

addedInstance, err := manager.Add(instance)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance, err := manager.Add(context.Background(), instance, "fake")
if err != nil {
t.Fatalf("Failed to add instance: %v", err)
}
Expand Down Expand Up @@ -562,6 +625,7 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Run("json output removes correct workspace when multiple exist", func(t *testing.T) {
t.Parallel()

ctx := context.Background()
storageDir := t.TempDir()
sourcesDir1 := t.TempDir()
sourcesDir2 := t.TempDir()
Expand All @@ -588,12 +652,17 @@ func TestWorkspaceRemoveCmd_E2E(t *testing.T) {
t.Fatalf("Failed to create instance 2: %v", err)
}

addedInstance1, err := manager.Add(instance1)
// Register fake runtime
if err := manager.RegisterRuntime(fake.New()); err != nil {
t.Fatalf("Failed to register fake runtime: %v", err)
}

addedInstance1, err := manager.Add(ctx, instance1, "fake")
if err != nil {
t.Fatalf("Failed to add instance 1: %v", err)
}

addedInstance2, err := manager.Add(instance2)
addedInstance2, err := manager.Add(ctx, instance2, "fake")
if err != nil {
t.Fatalf("Failed to add instance 2: %v", err)
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/instances/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package instances

import (
"errors"
"maps"
"os"
"path/filepath"
)
Expand All @@ -37,6 +38,18 @@ type InstancePaths struct {
Configuration string `json:"configuration"`
}

// RuntimeData represents runtime-specific information for an instance
type RuntimeData struct {
// Type is the runtime type (e.g., "fake", "podman", "docker")
Type string `json:"type"`
// InstanceID is the runtime-assigned instance identifier
InstanceID string `json:"instance_id"`
// State is the current runtime state (e.g., "created", "running", "stopped")
State string `json:"state"`
// Info contains runtime-specific metadata
Info map[string]string `json:"info"`
}

// InstanceData represents the serializable data of an instance
type InstanceData struct {
// ID is the unique identifier for the instance
Expand All @@ -45,6 +58,8 @@ type InstanceData struct {
Name string `json:"name"`
// Paths contains the source and configuration directories
Paths InstancePaths `json:"paths"`
// Runtime contains runtime-specific information
Runtime RuntimeData `json:"runtime"`
}

// Instance represents a workspace instance with source and configuration directories.
Expand All @@ -63,6 +78,10 @@ type Instance interface {
GetConfigDir() string
// IsAccessible checks if both source and config directories are accessible
IsAccessible() bool
// GetRuntimeType returns the runtime type for this instance
GetRuntimeType() string
// GetRuntimeData returns the complete runtime data for this instance
GetRuntimeData() RuntimeData
// Dump returns the serializable data of the instance
Dump() InstanceData
}
Expand All @@ -79,6 +98,8 @@ type instance struct {
// ConfigDir is the directory containing workspace configuration.
// This is always stored as an absolute path.
ConfigDir string
// Runtime contains runtime-specific information
Runtime RuntimeData
}

// Compile-time check to ensure instance implements Instance interface
Expand Down Expand Up @@ -115,6 +136,23 @@ func (i *instance) IsAccessible() bool {
return true
}

// GetRuntimeType returns the runtime type for this instance
func (i *instance) GetRuntimeType() string {
return i.Runtime.Type
}

// GetRuntimeData returns the complete runtime data for this instance
func (i *instance) GetRuntimeData() RuntimeData {
infoCopy := make(map[string]string, len(i.Runtime.Info))
maps.Copy(infoCopy, i.Runtime.Info)
return RuntimeData{
Type: i.Runtime.Type,
InstanceID: i.Runtime.InstanceID,
State: i.Runtime.State,
Info: infoCopy,
}
}

// Dump returns the serializable data of the instance
func (i *instance) Dump() InstanceData {
return InstanceData{
Expand All @@ -124,6 +162,7 @@ func (i *instance) Dump() InstanceData {
Source: i.SourceDir,
Configuration: i.ConfigDir,
},
Runtime: i.Runtime,
}
}

Expand Down Expand Up @@ -188,6 +227,7 @@ func NewInstanceFromData(data InstanceData) (Instance, error) {
Name: data.Name,
SourceDir: data.Paths.Source,
ConfigDir: data.Paths.Configuration,
Runtime: data.Runtime,
}, nil
}

Expand Down
Loading
Loading