package config import ( "fmt" "os" "path/filepath" "strings" ) // ValidationError 表示配置验证错误 type ValidationError struct { Field string Message string } // ValidationResult 表示配置验证结果 type ValidationResult struct { Valid bool Errors []ValidationError } // String 返回验证结果的字符串表示 func (vr ValidationResult) String() string { if vr.Valid { return "配置验证通过" } var sb strings.Builder sb.WriteString(fmt.Sprintf("配置验证失败,共有 %d 个错误:\n", len(vr.Errors))) for i, err := range vr.Errors { sb.WriteString(fmt.Sprintf("%d. %s: %s\n", i+1, err.Field, err.Message)) } return sb.String() } // Validate 验证配置是否有效 func (c *Config) Validate(strict bool) ValidationResult { result := ValidationResult{ Valid: true, Errors: []ValidationError{}, } // 验证基本信息 if c.Name == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: "name", Message: "安装包名称不能为空", }) } if c.Version == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: "version", Message: "安装包版本不能为空", }) } // 验证构建设置 if len(c.Build.Targets) == 0 { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: "build.targets", Message: "至少需要指定一个目标平台", }) } if len(c.Build.Formats) == 0 { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: "build.formats", Message: "至少需要指定一个输出格式", }) } // 验证文件和目录 for i, file := range c.Contents.Files { if file.Source == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.files[%d].source", i), Message: "源文件路径不能为空", }) } else if strict { // 在严格模式下,验证源文件是否存在 if _, err := os.Stat(file.Source); os.IsNotExist(err) { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.files[%d].source", i), Message: fmt.Sprintf("源文件 '%s' 不存在", file.Source), }) } } if file.Destination == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.files[%d].destination", i), Message: "目标路径不能为空", }) } } // 验证脚本 for i, script := range c.Contents.Scripts { if script.Type == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.scripts[%d].type", i), Message: "脚本类型不能为空", }) } else if !isValidScriptType(script.Type) { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.scripts[%d].type", i), Message: fmt.Sprintf("无效的脚本类型: %s", script.Type), }) } if script.Content == "" && script.Source == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.scripts[%d]", i), Message: "脚本内容和源文件路径不能同时为空", }) } if script.Source != "" && strict { // 在严格模式下,验证脚本源文件是否存在 if _, err := os.Stat(script.Source); os.IsNotExist(err) { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.scripts[%d].source", i), Message: fmt.Sprintf("脚本源文件 '%s' 不存在", script.Source), }) } } } // 验证符号链接 for i, symlink := range c.Contents.Symlinks { if symlink.Source == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.symlinks[%d].source", i), Message: "符号链接源路径不能为空", }) } if symlink.Destination == "" { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: fmt.Sprintf("contents.symlinks[%d].destination", i), Message: "符号链接目标路径不能为空", }) } } // 验证平台特定配置 for _, target := range c.Build.Targets { switch target { case "windows": // 验证Windows MSI配置 if containsFormat(c.Build.Formats, "msi") { if c.Platforms.Windows.Msi.UpgradeCode == "" && strict { result.Valid = false result.Errors = append(result.Errors, ValidationError{ Field: "platforms.windows.msi.upgradeCode", Message: "MSI升级代码不能为空", }) } } case "linux": // 验证Linux DEB配置 if containsFormat(c.Build.Formats, "deb") { // 可以添加更多DEB特定的验证 } // 验证Linux RPM配置 if containsFormat(c.Build.Formats, "rpm") { // 可以添加更多RPM特定的验证 } } } return result } // 检查是否为有效的脚本类型 func isValidScriptType(scriptType string) bool { validTypes := []string{ "preinstall", "postinstall", "preuninstall", "postuninstall", } for _, validType := range validTypes { if scriptType == validType { return true } } return false } // 检查格式列表是否包含指定格式 func containsFormat(formats []string, format string) bool { for _, f := range formats { if f == format { return true } } return false } // ValidateConfigFile 验证指定路径的配置文件 func ValidateConfigFile(path string, strict bool) (ValidationResult, error) { config, err := LoadConfig(path) if err != nil { return ValidationResult{ Valid: false, Errors: []ValidationError{ { Field: "file", Message: err.Error(), }, }, }, err } // 设置工作目录为配置文件所在目录,以便相对路径的验证 if strict { oldWd, err := os.Getwd() if err != nil { return ValidationResult{}, err } defer os.Chdir(oldWd) configDir := filepath.Dir(path) if err := os.Chdir(configDir); err != nil { return ValidationResult{}, err } } return config.Validate(strict), nil }