Revert "feat: add CI build matrix planning from manifest targets"

This reverts commit f5e52463f2.
This commit is contained in:
thibaud-lclr 2026-04-15 09:37:01 +02:00
parent f5e52463f2
commit 5e115893cc
7 changed files with 32 additions and 512 deletions

View file

@ -47,12 +47,6 @@ les projets consommateurs :
go run gitea.lclr.dev/AI/mcp-framework/cmd/mcp-framework@latest build go run gitea.lclr.dev/AI/mcp-framework/cmd/mcp-framework@latest build
``` ```
Et une commande de planification de matrice CI :
```bash
go run gitea.lclr.dev/AI/mcp-framework/cmd/mcp-framework@latest build plan --format github
```
Par défaut la commande : Par défaut la commande :
- lit `mcp.toml` (en remontant les répertoires parents) - lit `mcp.toml` (en remontant les répertoires parents)
@ -70,59 +64,10 @@ Options principales :
- `--build-dir` - `--build-dir`
- `--goos` - `--goos`
- `--goarch` - `--goarch`
- `--target` (`os/arch`, ex: `linux/amd64`)
- `--version` - `--version`
- `--version-var` - `--version-var`
- `--ldflag` (répétable) - `--ldflag` (répétable)
`build plan` options principales :
- `--manifest-dir`
- `--binary`
- `--package`
- `--build-dir`
- `--version-var`
- `--format` (`json`, `github`, `gitlab`)
Exemple GitHub Actions (matrix dynamique) :
```yaml
jobs:
plan:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: plan
run: echo "matrix=$(mcp-framework build plan --format github | tr -d '\n')" >> "$GITHUB_OUTPUT"
build:
needs: [plan]
strategy:
matrix: ${{ fromJson(needs.plan.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mcp-framework build --target "${{ matrix.target }}" --version "${{ github.ref_name }}"
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_path }}
```
Exemple GitLab CI (snippet matrix généré) :
```bash
mcp-framework build plan --format gitlab > matrix.yml
```
Puis inclure le snippet `parallel.matrix` dans le job de build et exécuter :
```bash
mcp-framework build --target "${GOOS}/${GOARCH}" --version "${CI_COMMIT_TAG}"
```
## Packages ## Packages
- `bootstrap` : couche CLI optionnelle avec sous-commandes communes (`setup`, `mcp`, `config show|test`, `update`, `version`) et hooks métier explicites. - `bootstrap` : couche CLI optionnelle avec sous-commandes communes (`setup`, `mcp`, `config show|test`, `update`, `version`) et hooks métier explicites.
@ -216,17 +161,6 @@ token_header = "Authorization"
token_prefix = "token" token_prefix = "token"
token_env_names = ["GITEA_TOKEN"] token_env_names = ["GITEA_TOKEN"]
[build]
main_package = "./cmd/my-mcp"
output_dir = "build"
version_var = "main.version"
[[build.targets]]
os = "linux"
arch = "amd64"
[[build.targets]]
os = "darwin"
arch = "arm64"
[environment] [environment]
known = ["MY_MCP_PROFILE", "MY_MCP_URL", "MY_MCP_TOKEN"] known = ["MY_MCP_PROFILE", "MY_MCP_URL", "MY_MCP_TOKEN"]
@ -262,7 +196,6 @@ Champs supportés :
- `[build].main_package` : package main à builder (ex: `./cmd/my-mcp`). - `[build].main_package` : package main à builder (ex: `./cmd/my-mcp`).
- `[build].output_dir` : répertoire de sortie des artefacts (défaut `build`). - `[build].output_dir` : répertoire de sortie des artefacts (défaut `build`).
- `[build].version_var` : variable Go ciblée par `-X` pour injecter la version (`main.version`, `gitlab.../internal/app.Version`, etc.). - `[build].version_var` : variable Go ciblée par `-X` pour injecter la version (`main.version`, `gitlab.../internal/app.Version`, etc.).
- `[[build.targets]]` : cibles de compilation (`os`, `arch`) exploitées par `mcp-framework build plan`.
- `[environment].known` : variables d'environnement connues du projet. - `[environment].known` : variables d'environnement connues du projet.
- `[secret_store].backend_policy` : politique de secret store (`auto`, `kwallet-only`, `keyring-any`, `env-only`). - `[secret_store].backend_policy` : politique de secret store (`auto`, `kwallet-only`, `keyring-any`, `env-only`).
- `[profiles].default` : profil recommandé par défaut. - `[profiles].default` : profil recommandé par défaut.

View file

@ -1,7 +1,6 @@
package main package main
import ( import (
"encoding/json"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@ -17,43 +16,6 @@ import (
type stringListFlag []string type stringListFlag []string
type buildTarget struct {
GOOS string
GOARCH string
}
type buildConfig struct {
ProjectDir string
BinaryName string
MainPackage string
BuildDir string
VersionVar string
ManifestTargets []manifestpkg.BuildTarget
}
type buildConfigOverrides struct {
BinaryName string
MainPackage string
BuildDir string
VersionVar string
}
type buildPlanTarget struct {
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
Target string `json:"target"`
ArtifactName string `json:"artifact_name"`
ArtifactPath string `json:"artifact_path"`
}
type buildPlanOutput struct {
BinaryName string `json:"binary_name"`
MainPackage string `json:"main_package"`
BuildDir string `json:"build_dir"`
VersionVar string `json:"version_var"`
Targets []buildPlanTarget `json:"targets"`
}
func (f *stringListFlag) String() string { func (f *stringListFlag) String() string {
return strings.Join(*f, ",") return strings.Join(*f, ",")
} }
@ -64,13 +26,6 @@ func (f *stringListFlag) Set(value string) error {
} }
func runBuild(args []string, stdout, stderr io.Writer) error { func runBuild(args []string, stdout, stderr io.Writer) error {
if len(args) > 0 {
switch strings.TrimSpace(args[0]) {
case "plan":
return runBuildPlan(args[1:], stdout, stderr)
}
}
if shouldShowHelp(args) { if shouldShowHelp(args) {
printBuildHelp(stdout) printBuildHelp(stdout)
return nil return nil
@ -79,8 +34,15 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
fs := flag.NewFlagSet("build", flag.ContinueOnError) fs := flag.NewFlagSet("build", flag.ContinueOnError)
fs.SetOutput(io.Discard) fs.SetOutput(io.Discard)
defaultGOOS := resolveDefaultGOOS() defaultGOOS := strings.TrimSpace(os.Getenv("GOOS"))
defaultGOARCH := resolveDefaultGOARCH() if defaultGOOS == "" {
defaultGOOS = runtime.GOOS
}
defaultGOARCH := strings.TrimSpace(os.Getenv("GOARCH"))
if defaultGOARCH == "" {
defaultGOARCH = runtime.GOARCH
}
var manifestDir string var manifestDir string
var binaryName string var binaryName string
@ -88,7 +50,6 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
var buildDir string var buildDir string
var goos string var goos string
var goarch string var goarch string
var target string
var version string var version string
var versionVar string var versionVar string
var gocache string var gocache string
@ -100,7 +61,6 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
fs.StringVar(&buildDir, "build-dir", "", "Répertoire de sortie (override [build].output_dir)") fs.StringVar(&buildDir, "build-dir", "", "Répertoire de sortie (override [build].output_dir)")
fs.StringVar(&goos, "goos", defaultGOOS, "GOOS cible") fs.StringVar(&goos, "goos", defaultGOOS, "GOOS cible")
fs.StringVar(&goarch, "goarch", defaultGOARCH, "GOARCH cible") fs.StringVar(&goarch, "goarch", defaultGOARCH, "GOARCH cible")
fs.StringVar(&target, "target", "", "Cible au format os/arch (override --goos/--goarch)")
fs.StringVar(&version, "version", "", "Version injectée (par défaut VERSION env, puis git describe, puis dev)") fs.StringVar(&version, "version", "", "Version injectée (par défaut VERSION env, puis git describe, puis dev)")
fs.StringVar(&versionVar, "version-var", "", "Variable cible pour -X (override [build].version_var)") fs.StringVar(&versionVar, "version-var", "", "Variable cible pour -X (override [build].version_var)")
fs.StringVar(&gocache, "gocache", strings.TrimSpace(os.Getenv("GOCACHE")), "Valeur GOCACHE pour go build") fs.StringVar(&gocache, "gocache", strings.TrimSpace(os.Getenv("GOCACHE")), "Valeur GOCACHE pour go build")
@ -115,31 +75,30 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
return fmt.Errorf("unexpected argument(s): %s", strings.Join(fs.Args(), ", ")) return fmt.Errorf("unexpected argument(s): %s", strings.Join(fs.Args(), ", "))
} }
cfg, err := resolveBuildConfig(manifestDir, buildConfigOverrides{ file, manifestPath, err := manifestpkg.LoadDefault(manifestDir)
BinaryName: binaryName,
MainPackage: mainPackage,
BuildDir: buildDir,
VersionVar: versionVar,
})
if err != nil { if err != nil {
return err return err
} }
if strings.TrimSpace(target) != "" { projectDir := filepath.Dir(manifestPath)
parsedTarget, err := parseBuildTarget(target)
if err != nil { binaryName = firstNonEmpty(binaryName, file.BinaryName)
return err if binaryName == "" {
} return errors.New("binary name is required (set binary_name in mcp.toml or --binary)")
goos = parsedTarget.GOOS
goarch = parsedTarget.GOARCH
} }
goos = firstNonEmpty(goos, resolveDefaultGOOS()) mainPackage = firstNonEmpty(mainPackage, file.Build.MainPackage)
goarch = firstNonEmpty(goarch, resolveDefaultGOARCH()) if mainPackage == "" {
version = resolveBuildVersion(version, cfg.ProjectDir) mainPackage = fmt.Sprintf("./cmd/%s", binaryName)
versionVar = cfg.VersionVar }
outputPath, err := buildOutputPath(cfg.ProjectDir, cfg.BuildDir, cfg.BinaryName, goos, goarch) buildDir = firstNonEmpty(buildDir, file.Build.OutputDir, "build")
goos = firstNonEmpty(goos, runtime.GOOS)
goarch = firstNonEmpty(goarch, runtime.GOARCH)
version = resolveBuildVersion(version, projectDir)
versionVar = firstNonEmpty(versionVar, file.Build.VersionVar, "main.version")
outputPath, err := buildOutputPath(projectDir, buildDir, binaryName, goos, goarch)
if err != nil { if err != nil {
return err return err
} }
@ -161,10 +120,10 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
if len(ldflags) > 0 { if len(ldflags) > 0 {
cmdArgs = append(cmdArgs, "-ldflags", strings.Join(ldflags, " ")) cmdArgs = append(cmdArgs, "-ldflags", strings.Join(ldflags, " "))
} }
cmdArgs = append(cmdArgs, "-o", outputPath, cfg.MainPackage) cmdArgs = append(cmdArgs, "-o", outputPath, mainPackage)
cmd := exec.Command("go", cmdArgs...) cmd := exec.Command("go", cmdArgs...)
cmd.Dir = cfg.ProjectDir cmd.Dir = projectDir
cmd.Stdout = stdout cmd.Stdout = stdout
cmd.Stderr = stderr cmd.Stderr = stderr
cmd.Env = withEnvOverrides(os.Environ(), map[string]string{ cmd.Env = withEnvOverrides(os.Environ(), map[string]string{
@ -184,210 +143,10 @@ func runBuild(args []string, stdout, stderr io.Writer) error {
return nil return nil
} }
func runBuildPlan(args []string, stdout, stderr io.Writer) error {
if shouldShowHelp(args) {
printBuildPlanHelp(stdout)
return nil
}
fs := flag.NewFlagSet("build plan", flag.ContinueOnError)
fs.SetOutput(io.Discard)
var manifestDir string
var binaryName string
var mainPackage string
var buildDir string
var versionVar string
var format string
fs.StringVar(&manifestDir, "manifest-dir", ".", "Répertoire de départ pour trouver mcp.toml")
fs.StringVar(&binaryName, "binary", "", "Nom du binaire (override binary_name)")
fs.StringVar(&mainPackage, "package", "", "Package main à builder (override [build].main_package)")
fs.StringVar(&buildDir, "build-dir", "", "Répertoire de sortie (override [build].output_dir)")
fs.StringVar(&versionVar, "version-var", "", "Variable cible pour -X (override [build].version_var)")
fs.StringVar(&format, "format", "json", "Format de sortie: json|github|gitlab")
if err := fs.Parse(args); err != nil {
_ = stderr
return fmt.Errorf("parse build plan flags: %w", err)
}
if fs.NArg() > 0 {
return fmt.Errorf("unexpected argument(s): %s", strings.Join(fs.Args(), ", "))
}
cfg, err := resolveBuildConfig(manifestDir, buildConfigOverrides{
BinaryName: binaryName,
MainPackage: mainPackage,
BuildDir: buildDir,
VersionVar: versionVar,
})
if err != nil {
return err
}
targets, err := resolveBuildTargets(cfg.ManifestTargets, resolveDefaultGOOS(), resolveDefaultGOARCH())
if err != nil {
return err
}
planTargets := make([]buildPlanTarget, 0, len(targets))
for _, target := range targets {
outputPath, err := buildOutputPath(cfg.ProjectDir, cfg.BuildDir, cfg.BinaryName, target.GOOS, target.GOARCH)
if err != nil {
return err
}
planTargets = append(planTargets, buildPlanTarget{
GOOS: target.GOOS,
GOARCH: target.GOARCH,
Target: formatBuildTarget(target.GOOS, target.GOARCH),
ArtifactName: filepath.Base(outputPath),
ArtifactPath: formatArtifactPath(cfg.ProjectDir, outputPath),
})
}
switch strings.ToLower(strings.TrimSpace(format)) {
case "json":
return renderJSON(stdout, buildPlanOutput{
BinaryName: cfg.BinaryName,
MainPackage: cfg.MainPackage,
BuildDir: cfg.BuildDir,
VersionVar: cfg.VersionVar,
Targets: planTargets,
})
case "github":
include := make([]map[string]string, 0, len(planTargets))
for _, target := range planTargets {
include = append(include, map[string]string{
"goos": target.GOOS,
"goarch": target.GOARCH,
"target": target.Target,
"artifact_name": target.ArtifactName,
"artifact_path": target.ArtifactPath,
})
}
return renderJSON(stdout, map[string]any{"include": include})
case "gitlab":
return renderGitLabMatrix(stdout, planTargets)
default:
return fmt.Errorf("unsupported build plan format %q (expected json, github or gitlab)", format)
}
}
func resolveBuildConfig(manifestDir string, overrides buildConfigOverrides) (buildConfig, error) {
file, manifestPath, err := manifestpkg.LoadDefault(manifestDir)
if err != nil {
return buildConfig{}, err
}
projectDir := filepath.Dir(manifestPath)
binaryName := firstNonEmpty(overrides.BinaryName, file.BinaryName)
if binaryName == "" {
return buildConfig{}, errors.New("binary name is required (set binary_name in mcp.toml or --binary)")
}
mainPackage := firstNonEmpty(overrides.MainPackage, file.Build.MainPackage)
if mainPackage == "" {
mainPackage = fmt.Sprintf("./cmd/%s", binaryName)
}
return buildConfig{
ProjectDir: projectDir,
BinaryName: binaryName,
MainPackage: mainPackage,
BuildDir: firstNonEmpty(overrides.BuildDir, file.Build.OutputDir, "build"),
VersionVar: firstNonEmpty(overrides.VersionVar, file.Build.VersionVar, "main.version"),
ManifestTargets: append([]manifestpkg.BuildTarget(nil), file.Build.Targets...),
}, nil
}
func resolveBuildTargets(manifestTargets []manifestpkg.BuildTarget, defaultGOOS, defaultGOARCH string) ([]buildTarget, error) {
if len(manifestTargets) == 0 {
defaultTarget, err := parseBuildTarget(formatBuildTarget(defaultGOOS, defaultGOARCH))
if err != nil {
return nil, err
}
return []buildTarget{defaultTarget}, nil
}
targets := make([]buildTarget, 0, len(manifestTargets))
for _, manifestTarget := range manifestTargets {
target, err := parseBuildTarget(formatBuildTarget(manifestTarget.OS, manifestTarget.Arch))
if err != nil {
return nil, err
}
targets = append(targets, target)
}
return targets, nil
}
func parseBuildTarget(value string) (buildTarget, error) {
raw := strings.TrimSpace(value)
parts := strings.Split(raw, "/")
if len(parts) != 2 {
return buildTarget{}, fmt.Errorf("invalid target %q (expected os/arch)", value)
}
goos := strings.ToLower(strings.TrimSpace(parts[0]))
goarch := strings.ToLower(strings.TrimSpace(parts[1]))
if goos == "" || goarch == "" {
return buildTarget{}, fmt.Errorf("invalid target %q (expected non-empty os/arch)", value)
}
return buildTarget{GOOS: goos, GOARCH: goarch}, nil
}
func formatBuildTarget(goos, goarch string) string {
return strings.ToLower(strings.TrimSpace(goos)) + "/" + strings.ToLower(strings.TrimSpace(goarch))
}
func formatArtifactPath(projectDir, outputPath string) string {
rel, err := filepath.Rel(projectDir, outputPath)
if err == nil && rel != "" && !strings.HasPrefix(rel, "..") && !filepath.IsAbs(rel) {
return filepath.ToSlash(rel)
}
return filepath.ToSlash(outputPath)
}
func renderJSON(w io.Writer, payload any) error {
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(payload)
}
func renderGitLabMatrix(w io.Writer, targets []buildPlanTarget) error {
if _, err := fmt.Fprintln(w, "parallel:"); err != nil {
return err
}
if _, err := fmt.Fprintln(w, " matrix:"); err != nil {
return err
}
for _, target := range targets {
if _, err := fmt.Fprintf(w, " - GOOS: %q\n", target.GOOS); err != nil {
return err
}
if _, err := fmt.Fprintf(w, " GOARCH: %q\n", target.GOARCH); err != nil {
return err
}
}
return nil
}
func printBuildHelp(w io.Writer) { func printBuildHelp(w io.Writer) {
fmt.Fprintf( fmt.Fprintf(
w, w,
"Usage:\n %s build [flags]\n %s build plan [flags]\n\nBuild flags:\n --manifest-dir Répertoire de départ pour trouver mcp.toml (défaut: .)\n --binary Override de binary_name\n --package Override de [build].main_package\n --build-dir Override de [build].output_dir\n --goos GOOS cible (défaut: env GOOS ou runtime)\n --goarch GOARCH cible (défaut: env GOARCH ou runtime)\n --target Cible os/arch (ex: linux/amd64), prioritaire sur --goos/--goarch\n --version Version injectée (défaut: env VERSION, git describe, puis dev)\n --version-var Variable cible pour -X (défaut: [build].version_var puis main.version)\n --gocache Valeur GOCACHE pour go build\n --ldflag Option additionnelle pour -ldflags (répéter si besoin)\n\nBuild plan flags:\n --manifest-dir Répertoire de départ pour trouver mcp.toml\n --binary Override de binary_name\n --package Override de [build].main_package\n --build-dir Override de [build].output_dir\n --version-var Override de [build].version_var\n --format json|github|gitlab\n\nManifest optional ([build]):\n main_package = \"./cmd/<binary>\"\n output_dir = \"build\"\n version_var = \"main.version\"\n [[build.targets]]\n os = \"linux\"\n arch = \"amd64\"\n", "Usage:\n %s build [flags]\n\nFlags:\n --manifest-dir Répertoire de départ pour trouver mcp.toml (défaut: .)\n --binary Override de binary_name\n --package Override de [build].main_package\n --build-dir Override de [build].output_dir\n --goos GOOS cible (défaut: env GOOS ou runtime)\n --goarch GOARCH cible (défaut: env GOARCH ou runtime)\n --version Version injectée (défaut: env VERSION, git describe, puis dev)\n --version-var Variable cible pour -X (défaut: [build].version_var puis main.version)\n --gocache Valeur GOCACHE pour go build\n --ldflag Option additionnelle pour -ldflags (répéter si besoin)\n\nManifest optional ([build]):\n main_package = \"./cmd/<binary>\"\n output_dir = \"build\"\n version_var = \"main.version\"\n",
toolName,
toolName,
)
}
func printBuildPlanHelp(w io.Writer) {
fmt.Fprintf(
w,
"Usage:\n %s build plan [flags]\n\nFlags:\n --manifest-dir Répertoire de départ pour trouver mcp.toml\n --binary Override de binary_name\n --package Override de [build].main_package\n --build-dir Override de [build].output_dir\n --version-var Override de [build].version_var\n --format json|github|gitlab (défaut: json)\n",
toolName, toolName,
) )
} }
@ -473,11 +232,3 @@ func firstNonEmpty(values ...string) string {
} }
return "" return ""
} }
func resolveDefaultGOOS() string {
return firstNonEmpty(os.Getenv("GOOS"), runtime.GOOS)
}
func resolveDefaultGOARCH() string {
return firstNonEmpty(os.Getenv("GOARCH"), runtime.GOARCH)
}

View file

@ -2,7 +2,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -111,108 +110,6 @@ func main() {
} }
} }
func TestRunBuildSupportsTargetFlag(t *testing.T) {
projectDir := t.TempDir()
writeBuildFixture(t, projectDir, "target-flag", `
module example.com/target-flag
go 1.25.0
`, `
binary_name = "target-flag"
`, `
package main
import "fmt"
var version = "dev"
func main() {
fmt.Print(version)
}
`)
var stdout bytes.Buffer
var stderr bytes.Buffer
err := run([]string{
"build",
"--manifest-dir", projectDir,
"--target", runtime.GOOS + "/" + runtime.GOARCH,
"--version", "2.0.0",
}, &stdout, &stderr)
if err != nil {
t.Fatalf("run returned error: %v (stderr: %s)", err, stderr.String())
}
artifactPath := artifactPath(projectDir, "build", "target-flag", runtime.GOOS, runtime.GOARCH)
output, err := exec.Command(artifactPath).Output()
if err != nil {
t.Fatalf("run artifact: %v", err)
}
if strings.TrimSpace(string(output)) != "2.0.0" {
t.Fatalf("artifact output = %q, want %q", strings.TrimSpace(string(output)), "2.0.0")
}
}
func TestRunBuildPlanGithubFormat(t *testing.T) {
projectDir := t.TempDir()
writeBuildFixture(t, projectDir, "matrix-mcp", `
module example.com/matrix-mcp
go 1.25.0
`, `
binary_name = "matrix-mcp"
[build]
output_dir = "dist"
[[build.targets]]
os = "linux"
arch = "amd64"
[[build.targets]]
os = "darwin"
arch = "arm64"
`, `
package main
func main() {}
`)
var stdout bytes.Buffer
var stderr bytes.Buffer
err := run([]string{
"build", "plan",
"--manifest-dir", projectDir,
"--format", "github",
}, &stdout, &stderr)
if err != nil {
t.Fatalf("run returned error: %v (stderr: %s)", err, stderr.String())
}
var payload struct {
Include []struct {
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
Target string `json:"target"`
ArtifactPath string `json:"artifact_path"`
} `json:"include"`
}
if err := json.Unmarshal(stdout.Bytes(), &payload); err != nil {
t.Fatalf("decode github matrix: %v", err)
}
if len(payload.Include) != 2 {
t.Fatalf("include length = %d, want 2", len(payload.Include))
}
if payload.Include[0].Target != "linux/amd64" {
t.Fatalf("first target = %q", payload.Include[0].Target)
}
if payload.Include[0].ArtifactPath != "dist/matrix-mcp-linux-amd64" {
t.Fatalf("first artifact path = %q", payload.Include[0].ArtifactPath)
}
if payload.Include[1].Target != "darwin/arm64" {
t.Fatalf("second target = %q", payload.Include[1].Target)
}
}
func TestBuildHelp(t *testing.T) { func TestBuildHelp(t *testing.T) {
var stdout bytes.Buffer var stdout bytes.Buffer
var stderr bytes.Buffer var stderr bytes.Buffer
@ -228,9 +125,6 @@ func TestBuildHelp(t *testing.T) {
if !strings.Contains(output, "version_var") { if !strings.Contains(output, "version_var") {
t.Fatalf("build help should mention manifest build config: %q", output) t.Fatalf("build help should mention manifest build config: %q", output)
} }
if !strings.Contains(output, "build plan") {
t.Fatalf("build help should mention build plan: %q", output)
}
} }
func writeBuildFixture(t *testing.T, projectDir, binaryName, goModContent, manifestContent, mainContent string) { func writeBuildFixture(t *testing.T, projectDir, binaryName, goModContent, manifestContent, mainContent string) {

View file

@ -43,12 +43,6 @@ type Build struct {
MainPackage string `toml:"main_package"` MainPackage string `toml:"main_package"`
OutputDir string `toml:"output_dir"` OutputDir string `toml:"output_dir"`
VersionVar string `toml:"version_var"` VersionVar string `toml:"version_var"`
Targets []BuildTarget `toml:"targets"`
}
type BuildTarget struct {
OS string `toml:"os"`
Arch string `toml:"arch"`
} }
type Environment struct { type Environment struct {
@ -178,7 +172,6 @@ func (b *Build) normalize() {
b.MainPackage = strings.TrimSpace(b.MainPackage) b.MainPackage = strings.TrimSpace(b.MainPackage)
b.OutputDir = strings.TrimSpace(b.OutputDir) b.OutputDir = strings.TrimSpace(b.OutputDir)
b.VersionVar = strings.TrimSpace(b.VersionVar) b.VersionVar = strings.TrimSpace(b.VersionVar)
b.Targets = normalizeBuildTargets(b.Targets)
} }
func (e *Environment) normalize() { func (e *Environment) normalize() {
@ -250,30 +243,3 @@ func normalizeStringList(values []string) []string {
} }
return normalized return normalized
} }
func normalizeBuildTargets(values []BuildTarget) []BuildTarget {
if len(values) == 0 {
return values
}
normalized := values[:0]
seen := make(map[string]struct{}, len(values))
for _, value := range values {
target := BuildTarget{
OS: strings.ToLower(strings.TrimSpace(value.OS)),
Arch: strings.ToLower(strings.TrimSpace(value.Arch)),
}
if target.OS == "" || target.Arch == "" {
continue
}
key := target.OS + "/" + target.Arch
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
normalized = append(normalized, target)
}
return normalized
}

View file

@ -155,18 +155,6 @@ latest_release_url = "https://example.com/latest"
main_package = " ./cmd/my-mcp " main_package = " ./cmd/my-mcp "
output_dir = " build " output_dir = " build "
version_var = " main.version " version_var = " main.version "
[[build.targets]]
os = " Linux "
arch = " AMD64 "
[[build.targets]]
os = "darwin"
arch = "arm64"
[[build.targets]]
os = "linux"
arch = "amd64"
[[build.targets]]
os = ""
arch = "amd64"
[environment] [environment]
known = [" MCP_PROFILE ", "", "MCP_TOKEN"] known = [" MCP_PROFILE ", "", "MCP_TOKEN"]
@ -209,12 +197,6 @@ description = " Client MCP interne "
if file.Build.VersionVar != "main.version" { if file.Build.VersionVar != "main.version" {
t.Fatalf("build version var = %q", file.Build.VersionVar) t.Fatalf("build version var = %q", file.Build.VersionVar)
} }
if !slices.Equal(file.Build.Targets, []BuildTarget{
{OS: "linux", Arch: "amd64"},
{OS: "darwin", Arch: "arm64"},
}) {
t.Fatalf("build targets = %#v", file.Build.Targets)
}
if file.SecretStore.BackendPolicy != "auto" { if file.SecretStore.BackendPolicy != "auto" {
t.Fatalf("secret store policy = %q", file.SecretStore.BackendPolicy) t.Fatalf("secret store policy = %q", file.SecretStore.BackendPolicy)
} }

View file

@ -665,9 +665,6 @@ token_env_names = ["{{.ReleaseTokenEnv}}"]
main_package = "./cmd/{{.BinaryName}}" main_package = "./cmd/{{.BinaryName}}"
output_dir = "build" output_dir = "build"
version_var = "main.version" version_var = "main.version"
[[build.targets]]
os = "linux"
arch = "amd64"
[environment] [environment]
known = [{{- range $index, $value := .KnownEnvironmentVariables}}{{if $index}}, {{end}}"{{$value}}"{{- end}}] known = [{{- range $index, $value := .KnownEnvironmentVariables}}{{if $index}}, {{end}}"{{$value}}"{{- end}}]

View file

@ -90,9 +90,6 @@ func TestGenerateCreatesRecommendedSkeleton(t *testing.T) {
"[environment]", "[environment]",
"[profiles]", "[profiles]",
"version_var = \"main.version\"", "version_var = \"main.version\"",
"[[build.targets]]",
"os = \"linux\"",
"arch = \"amd64\"",
"backend_policy = \"auto\"", "backend_policy = \"auto\"",
} { } {
if !strings.Contains(string(manifestContent), snippet) { if !strings.Contains(string(manifestContent), snippet) {