agent/pkg/setup/config.go

209 lines
4.6 KiB
Go
Raw Normal View History

2026-03-20 19:31:45 +00:00
// SPDX-License-Identifier: EUPL-1.2
package setup
import (
"os/exec"
"path/filepath"
"strings"
core "dappco.re/go/core"
"gopkg.in/yaml.v3"
2026-03-20 19:31:45 +00:00
)
// ConfigData holds the data passed to config templates.
type ConfigData struct {
Name string
Description string
Type string
Module string
Repository string
GoVersion string
Targets []Target
Commands []Command
Env map[string]string
}
// Target is a build target (os/arch pair).
type Target struct {
OS string
Arch string
}
// Command is a named runnable command.
type Command struct {
Name string
Run string
}
type configSection struct {
Key string
Values []configValue
}
type configValue struct {
Key string
Value any
}
2026-03-20 19:31:45 +00:00
// GenerateBuildConfig renders a build.yaml for the detected project type.
//
// content, err := setup.GenerateBuildConfig("/repo", setup.TypeGo)
2026-03-20 19:31:45 +00:00
func GenerateBuildConfig(path string, projType ProjectType) (string, error) {
name := filepath.Base(path)
sections := []configSection{
{
Key: "project",
Values: []configValue{
{Key: "name", Value: name},
{Key: "type", Value: string(projType)},
2026-03-20 19:31:45 +00:00
},
},
}
switch projType {
case TypeGo, TypeWails:
sections = append(sections, configSection{
Key: "build",
Values: []configValue{
{Key: "main", Value: "./cmd/" + name},
{Key: "binary", Value: name},
{Key: "cgo", Value: false},
2026-03-20 19:31:45 +00:00
},
})
2026-03-20 19:31:45 +00:00
case TypePHP:
sections = append(sections, configSection{
Key: "build",
Values: []configValue{
{Key: "dockerfile", Value: "Dockerfile"},
{Key: "image", Value: name},
2026-03-20 19:31:45 +00:00
},
})
2026-03-20 19:31:45 +00:00
case TypeNode:
sections = append(sections, configSection{
Key: "build",
Values: []configValue{
{Key: "script", Value: "npm run build"},
{Key: "output", Value: "dist"},
2026-03-20 19:31:45 +00:00
},
})
2026-03-20 19:31:45 +00:00
}
return renderConfig(name+" build configuration", sections)
2026-03-20 19:31:45 +00:00
}
// GenerateTestConfig renders a test.yaml for the detected project type.
//
// content, err := setup.GenerateTestConfig(setup.TypeGo)
2026-03-20 19:31:45 +00:00
func GenerateTestConfig(projType ProjectType) (string, error) {
var sections []configSection
2026-03-20 19:31:45 +00:00
switch projType {
case TypeGo, TypeWails:
sections = []configSection{
2026-03-20 19:31:45 +00:00
{
Key: "commands",
Values: []configValue{
{Key: "unit", Value: "go test ./..."},
{Key: "coverage", Value: "go test -coverprofile=coverage.out ./..."},
{Key: "race", Value: "go test -race ./..."},
2026-03-20 19:31:45 +00:00
},
},
}
case TypePHP:
sections = []configSection{
2026-03-20 19:31:45 +00:00
{
Key: "commands",
Values: []configValue{
{Key: "unit", Value: "vendor/bin/pest --parallel"},
{Key: "lint", Value: "vendor/bin/pint --test"},
2026-03-20 19:31:45 +00:00
},
},
}
case TypeNode:
sections = []configSection{
2026-03-20 19:31:45 +00:00
{
Key: "commands",
Values: []configValue{
{Key: "unit", Value: "npm test"},
{Key: "lint", Value: "npm run lint"},
2026-03-20 19:31:45 +00:00
},
},
}
}
return renderConfig("Test configuration", sections)
}
func renderConfig(comment string, sections []configSection) (string, error) {
var builder strings.Builder
if comment != "" {
builder.WriteString("# ")
builder.WriteString(comment)
builder.WriteString("\n\n")
}
for idx, section := range sections {
builder.WriteString(section.Key)
builder.WriteString(":\n")
for _, value := range section.Values {
scalar, err := yaml.Marshal(value.Value)
if err != nil {
return "", core.E("setup.renderConfig", "marshal "+section.Key+"."+value.Key, err)
}
builder.WriteString(" ")
builder.WriteString(value.Key)
builder.WriteString(": ")
builder.WriteString(strings.TrimSpace(string(scalar)))
builder.WriteString("\n")
}
if idx < len(sections)-1 {
builder.WriteString("\n")
}
}
return builder.String(), nil
2026-03-20 19:31:45 +00:00
}
// detectGitRemote extracts owner/repo from git remote origin.
func detectGitRemote(path string) string {
2026-03-20 19:31:45 +00:00
cmd := exec.Command("git", "remote", "get-url", "origin")
cmd.Dir = path
2026-03-20 19:31:45 +00:00
output, err := cmd.Output()
if err != nil {
return ""
}
url := strings.TrimSpace(string(output))
// SSH: git@github.com:owner/repo.git or ssh://git@forge.lthn.ai:2223/core/agent.git
if strings.Contains(url, ":") {
parts := strings.SplitN(url, ":", 2)
if len(parts) == 2 {
repo := parts[1]
repo = strings.TrimSuffix(repo, ".git")
// Handle port in SSH URL (ssh://git@host:port/path)
if strings.Contains(repo, "/") {
segments := strings.SplitN(repo, "/", 2)
if len(segments) == 2 && strings.ContainsAny(segments[0], "0123456789") {
repo = segments[1]
}
}
return repo
}
}
// HTTPS: https://github.com/owner/repo.git
for _, host := range []string{"github.com/", "forge.lthn.ai/"} {
if idx := strings.Index(url, host); idx >= 0 {
repo := url[idx+len(host):]
return strings.TrimSuffix(repo, ".git")
}
}
return ""
}