From 7c239a7e9771fa00a0b01e3e4033d334ac824520 Mon Sep 17 00:00:00 2001 From: thibaud-lclr Date: Wed, 15 Apr 2026 09:37:03 +0200 Subject: [PATCH] Revert "feat: add unified build command driven by mcp.toml" This reverts commit 845d20541b5f667ed7305d5045e025fd9e4b8af8. --- README.md | 34 ----- cmd/mcp-framework/build.go | 234 -------------------------------- cmd/mcp-framework/build_test.go | 155 --------------------- cmd/mcp-framework/main.go | 4 +- cmd/mcp-framework/main_test.go | 3 - manifest/manifest.go | 14 -- manifest/manifest_test.go | 14 -- scaffold/scaffold.go | 5 - scaffold/scaffold_test.go | 2 - 9 files changed, 1 insertion(+), 464 deletions(-) delete mode 100644 cmd/mcp-framework/build.go delete mode 100644 cmd/mcp-framework/build_test.go diff --git a/README.md b/README.md index 18502d8..f9145a8 100644 --- a/README.md +++ b/README.md @@ -38,36 +38,6 @@ go mod tidy go run ./cmd/my-mcp help ``` -## CLI de build unifiée - -Le binaire `mcp-framework` expose aussi une commande de build standardisée pour -les projets consommateurs : - -```bash -go run gitea.lclr.dev/AI/mcp-framework/cmd/mcp-framework@latest build -``` - -Par défaut la commande : - -- lit `mcp.toml` (en remontant les répertoires parents) -- récupère `binary_name` -- build `./cmd/` -- produit `build/--{.exe}` -- injecte la version via `-X main.version=` - (`VERSION` env, sinon `git describe`, sinon `dev`) - -Options principales : - -- `--manifest-dir` -- `--binary` -- `--package` -- `--build-dir` -- `--goos` -- `--goarch` -- `--version` -- `--version-var` -- `--ldflag` (répétable) - ## Packages - `bootstrap` : couche CLI optionnelle avec sous-commandes communes (`setup`, `mcp`, `config show|test`, `update`, `version`) et hooks métier explicites. @@ -180,7 +150,6 @@ Champs supportés : - `binary_name` : nom du binaire (utilisable par le bootstrap/scaffolding). - `docs_url` : URL de documentation projet. - `[update]` : source de release consommée par `update`. -- `[build]` : paramètres de build pour `mcp-framework build`. - `source_name` : nom humain de la source de release, utilisé dans certains messages d'erreur. - `driver` : driver de forge (`gitea`, `gitlab`, `github`) pour déduire automatiquement l'endpoint latest. @@ -193,9 +162,6 @@ Champs supportés : - `token_header` : header HTTP à utiliser pour l'authentification. - `token_prefix` : préfixe appliqué devant le token (`Bearer`, `token`, ...). - `token_env_names` : liste de variables d'environnement candidates pour retrouver le token. -- `[build].main_package` : package main à builder (ex: `./cmd/my-mcp`). -- `[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.). - `[environment].known` : variables d'environnement connues du projet. - `[secret_store].backend_policy` : politique de secret store (`auto`, `kwallet-only`, `keyring-any`, `env-only`). - `[profiles].default` : profil recommandé par défaut. diff --git a/cmd/mcp-framework/build.go b/cmd/mcp-framework/build.go deleted file mode 100644 index de70f8d..0000000 --- a/cmd/mcp-framework/build.go +++ /dev/null @@ -1,234 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - - manifestpkg "gitea.lclr.dev/AI/mcp-framework/manifest" -) - -type stringListFlag []string - -func (f *stringListFlag) String() string { - return strings.Join(*f, ",") -} - -func (f *stringListFlag) Set(value string) error { - *f = append(*f, strings.TrimSpace(value)) - return nil -} - -func runBuild(args []string, stdout, stderr io.Writer) error { - if shouldShowHelp(args) { - printBuildHelp(stdout) - return nil - } - - fs := flag.NewFlagSet("build", flag.ContinueOnError) - fs.SetOutput(io.Discard) - - defaultGOOS := strings.TrimSpace(os.Getenv("GOOS")) - if defaultGOOS == "" { - defaultGOOS = runtime.GOOS - } - - defaultGOARCH := strings.TrimSpace(os.Getenv("GOARCH")) - if defaultGOARCH == "" { - defaultGOARCH = runtime.GOARCH - } - - var manifestDir string - var binaryName string - var mainPackage string - var buildDir string - var goos string - var goarch string - var version string - var versionVar string - var gocache string - var extraLDFlags stringListFlag - - 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(&goos, "goos", defaultGOOS, "GOOS cible") - fs.StringVar(&goarch, "goarch", defaultGOARCH, "GOARCH cible") - 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(&gocache, "gocache", strings.TrimSpace(os.Getenv("GOCACHE")), "Valeur GOCACHE pour go build") - fs.Var(&extraLDFlags, "ldflag", "Option additionnelle passée à -ldflags (répéter si nécessaire)") - - if err := fs.Parse(args); err != nil { - _ = stderr - return fmt.Errorf("parse build flags: %w", err) - } - - if fs.NArg() > 0 { - return fmt.Errorf("unexpected argument(s): %s", strings.Join(fs.Args(), ", ")) - } - - file, manifestPath, err := manifestpkg.LoadDefault(manifestDir) - if err != nil { - return err - } - - projectDir := filepath.Dir(manifestPath) - - binaryName = firstNonEmpty(binaryName, file.BinaryName) - if binaryName == "" { - return errors.New("binary name is required (set binary_name in mcp.toml or --binary)") - } - - mainPackage = firstNonEmpty(mainPackage, file.Build.MainPackage) - if mainPackage == "" { - mainPackage = fmt.Sprintf("./cmd/%s", binaryName) - } - - 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 { - return err - } - - outputDir := filepath.Dir(outputPath) - if err := os.MkdirAll(outputDir, 0o755); err != nil { - return fmt.Errorf("create build directory %q: %w", outputDir, err) - } - - ldflags := normalizeStringList(extraLDFlags) - if versionVar != "-" { - versionVar = strings.TrimSpace(versionVar) - if versionVar != "" { - ldflags = append([]string{fmt.Sprintf("-X %s=%s", versionVar, version)}, ldflags...) - } - } - - cmdArgs := []string{"build"} - if len(ldflags) > 0 { - cmdArgs = append(cmdArgs, "-ldflags", strings.Join(ldflags, " ")) - } - cmdArgs = append(cmdArgs, "-o", outputPath, mainPackage) - - cmd := exec.Command("go", cmdArgs...) - cmd.Dir = projectDir - cmd.Stdout = stdout - cmd.Stderr = stderr - cmd.Env = withEnvOverrides(os.Environ(), map[string]string{ - "GOOS": goos, - "GOARCH": goarch, - "GOCACHE": strings.TrimSpace(gocache), - }) - - if err := cmd.Run(); err != nil { - return fmt.Errorf("go build failed: %w", err) - } - - if _, err := fmt.Fprintf(stdout, "Build artifact: %s\n", outputPath); err != nil { - return err - } - - return nil -} - -func printBuildHelp(w io.Writer) { - fmt.Fprintf( - w, - "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/\"\n output_dir = \"build\"\n version_var = \"main.version\"\n", - toolName, - ) -} - -func resolveBuildVersion(explicit, projectDir string) string { - if value := strings.TrimSpace(explicit); value != "" { - return value - } - if value := strings.TrimSpace(os.Getenv("VERSION")); value != "" { - return value - } - - describe := exec.Command("git", "describe", "--tags", "--always", "--dirty") - describe.Dir = projectDir - out, err := describe.Output() - if err == nil { - if value := strings.TrimSpace(string(out)); value != "" { - return value - } - } - - return "dev" -} - -func buildOutputPath(projectDir, buildDir, binaryName, goos, goarch string) (string, error) { - artifactName := fmt.Sprintf("%s-%s-%s", binaryName, goos, goarch) - if goos == "windows" { - artifactName += ".exe" - } - - dir := strings.TrimSpace(buildDir) - if dir == "" { - dir = "build" - } - - if filepath.IsAbs(dir) { - return filepath.Join(dir, artifactName), nil - } - if strings.TrimSpace(projectDir) == "" { - return "", errors.New("project directory is required") - } - return filepath.Join(projectDir, dir, artifactName), nil -} - -func withEnvOverrides(base []string, overrides map[string]string) []string { - result := make([]string, 0, len(base)+len(overrides)) - for _, entry := range base { - key, _, found := strings.Cut(entry, "=") - if !found { - continue - } - if _, overridden := overrides[key]; overridden { - continue - } - result = append(result, entry) - } - - for key, value := range overrides { - if strings.TrimSpace(value) == "" { - continue - } - result = append(result, key+"="+value) - } - - return result -} - -func normalizeStringList(values []string) []string { - normalized := make([]string, 0, len(values)) - for _, value := range values { - if trimmed := strings.TrimSpace(value); trimmed != "" { - normalized = append(normalized, trimmed) - } - } - return normalized -} - -func firstNonEmpty(values ...string) string { - for _, value := range values { - if trimmed := strings.TrimSpace(value); trimmed != "" { - return trimmed - } - } - return "" -} diff --git a/cmd/mcp-framework/build_test.go b/cmd/mcp-framework/build_test.go deleted file mode 100644 index 5fa1fd4..0000000 --- a/cmd/mcp-framework/build_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package main - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "testing" -) - -func TestRunBuildBuildsArtifactFromManifest(t *testing.T) { - projectDir := t.TempDir() - writeBuildFixture(t, projectDir, "demo-mcp", ` -module example.com/demo - -go 1.25.0 -`, ` -binary_name = "demo-mcp" - -[build] -main_package = "./cmd/demo-mcp" -output_dir = "build" -version_var = "main.version" -`, ` -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, - "--goos", runtime.GOOS, - "--goarch", runtime.GOARCH, - "--version", "1.2.3", - }, &stdout, &stderr) - if err != nil { - t.Fatalf("run returned error: %v (stderr: %s)", err, stderr.String()) - } - - artifactPath := artifactPath(projectDir, "build", "demo-mcp", runtime.GOOS, runtime.GOARCH) - if _, err := os.Stat(artifactPath); err != nil { - t.Fatalf("expected artifact at %s: %v", artifactPath, err) - } - - output, err := exec.Command(artifactPath).Output() - if err != nil { - t.Fatalf("run artifact: %v", err) - } - if strings.TrimSpace(string(output)) != "1.2.3" { - t.Fatalf("artifact output = %q, want %q", strings.TrimSpace(string(output)), "1.2.3") - } -} - -func TestRunBuildUsesManifestVersionVar(t *testing.T) { - projectDir := t.TempDir() - writeBuildFixture(t, projectDir, "custom-version", ` -module example.com/custom-version - -go 1.25.0 -`, ` -binary_name = "custom-version" - -[build] -main_package = "./cmd/custom-version" -output_dir = "dist" -version_var = "main.releaseVersion" -`, ` -package main - -import "fmt" - -var releaseVersion = "dev" - -func main() { - fmt.Print(releaseVersion) -} -`) - - var stdout bytes.Buffer - var stderr bytes.Buffer - err := run([]string{ - "build", - "--manifest-dir", projectDir, - "--goos", runtime.GOOS, - "--goarch", runtime.GOARCH, - "--version", "9.9.9", - }, &stdout, &stderr) - if err != nil { - t.Fatalf("run returned error: %v (stderr: %s)", err, stderr.String()) - } - - artifactPath := artifactPath(projectDir, "dist", "custom-version", runtime.GOOS, runtime.GOARCH) - output, err := exec.Command(artifactPath).Output() - if err != nil { - t.Fatalf("run artifact: %v", err) - } - if strings.TrimSpace(string(output)) != "9.9.9" { - t.Fatalf("artifact output = %q, want %q", strings.TrimSpace(string(output)), "9.9.9") - } -} - -func TestBuildHelp(t *testing.T) { - var stdout bytes.Buffer - var stderr bytes.Buffer - - if err := run([]string{"build", "--help"}, &stdout, &stderr); err != nil { - t.Fatalf("run returned error: %v", err) - } - - output := stdout.String() - if !strings.Contains(output, "--manifest-dir") { - t.Fatalf("build help should mention --manifest-dir: %q", output) - } - if !strings.Contains(output, "version_var") { - t.Fatalf("build help should mention manifest build config: %q", output) - } -} - -func writeBuildFixture(t *testing.T, projectDir, binaryName, goModContent, manifestContent, mainContent string) { - t.Helper() - - if err := os.WriteFile(filepath.Join(projectDir, "go.mod"), []byte(strings.TrimSpace(goModContent)+"\n"), 0o644); err != nil { - t.Fatalf("write go.mod: %v", err) - } - if err := os.WriteFile(filepath.Join(projectDir, "mcp.toml"), []byte(strings.TrimSpace(manifestContent)+"\n"), 0o644); err != nil { - t.Fatalf("write mcp.toml: %v", err) - } - - cmdDir := filepath.Join(projectDir, "cmd", binaryName) - if err := os.MkdirAll(cmdDir, 0o755); err != nil { - t.Fatalf("mkdir cmd dir: %v", err) - } - if err := os.WriteFile(filepath.Join(cmdDir, "main.go"), []byte(strings.TrimSpace(mainContent)+"\n"), 0o644); err != nil { - t.Fatalf("write main.go: %v", err) - } -} - -func artifactPath(projectDir, outDir, binaryName, goos, goarch string) string { - name := binaryName + "-" + goos + "-" + goarch - if goos == "windows" { - name += ".exe" - } - return filepath.Join(projectDir, outDir, name) -} diff --git a/cmd/mcp-framework/main.go b/cmd/mcp-framework/main.go index d380a8c..d6f98b9 100644 --- a/cmd/mcp-framework/main.go +++ b/cmd/mcp-framework/main.go @@ -34,8 +34,6 @@ func run(args []string, stdout, stderr io.Writer) error { } switch args[0] { - case "build": - return runBuild(args[1:], stdout, stderr) case "scaffold": return runScaffold(args[1:], stdout, stderr) default: @@ -144,7 +142,7 @@ func runScaffoldInit(args []string, stdout, stderr io.Writer) error { func printGlobalHelp(w io.Writer) { fmt.Fprintf( w, - "Usage:\n %s [options]\n\nCommands:\n build Build un binaire MCP de manière standardisée\n scaffold init Génère un nouveau projet MCP\n\nUse `%s help` for this message.\n", + "Usage:\n %s [options]\n\nCommands:\n scaffold init Génère un nouveau projet MCP\n\nUse `%s help` for this message.\n", toolName, toolName, ) diff --git a/cmd/mcp-framework/main_test.go b/cmd/mcp-framework/main_test.go index 1476bf7..6f32c6f 100644 --- a/cmd/mcp-framework/main_test.go +++ b/cmd/mcp-framework/main_test.go @@ -23,9 +23,6 @@ func TestRunPrintsGlobalHelp(t *testing.T) { if !strings.Contains(output, "scaffold init") { t.Fatalf("global help should mention scaffold init: %q", output) } - if !strings.Contains(output, "build") { - t.Fatalf("global help should mention build: %q", output) - } } func TestRunScaffoldInitCreatesProject(t *testing.T) { diff --git a/manifest/manifest.go b/manifest/manifest.go index b9238f3..b3c9414 100644 --- a/manifest/manifest.go +++ b/manifest/manifest.go @@ -18,7 +18,6 @@ type File struct { BinaryName string `toml:"binary_name"` DocsURL string `toml:"docs_url"` Update Update `toml:"update"` - Build Build `toml:"build"` Environment Environment `toml:"environment"` SecretStore SecretStore `toml:"secret_store"` Profiles Profiles `toml:"profiles"` @@ -39,12 +38,6 @@ type Update struct { TokenEnvNames []string `toml:"token_env_names"` } -type Build struct { - MainPackage string `toml:"main_package"` - OutputDir string `toml:"output_dir"` - VersionVar string `toml:"version_var"` -} - type Environment struct { Known []string `toml:"known"` } @@ -148,7 +141,6 @@ func (f *File) normalize() { f.BinaryName = strings.TrimSpace(f.BinaryName) f.DocsURL = strings.TrimSpace(f.DocsURL) f.Update.normalize() - f.Build.normalize() f.Environment.normalize() f.SecretStore.normalize() f.Profiles.normalize() @@ -168,12 +160,6 @@ func (u *Update) normalize() { u.TokenEnvNames = normalizeStringList(u.TokenEnvNames) } -func (b *Build) normalize() { - b.MainPackage = strings.TrimSpace(b.MainPackage) - b.OutputDir = strings.TrimSpace(b.OutputDir) - b.VersionVar = strings.TrimSpace(b.VersionVar) -} - func (e *Environment) normalize() { e.Known = normalizeStringList(e.Known) } diff --git a/manifest/manifest_test.go b/manifest/manifest_test.go index 6ccf1d6..83ea7d5 100644 --- a/manifest/manifest_test.go +++ b/manifest/manifest_test.go @@ -151,11 +151,6 @@ docs_url = " https://docs.example.com/mcp " [update] latest_release_url = "https://example.com/latest" -[build] -main_package = " ./cmd/my-mcp " -output_dir = " build " -version_var = " main.version " - [environment] known = [" MCP_PROFILE ", "", "MCP_TOKEN"] @@ -188,15 +183,6 @@ description = " Client MCP interne " if !slices.Equal(file.Environment.Known, []string{"MCP_PROFILE", "MCP_TOKEN"}) { t.Fatalf("environment known = %v", file.Environment.Known) } - if file.Build.MainPackage != "./cmd/my-mcp" { - t.Fatalf("build main package = %q", file.Build.MainPackage) - } - if file.Build.OutputDir != "build" { - t.Fatalf("build output dir = %q", file.Build.OutputDir) - } - if file.Build.VersionVar != "main.version" { - t.Fatalf("build version var = %q", file.Build.VersionVar) - } if file.SecretStore.BackendPolicy != "auto" { t.Fatalf("secret store policy = %q", file.SecretStore.BackendPolicy) } diff --git a/scaffold/scaffold.go b/scaffold/scaffold.go index 7a543f7..bd2c96e 100644 --- a/scaffold/scaffold.go +++ b/scaffold/scaffold.go @@ -661,11 +661,6 @@ token_header = "Authorization" token_prefix = "token" token_env_names = ["{{.ReleaseTokenEnv}}"] -[build] -main_package = "./cmd/{{.BinaryName}}" -output_dir = "build" -version_var = "main.version" - [environment] known = [{{- range $index, $value := .KnownEnvironmentVariables}}{{if $index}}, {{end}}"{{$value}}"{{- end}}] diff --git a/scaffold/scaffold_test.go b/scaffold/scaffold_test.go index 7425d65..c166a3b 100644 --- a/scaffold/scaffold_test.go +++ b/scaffold/scaffold_test.go @@ -85,11 +85,9 @@ func TestGenerateCreatesRecommendedSkeleton(t *testing.T) { for _, snippet := range []string{ "binary_name = \"my-mcp\"", "[update]", - "[build]", "[secret_store]", "[environment]", "[profiles]", - "version_var = \"main.version\"", "backend_policy = \"auto\"", } { if !strings.Contains(string(manifestContent), snippet) {