Files
wild-cloud/wild-cli/cmd/wild/app/add.go

181 lines
4.4 KiB
Go

package app
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
"github.com/wild-cloud/wild-cli/internal/apps"
"github.com/wild-cloud/wild-cli/internal/config"
"github.com/wild-cloud/wild-cli/internal/environment"
"github.com/wild-cloud/wild-cli/internal/output"
)
func newAddCommand() *cobra.Command {
return &cobra.Command{
Use: "add <name>",
Short: "Add an application to the project",
Long: `Add an application to the project with configuration.
This copies the cached application template to your project's apps/
directory and creates initial configuration.
Examples:
wild app add nextcloud
wild app add postgresql`,
Args: cobra.ExactArgs(1),
RunE: runAdd,
}
}
func runAdd(cmd *cobra.Command, args []string) error {
appName := args[0]
output.Header("Adding Application")
output.Info("App: " + appName)
// Initialize environment
env := environment.New()
if err := env.RequiresProject(); err != nil {
return err
}
// Create catalog
catalog := apps.NewCatalog(env.CacheDir())
// Check if app is cached
if !catalog.IsAppCached(appName) {
output.Warning("App '" + appName + "' is not cached locally")
output.Info("Run 'wild app fetch " + appName + "' first")
return nil
}
// Check if app already exists in project
appDir := filepath.Join(env.AppsDir(), appName)
if _, err := os.Stat(appDir); err == nil {
output.Warning("App '" + appName + "' already exists in project")
output.Info("App directory: " + appDir)
return nil
}
// Get app info
app, err := catalog.FindApp(appName)
if err != nil {
return fmt.Errorf("getting app info: %w", err)
}
output.Info("Description: " + app.Description)
output.Info("Version: " + app.Version)
// Check dependencies
if len(app.Requires) > 0 {
output.Info("Dependencies: " + fmt.Sprintf("%v", app.Requires))
// Check if dependencies are available
for _, dep := range app.Requires {
depDir := filepath.Join(env.AppsDir(), dep)
if _, err := os.Stat(depDir); os.IsNotExist(err) {
output.Warning("Dependency '" + dep + "' not found in project")
output.Info("Consider adding it first: wild app add " + dep)
} else {
output.Success("Dependency '" + dep + "' found")
}
}
}
// Copy app template from cache to project
cacheDir := filepath.Join(env.CacheDir(), "apps", appName)
if err := copyDir(cacheDir, appDir); err != nil {
return fmt.Errorf("copying app template: %w", err)
}
// Create initial configuration in config.yaml
if err := addAppConfig(env, appName, app); err != nil {
output.Warning("Failed to add app configuration: " + err.Error())
} else {
output.Success("Added default configuration to config.yaml")
}
output.Success("App '" + appName + "' added to project")
output.Info("")
output.Info("App directory: " + appDir)
output.Info("Next steps:")
output.Info(" wild config set apps." + appName + ".enabled true")
output.Info(" wild app deploy " + appName)
return nil
}
// copyDir recursively copies a directory
func copyDir(src, dst string) error {
entries, err := os.ReadDir(src)
if err != nil {
return err
}
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
if err := copyDir(srcPath, dstPath); err != nil {
return err
}
} else {
if err := copyFile(srcPath, dstPath); err != nil {
return err
}
}
}
return nil
}
// copyFile copies a single file
func copyFile(src, dst string) error {
data, err := os.ReadFile(src)
if err != nil {
return err
}
return os.WriteFile(dst, data, 0644)
}
// addAppConfig adds default app configuration to config.yaml
func addAppConfig(env *environment.Environment, appName string, app *apps.App) error {
mgr := config.NewManager(env.ConfigPath(), env.SecretsPath())
// Create default app configuration
appConfig := map[string]interface{}{
"enabled": false,
"image": appName + ":latest",
}
// Add any default config from the app manifest
if app.Config != nil {
for key, value := range app.Config {
appConfig[key] = value
}
}
// Convert to YAML and set in config
configData, err := yaml.Marshal(appConfig)
if err != nil {
return fmt.Errorf("marshaling config: %w", err)
}
configPath := "apps." + appName
if err := mgr.Set(configPath, string(configData)); err != nil {
return fmt.Errorf("setting config: %w", err)
}
return nil
}