169 lines
4.4 KiB
Go
169 lines
4.4 KiB
Go
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
|
|
}
|