291 lines
7.5 KiB
Go
291 lines
7.5 KiB
Go
// Package linux 提供Linux平台安装包构建功能
|
||
package linux
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/builder"
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/config"
|
||
"git.kingecg.top/kingecg/installerbuilder/internal/logger"
|
||
)
|
||
|
||
// DEBBuilder 是Linux DEB安装包构建器
|
||
type DEBBuilder struct {
|
||
config *config.Config
|
||
outputDir string
|
||
}
|
||
|
||
// 确保DEBBuilder实现了Builder接口
|
||
var _ builder.Builder = (*DEBBuilder)(nil)
|
||
|
||
// 注册构建器
|
||
func init() {
|
||
builder.RegisterBuilder("linux-deb", func(cfg *config.Config, outputDir string) builder.Builder {
|
||
return NewDEBBuilder(cfg, outputDir)
|
||
})
|
||
}
|
||
|
||
// NewDEBBuilder 创建一个新的DEB构建器
|
||
func NewDEBBuilder(cfg *config.Config, outputDir string) *DEBBuilder {
|
||
return &DEBBuilder{
|
||
config: cfg,
|
||
outputDir: outputDir,
|
||
}
|
||
}
|
||
|
||
// Name 返回构建器名称
|
||
func (b *DEBBuilder) Name() string {
|
||
return "Linux DEB Builder"
|
||
}
|
||
|
||
// SupportedPlatforms 返回支持的平台列表
|
||
func (b *DEBBuilder) SupportedPlatforms() []string {
|
||
return []string{"linux"}
|
||
}
|
||
|
||
// SupportedFormats 返回支持的格式列表
|
||
func (b *DEBBuilder) SupportedFormats() []string {
|
||
return []string{"deb"}
|
||
}
|
||
|
||
// Build 构建DEB安装包
|
||
func (b *DEBBuilder) Build() error {
|
||
logger.Info("开始构建Linux DEB安装包")
|
||
|
||
// 创建临时工作目录
|
||
workDir, err := os.MkdirTemp("", "installer-builder-deb-*")
|
||
if err != nil {
|
||
return fmt.Errorf("创建临时工作目录失败: %w", err)
|
||
}
|
||
defer os.RemoveAll(workDir)
|
||
|
||
logger.Debugf("使用临时工作目录: %s", workDir)
|
||
|
||
// 创建DEB包结构
|
||
if err := b.createDebStructure(workDir); err != nil {
|
||
return fmt.Errorf("创建DEB包结构失败: %w", err)
|
||
}
|
||
|
||
// 复制文件到临时目录
|
||
if err := b.copyFiles(workDir); err != nil {
|
||
return fmt.Errorf("复制文件失败: %w", err)
|
||
}
|
||
|
||
// 创建控制文件
|
||
if err := b.createControlFile(workDir); err != nil {
|
||
return fmt.Errorf("创建控制文件失败: %w", err)
|
||
}
|
||
|
||
// 创建安装脚本
|
||
if err := b.createScripts(workDir); err != nil {
|
||
return fmt.Errorf("创建安装脚本失败: %w", err)
|
||
}
|
||
|
||
// 构建DEB包
|
||
debFile := filepath.Join(b.outputDir, fmt.Sprintf("%s_%s_amd64.deb", b.config.Name, b.config.Version))
|
||
if err := b.buildDeb(workDir, debFile); err != nil {
|
||
return fmt.Errorf("构建DEB包失败: %w", err)
|
||
}
|
||
|
||
logger.Infof("成功构建DEB安装包: %s", debFile)
|
||
return nil
|
||
}
|
||
|
||
// createDebStructure 创建DEB包结构
|
||
func (b *DEBBuilder) createDebStructure(workDir string) error {
|
||
// 创建DEBIAN目录
|
||
debianDir := filepath.Join(workDir, "DEBIAN")
|
||
if err := os.MkdirAll(debianDir, 0755); err != nil {
|
||
return fmt.Errorf("创建DEBIAN目录失败: %w", err)
|
||
}
|
||
|
||
// 创建安装目录
|
||
installDir := filepath.Join(workDir, "usr", "local", "bin")
|
||
if err := os.MkdirAll(installDir, 0755); err != nil {
|
||
return fmt.Errorf("创建安装目录失败: %w", err)
|
||
}
|
||
|
||
// 创建文档目录
|
||
docDir := filepath.Join(workDir, "usr", "share", "doc", b.config.Name)
|
||
if err := os.MkdirAll(docDir, 0755); err != nil {
|
||
return fmt.Errorf("创建文档目录失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// copyFiles 复制文件到临时目录
|
||
func (b *DEBBuilder) copyFiles(workDir string) error {
|
||
// 这里应该实现文件复制逻辑
|
||
// 实际应用中,应该根据配置复制文件到临时目录
|
||
logger.Debug("复制文件到临时目录")
|
||
return nil
|
||
}
|
||
|
||
// createControlFile 创建控制文件
|
||
func (b *DEBBuilder) createControlFile(workDir string) error {
|
||
controlFile := filepath.Join(workDir, "DEBIAN", "control")
|
||
|
||
// 准备依赖项列表
|
||
var depends string
|
||
if len(b.config.Platforms.Linux.Deb.Depends) > 0 {
|
||
depends = strings.Join(b.config.Platforms.Linux.Deb.Depends, ", ")
|
||
} else {
|
||
depends = ""
|
||
}
|
||
|
||
// 准备推荐项列表
|
||
var recommends string
|
||
if len(b.config.Platforms.Linux.Deb.Recommends) > 0 {
|
||
recommends = strings.Join(b.config.Platforms.Linux.Deb.Recommends, ", ")
|
||
} else {
|
||
recommends = ""
|
||
}
|
||
|
||
// 准备建议项列表
|
||
var suggests string
|
||
if len(b.config.Platforms.Linux.Deb.Suggests) > 0 {
|
||
suggests = strings.Join(b.config.Platforms.Linux.Deb.Suggests, ", ")
|
||
} else {
|
||
suggests = ""
|
||
}
|
||
|
||
// 准备冲突项列表
|
||
var conflicts string
|
||
if len(b.config.Platforms.Linux.Deb.Conflicts) > 0 {
|
||
conflicts = strings.Join(b.config.Platforms.Linux.Deb.Conflicts, ", ")
|
||
} else {
|
||
conflicts = ""
|
||
}
|
||
|
||
// 准备替换项列表
|
||
var replaces string
|
||
if len(b.config.Platforms.Linux.Deb.Replaces) > 0 {
|
||
replaces = strings.Join(b.config.Platforms.Linux.Deb.Replaces, ", ")
|
||
} else {
|
||
replaces = ""
|
||
}
|
||
|
||
// 创建控制文件内容
|
||
content := fmt.Sprintf(`Package: %s
|
||
Version: %s
|
||
Section: %s
|
||
Priority: %s
|
||
Architecture: %s
|
||
Maintainer: %s
|
||
Description: %s
|
||
`,
|
||
b.config.Name,
|
||
b.config.Version,
|
||
b.config.Platforms.Linux.Deb.Section,
|
||
b.config.Platforms.Linux.Deb.Priority,
|
||
b.config.Platforms.Linux.Deb.Architecture,
|
||
b.config.Author,
|
||
b.config.Description)
|
||
|
||
// 添加可选字段
|
||
if depends != "" {
|
||
content += fmt.Sprintf("Depends: %s\n", depends)
|
||
}
|
||
if recommends != "" {
|
||
content += fmt.Sprintf("Recommends: %s\n", recommends)
|
||
}
|
||
if suggests != "" {
|
||
content += fmt.Sprintf("Suggests: %s\n", suggests)
|
||
}
|
||
if conflicts != "" {
|
||
content += fmt.Sprintf("Conflicts: %s\n", conflicts)
|
||
}
|
||
if replaces != "" {
|
||
content += fmt.Sprintf("Replaces: %s\n", replaces)
|
||
}
|
||
|
||
// 写入控制文件
|
||
return os.WriteFile(controlFile, []byte(content), 0644)
|
||
}
|
||
|
||
// createScripts 创建安装脚本
|
||
func (b *DEBBuilder) createScripts(workDir string) error {
|
||
// 脚本类型到文件名的映射
|
||
scriptFiles := map[string]string{
|
||
"preinstall": "preinst",
|
||
"postinstall": "postinst",
|
||
"preuninstall": "prerm",
|
||
"postuninstall": "postrm",
|
||
}
|
||
|
||
// 遍历配置中的脚本
|
||
for _, script := range b.config.Contents.Scripts {
|
||
// 检查是否是DEB支持的脚本类型
|
||
filename, ok := scriptFiles[script.Type]
|
||
if !ok {
|
||
logger.Warnf("跳过不支持的脚本类型: %s", script.Type)
|
||
continue
|
||
}
|
||
|
||
// 脚本文件路径
|
||
scriptPath := filepath.Join(workDir, "DEBIAN", filename)
|
||
|
||
// 获取脚本内容
|
||
var content string
|
||
if script.Content != "" {
|
||
content = script.Content
|
||
} else if script.Source != "" {
|
||
// 从源文件读取脚本内容
|
||
data, err := os.ReadFile(script.Source)
|
||
if err != nil {
|
||
return fmt.Errorf("读取脚本源文件失败: %w", err)
|
||
}
|
||
content = string(data)
|
||
} else {
|
||
logger.Warnf("跳过空脚本: %s", script.Type)
|
||
continue
|
||
}
|
||
|
||
// 确保脚本以#!/bin/sh开头
|
||
if !strings.HasPrefix(content, "#!") {
|
||
content = "#!/bin/sh\n" + content
|
||
}
|
||
|
||
// 写入脚本文件
|
||
if err := os.WriteFile(scriptPath, []byte(content), 0755); err != nil {
|
||
return fmt.Errorf("写入脚本文件失败: %w", err)
|
||
}
|
||
|
||
logger.Debugf("创建脚本文件: %s", scriptPath)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// buildDeb 构建DEB包
|
||
func (b *DEBBuilder) buildDeb(workDir, outputFile string) error {
|
||
// 检查是否安装了dpkg-deb
|
||
dpkgDeb, err := exec.LookPath("dpkg-deb")
|
||
if err != nil {
|
||
return fmt.Errorf("未找到dpkg-deb工具,请确保已安装dpkg-deb并添加到PATH中")
|
||
}
|
||
|
||
// 确保输出目录存在
|
||
if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
|
||
return fmt.Errorf("创建输出目录失败: %w", err)
|
||
}
|
||
|
||
// 构建命令
|
||
cmd := exec.Command(dpkgDeb, "--build", workDir, outputFile)
|
||
cmd.Stdout = os.Stdout
|
||
cmd.Stderr = os.Stderr
|
||
|
||
// 执行命令
|
||
logger.Debugf("执行命令: %s", strings.Join(cmd.Args, " "))
|
||
if err := cmd.Run(); err != nil {
|
||
return fmt.Errorf("执行dpkg-deb命令失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|