Skip to content

Commit

Permalink
feat: add deployment reset command (#89)
Browse files Browse the repository at this point in the history
Closes: WORLD-1327 & WORLD-1328

## Overview

Check destroy command and add reset command

## Brief Changelog

- merge the deploy, destroy func

## Testing and Verifying

Manual test runing `world forge deployment reset` command

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Release Notes

- **New Features**
    - Added a new project reset functionality
    - Enhanced deployment commands with more flexible action types

- **Improvements**
    - Consolidated deployment-related actions into a single method
    - Improved error handling and context for deployment operations

- **Changes**
    - Updated deployment command structure to support deploy, destroy, and reset actions
    - Standardized deployment method signature across different operations

The release introduces more flexible project management capabilities with a unified deployment approach.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
zulkhair committed Jan 14, 2025
1 parent bd05e4e commit ea0a524
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 75 deletions.
77 changes: 6 additions & 71 deletions cmd/world/forge/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (

var statusFailRegEx = regexp.MustCompile(`[^a-zA-Z0-9\. ]+`)

// Deploy a project
func deploy(ctx context.Context) error {
// Deployment a project
func deployment(ctx context.Context, deployType string) error {
globalConfig, err := globalconfig.GetGlobalConfig()
if err != nil {
return eris.Wrap(err, "Failed to get global config")
Expand Down Expand Up @@ -56,79 +56,14 @@ func deploy(ctx context.Context) error {
fmt.Printf("Project Slug: %s\n", prj.Slug)
fmt.Printf("Repository: %s\n\n", prj.RepoURL)

deployURL := fmt.Sprintf("%s/api/organization/%s/project/%s/deploy", baseURL, organizationID, projectID)
deployURL := fmt.Sprintf("%s/api/organization/%s/project/%s/%s", baseURL, organizationID, projectID, deployType)
_, err = sendRequest(ctx, http.MethodPost, deployURL, nil)
if err != nil {
return eris.Wrap(err, "Failed to deploy project")
return eris.Wrap(err, fmt.Sprintf("Failed to %s project", deployType))
}

fmt.Println("\n✨ Your deployment is being processed! ✨")
fmt.Println("\nTo check the status of your deployment, run:")
fmt.Println(" $ 'world forge deployment status'")

return nil
}

// Destroy a project
func destroy(ctx context.Context) error {
globalConfig, err := globalconfig.GetGlobalConfig()
if err != nil {
return eris.Wrap(err, "Failed to get global config")
}

projectID := globalConfig.ProjectID
organizationID := globalConfig.OrganizationID

if organizationID == "" {
printNoSelectedOrganization()
return nil
}

if projectID == "" {
printNoSelectedProject()
return nil
}

// Get organization details
org, err := getSelectedOrganization(ctx)
if err != nil {
return eris.Wrap(err, "Failed to get organization details")
}

// Get project details
prj, err := getSelectedProject(ctx)
if err != nil {
return eris.Wrap(err, "Failed to get project details")
}

fmt.Println("Project Details")
fmt.Println("-----------------")
fmt.Printf("Organization: %s\n", org.Name)
fmt.Printf("Org Slug: %s\n", org.Slug)
fmt.Printf("Project: %s\n", prj.Name)
fmt.Printf("Project Slug: %s\n", prj.Slug)
fmt.Printf("Repository: %s\n\n", prj.RepoURL)

fmt.Print("Are you sure you want to destroy this project? (y/N): ")
response, err := getInput()
if err != nil {
return eris.Wrap(err, "Failed to read response")
}

response = strings.ToLower(strings.TrimSpace(response))
if response != "y" {
fmt.Println("Destroy cancelled")
return nil
}

destroyURL := fmt.Sprintf("%s/api/organization/%s/project/%s/destroy", baseURL, organizationID, projectID)
_, err = sendRequest(ctx, http.MethodPost, destroyURL, nil)
if err != nil {
return eris.Wrap(err, "Failed to destroy project")
}

fmt.Println("\n🗑️ Your destroy request is being processed!")
fmt.Println("\nTo check the status of your destroy request, run:")
fmt.Printf("\n✨ Your %s is being processed! ✨\n", deployType)
fmt.Printf("\nTo check the status of your %s, run:\n", deployType)
fmt.Println(" $ 'world forge deployment status'")

return nil
Expand Down
16 changes: 14 additions & 2 deletions cmd/world/forge/forge.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ var (
if !checkLogin() {
return nil
}
return deploy(cmd.Context())
return deployment(cmd.Context(), "deploy")
},
}

Expand All @@ -170,7 +170,18 @@ var (
if !checkLogin() {
return nil
}
return destroy(cmd.Context())
return deployment(cmd.Context(), "destroy")
},
}

resetCmd = &cobra.Command{
Use: "reset",
Short: "Reset a project",
RunE: func(cmd *cobra.Command, _ []string) error {
if !checkLogin() {
return nil
}
return deployment(cmd.Context(), "reset")
},
}

Expand Down Expand Up @@ -218,5 +229,6 @@ func init() {
deploymentCmd.AddCommand(deployCmd)
deploymentCmd.AddCommand(destroyCmd)
deploymentCmd.AddCommand(statusCmd)
deploymentCmd.AddCommand(resetCmd)
BaseCmd.AddCommand(deploymentCmd)
}
102 changes: 100 additions & 2 deletions cmd/world/forge/forge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func (s *ForgeTestSuite) SetupTest() {
s.handleDeploy(w, r)
case "/api/organization/test-org-id/project/test-project-id/destroy":
s.handleDestroy(w, r)
case "/api/organization/test-org-id/project/test-project-id/reset":
s.handleReset(w, r)
case "/api/organization/invalid-org-id/project/test-project-id/deploy":
http.Error(w, "Organization not found", http.StatusNotFound)
case "/api/organization/test-org-id/project/invalid-project-id/deploy":
Expand Down Expand Up @@ -375,6 +377,14 @@ func (s *ForgeTestSuite) handleGetToken(w http.ResponseWriter, r *http.Request)
}
}

func (s *ForgeTestSuite) handleReset(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
s.writeJSON(w, map[string]interface{}{"data": "reset started"})
}

func (s *ForgeTestSuite) writeJSON(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(data)
Expand Down Expand Up @@ -652,7 +662,7 @@ func (s *ForgeTestSuite) TestDeploy() {
err := globalconfig.SaveGlobalConfig(tc.config)
s.Require().NoError(err)

err = deploy(s.ctx)
err = deployment(s.ctx, "deploy")
if tc.expectedError {
s.Require().Error(err)
} else {
Expand Down Expand Up @@ -843,7 +853,95 @@ func (s *ForgeTestSuite) TestDestroy() {
}
defer func() { getInput = originalGetInput }()

err = destroy(s.ctx)
err = deployment(s.ctx, "destroy")
if tc.expectedError {
s.Require().Error(err)
} else {
s.Require().NoError(err)
}
})
}
}

func (s *ForgeTestSuite) TestReset() {
testCases := []struct {
name string
config globalconfig.GlobalConfig
input string
expectedError bool
}{
{
name: "Success",
config: globalconfig.GlobalConfig{
OrganizationID: "test-org-id",
ProjectID: "test-project-id",
Credential: globalconfig.Credential{
Token: "test-token",
},
},
input: "y",
expectedError: false,
},
{
name: "Error - Invalid organization ID",
config: globalconfig.GlobalConfig{
OrganizationID: "invalid-org-id",
ProjectID: "test-project-id",
Credential: globalconfig.Credential{
Token: "test-token",
},
},
input: "y",
expectedError: true,
},
{
name: "Error - Invalid project ID",
config: globalconfig.GlobalConfig{
OrganizationID: "test-org-id",
ProjectID: "invalid-project-id",
Credential: globalconfig.Credential{
Token: "test-token",
},
},
input: "y",
expectedError: true,
},
{
name: "Error - No organization selected",
config: globalconfig.GlobalConfig{
ProjectID: "test-project-id",
Credential: globalconfig.Credential{
Token: "test-token",
},
},
input: "y",
expectedError: false,
},
{
name: "Error - No project selected",
config: globalconfig.GlobalConfig{
OrganizationID: "test-org-id",
Credential: globalconfig.Credential{
Token: "test-token",
},
},
input: "y",
expectedError: false,
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
// Setup test config
err := globalconfig.SaveGlobalConfig(tc.config)
s.Require().NoError(err)

getInput = func() (string, error) {
return tc.input, nil
}
defer func() { getInput = originalGetInput }()

err = deployment(s.ctx, "reset")
if tc.expectedError {
s.Require().Error(err)
} else {
Expand Down

0 comments on commit ea0a524

Please sign in to comment.