249 lines
6.1 KiB
Go
249 lines
6.1 KiB
Go
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
|
|
}
|