Skip to content

Commit

Permalink
feat: world forge organization and project
Browse files Browse the repository at this point in the history
  • Loading branch information
zulkhair committed Dec 23, 2024
1 parent caa3fc3 commit 57331a0
Show file tree
Hide file tree
Showing 13 changed files with 1,294 additions and 41 deletions.
107 changes: 107 additions & 0 deletions cmd/world/forge/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package forge

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/rotisserie/eris"

"pkg.world.dev/world-cli/common/globalconfig"
)

// sendRequest sends an HTTP request with auth token and returns the response body
func sendRequest(ctx context.Context, method, url string, body interface{}) ([]byte, error) {
var bodyReader io.Reader

if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, eris.Wrap(err, "Failed to marshal request body")
}
bodyReader = bytes.NewReader(jsonBody)
}

// Get credential from config
cred, err := globalconfig.GetGlobalConfig()
if err != nil {
return nil, eris.Wrap(err, "Failed to get credential")
}

// Create request
req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
if err != nil {
return nil, eris.Wrap(err, "Failed to create request")
}

// Add authorization header
req.Header.Add("Authorization", "Bearer "+cred.Credential.Token)

// Add content-type header for requests with body
if body != nil {
req.Header.Set("Content-Type", "application/json")
}

// Make request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, eris.Wrap(err, "Failed to make request")
}
defer resp.Body.Close()

// Check status code
if resp.StatusCode != http.StatusOK {
return nil, eris.Errorf("Unexpected status code: %d", resp.StatusCode)
}

// Read response body
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, eris.Wrap(err, "Failed to read response body")
}

return respBody, nil
}

func parseResponse[T any](body []byte) (*T, error) {
// Parse wrapper response
var response struct {
Data T `json:"data"`
}
if err := json.Unmarshal(body, &response); err != nil {
return nil, eris.Wrap(err, "Failed to parse response")
}

return &response.Data, nil
}

func printNoSelectedOrganization() {
fmt.Println("You don't have any organization selected.")
fmt.Println("Use 'world forge organization switch' to select one.")
fmt.Println()
}

func printNoSelectedProject() {
fmt.Println("You don't have any project selected.")
fmt.Println("Use 'world forge project switch' to select one.")
fmt.Println()
}

func printNoProjectsInOrganization() {
fmt.Println("You don't have any projects in this organization yet.")
fmt.Println("Use 'world forge project create' to create one.")
fmt.Println()
}

func isAlphanumeric(s string) bool {
for _, r := range s {
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) {
return false
}
}
return true
}
130 changes: 130 additions & 0 deletions cmd/world/forge/deployment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package forge

import (
"context"
"fmt"
"net/http"
"strings"

"github.com/rotisserie/eris"

"pkg.world.dev/world-cli/common/globalconfig"
)

// Deploy a project
func deploy(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("Deployment 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)

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

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): ")
var response string
if _, err := fmt.Scanln(&response); 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.Println(" $ 'world forge deployment status'")

return nil
}
Loading

0 comments on commit 57331a0

Please sign in to comment.