diff --git a/internal/cmd/fetcher/main.go b/internal/cmd/fetcher/main.go index 327c18350..d05beddfa 100644 --- a/internal/cmd/fetcher/main.go +++ b/internal/cmd/fetcher/main.go @@ -24,6 +24,7 @@ import ( "github.com/bufbuild/plugins/internal/docker" "github.com/bufbuild/plugins/internal/fetchclient" + "github.com/bufbuild/plugins/internal/maven" "github.com/bufbuild/plugins/internal/plugin" "github.com/bufbuild/plugins/internal/source" ) @@ -131,6 +132,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) } @@ -273,6 +277,14 @@ 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/fetcher/main_test.go b/internal/cmd/fetcher/main_test.go index 231fd7667..9630c0a8f 100644 --- a/internal/cmd/fetcher/main_test.go +++ b/internal/cmd/fetcher/main_test.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/xml" "log/slog" "os" "path/filepath" @@ -15,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/bufbuild/plugins/internal/maven" "github.com/bufbuild/plugins/internal/source" ) @@ -358,3 +360,260 @@ func newTestContainer(t *testing.T, root string) appext.Container { logger := slog.New(slog.NewTextHandler(testWriter{t}, &slog.HandlerOptions{Level: slog.LevelDebug})) return appext.NewContainer(nameContainer, logger) } + +func TestRegenerateMavenDepsWithPluginDeps(t *testing.T) { + t.Parallel() + tmpDir := t.TempDir() + + // Setup: base-plugin has Maven deps including an additional lite runtime. + baseDir := filepath.Join(tmpDir, "plugins", "test", "base-plugin", "v1.0.0") + require.NoError(t, os.MkdirAll(baseDir, 0755)) + baseYAML := `version: v1 +name: buf.build/test/base-plugin +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - com.google.protobuf:protobuf-java:4.33.5 + additional_runtimes: + - name: lite + deps: + - com.google.protobuf:protobuf-javalite:4.33.5 + - build.buf:protobuf-javalite:4.33.5 + opts: [lite] +` + 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. + consumerDir := filepath.Join(tmpDir, "plugins", "test", "consumer-plugin", "v1.0.0") + require.NoError(t, os.MkdirAll(consumerDir, 0755)) + consumerYAML := `version: v1 +name: buf.build/test/consumer-plugin +plugin_version: v1.0.0 +deps: + - plugin: buf.build/test/base-plugin:v1.0.0 +output_languages: + - kotlin +registry: + maven: + compiler: + kotlin: + version: 1.8.22 + deps: + - com.google.protobuf:protobuf-kotlin:4.33.5 + additional_runtimes: + - name: lite + deps: + - com.google.protobuf:protobuf-kotlin-lite:4.33.5 + opts: [lite] +` + 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 + err := regenerateMavenDeps(createdPlugin{ + org: "test", + name: "consumer-plugin", + pluginDir: filepath.Join(tmpDir, "plugins", "test", "consumer-plugin"), + newVersion: "v1.0.0", + }) + 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 "+maven.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) + var pom testPOMProject + require.NoError(t, xml.Unmarshal(pomBytes, &pom)) + var depVersions []string + for _, dep := range pom.Dependencies { + depVersions = append(depVersions, dep.String()) + } + // Consumer's own deps should be present. + assert.Contains(t, depVersions, "com.google.protobuf:protobuf-kotlin:4.33.5") + assert.Contains(t, depVersions, "com.google.protobuf:protobuf-kotlin-lite:4.33.5") + // Base plugin's main deps should be merged in. + assert.Contains(t, depVersions, "com.google.protobuf:protobuf-java:4.33.5") + // Base plugin's lite runtime deps should be merged into the + // matching lite runtime section. + assert.Contains(t, depVersions, "com.google.protobuf:protobuf-javalite:4.33.5") + assert.Contains(t, depVersions, "build.buf:protobuf-javalite:4.33.5") +} + +func TestMergeDepsMavenDepsTransitive(t *testing.T) { + t.Parallel() + tmpDir := t.TempDir() + + // Setup: grandparent -> parent -> child chain + grandparentDir := filepath.Join(tmpDir, "plugins", "test", "grandparent", "v1.0.0") + require.NoError(t, os.MkdirAll(grandparentDir, 0755)) + grandparentYAML := `version: v1 +name: buf.build/test/grandparent +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - org.example:grandparent-dep:1.0.0 +` + require.NoError(t, os.WriteFile(filepath.Join(grandparentDir, "buf.plugin.yaml"), []byte(grandparentYAML), 0644)) + + parentDir := filepath.Join(tmpDir, "plugins", "test", "parent", "v1.0.0") + require.NoError(t, os.MkdirAll(parentDir, 0755)) + parentYAML := `version: v1 +name: buf.build/test/parent +plugin_version: v1.0.0 +deps: + - plugin: buf.build/test/grandparent:v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - org.example:parent-dep:1.0.0 +` + require.NoError(t, os.WriteFile(filepath.Join(parentDir, "buf.plugin.yaml"), []byte(parentYAML), 0644)) + + childDir := filepath.Join(tmpDir, "plugins", "test", "child", "v1.0.0") + require.NoError(t, os.MkdirAll(childDir, 0755)) + childYAML := `version: v1 +name: buf.build/test/child +plugin_version: v1.0.0 +deps: + - plugin: buf.build/test/parent:v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - org.example:child-dep:1.0.0 +` + require.NoError(t, os.WriteFile(filepath.Join(childDir, "buf.plugin.yaml"), []byte(childYAML), 0644)) + + // Parse the child plugin config + childConfig, err := bufremotepluginconfig.ParseConfig( + filepath.Join(childDir, "buf.plugin.yaml"), + ) + require.NoError(t, err) + + // Merge transitive deps + pluginsDir := filepath.Join(tmpDir, "plugins") + err = maven.MergeTransitiveDeps(childConfig, pluginsDir) + require.NoError(t, err) + + // Child should now have all three deps: child-dep, parent-dep, + // grandparent-dep (transitive through parent) + var artifactIDs []string + for _, dep := range childConfig.Registry.Maven.Deps { + artifactIDs = append(artifactIDs, dep.ArtifactID) + } + assert.Contains(t, artifactIDs, "child-dep") + assert.Contains(t, artifactIDs, "parent-dep") + assert.Contains(t, artifactIDs, "grandparent-dep") +} + +func TestDeduplicateAllDeps(t *testing.T) { + t.Parallel() + tests := []struct { + name string + deps []bufremotepluginconfig.MavenDependencyConfig + want []bufremotepluginconfig.MavenDependencyConfig + wantErr string + }{ + { + name: "no duplicates", + deps: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + {GroupID: "com.example", ArtifactID: "b", Version: "1.0"}, + }, + want: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + {GroupID: "com.example", ArtifactID: "b", Version: "1.0"}, + }, + }, + { + name: "exact duplicate removed", + deps: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + }, + want: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + }, + }, + { + name: "version conflict returns error", + deps: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + {GroupID: "com.example", ArtifactID: "a", Version: "2.0"}, + }, + wantErr: "duplicate Maven dependency com.example:a with conflicting versions", + }, + { + name: "different classifiers are distinct", + deps: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0", Classifier: "sources"}, + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + }, + want: []bufremotepluginconfig.MavenDependencyConfig{ + {GroupID: "com.example", ArtifactID: "a", Version: "1.0", Classifier: "sources"}, + {GroupID: "com.example", ArtifactID: "a", Version: "1.0"}, + }, + }, + { + name: "nil input", + deps: nil, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + config := &bufremotepluginconfig.MavenRegistryConfig{ + Deps: tt.deps, + } + err := maven.DeduplicateAllDeps(config) + if tt.wantErr != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, config.Deps) + }) + } +} + +// testPOMProject mirrors the Maven POM structure for test assertions. +type testPOMProject struct { + XMLName xml.Name `xml:"project"` + Dependencies []testDep `xml:"dependencies>dependency"` +} + +type testDep struct { + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` +} + +func (d testDep) String() string { + return d.GroupID + ":" + d.ArtifactID + ":" + d.Version +} diff --git a/internal/cmd/regenerate-maven-poms/main.go b/internal/cmd/regenerate-maven-poms/main.go new file mode 100644 index 000000000..13c061629 --- /dev/null +++ b/internal/cmd/regenerate-maven-poms/main.go @@ -0,0 +1,37 @@ +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/deps.go b/internal/maven/deps.go new file mode 100644 index 000000000..ce3b42281 --- /dev/null +++ b/internal/maven/deps.go @@ -0,0 +1,133 @@ +package maven + +import ( + "fmt" + "path/filepath" + + "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin/bufremotepluginconfig" +) + +// MergeTransitiveDeps resolves Maven dependencies from the top-level deps +// stanza in the plugin config and merges them into the plugin's Maven +// registry config. Dependencies are resolved transitively so that all +// Maven artifacts needed for offline builds are included in the POM. +func MergeTransitiveDeps( + pluginConfig *bufremotepluginconfig.Config, + pluginsDir string, +) error { + if pluginConfig.Registry == nil || pluginConfig.Registry.Maven == nil { + return nil + } + visited := make(map[string]bool) + return mergeTransitiveDepsRecursive(pluginConfig, pluginsDir, visited) +} + +func mergeTransitiveDepsRecursive( + pluginConfig *bufremotepluginconfig.Config, + pluginsDir string, + visited map[string]bool, +) error { + for _, dep := range pluginConfig.Dependencies { + depKey := dep.IdentityString() + ":" + dep.Version() + if visited[depKey] { + continue + } + visited[depKey] = true + depPath := filepath.Join( + pluginsDir, dep.Owner(), dep.Plugin(), + dep.Version(), "buf.plugin.yaml", + ) + depConfig, err := bufremotepluginconfig.ParseConfig(depPath) + if err != nil { + return fmt.Errorf("loading dep config %s from %s: %w", depKey, depPath, err) + } + // Recursively resolve transitive dependencies first so + // that depConfig.Registry.Maven accumulates the full + // transitive closure before we merge into pluginConfig. + if err := mergeTransitiveDepsRecursive(depConfig, pluginsDir, visited); err != nil { + return err + } + if depConfig.Registry == nil || depConfig.Registry.Maven == nil { + continue + } + depMaven := depConfig.Registry.Maven + pluginConfig.Registry.Maven.Deps = append( + pluginConfig.Registry.Maven.Deps, depMaven.Deps..., + ) + // Merge additional runtimes: for matching runtime names, + // append deps; otherwise add the new runtime entry. + for _, depRuntime := range depMaven.AdditionalRuntimes { + merged := false + for i, runtime := range pluginConfig.Registry.Maven.AdditionalRuntimes { + if runtime.Name == depRuntime.Name { + pluginConfig.Registry.Maven.AdditionalRuntimes[i].Deps = append( + pluginConfig.Registry.Maven.AdditionalRuntimes[i].Deps, + depRuntime.Deps..., + ) + merged = true + break + } + } + if !merged { + pluginConfig.Registry.Maven.AdditionalRuntimes = append( + pluginConfig.Registry.Maven.AdditionalRuntimes, + depRuntime, + ) + } + } + } + return nil +} + +// DeduplicateAllDeps deduplicates across the main Deps and all +// AdditionalRuntimes Deps using a shared seen set. This ensures the +// flat block in the rendered POM contains no duplicates. +// Returns an error if two entries share the same groupId:artifactId +// coordinate but differ in version. +func DeduplicateAllDeps( + mavenConfig *bufremotepluginconfig.MavenRegistryConfig, +) error { + if mavenConfig == nil { + return nil + } + seen := make(map[string]string) + var err error + mavenConfig.Deps, err = deduplicateWithSeen(mavenConfig.Deps, seen) + if err != nil { + return err + } + for i := range mavenConfig.AdditionalRuntimes { + mavenConfig.AdditionalRuntimes[i].Deps, err = deduplicateWithSeen( + mavenConfig.AdditionalRuntimes[i].Deps, seen, + ) + if err != nil { + return err + } + } + return nil +} + +func deduplicateWithSeen( + deps []bufremotepluginconfig.MavenDependencyConfig, + seen map[string]string, +) ([]bufremotepluginconfig.MavenDependencyConfig, error) { + var result []bufremotepluginconfig.MavenDependencyConfig + for _, dep := range deps { + key := dep.GroupID + ":" + dep.ArtifactID + if dep.Classifier != "" { + key += ":" + dep.Classifier + } + if existingVersion, ok := seen[key]; ok { + if existingVersion != dep.Version { + return nil, fmt.Errorf( + "duplicate Maven dependency %s with conflicting versions: %s vs %s", + key, existingVersion, dep.Version, + ) + } + continue + } + seen[key] = dep.Version + result = append(result, dep) + } + return result, nil +} diff --git a/internal/maven/dockerfile.go b/internal/maven/dockerfile.go new file mode 100644 index 000000000..b47a4e64d --- /dev/null +++ b/internal/maven/dockerfile.go @@ -0,0 +1,112 @@ +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/pom.go b/internal/maven/pom.go new file mode 100644 index 000000000..50ca84b78 --- /dev/null +++ b/internal/maven/pom.go @@ -0,0 +1,63 @@ +package maven + +import ( + "bytes" + _ "embed" + "encoding/xml" + "errors" + "fmt" + "io" + "strings" + "text/template" + + "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin/bufremotepluginconfig" +) + +type templateData struct { + *bufremotepluginconfig.Config +} + +var ( + //go:embed pom.xml.gotext + pomTemplateContents string + + pomTemplate = template.Must(template.New("pom.xml").Funcs(template.FuncMap{ + "xml": xmlEscape, + }).Parse(pomTemplateContents)) +) + +// RenderPOM generates a Maven POM XML from a parsed plugin config. +// The POM includes all runtime dependencies, additional runtimes, and +// kotlin-maven-plugin for Kotlin plugins. maven-compiler-plugin and +// maven-source-plugin are bundled in the maven-jdk base image. +func RenderPOM(pluginConfig *bufremotepluginconfig.Config) (string, error) { + if pluginConfig.Registry == nil || pluginConfig.Registry.Maven == nil { + return "", fmt.Errorf("no Maven registry configured for %q", pluginConfig.Name) + } + data := templateData{ + Config: pluginConfig, + } + var buf bytes.Buffer + if err := pomTemplate.Execute(&buf, data); err != nil { + return "", err + } + pom := buf.String() + decoder := xml.NewDecoder(strings.NewReader(pom)) + for { + if _, err := decoder.Token(); err != nil { + if errors.Is(err, io.EOF) { + break + } + return "", fmt.Errorf("generated POM is not well-formed XML: %w", err) + } + } + return pom, nil +} + +func xmlEscape(raw string) (string, error) { + var b strings.Builder + if err := xml.EscapeText(&b, []byte(raw)); err != nil { + return "", err + } + return b.String(), nil +} diff --git a/internal/maven/pom.xml.gotext b/internal/maven/pom.xml.gotext new file mode 100644 index 000000000..141a4a046 --- /dev/null +++ b/internal/maven/pom.xml.gotext @@ -0,0 +1,62 @@ + + 4.0.0 + temp + temp + 1.0 + {{- $kotlinCompiler := .Registry.Maven.Compiler.Kotlin }} + + {{- range $dep := .Registry.Maven.Deps }} + + {{ $dep.GroupID | xml }} + {{ $dep.ArtifactID | xml }} + {{ $dep.Version | xml }} + {{- with $dep.Classifier }} + {{ . | xml }} + {{- end }} + {{- with $dep.Extension }} + {{ . | xml }} + {{- end }} + + {{- end }} + {{- range $runtime := .Registry.Maven.AdditionalRuntimes }} + + {{- range $dep := $runtime.Deps }} + + {{ $dep.GroupID | xml }} + {{ $dep.ArtifactID | xml }} + {{ $dep.Version | xml }} + {{- with $dep.Classifier }} + {{ . | xml }} + {{- end }} + {{- with $dep.Extension }} + {{ . | xml }} + {{- end }} + + {{- end }} + {{- end }} + + {{- if $kotlinCompiler.Version }} + + + + org.jetbrains.kotlin + kotlin-maven-plugin + {{- with $kotlinCompiler.Version }} + {{ . | xml }} + {{- end }} + + {{- with $kotlinCompiler.APIVersion }} + {{ . | xml }} + {{- end }} + {{- with $kotlinCompiler.JVMTarget }} + {{ . | xml }} + {{- end }} + {{- with $kotlinCompiler.LanguageVersion }} + {{ . | xml }} + {{- end }} + + + + + {{- end }} + diff --git a/internal/maven/pom_test.go b/internal/maven/pom_test.go new file mode 100644 index 000000000..dce76a13b --- /dev/null +++ b/internal/maven/pom_test.go @@ -0,0 +1,224 @@ +package maven + +import ( + "encoding/xml" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin/bufremotepluginconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// pomProject mirrors the Maven POM structure for test assertions. +type pomProject struct { + XMLName xml.Name `xml:"project"` + ModelVersion string `xml:"modelVersion"` + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` + Dependencies []pomDependency `xml:"dependencies>dependency"` + Build *pomBuild `xml:"build"` +} + +type pomDependency struct { + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` + Classifier string `xml:"classifier"` + Type string `xml:"type"` +} + +type pomBuild struct { + Plugins []pomPlugin `xml:"plugins>plugin"` +} + +type pomPlugin struct { + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` + Configuration *pomConfiguration `xml:"configuration"` +} + +type pomConfiguration struct { + APIVersion string `xml:"apiVersion"` + JVMTarget string `xml:"jvmTarget"` + LanguageVersion string `xml:"languageVersion"` +} + +func TestRenderPOM(t *testing.T) { + t.Parallel() + tests := []struct { + name string + yaml string + wantErr string + // check runs assertions against the parsed POM. XML comments + // are not preserved by encoding/xml, so rawPOM is provided + // for comment checks. + check func(t *testing.T, p pomProject, rawPOM string) + }{ + { + name: "basic Java plugin", + yaml: `version: v1 +name: buf.build/test/plugin +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - com.google.protobuf:protobuf-java:4.33.5 +`, + check: func(t *testing.T, p pomProject, _ string) { //nolint:thelper + require.Len(t, p.Dependencies, 1) + dep := p.Dependencies[0] + assert.Equal(t, "com.google.protobuf", dep.GroupID) + assert.Equal(t, "protobuf-java", dep.ArtifactID) + assert.Equal(t, "4.33.5", dep.Version) + }, + }, + { + name: "Kotlin compiler plugin configuration", + yaml: `version: v1 +name: buf.build/test/kotlin-plugin +plugin_version: v1.0.0 +output_languages: + - kotlin +registry: + maven: + compiler: + kotlin: + version: 1.8.22 + jvm_target: "1.8" + language_version: "1.8" + api_version: "1.8" + deps: [] +`, + check: func(t *testing.T, p pomProject, _ string) { //nolint:thelper + require.NotNil(t, p.Build) + require.Len(t, p.Build.Plugins, 1) + plugin := p.Build.Plugins[0] + assert.Equal(t, "org.jetbrains.kotlin", plugin.GroupID) + assert.Equal(t, "kotlin-maven-plugin", plugin.ArtifactID) + assert.Equal(t, "1.8.22", plugin.Version) + require.NotNil(t, plugin.Configuration) + assert.Equal(t, "1.8", plugin.Configuration.JVMTarget) + assert.Equal(t, "1.8", plugin.Configuration.LanguageVersion) + assert.Equal(t, "1.8", plugin.Configuration.APIVersion) + }, + }, + { + name: "additional runtimes", + yaml: `version: v1 +name: buf.build/test/plugin +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - com.google.protobuf:protobuf-java:4.33.5 + additional_runtimes: + - name: lite + deps: + - com.google.protobuf:protobuf-javalite:4.33.5 + opts: [lite] +`, + check: func(t *testing.T, p pomProject, rawPOM string) { //nolint:thelper + require.Len(t, p.Dependencies, 2) + assert.Equal(t, "protobuf-java", p.Dependencies[0].ArtifactID) + assert.Equal(t, "protobuf-javalite", p.Dependencies[1].ArtifactID) + // XML comments are not preserved by encoding/xml. + assert.Contains(t, rawPOM, "") + }, + }, + { + name: "no Maven config returns error", + yaml: `version: v1 +name: buf.build/test/plugin +plugin_version: v1.0.0 +output_languages: + - go +`, + wantErr: "no Maven registry configured", + }, + { + name: "empty deps renders valid structure", + yaml: `version: v1 +name: buf.build/test/plugin +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: [] +`, + check: func(t *testing.T, p pomProject, _ string) { //nolint:thelper + assert.Empty(t, p.Dependencies) + }, + }, + { + name: "malformed XML detected", + yaml: `version: v1 +name: buf.build/test/plugin +plugin_version: v1.0.0 +output_languages: + - java +registry: + maven: + deps: + - com.google.protobuf:protobuf-java:4.33.5 + additional_runtimes: + - name: "bad--name" + deps: + - com.google.protobuf:protobuf-javalite:4.33.5 + opts: [lite] +`, + wantErr: "generated POM is not well-formed XML", + }, + { + name: "Kotlin plugin with no explicit deps", + yaml: `version: v1 +name: buf.build/test/kotlin-plugin +plugin_version: v1.0.0 +output_languages: + - kotlin +registry: + maven: + compiler: + kotlin: + version: 1.9.0 + deps: [] +`, + check: func(t *testing.T, p pomProject, _ string) { //nolint:thelper + assert.Empty(t, p.Dependencies) + require.NotNil(t, p.Build) + require.Len(t, p.Build.Plugins, 1) + assert.Equal(t, "kotlin-maven-plugin", p.Build.Plugins[0].ArtifactID) + assert.Equal(t, "1.9.0", p.Build.Plugins[0].Version) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + tmpDir := t.TempDir() + yamlPath := filepath.Join(tmpDir, "buf.plugin.yaml") + require.NoError(t, os.WriteFile(yamlPath, []byte(tt.yaml), 0644)) + config, err := bufremotepluginconfig.ParseConfig(yamlPath) + require.NoError(t, err) + pom, err := RenderPOM(config) + if tt.wantErr != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + return + } + require.NoError(t, err) + var project pomProject + require.NoError(t, xml.NewDecoder(strings.NewReader(pom)).Decode(&project)) + tt.check(t, project, pom) + }) + } +} diff --git a/internal/maven/regenerate.go b/internal/maven/regenerate.go new file mode 100644 index 000000000..5e3bff3ca --- /dev/null +++ b/internal/maven/regenerate.go @@ -0,0 +1,78 @@ +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. +func RegenerateMavenDeps(pluginVersionDir, pluginsDir string) error { + yamlPath := filepath.Join(pluginVersionDir, "buf.plugin.yaml") + pluginConfig, err := bufremotepluginconfig.ParseConfig(yamlPath) + if err != nil { + return err + } + if pluginConfig.Registry == nil || pluginConfig.Registry.Maven == nil { + return nil + } + if err := MergeTransitiveDeps(pluginConfig, pluginsDir); err != nil { + return fmt.Errorf("merging transitive deps: %w", err) + } + if err := DeduplicateAllDeps(pluginConfig.Registry.Maven); err != nil { + return fmt.Errorf("deduplicating deps: %w", err) + } + pom, err := RenderPOM(pluginConfig) + if err != nil { + return fmt.Errorf("rendering POM: %w", err) + } + pomPath := filepath.Join(pluginVersionDir, "pom.xml") + 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/plugins/apple/servicetalk/v0.42.63/.dockerignore b/plugins/apple/servicetalk/v0.42.63/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/apple/servicetalk/v0.42.63/.dockerignore +++ b/plugins/apple/servicetalk/v0.42.63/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/apple/servicetalk/v0.42.63/Dockerfile b/plugins/apple/servicetalk/v0.42.63/Dockerfile index 882144afe..bf51e0b77 100644 --- a/plugins/apple/servicetalk/v0.42.63/Dockerfile +++ b/plugins/apple/servicetalk/v0.42.63/Dockerfile @@ -8,8 +8,13 @@ RUN curl -fsSL -o servicetalk-grpc-protoc.jar https://repo1.maven.org/maven2/io/ FROM gcr.io/distroless/java21-debian12:latest@sha256:f34fd3e4e2d7a246d764d0614f5e6ffb3a735930723fac4cfc25a72798950262 AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=base --link / / COPY --from=build --link --chmod=0755 --chown=root:root /app/servicetalk-grpc-protoc.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/usr/bin/java", "-jar", "/servicetalk-grpc-protoc.jar"] diff --git a/plugins/apple/servicetalk/v0.42.63/pom.xml b/plugins/apple/servicetalk/v0.42.63/pom.xml new file mode 100644 index 000000000..783a521b2 --- /dev/null +++ b/plugins/apple/servicetalk/v0.42.63/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.servicetalk + servicetalk-data-protobuf + 0.42.63 + + + io.servicetalk + servicetalk-grpc-api + 0.42.63 + + + io.servicetalk + servicetalk-grpc-protobuf + 0.42.63 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + diff --git a/plugins/bufbuild/connect-kotlin/v0.1.10/.dockerignore b/plugins/bufbuild/connect-kotlin/v0.1.10/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/bufbuild/connect-kotlin/v0.1.10/.dockerignore +++ b/plugins/bufbuild/connect-kotlin/v0.1.10/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/bufbuild/connect-kotlin/v0.1.10/Dockerfile b/plugins/bufbuild/connect-kotlin/v0.1.10/Dockerfile index 5b494fd08..c4cdfbce7 100644 --- a/plugins/bufbuild/connect-kotlin/v0.1.10/Dockerfile +++ b/plugins/bufbuild/connect-kotlin/v0.1.10/Dockerfile @@ -5,7 +5,12 @@ RUN apt-get update \ WORKDIR /app RUN curl -fsSL -o /app/protoc-gen-connect-kotlin.jar https://repo1.maven.org/maven2/build/buf/protoc-gen-connect-kotlin/0.1.10/protoc-gen-connect-kotlin-0.1.10.jar +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM gcr.io/distroless/java17-debian11 WORKDIR /app COPY --from=build /app/protoc-gen-connect-kotlin.jar /app +COPY --from=maven-deps /root/.m2/repository /maven-repository CMD ["/app/protoc-gen-connect-kotlin.jar"] diff --git a/plugins/bufbuild/connect-kotlin/v0.1.10/pom.xml b/plugins/bufbuild/connect-kotlin/v0.1.10/pom.xml new file mode 100644 index 000000000..0cf0763c4 --- /dev/null +++ b/plugins/bufbuild/connect-kotlin/v0.1.10/pom.xml @@ -0,0 +1,75 @@ + + 4.0.0 + temp + temp + 1.0 + + + build.buf + connect-kotlin + 0.1.10 + + + build.buf + connect-kotlin-google-java-ext + 0.1.10 + + + build.buf + connect-kotlin-okhttp + 0.1.10 + + + com.google.protobuf + protobuf-kotlin + 3.24.3 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + com.google.protobuf + protobuf-java + 3.24.3 + + + + build.buf + connect-kotlin-google-javalite-ext + 0.1.10 + + + com.google.protobuf + protobuf-kotlin-lite + 3.24.3 + + + com.google.protobuf + protobuf-javalite + 3.24.3 + + + build.buf + protobuf-javalite + 3.24.3 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 1.8.22 + + + + + + diff --git a/plugins/bufbuild/validate-java/v1.3.3/.dockerignore b/plugins/bufbuild/validate-java/v1.3.3/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/bufbuild/validate-java/v1.3.3/.dockerignore +++ b/plugins/bufbuild/validate-java/v1.3.3/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/bufbuild/validate-java/v1.3.3/Dockerfile b/plugins/bufbuild/validate-java/v1.3.3/Dockerfile index 3b6ae2357..bad885156 100644 --- a/plugins/bufbuild/validate-java/v1.3.3/Dockerfile +++ b/plugins/bufbuild/validate-java/v1.3.3/Dockerfile @@ -2,8 +2,13 @@ FROM golang:1.26.0-bookworm AS build RUN CGO_ENABLED=0 go install -ldflags "-s -w" -trimpath github.com/envoyproxy/protoc-gen-validate/cmd/protoc-gen-validate-java@v1.3.3 +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=build --link /etc/passwd /etc/passwd COPY --from=build --link --chown=root:root /go/bin/protoc-gen-validate-java . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/protoc-gen-validate-java" ] diff --git a/plugins/bufbuild/validate-java/v1.3.3/pom.xml b/plugins/bufbuild/validate-java/v1.3.3/pom.xml new file mode 100644 index 000000000..288dddd38 --- /dev/null +++ b/plugins/bufbuild/validate-java/v1.3.3/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + temp + temp + 1.0 + + + build.buf.protoc-gen-validate + pgv-java-stub + 1.3.3 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + diff --git a/plugins/community/salesforce-reactive-grpc/v1.2.4/.dockerignore b/plugins/community/salesforce-reactive-grpc/v1.2.4/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/community/salesforce-reactive-grpc/v1.2.4/.dockerignore +++ b/plugins/community/salesforce-reactive-grpc/v1.2.4/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/community/salesforce-reactive-grpc/v1.2.4/Dockerfile b/plugins/community/salesforce-reactive-grpc/v1.2.4/Dockerfile index e02656e52..7782a7695 100644 --- a/plugins/community/salesforce-reactive-grpc/v1.2.4/Dockerfile +++ b/plugins/community/salesforce-reactive-grpc/v1.2.4/Dockerfile @@ -7,8 +7,13 @@ RUN curl -fsSL -o reactor-grpc-protoc.jar https://repo1.maven.org/maven2/com/sal FROM gcr.io/distroless/java21-debian12:latest@sha256:7c9a9a362eadadb308d29b9c7fec2b39e5d5aa21d58837176a2cca50bdd06609 AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=base --link / / COPY --from=build --link --chmod=0755 --chown=root:root /app/reactor-grpc-protoc.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/usr/bin/java", "-jar", "/reactor-grpc-protoc.jar"] diff --git a/plugins/community/salesforce-reactive-grpc/v1.2.4/pom.xml b/plugins/community/salesforce-reactive-grpc/v1.2.4/pom.xml new file mode 100644 index 000000000..4ad758518 --- /dev/null +++ b/plugins/community/salesforce-reactive-grpc/v1.2.4/pom.xml @@ -0,0 +1,54 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.salesforce.servicelibs + reactor-grpc-stub + 1.2.4 + + + io.projectreactor + reactor-core + 3.5.4 + + + com.google.protobuf + protobuf-java + 4.31.0 + + + io.grpc + grpc-core + 1.73.0 + + + io.grpc + grpc-protobuf + 1.73.0 + + + io.grpc + grpc-stub + 1.73.0 + + + + io.grpc + grpc-protobuf-lite + 1.73.0 + + + com.google.protobuf + protobuf-javalite + 4.31.0 + + + build.buf + protobuf-javalite + 4.31.0 + + + diff --git a/plugins/connectrpc/kotlin/v0.7.4/.dockerignore b/plugins/connectrpc/kotlin/v0.7.4/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/connectrpc/kotlin/v0.7.4/.dockerignore +++ b/plugins/connectrpc/kotlin/v0.7.4/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/connectrpc/kotlin/v0.7.4/Dockerfile b/plugins/connectrpc/kotlin/v0.7.4/Dockerfile index ef473a1e4..22137d55d 100644 --- a/plugins/connectrpc/kotlin/v0.7.4/Dockerfile +++ b/plugins/connectrpc/kotlin/v0.7.4/Dockerfile @@ -8,8 +8,13 @@ RUN curl -fsSL -o /app/protoc-gen-connect-kotlin.jar https://repo1.maven.org/mav FROM gcr.io/distroless/java21-debian12:latest@sha256:7c05bf8a64ff1a70a16083e9bdd35b463aa0d014c2fc782d31d13ea7a61de633 as base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=base --link / / COPY --from=build --link --chmod=0755 --chown=root:root /app/protoc-gen-connect-kotlin.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/usr/bin/java", "-jar", "/protoc-gen-connect-kotlin.jar"] diff --git a/plugins/connectrpc/kotlin/v0.7.4/pom.xml b/plugins/connectrpc/kotlin/v0.7.4/pom.xml new file mode 100644 index 000000000..b9d6a8bfe --- /dev/null +++ b/plugins/connectrpc/kotlin/v0.7.4/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.connectrpc + connect-kotlin + 0.7.4 + + + com.connectrpc + connect-kotlin-google-java-ext + 0.7.4 + + + com.connectrpc + connect-kotlin-okhttp + 0.7.4 + + + com.google.protobuf + protobuf-kotlin + 4.31.1 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + com.google.protobuf + protobuf-java + 4.31.1 + + + + com.connectrpc + connect-kotlin-google-javalite-ext + 0.7.4 + + + com.google.protobuf + protobuf-kotlin-lite + 4.31.1 + + + com.google.protobuf + protobuf-javalite + 4.31.1 + + + build.buf + protobuf-javalite + 4.31.1 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 2.1.0 + + 1.8 + + + + + diff --git a/plugins/grpc/java/v1.79.0/.dockerignore b/plugins/grpc/java/v1.79.0/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/grpc/java/v1.79.0/.dockerignore +++ b/plugins/grpc/java/v1.79.0/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/grpc/java/v1.79.0/Dockerfile b/plugins/grpc/java/v1.79.0/Dockerfile index 5e314866a..a933e7d21 100644 --- a/plugins/grpc/java/v1.79.0/Dockerfile +++ b/plugins/grpc/java/v1.79.0/Dockerfile @@ -17,8 +17,13 @@ RUN arch=${TARGETARCH}; \ FROM gcr.io/distroless/cc-debian12:latest@sha256:72344f7f909a8bf003c67f55687e6d51a441b49661af8f660aa7b285f00e57df AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --link --from=base / / COPY --link --from=build --chmod=0755 --chown=root:root /build/protoc-gen-grpc-java . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/protoc-gen-grpc-java" ] diff --git a/plugins/grpc/java/v1.79.0/pom.xml b/plugins/grpc/java/v1.79.0/pom.xml new file mode 100644 index 000000000..bb06ec6c0 --- /dev/null +++ b/plugins/grpc/java/v1.79.0/pom.xml @@ -0,0 +1,44 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.grpc + grpc-core + 1.79.0 + + + io.grpc + grpc-protobuf + 1.79.0 + + + io.grpc + grpc-stub + 1.79.0 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + io.grpc + grpc-protobuf-lite + 1.79.0 + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + diff --git a/plugins/grpc/kotlin/v1.5.0/.dockerignore b/plugins/grpc/kotlin/v1.5.0/.dockerignore index 5d0f124ff..7ff6309df 100644 --- a/plugins/grpc/kotlin/v1.5.0/.dockerignore +++ b/plugins/grpc/kotlin/v1.5.0/.dockerignore @@ -1,2 +1,3 @@ * !Dockerfile +!pom.xml diff --git a/plugins/grpc/kotlin/v1.5.0/Dockerfile b/plugins/grpc/kotlin/v1.5.0/Dockerfile index eed8a60dc..d74acfe39 100644 --- a/plugins/grpc/kotlin/v1.5.0/Dockerfile +++ b/plugins/grpc/kotlin/v1.5.0/Dockerfile @@ -10,8 +10,13 @@ RUN curl -fsSL -o protoc-gen-grpc-kotlin.jar https://repo1.maven.org/maven2/io/g FROM gcr.io/distroless/java21-debian12:latest@sha256:418b2e2a9e452aa9299511427f2ae404dfc910ecfa78feb53b1c60c22c3b640c AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --link --from=base / / COPY --link --from=build --chmod=0644 --chown=root:root /build/protoc-gen-grpc-kotlin.jar . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT [ "/usr/bin/java", "-jar", "/protoc-gen-grpc-kotlin.jar" ] diff --git a/plugins/grpc/kotlin/v1.5.0/pom.xml b/plugins/grpc/kotlin/v1.5.0/pom.xml new file mode 100644 index 000000000..6e18f9727 --- /dev/null +++ b/plugins/grpc/kotlin/v1.5.0/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + temp + temp + 1.0 + + + io.grpc + grpc-kotlin-stub + 1.5.0 + + + org.jetbrains.kotlinx + kotlinx-coroutines-core-jvm + 1.10.2 + + + io.grpc + grpc-core + 1.75.0 + + + io.grpc + grpc-protobuf + 1.75.0 + + + io.grpc + grpc-stub + 1.75.0 + + + com.google.protobuf + protobuf-java + 4.32.0 + + + com.google.protobuf + protobuf-kotlin + 4.32.0 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + + io.grpc + grpc-protobuf-lite + 1.75.0 + + + com.google.protobuf + protobuf-javalite + 4.32.0 + + + build.buf + protobuf-javalite + 4.32.0 + + + com.google.protobuf + protobuf-kotlin-lite + 4.32.0 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 2.2.20 + + + + + + diff --git a/plugins/protocolbuffers/java/v33.5/.dockerignore b/plugins/protocolbuffers/java/v33.5/.dockerignore index 9612bcbe0..f93a3ef18 100644 --- a/plugins/protocolbuffers/java/v33.5/.dockerignore +++ b/plugins/protocolbuffers/java/v33.5/.dockerignore @@ -2,3 +2,4 @@ !BUILD !Dockerfile !java.cc +!pom.xml diff --git a/plugins/protocolbuffers/java/v33.5/Dockerfile b/plugins/protocolbuffers/java/v33.5/Dockerfile index b857d8dc3..06b3381cb 100644 --- a/plugins/protocolbuffers/java/v33.5/Dockerfile +++ b/plugins/protocolbuffers/java/v33.5/Dockerfile @@ -23,8 +23,13 @@ RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-java.stripped' FROM gcr.io/distroless/cc-debian12:latest@sha256:72344f7f909a8bf003c67f55687e6d51a441b49661af8f660aa7b285f00e57df AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=base --link / / COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-java . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT ["/protoc-gen-java"] diff --git a/plugins/protocolbuffers/java/v33.5/pom.xml b/plugins/protocolbuffers/java/v33.5/pom.xml new file mode 100644 index 000000000..09e0eca76 --- /dev/null +++ b/plugins/protocolbuffers/java/v33.5/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + diff --git a/plugins/protocolbuffers/kotlin/v33.5/.dockerignore b/plugins/protocolbuffers/kotlin/v33.5/.dockerignore index d02a40d82..c881939e8 100644 --- a/plugins/protocolbuffers/kotlin/v33.5/.dockerignore +++ b/plugins/protocolbuffers/kotlin/v33.5/.dockerignore @@ -2,3 +2,4 @@ !BUILD !Dockerfile !kotlin.cc +!pom.xml diff --git a/plugins/protocolbuffers/kotlin/v33.5/Dockerfile b/plugins/protocolbuffers/kotlin/v33.5/Dockerfile index f1c72e8fd..82e22f49f 100644 --- a/plugins/protocolbuffers/kotlin/v33.5/Dockerfile +++ b/plugins/protocolbuffers/kotlin/v33.5/Dockerfile @@ -23,8 +23,13 @@ RUN bazelisk ${BAZEL_OPTS} build '//plugins:protoc-gen-kotlin.stripped' FROM gcr.io/distroless/cc-debian12:latest@sha256:72344f7f909a8bf003c67f55687e6d51a441b49661af8f660aa7b285f00e57df AS base +FROM maven:3.9.11-eclipse-temurin-21 AS maven-deps +COPY pom.xml /tmp/pom.xml +RUN cd /tmp && mvn -f pom.xml dependency:go-offline + FROM scratch COPY --from=base --link / / COPY --from=build --link --chmod=0755 /build/bazel-bin/plugins/protoc-gen-kotlin . +COPY --from=maven-deps /root/.m2/repository /maven-repository USER nobody ENTRYPOINT ["/protoc-gen-kotlin"] diff --git a/plugins/protocolbuffers/kotlin/v33.5/pom.xml b/plugins/protocolbuffers/kotlin/v33.5/pom.xml new file mode 100644 index 000000000..ca57d2f7f --- /dev/null +++ b/plugins/protocolbuffers/kotlin/v33.5/pom.xml @@ -0,0 +1,55 @@ + + 4.0.0 + temp + temp + 1.0 + + + com.google.protobuf + protobuf-kotlin + 4.33.5 + + + org.jetbrains.kotlin + kotlin-stdlib + 1.8.22 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + + + com.google.protobuf + protobuf-java + 4.33.5 + + + + com.google.protobuf + protobuf-kotlin-lite + 4.33.5 + + + com.google.protobuf + protobuf-javalite + 4.33.5 + + + build.buf + protobuf-javalite + 4.33.5 + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + 1.8.22 + + + + + +