// internal/templating/engine.go package templating import ( "bytes" "fmt" "os" "path/filepath" "text/template" "gopkg.in/yaml.v3" ) // LoadTemplate 从文件加载 YAML 模板 func LoadTemplate(path string) (*Template, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("无法读取模板文件 %s: %w", path, err) } var tmpl Template if err := yaml.Unmarshal(data, &tmpl); err != nil { return nil, fmt.Errorf("YAML 解析失败: %w", err) } return &tmpl, nil } // Render 渲染模板为具体操作 func (t *Template) Render(ctx Context) (map[string][]RenderedStep, error) { result := make(map[string][]RenderedStep) for stageName, steps := range t.Stages { var renderedSteps []RenderedStep for _, step := range steps { // 处理 condition if step.Condition != "" { condTmpl, err := template.New("condition").Parse(step.Condition) if err != nil { return nil, fmt.Errorf("条件模板语法错误: %w", err) } var buf bytes.Buffer if err := condTmpl.Execute(&buf, ctx); err != nil { return nil, fmt.Errorf("执行条件模板失败: %w", err) } if buf.String() == "" { continue // 条件不满足,跳过 } } // 渲染 content contentTmpl, err := template.New("content").Parse(step.Content) if err != nil { return nil, fmt.Errorf("内容模板语法错误: %w", err) } var buf bytes.Buffer if err := contentTmpl.Execute(&buf, ctx); err != nil { return nil, fmt.Errorf("执行内容模板失败: %w", err) } renderedSteps = append(renderedSteps, RenderedStep{ Type: step.Type, Path: step.Path, Content: buf.String(), }) } result[stageName] = renderedSteps } return result, nil } // RenderedStep 是渲染后的步骤 type RenderedStep struct { Type string Path string Content string } // WriteFiles 将 file 类型步骤写入磁盘 func WriteFiles(steps []RenderedStep, rootDir string) error { for _, s := range steps { if s.Type != "file" { continue } fullPath := s.Path if !filepath.IsAbs(s.Path) { fullPath = filepath.Join(rootDir, s.Path) } if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil { return err } if err := os.WriteFile(fullPath, []byte(s.Content), 0644); err != nil { return fmt.Errorf("写入文件 %s 失败: %w", fullPath, err) } } return nil } // PrintScripts 打印 script 内容(安全起见,先不自动执行) func PrintScripts(steps []RenderedStep) { for _, s := range steps { if s.Type == "script" { fmt.Printf("# --- 脚本开始 ---\n%s\n# --- 脚本结束 ---\n", s.Content) } } }