package pluginctl import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/mattermost/mattermost/server/public/model" ) const PluginManifestName = "plugin.json" // PluginCtlConfig represents the configuration for pluginctl stored in the manifest props. type PluginCtlConfig struct { IgnoreAssets []string `json:"ignore_assets,omitempty"` } // LoadPluginManifest loads and parses the plugin.json file from the current directory. func LoadPluginManifest() (*model.Manifest, error) { return LoadPluginManifestFromPath(".") } // LoadPluginManifestFromPath loads and parses the plugin.json file from the specified directory. func LoadPluginManifestFromPath(dir string) (*model.Manifest, error) { manifestPath := filepath.Join(dir, PluginManifestName) // Check if plugin.json exists if _, err := os.Stat(manifestPath); os.IsNotExist(err) { return nil, fmt.Errorf("plugin.json not found in directory %s", dir) } // Read the file data, err := os.ReadFile(manifestPath) if err != nil { return nil, fmt.Errorf("failed to read plugin.json: %w", err) } // Parse JSON into Manifest struct var manifest model.Manifest if err := json.Unmarshal(data, &manifest); err != nil { return nil, fmt.Errorf("failed to parse plugin.json: %w", err) } return &manifest, nil } // HasServerCode checks if the plugin contains server-side code. func HasServerCode(manifest *model.Manifest) bool { return manifest.Server != nil && len(manifest.Server.Executables) > 0 } // HasWebappCode checks if the plugin contains webapp code. func HasWebappCode(manifest *model.Manifest) bool { return manifest.Webapp != nil && manifest.Webapp.BundlePath != "" } // IsValidPluginDirectory checks if the current directory contains a valid plugin. func IsValidPluginDirectory() error { _, err := LoadPluginManifest() return err } // GetEffectivePluginPath determines the plugin path from flag, environment variable, or current directory. func GetEffectivePluginPath(flagPath string) string { const EnvPluginPath = "PLUGINCTL_PLUGIN_PATH" // Priority: 1. Command line flag, 2. Environment variable, 3. Current directory if flagPath != "" { return flagPath } if envPath := os.Getenv(EnvPluginPath); envPath != "" { return envPath } // Default to current directory cwd, err := os.Getwd() if err != nil { return "." } return cwd } // ParsePluginCtlConfig extracts and parses the pluginctl configuration from the manifest props. func ParsePluginCtlConfig(manifest *model.Manifest) (*PluginCtlConfig, error) { // Default configuration config := &PluginCtlConfig{ IgnoreAssets: []string{}, } // Check if props exist if manifest.Props == nil { return config, nil } // Check if pluginctl config exists in props pluginctlData, exists := manifest.Props["pluginctl"] if !exists { return config, nil } // Convert to JSON and parse jsonData, err := json.Marshal(pluginctlData) if err != nil { return nil, fmt.Errorf("failed to marshal pluginctl config: %w", err) } if err := json.Unmarshal(jsonData, config); err != nil { return nil, fmt.Errorf("failed to parse pluginctl config: %w", err) } return config, nil } const ( HelpFlagLong = "--help" HelpFlagShort = "-h" ) // CheckForHelpFlag checks if --help is in the arguments and shows help if found. // Returns true if help was shown, false otherwise. func CheckForHelpFlag(args []string, helpText string) bool { for _, arg := range args { if arg == HelpFlagLong || arg == HelpFlagShort { Logger.Info(helpText) return true } } return false } // ShowErrorWithHelp displays an error message followed by command help. func ShowErrorWithHelp(errorMsg, helpText string) error { Logger.Error(errorMsg) Logger.Info(helpText) return fmt.Errorf("%s", errorMsg) }