diff --git a/internal/cmd/fetcher/main.go b/internal/cmd/fetcher/main.go index eafaf23ae..339cb92ec 100644 --- a/internal/cmd/fetcher/main.go +++ b/internal/cmd/fetcher/main.go @@ -286,8 +286,7 @@ 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. +// 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)) 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)