diff --git a/cmd/pluginctl/main.go b/cmd/pluginctl/main.go index ed79db3..2baeb9f 100644 --- a/cmd/pluginctl/main.go +++ b/cmd/pluginctl/main.go @@ -131,7 +131,7 @@ Commands: reset Reset plugin from current directory (disable then enable) deploy Upload and enable plugin bundle to Mattermost server updateassets Update plugin files from embedded assets - manifest Get plugin manifest information using templates (get {{.field}}, check) + manifest Get plugin manifest information using templates (get {{.field}}, apply, check) logs View plugin logs (use --watch to follow logs in real-time) create-plugin Create a new plugin from the starter template (supports --name and --module flags) help Show this help message @@ -147,6 +147,7 @@ Examples: pluginctl deploy --bundle-path ./bundle.tar.gz # Deploy specific bundle file pluginctl updateassets # Update plugin files from embedded assets pluginctl manifest get '{{.id}}' # Get any manifest field using templates + pluginctl manifest apply # Generate manifest files for server/webapp pluginctl manifest check # Validate plugin manifest pluginctl logs # View recent plugin logs pluginctl logs --watch # Watch plugin logs in real-time diff --git a/manifest.go b/manifest.go index 22f976a..e2347e6 100644 --- a/manifest.go +++ b/manifest.go @@ -2,15 +2,97 @@ package pluginctl import ( "bytes" + "encoding/json" "fmt" + "os" "path/filepath" "text/template" + + "github.com/mattermost/mattermost/server/public/model" ) +const ( + // File permissions for generated directories and files. + manifestDirPerm = 0o750 + manifestFilePerm = 0o600 +) + +const pluginIDGoFileTemplate = `// This file is automatically generated. Do not modify it manually. + +package main + +import ( + "encoding/json" + "strings" + + "github.com/mattermost/mattermost/server/public/model" +) + +var manifest *model.Manifest + +const manifestStr = ` + "`%s`" + ` + +func init() { + _ = json.NewDecoder(strings.NewReader(manifestStr)).Decode(&manifest) +} +` + +const pluginIDJSFileTemplate = `// This file is automatically generated. Do not modify it manually. + +const manifest = JSON.parse(` + "`%s`" + `); + +export default manifest; +` + +// applyManifest generates manifest files for server and webapp components. +func applyManifest(manifest *model.Manifest, pluginPath string) error { + manifestBytes, err := json.Marshal(manifest) + if err != nil { + return fmt.Errorf("failed to marshal manifest: %w", err) + } + manifestStr := string(manifestBytes) + + // Generate server manifest file if server exists + if HasServerCode(manifest) { + serverDir := filepath.Join(pluginPath, "server") + if err := os.MkdirAll(serverDir, manifestDirPerm); err != nil { + return fmt.Errorf("failed to create server directory: %w", err) + } + + serverManifestPath := filepath.Join(serverDir, "manifest.go") + serverContent := fmt.Sprintf(pluginIDGoFileTemplate, manifestStr) + + if err := os.WriteFile(serverManifestPath, []byte(serverContent), manifestFilePerm); err != nil { + return fmt.Errorf("failed to write server manifest: %w", err) + } + + Logger.Info("Generated server manifest", "path", serverManifestPath) + } + + // Generate webapp manifest file if webapp exists + if HasWebappCode(manifest) { + webappDir := filepath.Join(pluginPath, "webapp", "src") + if err := os.MkdirAll(webappDir, manifestDirPerm); err != nil { + return fmt.Errorf("failed to create webapp directory: %w", err) + } + + webappManifestPath := filepath.Join(webappDir, "manifest.ts") + webappContent := fmt.Sprintf(pluginIDJSFileTemplate, manifestStr) + + if err := os.WriteFile(webappManifestPath, []byte(webappContent), manifestFilePerm); err != nil { + return fmt.Errorf("failed to write webapp manifest: %w", err) + } + + Logger.Info("Generated webapp manifest", "path", webappManifestPath) + } + + return nil +} + // RunManifestCommand implements the 'manifest' command functionality with subcommands. func RunManifestCommand(args []string, pluginPath string) error { if len(args) == 0 { - return fmt.Errorf("manifest command requires a subcommand: get {{.field_name}}, check") + return fmt.Errorf("manifest command requires a subcommand: get {{.field_name}}, apply, check") } // Convert to absolute path @@ -45,6 +127,10 @@ func RunManifestCommand(args []string, pluginPath string) error { } fmt.Print(buf.String()) + case "apply": + if err := applyManifest(manifest, absPath); err != nil { + return fmt.Errorf("failed to apply manifest: %w", err) + } case "check": if err := manifest.IsValid(); err != nil { Logger.Error("Plugin manifest validation failed", "error", err) @@ -53,7 +139,8 @@ func RunManifestCommand(args []string, pluginPath string) error { } Logger.Info("Plugin manifest is valid") default: - return fmt.Errorf("unknown subcommand: %s. Available subcommands: get {{.field_name}}, check", subcommand) + return fmt.Errorf("unknown subcommand: %s. Available subcommands: get {{.field_name}}, apply, check", + subcommand) } return nil