First commit of golang CLI.
This commit is contained in:
168
wild-cli/cmd/wild/app/fetch.go
Normal file
168
wild-cli/cmd/wild/app/fetch.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/wild-cloud/wild-cli/internal/environment"
|
||||
"github.com/wild-cloud/wild-cli/internal/output"
|
||||
)
|
||||
|
||||
var (
|
||||
updateCache bool
|
||||
)
|
||||
|
||||
func newFetchCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "fetch <name>",
|
||||
Short: "Fetch an application template",
|
||||
Long: `Fetch an app template from the Wild Cloud repository to cache.
|
||||
|
||||
This command copies an application template from WC_ROOT/apps to your
|
||||
project's cache directory (.wildcloud/cache/apps) for configuration and deployment.
|
||||
|
||||
Examples:
|
||||
wild app fetch postgres
|
||||
wild app fetch immich
|
||||
wild app fetch redis --update`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runFetch,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&updateCache, "update", false, "overwrite existing cached files without confirmation")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runFetch(cmd *cobra.Command, args []string) error {
|
||||
appName := args[0]
|
||||
|
||||
output.Header("Fetching Application")
|
||||
output.Info("App: " + appName)
|
||||
|
||||
// Initialize environment
|
||||
env := environment.New()
|
||||
if err := env.RequiresInstallation(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := env.RequiresProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if source app exists
|
||||
sourceAppDir := filepath.Join(env.WCRoot(), "apps", appName)
|
||||
if _, err := os.Stat(sourceAppDir); os.IsNotExist(err) {
|
||||
return fmt.Errorf("app '%s' not found at %s", appName, sourceAppDir)
|
||||
}
|
||||
|
||||
// Read app manifest for info
|
||||
manifestPath := filepath.Join(sourceAppDir, "manifest.yaml")
|
||||
if manifestData, err := os.ReadFile(manifestPath); err == nil {
|
||||
var manifest AppManifest
|
||||
if err := yaml.Unmarshal(manifestData, &manifest); err == nil {
|
||||
output.Info("Description: " + manifest.Description)
|
||||
output.Info("Version: " + manifest.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up cache directory
|
||||
cacheAppDir := filepath.Join(env.WCHome(), ".wildcloud", "cache", "apps", appName)
|
||||
|
||||
// Create cache directory structure
|
||||
if err := os.MkdirAll(filepath.Join(env.WCHome(), ".wildcloud", "cache", "apps"), 0755); err != nil {
|
||||
return fmt.Errorf("creating cache directory: %w", err)
|
||||
}
|
||||
|
||||
// Check if already cached
|
||||
if _, err := os.Stat(cacheAppDir); err == nil {
|
||||
if updateCache {
|
||||
output.Info("Updating cached app '" + appName + "'")
|
||||
if err := os.RemoveAll(cacheAppDir); err != nil {
|
||||
return fmt.Errorf("removing existing cache: %w", err)
|
||||
}
|
||||
} else {
|
||||
output.Warning("Cache directory " + cacheAppDir + " already exists")
|
||||
output.Printf("Do you want to overwrite it? (y/N): ")
|
||||
var response string
|
||||
if _, err := fmt.Scanln(&response); err != nil || (response != "y" && response != "Y") {
|
||||
output.Info("Fetch cancelled")
|
||||
return nil
|
||||
}
|
||||
if err := os.RemoveAll(cacheAppDir); err != nil {
|
||||
return fmt.Errorf("removing existing cache: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.Info(fmt.Sprintf("Fetching app '%s' from %s to %s", appName, sourceAppDir, cacheAppDir))
|
||||
|
||||
// Copy the entire directory structure
|
||||
if err := copyDirFetch(sourceAppDir, cacheAppDir); err != nil {
|
||||
return fmt.Errorf("copying app directory: %w", err)
|
||||
}
|
||||
|
||||
output.Success("Successfully fetched app '" + appName + "' to cache")
|
||||
output.Info("")
|
||||
output.Info("Next steps:")
|
||||
output.Info(" wild app add " + appName + " # Add to project with configuration")
|
||||
output.Info(" wild app deploy " + appName + " # Deploy to cluster")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyDirFetch recursively copies a directory from src to dst
|
||||
func copyDirFetch(src, dst string) error {
|
||||
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate relative path
|
||||
relPath, err := filepath.Rel(src, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate destination path
|
||||
dstPath := filepath.Join(dst, relPath)
|
||||
|
||||
if info.IsDir() {
|
||||
// Create directory
|
||||
return os.MkdirAll(dstPath, info.Mode())
|
||||
}
|
||||
|
||||
// Copy file
|
||||
return copyFileFetch(path, dstPath)
|
||||
})
|
||||
}
|
||||
|
||||
// copyFileFetch copies a single file from src to dst
|
||||
func copyFileFetch(src, dst string) error {
|
||||
// Create destination directory if it doesn't exist
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Open source file
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = srcFile.Close() }()
|
||||
|
||||
// Create destination file
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = dstFile.Close() }()
|
||||
|
||||
// Copy file contents
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user