From 7b2ca695379ae12e23337e965b742ee697085c71 Mon Sep 17 00:00:00 2001 From: David Marby Date: Fri, 13 Mar 2026 20:06:24 +0100 Subject: [PATCH 1/2] Remove regenerate-maven-poms command and dockerfile stage logic The fetcher copies old dockerfiles which already have maven-deps stages. Any new versions inherit those stages, then regenerate.go handles the pom.xml generation. Remove the unnecessary logic that was adding stages to dockerfiles and the regenerate-maven-poms CLI tool. --- internal/cmd/fetcher/main.go | 12 --- internal/cmd/regenerate-maven-poms/main.go | 37 ------- internal/maven/dockerfile.go | 112 --------------------- internal/maven/regenerate.go | 45 +-------- internal/maven/regenerate_test.go | 18 +--- 5 files changed, 4 insertions(+), 220 deletions(-) delete mode 100644 internal/cmd/regenerate-maven-poms/main.go delete mode 100644 internal/maven/dockerfile.go diff --git a/internal/cmd/fetcher/main.go b/internal/cmd/fetcher/main.go index eafaf23ae..8d655e01d 100644 --- a/internal/cmd/fetcher/main.go +++ b/internal/cmd/fetcher/main.go @@ -25,7 +25,6 @@ import ( "github.com/bufbuild/plugins/internal/docker" "github.com/bufbuild/plugins/internal/fetchclient" "github.com/bufbuild/plugins/internal/git" - "github.com/bufbuild/plugins/internal/maven" "github.com/bufbuild/plugins/internal/plugin" "github.com/bufbuild/plugins/internal/source" ) @@ -141,9 +140,6 @@ func postProcessCreatedPlugins(ctx context.Context, logger *slog.Logger, plugins } for _, plugin := range plugins { newPluginRef := plugin.String() - if err := regenerateMavenDeps(plugin); err != nil { - return fmt.Errorf("failed to regenerate maven deps for %s: %w", newPluginRef, err) - } if err := runGoModTidy(ctx, logger, plugin); err != nil { return fmt.Errorf("failed to run go mod tidy for %s: %w", newPluginRef, err) } @@ -286,14 +282,6 @@ func recreateSwiftPackageResolved(ctx context.Context, logger *slog.Logger, plug return nil } -// regenerateMavenDeps regenerates the pom.xml and Dockerfile's maven-deps -// stage from the plugin's buf.plugin.yaml. -func regenerateMavenDeps(plugin createdPlugin) error { - versionDir := filepath.Join(plugin.pluginDir, plugin.newVersion) - pluginsDir := filepath.Dir(filepath.Dir(plugin.pluginDir)) - return maven.RegenerateMavenDeps(versionDir, pluginsDir) -} - // runPluginTests runs 'make test PLUGINS="org/name:v"' in order to generate plugin.sum files. func runPluginTests(ctx context.Context, logger *slog.Logger, plugins []createdPlugin) error { pluginsEnv := make([]string, 0, len(plugins)) diff --git a/internal/cmd/regenerate-maven-poms/main.go b/internal/cmd/regenerate-maven-poms/main.go deleted file mode 100644 index 13c061629..000000000 --- a/internal/cmd/regenerate-maven-poms/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "context" - "fmt" - "path/filepath" - - "buf.build/go/app" - "buf.build/go/app/appcmd" - - "github.com/bufbuild/plugins/internal/maven" -) - -func main() { - appcmd.Main(context.Background(), newCommand("regenerate-maven-poms")) -} - -func newCommand(name string) *appcmd.Command { - return &appcmd.Command{ - Use: name + " [...]", - Short: "Regenerates maven-deps POM and Dockerfile stage for Java/Kotlin plugins", - Args: appcmd.MinimumNArgs(1), - Run: func(_ context.Context, container app.Container) error { - for i := range container.NumArgs() { - pluginDir := container.Arg(i) - // pluginDir is e.g. plugins/org/name/version, so - // plugins root is 3 levels up. - pluginsDir := filepath.Dir(filepath.Dir(filepath.Dir(pluginDir))) - if err := maven.RegenerateMavenDeps(pluginDir, pluginsDir); err != nil { - return fmt.Errorf("failed to regenerate %s: %w", pluginDir, err) - } - fmt.Fprintf(container.Stdout(), "regenerated: %s\n", pluginDir) - } - return nil - }, - } -} diff --git a/internal/maven/dockerfile.go b/internal/maven/dockerfile.go deleted file mode 100644 index b47a4e64d..000000000 --- a/internal/maven/dockerfile.go +++ /dev/null @@ -1,112 +0,0 @@ -package maven - -import ( - "errors" - "slices" - "strings" -) - -// MavenImage is the Maven Docker image used in the maven-deps stage. -const MavenImage = "maven:3.9.11-eclipse-temurin-21" - -// EnsureMavenDepsStage ensures a maven-deps stage exists in the -// Dockerfile with the correct Maven image. If the stage already -// exists, its FROM line is updated. If it does not exist, a new -// stage is inserted before the final FROM line and a -// COPY --from=maven-deps line is added in the final stage. -func EnsureMavenDepsStage(dockerfile string) (string, error) { - lines := strings.Split(dockerfile, "\n") - for i, line := range lines { - if isMavenDepsFromLine(line) { - lines[i] = "FROM " + MavenImage + " AS maven-deps" - return strings.Join(lines, "\n"), nil - } - } - return insertMavenDepsStage(lines) -} - -func insertMavenDepsStage(lines []string) (string, error) { - // Find the index of the last FROM line (the final stage). - lastFromIdx := -1 - for i, line := range lines { - if isFromLine(line) { - lastFromIdx = i - } - } - if lastFromIdx < 0 { - return "", errors.New("no FROM line found in Dockerfile") - } - - mavenDepsLines := []string{ - "FROM " + MavenImage + " AS maven-deps", - "COPY pom.xml /tmp/pom.xml", - "RUN cd /tmp && mvn -f pom.xml dependency:go-offline", - } - - // Find the insertion point: strip trailing blank lines before - // the last FROM so we can place exactly one blank line before - // the new stage. - insertAt := lastFromIdx - for insertAt > 0 && strings.TrimSpace(lines[insertAt-1]) == "" { - insertAt-- - } - - // Assemble: [build stage content] + blank line + - // maven-deps stage + blank line + final stage. - var newLines []string - newLines = append(newLines, lines[:insertAt]...) - newLines = append(newLines, "") - newLines = append(newLines, mavenDepsLines...) - newLines = append(newLines, "") - newLines = append(newLines, lines[lastFromIdx:]...) - - // Find the last FROM in the new lines array (the final stage). - finalFromIdx := -1 - for i, line := range newLines { - if isFromLine(line) { - finalFromIdx = i - } - } - - // Insert COPY --from=maven-deps before the first - // USER/CMD/ENTRYPOINT in the final stage. - copyInsertAt := -1 - for i := finalFromIdx + 1; i < len(newLines); i++ { - if isCopyInsertTarget(newLines[i]) { - copyInsertAt = i - break - } - } - - copyLine := "COPY --from=maven-deps /root/.m2/repository /maven-repository" - if copyInsertAt < 0 { - newLines = append(newLines, copyLine) - return strings.Join(newLines, "\n"), nil - } - finalLines := slices.Concat( - newLines[:copyInsertAt], - []string{copyLine}, - newLines[copyInsertAt:], - ) - return strings.Join(finalLines, "\n"), nil -} - -func isFromLine(line string) bool { - trimmed := strings.TrimSpace(line) - return strings.HasPrefix(strings.ToUpper(trimmed), "FROM ") -} - -func isMavenDepsFromLine(line string) bool { - trimmed := strings.TrimSpace(line) - upper := strings.ToUpper(trimmed) - return strings.HasPrefix(upper, "FROM ") && strings.Contains(strings.ToLower(trimmed), "as maven-deps") -} - -func isCopyInsertTarget(line string) bool { - upper := strings.ToUpper(strings.TrimSpace(line)) - return strings.HasPrefix(upper, "USER ") || - strings.HasPrefix(upper, "CMD ") || - strings.HasPrefix(upper, "CMD[") || - strings.HasPrefix(upper, "ENTRYPOINT ") || - strings.HasPrefix(upper, "ENTRYPOINT[") -} diff --git a/internal/maven/regenerate.go b/internal/maven/regenerate.go index 5e3bff3ca..960cb59ca 100644 --- a/internal/maven/regenerate.go +++ b/internal/maven/regenerate.go @@ -1,20 +1,17 @@ package maven import ( - "errors" "fmt" "os" "path/filepath" - "strings" "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin/bufremotepluginconfig" ) // RegenerateMavenDeps processes a Maven plugin version directory by -// merging transitive deps, deduplicating, rendering POM to a pom.xml -// file, and ensuring the Dockerfile has an up-to-date maven-deps -// stage. Returns nil without changes if the plugin has no Maven -// registry config. +// merging transitive deps, deduplicating, and rendering POM to a +// pom.xml file. Returns nil without changes if the plugin has no +// Maven registry config. func RegenerateMavenDeps(pluginVersionDir, pluginsDir string) error { yamlPath := filepath.Join(pluginVersionDir, "buf.plugin.yaml") pluginConfig, err := bufremotepluginconfig.ParseConfig(yamlPath) @@ -38,41 +35,5 @@ func RegenerateMavenDeps(pluginVersionDir, pluginsDir string) error { if err := os.WriteFile(pomPath, []byte(pom), 0644); err != nil { //nolint:gosec return fmt.Errorf("writing pom.xml: %w", err) } - dockerfilePath := filepath.Join(pluginVersionDir, "Dockerfile") - dockerfileBytes, err := os.ReadFile(dockerfilePath) - if err != nil { - return err - } - updated, err := EnsureMavenDepsStage(string(dockerfileBytes)) - if err != nil { - return fmt.Errorf("ensuring maven-deps stage: %w", err) - } - if err := os.WriteFile(dockerfilePath, []byte(updated), 0644); err != nil { //nolint:gosec - return fmt.Errorf("writing Dockerfile: %w", err) - } - dockerignorePath := filepath.Join(pluginVersionDir, ".dockerignore") - if err := ensureDockerignoreAllowsPOM(dockerignorePath); err != nil { - return fmt.Errorf("updating .dockerignore: %w", err) - } return nil } - -// ensureDockerignoreAllowsPOM adds "!pom.xml" to the .dockerignore if it -// exists and doesn't already allow pom.xml. The pom.xml must be present in -// the build context for the maven-deps COPY instruction to succeed. -func ensureDockerignoreAllowsPOM(path string) error { - content, err := os.ReadFile(path) - if errors.Is(err, os.ErrNotExist) { - return nil - } else if err != nil { - return err - } - const pomRule = "!pom.xml" - for line := range strings.SplitSeq(string(content), "\n") { - if strings.TrimSpace(line) == pomRule { - return nil - } - } - updated := strings.TrimRight(string(content), "\n") + "\n" + pomRule + "\n" - return os.WriteFile(path, []byte(updated), 0644) //nolint:gosec -} diff --git a/internal/maven/regenerate_test.go b/internal/maven/regenerate_test.go index dae97d622..08ed9aae1 100644 --- a/internal/maven/regenerate_test.go +++ b/internal/maven/regenerate_test.go @@ -36,7 +36,7 @@ registry: require.NoError(t, os.WriteFile(filepath.Join(baseDir, "buf.plugin.yaml"), []byte(baseYAML), 0644)) // Setup: consumer-plugin depends on base-plugin and has its own Maven - // deps plus a lite runtime. The Dockerfile has a maven-deps stage. + // deps plus a lite runtime. consumerDir := filepath.Join(tmpDir, "plugins", "test", "consumer-plugin", "v1.0.0") require.NoError(t, os.MkdirAll(consumerDir, 0755)) consumerYAML := `version: v1 @@ -61,27 +61,11 @@ registry: ` require.NoError(t, os.WriteFile(filepath.Join(consumerDir, "buf.plugin.yaml"), []byte(consumerYAML), 0644)) - dockerfile := `# syntax=docker/dockerfile:1.19 -FROM debian:bookworm AS build -RUN echo hello - -FROM scratch -COPY --from=build /app . -ENTRYPOINT ["/app"] -` - require.NoError(t, os.WriteFile(filepath.Join(consumerDir, "Dockerfile"), []byte(dockerfile), 0644)) - // Run RegenerateMavenDeps on the consumer plugin. pluginsDir := filepath.Join(tmpDir, "plugins") err := RegenerateMavenDeps(consumerDir, pluginsDir) require.NoError(t, err) - // Verify the maven-deps stage was inserted into the Dockerfile. - dockerfileBytes, err := os.ReadFile(filepath.Join(consumerDir, "Dockerfile")) - require.NoError(t, err) - assert.Contains(t, string(dockerfileBytes), "FROM "+MavenImage+" AS maven-deps") - assert.Contains(t, string(dockerfileBytes), "COPY --from=maven-deps /root/.m2/repository /maven-repository") - // Read and parse pom.xml to verify deps include versions. pomBytes, err := os.ReadFile(filepath.Join(consumerDir, "pom.xml")) require.NoError(t, err) From c7c15b3cb3d5d7c4accfe84a4085bddd58b67e1a Mon Sep 17 00:00:00 2001 From: David Marby Date: Fri, 13 Mar 2026 20:08:38 +0100 Subject: [PATCH 2/2] Restore regenerateMavenDeps call in fetcher post-processing The pom.xml still needs to be regenerated for new plugin versions. Only the Dockerfile stage insertion was meant to be removed. --- internal/cmd/fetcher/main.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/cmd/fetcher/main.go b/internal/cmd/fetcher/main.go index 8d655e01d..339cb92ec 100644 --- a/internal/cmd/fetcher/main.go +++ b/internal/cmd/fetcher/main.go @@ -25,6 +25,7 @@ import ( "github.com/bufbuild/plugins/internal/docker" "github.com/bufbuild/plugins/internal/fetchclient" "github.com/bufbuild/plugins/internal/git" + "github.com/bufbuild/plugins/internal/maven" "github.com/bufbuild/plugins/internal/plugin" "github.com/bufbuild/plugins/internal/source" ) @@ -140,6 +141,9 @@ func postProcessCreatedPlugins(ctx context.Context, logger *slog.Logger, plugins } for _, plugin := range plugins { newPluginRef := plugin.String() + if err := regenerateMavenDeps(plugin); err != nil { + return fmt.Errorf("failed to regenerate maven deps for %s: %w", newPluginRef, err) + } if err := runGoModTidy(ctx, logger, plugin); err != nil { return fmt.Errorf("failed to run go mod tidy for %s: %w", newPluginRef, err) } @@ -282,6 +286,13 @@ func recreateSwiftPackageResolved(ctx context.Context, logger *slog.Logger, plug return nil } +// regenerateMavenDeps regenerates the pom.xml from the plugin's buf.plugin.yaml. +func regenerateMavenDeps(plugin createdPlugin) error { + versionDir := filepath.Join(plugin.pluginDir, plugin.newVersion) + pluginsDir := filepath.Dir(filepath.Dir(plugin.pluginDir)) + return maven.RegenerateMavenDeps(versionDir, pluginsDir) +} + // runPluginTests runs 'make test PLUGINS="org/name:v"' in order to generate plugin.sum files. func runPluginTests(ctx context.Context, logger *slog.Logger, plugins []createdPlugin) error { pluginsEnv := make([]string, 0, len(plugins))