1196 lines
30 KiB
Markdown
1196 lines
30 KiB
Markdown
# 详细设计文档
|
||
|
||
本文档详细描述了Installer Builder项目的设计,包括各组件详细设计、类/接口定义、工作流程和错误处理策略等内容。这个文档将作为开发实现的具体指导。
|
||
|
||
## 1. 组件详细设计
|
||
|
||
### 1.1 配置管理器
|
||
|
||
配置管理器负责解析、验证和管理构建配置。
|
||
|
||
#### 1.1.1 配置模型
|
||
|
||
```go
|
||
// Config 表示安装包构建的完整配置
|
||
type Config struct {
|
||
// 基本信息
|
||
Name string `yaml:"name" json:"name"` // 应用名称
|
||
Version string `yaml:"version" json:"version"` // 应用版本
|
||
Description string `yaml:"description" json:"description"` // 应用描述
|
||
Author string `yaml:"author" json:"author"` // 作者信息
|
||
License string `yaml:"license" json:"license"` // 许可证信息
|
||
|
||
// 构建目标
|
||
Targets []Target `yaml:"targets" json:"targets"` // 构建目标列表
|
||
|
||
// 文件和目录
|
||
Files []File `yaml:"files" json:"files"` // 要包含的文件
|
||
Directories []Directory `yaml:"directories" json:"directories"` // 要包含的目录
|
||
|
||
// 脚本
|
||
PreInstall Script `yaml:"preInstall" json:"preInstall"` // 安装前脚本
|
||
PostInstall Script `yaml:"postInstall" json:"postInstall"` // 安装后脚本
|
||
|
||
// 依赖
|
||
Dependencies []Dependency `yaml:"dependencies" json:"dependencies"` // 依赖项
|
||
|
||
// 插件配置
|
||
Plugins map[string]interface{} `yaml:"plugins" json:"plugins"` // 插件特定配置
|
||
}
|
||
|
||
// Target 表示构建目标
|
||
type Target struct {
|
||
Platform string `yaml:"platform" json:"platform"` // 目标平台 (windows, linux)
|
||
Arch string `yaml:"arch" json:"arch"` // 目标架构 (amd64, arm64)
|
||
PackageType string `yaml:"packageType" json:"packageType"` // 包类型 (msi, deb, rpm, zip)
|
||
OutputPath string `yaml:"outputPath" json:"outputPath"` // 输出路径
|
||
}
|
||
|
||
// File 表示要包含在安装包中的文件
|
||
type File struct {
|
||
Source string `yaml:"source" json:"source"` // 源文件路径
|
||
Destination string `yaml:"destination" json:"destination"` // 目标路径
|
||
Permissions string `yaml:"permissions" json:"permissions"` // 文件权限
|
||
}
|
||
|
||
// Directory 表示要包含在安装包中的目录
|
||
type Directory struct {
|
||
Source string `yaml:"source" json:"source"` // 源目录路径
|
||
Destination string `yaml:"destination" json:"destination"` // 目标路径
|
||
Permissions string `yaml:"permissions" json:"permissions"` // 目录权限
|
||
Recursive bool `yaml:"recursive" json:"recursive"` // 是否递归包含子目录
|
||
}
|
||
|
||
// Script 表示安装脚本
|
||
type Script struct {
|
||
Path string `yaml:"path" json:"path"` // 脚本文件路径
|
||
Type string `yaml:"type" json:"type"` // 脚本类型 (node, shell)
|
||
Args []string `yaml:"args" json:"args"` // 脚本参数
|
||
}
|
||
|
||
// Dependency 表示依赖项
|
||
type Dependency struct {
|
||
Name string `yaml:"name" json:"name"` // 依赖名称
|
||
Version string `yaml:"version" json:"version"` // 依赖版本
|
||
Type string `yaml:"type" json:"type"` // 依赖类型
|
||
}
|
||
```
|
||
|
||
#### 1.1.2 配置解析器
|
||
|
||
```go
|
||
// ConfigParser 负责解析配置文件
|
||
type ConfigParser interface {
|
||
// Parse 从指定路径解析配置文件
|
||
Parse(path string) (*Config, error)
|
||
|
||
// Validate 验证配置是否有效
|
||
Validate(config *Config) error
|
||
}
|
||
|
||
// YAMLConfigParser 实现了YAML格式的配置解析
|
||
type YAMLConfigParser struct {
|
||
schema *jsonschema.Schema // JSON Schema用于验证
|
||
}
|
||
|
||
// JSONConfigParser 实现了JSON格式的配置解析
|
||
type JSONConfigParser struct {
|
||
schema *jsonschema.Schema // JSON Schema用于验证
|
||
}
|
||
```
|
||
|
||
#### 1.1.3 配置验证器
|
||
|
||
```go
|
||
// ConfigValidator 负责验证配置
|
||
type ConfigValidator interface {
|
||
// Validate 验证配置是否有效
|
||
Validate(config *Config) error
|
||
}
|
||
|
||
// SchemaValidator 使用JSON Schema验证配置
|
||
type SchemaValidator struct {
|
||
schema *jsonschema.Schema
|
||
}
|
||
```
|
||
|
||
### 1.2 构建协调器
|
||
|
||
构建协调器负责协调整个构建过程,包括调用平台适配器、包构建器和脚本执行器。
|
||
|
||
```go
|
||
// BuildCoordinator 负责协调构建过程
|
||
type BuildCoordinator interface {
|
||
// Build 执行构建过程
|
||
Build(config *Config) ([]BuildResult, error)
|
||
|
||
// BuildTarget 构建特定目标
|
||
BuildTarget(config *Config, target Target) (*BuildResult, error)
|
||
}
|
||
|
||
// DefaultBuildCoordinator 是BuildCoordinator的默认实现
|
||
type DefaultBuildCoordinator struct {
|
||
platformAdapters map[string]PlatformAdapter
|
||
packageBuilders map[string]PackageBuilder
|
||
scriptExecutor ScriptExecutor
|
||
pluginManager PluginManager
|
||
logger Logger
|
||
}
|
||
|
||
// BuildResult 表示构建结果
|
||
type BuildResult struct {
|
||
Target Target // 构建目标
|
||
OutputPath string // 输出文件路径
|
||
Success bool // 是否成功
|
||
Error error // 错误信息
|
||
BuildTime time.Duration // 构建时间
|
||
}
|
||
```
|
||
|
||
### 1.3 平台适配层
|
||
|
||
平台适配层处理不同操作系统和架构的特定逻辑。
|
||
|
||
```go
|
||
// PlatformAdapter 处理平台特定的逻辑
|
||
type PlatformAdapter interface {
|
||
// GetPlatformInfo 获取平台信息
|
||
GetPlatformInfo() PlatformInfo
|
||
|
||
// PrepareEnvironment 准备构建环境
|
||
PrepareEnvironment(config *Config, target Target) error
|
||
|
||
// CleanupEnvironment 清理构建环境
|
||
CleanupEnvironment(config *Config, target Target) error
|
||
}
|
||
|
||
// PlatformInfo 包含平台特定信息
|
||
type PlatformInfo struct {
|
||
Name string // 平台名称
|
||
Version string // 平台版本
|
||
Arch string // 平台架构
|
||
DefaultInstallDir string // 默认安装目录
|
||
PathSeparator string // 路径分隔符
|
||
}
|
||
|
||
// WindowsAdapter 实现Windows平台的适配
|
||
type WindowsAdapter struct {
|
||
// Windows特定字段
|
||
}
|
||
|
||
// LinuxAdapter 实现Linux平台的适配
|
||
type LinuxAdapter struct {
|
||
// Linux特定字段
|
||
}
|
||
```
|
||
|
||
### 1.4 包构建器
|
||
|
||
包构建器负责生成不同类型的安装包。
|
||
|
||
```go
|
||
// PackageBuilder 负责构建特定类型的安装包
|
||
type PackageBuilder interface {
|
||
// Build 构建安装包
|
||
Build(config *Config, target Target) (*BuildResult, error)
|
||
|
||
// GetSupportedPlatforms 获取支持的平台
|
||
GetSupportedPlatforms() []string
|
||
}
|
||
|
||
// MSIBuilder 构建Windows MSI安装包
|
||
type MSIBuilder struct {
|
||
tempDir string
|
||
platformAdapter PlatformAdapter
|
||
logger Logger
|
||
}
|
||
|
||
// DEBBuilder 构建Linux DEB安装包
|
||
type DEBBuilder struct {
|
||
tempDir string
|
||
platformAdapter PlatformAdapter
|
||
logger Logger
|
||
}
|
||
|
||
// RPMBuilder 构建Linux RPM安装包
|
||
type RPMBuilder struct {
|
||
tempDir string
|
||
platformAdapter PlatformAdapter
|
||
logger Logger
|
||
}
|
||
|
||
// ZipBuilder 构建跨平台ZIP压缩包
|
||
type ZipBuilder struct {
|
||
tempDir string
|
||
platformAdapter PlatformAdapter
|
||
logger Logger
|
||
}
|
||
```
|
||
|
||
### 1.5 脚本执行器
|
||
|
||
脚本执行器负责执行安装前后脚本。
|
||
|
||
```go
|
||
// ScriptExecutor 负责执行脚本
|
||
type ScriptExecutor interface {
|
||
// Execute 执行脚本
|
||
Execute(script Script, env map[string]string) (*ScriptResult, error)
|
||
}
|
||
|
||
// NodeScriptExecutor 执行Node.js脚本
|
||
type NodeScriptExecutor struct {
|
||
nodePath string
|
||
logger Logger
|
||
}
|
||
|
||
// ShellScriptExecutor 执行Shell脚本
|
||
type ShellScriptExecutor struct {
|
||
shellPath string
|
||
logger Logger
|
||
}
|
||
|
||
// ScriptResult 表示脚本执行结果
|
||
type ScriptResult struct {
|
||
ExitCode int // 退出码
|
||
Output string // 标准输出
|
||
Error string // 标准错误
|
||
ExecutionTime time.Duration // 执行时间
|
||
}
|
||
```
|
||
|
||
### 1.6 插件系统
|
||
|
||
插件系统提供扩展系统功能的机制。
|
||
|
||
```go
|
||
// Plugin 表示插件接口
|
||
type Plugin interface {
|
||
// Init 初始化插件
|
||
Init(config map[string]interface{}) error
|
||
|
||
// GetName 获取插件名称
|
||
GetName() string
|
||
|
||
// GetVersion 获取插件版本
|
||
GetVersion() string
|
||
}
|
||
|
||
// PackageTypePlugin 扩展包类型支持
|
||
type PackageTypePlugin interface {
|
||
Plugin
|
||
PackageBuilder
|
||
}
|
||
|
||
// PlatformPlugin 扩展平台支持
|
||
type PlatformPlugin interface {
|
||
Plugin
|
||
PlatformAdapter
|
||
}
|
||
|
||
// PluginManager 负责管理插件
|
||
type PluginManager interface {
|
||
// LoadPlugin 加载插件
|
||
LoadPlugin(path string) (Plugin, error)
|
||
|
||
// GetPlugin 获取插件
|
||
GetPlugin(name string) (Plugin, error)
|
||
|
||
// GetAllPlugins 获取所有插件
|
||
GetAllPlugins() []Plugin
|
||
}
|
||
```
|
||
|
||
## 2. 核心接口定义
|
||
|
||
### 2.1 日志接口
|
||
|
||
```go
|
||
// Logger 提供日志功能
|
||
type Logger interface {
|
||
Debug(msg string, args ...interface{})
|
||
Info(msg string, args ...interface{})
|
||
Warn(msg string, args ...interface{})
|
||
Error(msg string, args ...interface{})
|
||
Fatal(msg string, args ...interface{})
|
||
}
|
||
|
||
// DefaultLogger 是Logger的默认实现
|
||
type DefaultLogger struct {
|
||
level LogLevel
|
||
}
|
||
|
||
// LogLevel 表示日志级别
|
||
type LogLevel int
|
||
|
||
const (
|
||
DebugLevel LogLevel = iota
|
||
InfoLevel
|
||
WarnLevel
|
||
ErrorLevel
|
||
FatalLevel
|
||
)
|
||
```
|
||
|
||
### 2.2 错误接口
|
||
|
||
```go
|
||
// Error 表示构建过程中的错误
|
||
type Error interface {
|
||
error
|
||
// Code 返回错误代码
|
||
Code() string
|
||
// Component 返回发生错误的组件
|
||
Component() string
|
||
// Details 返回详细错误信息
|
||
Details() map[string]interface{}
|
||
}
|
||
|
||
// BuildError 实现了Error接口
|
||
type BuildError struct {
|
||
code string
|
||
component string
|
||
message string
|
||
details map[string]interface{}
|
||
cause error
|
||
}
|
||
```
|
||
|
||
### 2.3 资源管理接口
|
||
|
||
```go
|
||
// ResourceManager 管理构建过程中的资源
|
||
type ResourceManager interface {
|
||
// CreateTempDir 创建临时目录
|
||
CreateTempDir(prefix string) (string, error)
|
||
|
||
// CleanupTempDir 清理临时目录
|
||
CleanupTempDir(path string) error
|
||
|
||
// CopyFile 复制文件
|
||
CopyFile(src, dst string) error
|
||
|
||
// CopyDir 复制目录
|
||
CopyDir(src, dst string, recursive bool) error
|
||
}
|
||
|
||
// DefaultResourceManager 是ResourceManager的默认实现
|
||
type DefaultResourceManager struct {
|
||
logger Logger
|
||
}
|
||
```
|
||
|
||
## 3. 工作流程
|
||
|
||
### 3.1 配置解析流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[读取配置文件] --> B[检测文件格式]
|
||
B -->|YAML| C[YAML解析器]
|
||
B -->|JSON| D[JSON解析器]
|
||
C --> E[创建配置对象]
|
||
D --> E
|
||
E --> F[验证配置]
|
||
F -->|验证失败| G[返回错误]
|
||
F -->|验证成功| H[返回配置对象]
|
||
```
|
||
|
||
### 3.2 构建流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[接收构建请求] --> B[解析配置]
|
||
B --> C[验证配置]
|
||
C -->|验证失败| D[返回错误]
|
||
C -->|验证成功| E[准备构建环境]
|
||
E --> F[执行安装前脚本]
|
||
F --> G[并行构建多个目标]
|
||
G --> H[执行安装后脚本]
|
||
H --> I[清理构建环境]
|
||
I --> J[返回构建结果]
|
||
```
|
||
|
||
### 3.3 包构建流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[选择包构建器] --> B[创建临时目录]
|
||
B --> C[复制文件和目录]
|
||
C --> D[生成包特定文件]
|
||
D --> E[调用包构建工具]
|
||
E --> F[验证构建结果]
|
||
F -->|构建失败| G[返回错误]
|
||
F -->|构建成功| H[移动到输出目录]
|
||
H --> I[清理临时文件]
|
||
I --> J[返回构建结果]
|
||
```
|
||
|
||
### 3.4 脚本执行流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[接收脚本执行请求] --> B[检查脚本类型]
|
||
B -->|Node.js| C[选择Node.js执行器]
|
||
B -->|Shell| D[选择Shell执行器]
|
||
C --> E[准备执行环境]
|
||
D --> E
|
||
E --> F[执行脚本]
|
||
F --> G[捕获输出和错误]
|
||
G --> H[检查退出码]
|
||
H -->|非零| I[返回错误]
|
||
H -->|零| J[返回成功结果]
|
||
```
|
||
|
||
## 4. 错误处理策略
|
||
|
||
### 4.1 错误类型
|
||
|
||
```go
|
||
// 错误代码常量
|
||
const (
|
||
// 配置错误
|
||
ErrConfigParse = "CONFIG_PARSE_ERROR" // 配置解析错误
|
||
ErrConfigValidation = "CONFIG_VALIDATION_ERROR" // 配置验证错误
|
||
|
||
// 构建错误
|
||
ErrBuildFailed = "BUILD_FAILED" // 构建失败
|
||
ErrBuildEnvironment = "BUILD_ENVIRONMENT_ERROR" // 构建环境错误
|
||
|
||
// 平台错误
|
||
ErrPlatformNotSupported = "PLATFORM_NOT_SUPPORTED" // 平台不支持
|
||
ErrArchNotSupported = "ARCH_NOT_SUPPORTED" // 架构不支持
|
||
|
||
// 包构建错误
|
||
ErrPackageTypeFailed = "PACKAGE_TYPE_FAILED" // 包类型构建失败
|
||
ErrPackageToolNotFound = "PACKAGE_TOOL_NOT_FOUND" // 包构建工具未找到
|
||
|
||
// 脚本错误
|
||
ErrScriptExecution = "SCRIPT_EXECUTION_ERROR" // 脚本执行错误
|
||
ErrScriptNotFound = "SCRIPT_NOT_FOUND" // 脚本未找到
|
||
|
||
// 插件错误
|
||
ErrPluginLoad = "PLUGIN_LOAD_ERROR" // 插件加载错误
|
||
ErrPluginInit = "PLUGIN_INIT_ERROR" // 插件初始化错误
|
||
|
||
// 资源错误
|
||
ErrResourceNotFound = "RESOURCE_NOT_FOUND" // 资源未找到
|
||
ErrResourceCopy = "RESOURCE_COPY_ERROR" // 资源复制错误
|
||
)
|
||
```
|
||
|
||
### 4.2 错误处理原则
|
||
|
||
1. **错误传播**:错误应该向上传播,直到能够适当处理它们的层级
|
||
2. **错误包装**:使用`fmt.Errorf`和`%w`包装错误,保留原始错误信息
|
||
3. **错误上下文**:添加足够的上下文信息,帮助诊断问题
|
||
4. **错误恢复**:在适当的情况下进行错误恢复,避免整个构建过程失败
|
||
|
||
### 4.3 错误处理示例
|
||
|
||
```go
|
||
func (b *DefaultBuildCoordinator) BuildTarget(config *Config, target Target) (*BuildResult, error) {
|
||
// 检查平台支持
|
||
adapter, ok := b.platformAdapters[target.Platform]
|
||
if !ok {
|
||
return nil, &BuildError{
|
||
code: ErrPlatformNotSupported,
|
||
component: "BuildCoordinator",
|
||
message: fmt.Sprintf("Platform %s is not supported", target.Platform),
|
||
details: map[string]interface{}{"target": target},
|
||
}
|
||
}
|
||
|
||
// 准备环境
|
||
if err := adapter.PrepareEnvironment(config, target); err != nil {
|
||
return nil, fmt.Errorf("failed to prepare environment: %w", err)
|
||
}
|
||
|
||
// 构建包
|
||
builder, ok := b.packageBuilders[target.PackageType]
|
||
if !ok {
|
||
return nil, &BuildError{
|
||
code: ErrPackageTypeFailed,
|
||
component: "BuildCoordinator",
|
||
message: fmt.Sprintf("Package type %s is not supported", target.PackageType),
|
||
details: map[string]interface{}{"target": target},
|
||
}
|
||
}
|
||
|
||
result, err := builder.Build(config, target)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to build package: %w", err)
|
||
}
|
||
|
||
// 清理环境
|
||
if err := adapter.CleanupEnvironment(config, target); err != nil {
|
||
b.logger.Warn("Failed to cleanup environment", "error", err)
|
||
// 继续执行,不返回错误
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
```
|
||
|
||
## 5. 测试策略
|
||
|
||
### 5.1 测试级别
|
||
|
||
#### 5.1.1 单元测试
|
||
|
||
- 测试单个组件的功能
|
||
- 使用模拟(mock)隔离依赖
|
||
- 覆盖核心逻辑和边界条件
|
||
|
||
```go
|
||
func TestYAMLConfigParser_Parse(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
input string
|
||
expected *Config
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "Valid config",
|
||
input: "testdata/valid_config.yaml",
|
||
expected: &Config{
|
||
Name: "TestApp",
|
||
Version: "1.0.0",
|
||
// ...
|
||
},
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "Invalid config",
|
||
input: "testdata/invalid_config.yaml",
|
||
expected: nil,
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
parser := NewYAMLConfigParser()
|
||
got, err := parser.Parse(tt.input)
|
||
|
||
if tt.wantErr {
|
||
assert.Error(t, err)
|
||
} else {
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, tt.expected, got)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 5.1.2 集成测试
|
||
|
||
- 测试多个组件的交互
|
||
- 验证组件之间的接口契约
|
||
- 使用真实依赖,但可能使用测试替身
|
||
|
||
```go
|
||
func TestBuildCoordinator_Integration(t *testing.T) {
|
||
// 设置测试环境
|
||
tempDir, err := ioutil.TempDir("", "builder-test")
|
||
require.NoError(t, err)
|
||
defer os.RemoveAll(tempDir)
|
||
|
||
// 创建真实的依赖组件
|
||
logger := NewDefaultLogger(InfoLevel)
|
||
resourceManager := NewDefaultResourceManager(logger)
|
||
|
||
// 创建构建协调器
|
||
coordinator := NewDefaultBuildCoordinator(
|
||
map[string]PlatformAdapter{
|
||
"windows": NewWindowsAdapter(logger),
|
||
"linux": NewLinuxAdapter(logger),
|
||
},
|
||
map[string]PackageBuilder{
|
||
"zip": NewZipBuilder(tempDir, resourceManager, logger),
|
||
},
|
||
NewNodeScriptExecutor("node", logger),
|
||
NewDefaultPluginManager(logger),
|
||
logger,
|
||
)
|
||
|
||
// 准备测试配置
|
||
config := &Config{
|
||
Name: "TestApp",
|
||
Version: "1.0.0",
|
||
Targets: []Target{
|
||
{
|
||
Platform: "windows",
|
||
Arch: "amd64",
|
||
PackageType: "zip",
|
||
OutputPath: filepath.Join(tempDir, "output"),
|
||
},
|
||
},
|
||
// ...
|
||
}
|
||
|
||
// 执行构建
|
||
results, err := coordinator.Build(config)
|
||
require.NoError(t, err)
|
||
|
||
// 验证结果
|
||
require.Len(t, results, 1)
|
||
assert.True(t, results[0].Success)
|
||
assert.FileExists(t, results[0].OutputPath)
|
||
}
|
||
```
|
||
|
||
#### 5.1.3 端到端测试
|
||
|
||
- 测试完整的构建流程
|
||
- 验证生成的安装包
|
||
- 测试安装和卸载过程
|
||
|
||
```go
|
||
func TestEndToEnd(t *testing.T) {
|
||
if testing.Short() {
|
||
t.Skip("Skipping end-to-end test in short mode")
|
||
}
|
||
|
||
// 设置测试环境
|
||
tempDir, err := ioutil.TempDir("", "e2e-test")
|
||
require.NoError(t, err)
|
||
defer os.RemoveAll(tempDir)
|
||
|
||
// 准备测试应用
|
||
appDir := filepath.Join(tempDir, "app")
|
||
require.NoError(t, os.MkdirAll(appDir, 0755))
|
||
|
||
// 创建测试文件
|
||
testFile := filepath.Join(appDir, "test.txt")
|
||
require.NoError(t, ioutil.WriteFile(testFile, []byte("test content"), 0644))
|
||
|
||
// 创建配置文件
|
||
configPath := filepath.Join(tempDir, "config.yaml")
|
||
configContent := `
|
||
name: TestApp
|
||
version: 1.0.0
|
||
targets:
|
||
- platform: windows
|
||
arch: amd64
|
||
packageType: zip
|
||
outputPath: output
|
||
files:
|
||
- source: app/test.txt
|
||
destination: test.txt
|
||
`
|
||
require.NoError(t, ioutil.WriteFile(configPath, []byte(configContent), 0644))
|
||
|
||
// 执行CLI命令
|
||
cmd := exec.Command("installer-builder", "build", "--config", configPath)
|
||
cmd.Dir = tempDir
|
||
output, err := cmd.CombinedOutput()
|
||
require.NoError(t, err, "CLI command failed: %s", output)
|
||
|
||
// 验证输出文件
|
||
outputFile := filepath.Join(tempDir, "output", "TestApp-1.0.0-windows-amd64.zip")
|
||
assert.FileExists(t, outputFile)
|
||
|
||
// 解压并验证内容
|
||
extractDir := filepath.Join(tempDir, "extract")
|
||
require.NoError(t, os.MkdirAll(extractDir, 0755))
|
||
|
||
// 解压ZIP文件
|
||
cmd = exec.Command("unzip", outputFile, "-d", extractDir)
|
||
require.NoError(t, cmd.Run())
|
||
|
||
// 验证文件内容
|
||
extractedFile := filepath.Join(extractDir, "test.txt")
|
||
assert.FileExists(t, extractedFile)
|
||
|
||
content, err := ioutil.ReadFile(extractedFile)
|
||
require.NoError(t, err)
|
||
assert.Equal(t, "test content", string(content))
|
||
}
|
||
```
|
||
|
||
### 5.2 测试覆盖率目标
|
||
|
||
- 核心组件:80%以上的代码覆盖率
|
||
- 工具类和辅助函数:70%以上的代码覆盖率
|
||
- 整体项目:75%以上的代码覆盖率
|
||
|
||
### 5.3 测试工具
|
||
|
||
- 使用Go标准库的`testing`包
|
||
- 使用`testify`库提供断言和模拟功能
|
||
- 使用`go-cmp`进行深度比较
|
||
- 使用`go test -race`检测竞态条件
|
||
|
||
## 6. 部署和发布策略
|
||
|
||
### 6.1 版本控制
|
||
|
||
- 使用语义化版本(Semantic Versioning)
|
||
- 主版本号:不兼容的API变更
|
||
- 次版本号:向后兼容的功能性新增
|
||
- 修订号:向后兼容的问题修正
|
||
|
||
### 6.2 构建流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[代码提交] --> B[自动化测试]
|
||
B -->|测试通过| C[构建二进制]
|
||
C --> D[打包]
|
||
D --> E[发布]
|
||
B -->|测试失败| F[通知开发者]
|
||
```
|
||
|
||
### 6.3 发布渠道
|
||
|
||
- GitHub Releases:发布二进制文件和源代码
|
||
- Docker Hub:发布Docker镜像
|
||
- Homebrew:macOS用户的包管理器
|
||
- APT/YUM仓库:Linux用户的包管理器
|
||
|
||
### 6.4 持续集成/持续部署
|
||
|
||
- 使用GitHub Actions进行CI/CD
|
||
- 每次提交运行测试
|
||
- 每次发布标签构建和发布二进制文件
|
||
|
||
## 7. 性能优化策略
|
||
|
||
### 7.1 并行构建
|
||
|
||
- 使用工作池并行构建多个目标
|
||
- 限制并发数量,避免资源竞争
|
||
|
||
```go
|
||
func (b *DefaultBuildCoordinator) Build(config *Config) ([]BuildResult, error) {
|
||
results := make([]BuildResult, 0, len(config.Targets))
|
||
var mu sync.Mutex
|
||
var wg sync.WaitGroup
|
||
|
||
// 创建工作池,限制并发数
|
||
semaphore := make(chan struct{}, runtime.NumCPU())
|
||
|
||
// 执行安装前脚本
|
||
if config.PreInstall.Path != "" {
|
||
result, err := b.scriptExecutor.Execute(config.PreInstall, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("pre-install script failed: %w", err)
|
||
}
|
||
if result.ExitCode != 0 {
|
||
return nil, fmt.Errorf("pre-install script failed with exit code %d", result.ExitCode)
|
||
}
|
||
}
|
||
|
||
// 并行构建多个目标
|
||
errCh := make(chan error, len(config.Targets))
|
||
|
||
for _, target := range config.Targets {
|
||
wg.Add(1)
|
||
go func(t Target) {
|
||
defer wg.Done()
|
||
|
||
// 获取信号量
|
||
semaphore <- struct{}{}
|
||
defer func() { <-semaphore }()
|
||
|
||
result, err := b.BuildTarget(config, t)
|
||
if err != nil {
|
||
errCh <- err
|
||
return
|
||
}
|
||
|
||
mu.Lock()
|
||
results = append(results, *result)
|
||
mu.Unlock()
|
||
}(target)
|
||
}
|
||
|
||
// 等待所有构建完成
|
||
wg.Wait()
|
||
close(errCh)
|
||
|
||
// 检查错误
|
||
for err := range errCh {
|
||
if err != nil {
|
||
return results, err
|
||
}
|
||
}
|
||
|
||
// 执行安装后脚本
|
||
if config.PostInstall.Path != "" {
|
||
result, err := b.scriptExecutor.Execute(config.PostInstall, nil)
|
||
if err != nil {
|
||
return results, fmt.Errorf("post-install script failed: %w", err)
|
||
}
|
||
if result.ExitCode != 0 {
|
||
return results, fmt.Errorf("post-install script failed with exit code %d", result.ExitCode)
|
||
}
|
||
}
|
||
|
||
return results, nil
|
||
}
|
||
```
|
||
|
||
### 7.2 资源管理
|
||
|
||
- 重用临时目录,减少I/O操作
|
||
- 使用缓冲I/O,提高文件操作效率
|
||
- 及时清理临时资源,避免磁盘空间耗尽
|
||
|
||
### 7.3 缓存策略
|
||
|
||
- 缓存构建依赖,避免重复下载
|
||
- 缓存中间构建产物,加速增量构建
|
||
- 使用分层缓存,提高缓存命中率
|
||
|
||
## 8. 安全策略
|
||
|
||
### 8.1 安全检查
|
||
|
||
- 验证脚本和文件的完整性
|
||
- 检查依赖项的安全漏洞
|
||
- 限制脚本执行权限
|
||
|
||
### 8.2 安全最佳实践
|
||
|
||
- 使用最小权限原则执行脚本
|
||
- 避免在脚本中硬编码敏感信息
|
||
- 对用户输入进行验证和清理
|
||
- 使用安全的临时文件处理方式
|
||
|
||
### 8.3 数据安全
|
||
|
||
- 加密敏感配置信息
|
||
- 安全存储和传输凭证
|
||
- 确保安装包内容的完整性
|
||
|
||
## 9. 升级策略
|
||
|
||
### 9.1 版本检测
|
||
|
||
```go
|
||
// VersionChecker 负责检测版本信息
|
||
type VersionChecker interface {
|
||
// CheckForUpgrade 检查是否有可用升级
|
||
CheckForUpgrade(currentVersion string) (*UpgradeInfo, error)
|
||
|
||
// CompareVersions 比较两个版本
|
||
CompareVersions(v1, v2 string) (int, error)
|
||
}
|
||
|
||
// UpgradeInfo 包含升级信息
|
||
type UpgradeInfo struct {
|
||
AvailableVersion string // 可用的最新版本
|
||
ReleaseNotes string // 发布说明
|
||
DownloadURL string // 下载URL
|
||
Metadata map[string]string // 其他元数据
|
||
}
|
||
```
|
||
|
||
### 9.2 升级流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[检测当前版本] --> B[检查可用升级]
|
||
B -->|无可用升级| C[结束流程]
|
||
B -->|有可用升级| D[下载新版本]
|
||
D --> E[验证完整性]
|
||
E -->|验证失败| F[报告错误]
|
||
E -->|验证成功| G[备份当前版本]
|
||
G --> H[安装新版本]
|
||
H -->|安装失败| I[回滚到备份]
|
||
H -->|安装成功| J[清理备份]
|
||
J --> K[完成升级]
|
||
```
|
||
|
||
### 9.3 数据迁移
|
||
|
||
- 保留用户配置和数据
|
||
- 提供配置迁移工具
|
||
- 支持回滚到之前版本
|
||
|
||
### 9.4 升级模式
|
||
|
||
#### 9.4.1 原地升级
|
||
|
||
原地升级会替换现有安装的文件,保留配置和数据。
|
||
|
||
```go
|
||
// InPlaceUpgrader 执行原地升级
|
||
type InPlaceUpgrader struct {
|
||
backupDir string
|
||
logger Logger
|
||
}
|
||
|
||
func (u *InPlaceUpgrader) Upgrade(config *Config) error {
|
||
// 备份当前安装
|
||
// 安装新版本
|
||
// 迁移配置
|
||
// 清理备份(如果成功)
|
||
}
|
||
```
|
||
|
||
#### 9.4.2 并行安装
|
||
|
||
并行安装会在不同位置安装新版本,然后切换到新版本。
|
||
|
||
```go
|
||
// ParallelUpgrader 执行并行安装升级
|
||
type ParallelUpgrader struct {
|
||
installDir string
|
||
logger Logger
|
||
}
|
||
|
||
func (u *ParallelUpgrader) Upgrade(config *Config) error {
|
||
// 在新位置安装
|
||
// 迁移配置和数据
|
||
// 切换活动版本
|
||
// 清理旧版本(可选)
|
||
}
|
||
```
|
||
|
||
## 10. 国际化和本地化
|
||
|
||
### 10.1 消息本地化
|
||
|
||
```go
|
||
// Localizer 负责消息本地化
|
||
type Localizer interface {
|
||
// Localize 本地化消息
|
||
Localize(key string, args map[string]interface{}) string
|
||
|
||
// SetLocale 设置当前语言环境
|
||
SetLocale(locale string) error
|
||
}
|
||
|
||
// DefaultLocalizer 是Localizer的默认实现
|
||
type DefaultLocalizer struct {
|
||
locale string
|
||
messages map[string]map[string]string
|
||
}
|
||
```
|
||
|
||
### 10.2 支持的语言
|
||
|
||
- 英语(默认)
|
||
- 简体中文
|
||
- 日语
|
||
- 德语
|
||
- 西班牙语
|
||
|
||
### 10.3 本地化资源
|
||
|
||
- 界面文本
|
||
- 错误消息
|
||
- 安装向导
|
||
- 文档
|
||
|
||
## 11. 命令行接口
|
||
|
||
### 11.1 命令结构
|
||
|
||
```
|
||
installer-builder [global options] command [command options] [arguments...]
|
||
|
||
COMMANDS:
|
||
build 构建安装包
|
||
validate 验证配置文件
|
||
init 初始化配置文件模板
|
||
help 显示帮助信息
|
||
|
||
GLOBAL OPTIONS:
|
||
--verbose, -v 启用详细日志输出
|
||
--quiet, -q 只显示错误信息
|
||
--config FILE 指定配置文件路径
|
||
--help, -h 显示帮助信息
|
||
--version 显示版本信息
|
||
```
|
||
|
||
### 11.2 构建命令
|
||
|
||
```
|
||
installer-builder build [options]
|
||
|
||
OPTIONS:
|
||
--config FILE 指定配置文件路径
|
||
--target PLATFORM 只构建指定平台的安装包
|
||
--arch ARCH 只构建指定架构的安装包
|
||
--output DIR 指定输出目录
|
||
--clean 构建前清理输出目录
|
||
--parallel NUM 并行构建的数量
|
||
```
|
||
|
||
### 11.3 验证命令
|
||
|
||
```
|
||
installer-builder validate [options]
|
||
|
||
OPTIONS:
|
||
--config FILE 指定配置文件路径
|
||
--strict 启用严格验证模式
|
||
```
|
||
|
||
## 12. 配置文件示例
|
||
|
||
### 12.1 YAML配置示例
|
||
|
||
```yaml
|
||
name: "MyApp"
|
||
version: "1.0.0"
|
||
description: "My Awesome Application"
|
||
author: "John Doe"
|
||
license: "MIT"
|
||
|
||
targets:
|
||
- platform: "windows"
|
||
arch: "amd64"
|
||
packageType: "msi"
|
||
outputPath: "dist/windows"
|
||
- platform: "linux"
|
||
arch: "amd64"
|
||
packageType: "deb"
|
||
outputPath: "dist/linux"
|
||
- platform: "linux"
|
||
arch: "amd64"
|
||
packageType: "rpm"
|
||
outputPath: "dist/linux"
|
||
|
||
files:
|
||
- source: "bin/myapp.exe"
|
||
destination: "bin/myapp.exe"
|
||
permissions: "0755"
|
||
- source: "README.md"
|
||
destination: "docs/README.md"
|
||
permissions: "0644"
|
||
|
||
directories:
|
||
- source: "assets"
|
||
destination: "assets"
|
||
permissions: "0755"
|
||
recursive: true
|
||
|
||
preInstall:
|
||
path: "scripts/pre-install.sh"
|
||
type: "shell"
|
||
args: ["--check-deps"]
|
||
|
||
postInstall:
|
||
path: "scripts/post-install.js"
|
||
type: "node"
|
||
args: ["--configure"]
|
||
|
||
dependencies:
|
||
- name: "nodejs"
|
||
version: ">=14.0.0"
|
||
type: "runtime"
|
||
- name: "dotnet-runtime"
|
||
version: ">=5.0.0"
|
||
type: "runtime"
|
||
|
||
plugins:
|
||
notarize:
|
||
enabled: true
|
||
identity: "Developer ID"
|
||
compression:
|
||
level: "high"
|
||
```
|
||
|
||
### 12.2 JSON配置示例
|
||
|
||
```json
|
||
{
|
||
"name": "MyApp",
|
||
"version": "1.0.0",
|
||
"description": "My Awesome Application",
|
||
"author": "John Doe",
|
||
"license": "MIT",
|
||
"targets": [
|
||
{
|
||
"platform": "windows",
|
||
"arch": "amd64",
|
||
"packageType": "msi",
|
||
"outputPath": "dist/windows"
|
||
},
|
||
{
|
||
"platform": "linux",
|
||
"arch": "amd64",
|
||
"packageType": "deb",
|
||
"outputPath": "dist/linux"
|
||
},
|
||
{
|
||
"platform": "linux",
|
||
"arch": "amd64",
|
||
"packageType": "rpm",
|
||
"outputPath": "dist/linux"
|
||
}
|
||
],
|
||
"files": [
|
||
{
|
||
"source": "bin/myapp.exe",
|
||
"destination": "bin/myapp.exe",
|
||
"permissions": "0755"
|
||
},
|
||
{
|
||
"source": "README.md",
|
||
"destination": "docs/README.md",
|
||
"permissions": "0644"
|
||
}
|
||
],
|
||
"directories": [
|
||
{
|
||
"source": "assets",
|
||
"destination": "assets",
|
||
"permissions": "0755",
|
||
"recursive": true
|
||
}
|
||
],
|
||
"preInstall": {
|
||
"path": "scripts/pre-install.sh",
|
||
"type": "shell",
|
||
"args": ["--check-deps"]
|
||
},
|
||
"postInstall": {
|
||
"path": "scripts/post-install.js",
|
||
"type": "node",
|
||
"args": ["--configure"]
|
||
},
|
||
"dependencies": [
|
||
{
|
||
"name": "nodejs",
|
||
"version": ">=14.0.0",
|
||
"type": "runtime"
|
||
},
|
||
{
|
||
"name": "dotnet-runtime",
|
||
"version": ">=5.0.0",
|
||
"type": "runtime"
|
||
}
|
||
],
|
||
"plugins": {
|
||
"notarize": {
|
||
"enabled": true,
|
||
"identity": "Developer ID"
|
||
},
|
||
"compression": {
|
||
"level": "high"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 13. 总结
|
||
|
||
Installer Builder 是一个灵活、可扩展的安装包构建工具,支持多平台、多架构和多种包格式。它的模块化设计使得添加新的平台和包类型变得简单,插件系统允许进一步扩展功能。
|
||
|
||
主要特点包括:
|
||
|
||
1. 支持 Windows 和 Linux 平台
|
||
2. 支持 MSI、DEB、RPM 和 ZIP 包格式
|
||
3. 支持多架构构建
|
||
4. 基于配置文件的声明式定义
|
||
5. 支持安装前后脚本
|
||
6. 支持安装包升级
|
||
7. 插件系统用于扩展功能
|