Skip to content

Commit

Permalink
Support other formats for published image (#170)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Parmet <[email protected]>
  • Loading branch information
sato9818 and andrewparmet authored Jan 16, 2024
1 parent 7fc4efd commit 8da0e3a
Show file tree
Hide file tree
Showing 20 changed files with 445 additions and 8 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ When applied, the plugin creates the following tasks:
- `bufFormatApply` applies [`buf format`](https://buf.build/docs/format/style/)
- `bufFormatCheck` validates [`buf format`](https://buf.build/docs/format/style/)
- `bufLint` validates [`buf lint`](https://buf.build/docs/breaking/overview/)
- `bufBuild` builds an image with [`buf build`](https://buf.build/docs/reference/cli/buf/build)
- `bufBreaking` checks Protobuf schemas against a previous version for backwards-incompatible changes through [`buf breaking`](https://buf.build/docs/breaking/overview/)
- `bufGenerate` generates Protobuf code with [`buf generate`](https://buf.build/docs/generate/overview/)

Expand Down Expand Up @@ -203,6 +204,34 @@ buf {

`bufLint` is configured by creating `buf.yaml` in basic projects or projects using the `protobuf-gradle-plugin`. It is run automatically during the `check` task. Specification of `buf.yaml` is not supported for projects using a workspace.

### `bufBuild`

`bufBuild` is configured with the `build` closure:

```kotlin
buf {
build {
imageFormat = ImageFormat.JSON // JSON by default
compressionFormat = CompressionFormat.GZ // null by default (no compression)
}
}
```

Available image formats are:

- `binpb`
- `bin`
- `json`
- `txtpb`

Available compression formats are:

- `gz`
- `zst`

The file is built in the `bufbuild` directory in the project's build directory and has the name `image` followed by
the image format and optionally the compression format, e.g. `build/bufbuild/image.bin.zst`.

### `bufBreaking`

`bufBreaking` is more complicated since it requires a previous version of the Protobuf schema to validate the current version. Buf's built-in git integration isn't quite enough since it requires a buildable Protobuf source set, and the `protobuf-gradle-plugin`'s extraction step typically targets the project build directory which is ephemeral and is not committed.
Expand Down
36 changes: 36 additions & 0 deletions src/main/kotlin/build/buf/gradle/BufExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ open class BufExtension {
*/
var toolVersion = "1.27.1"

internal var buildDetails: BuildDetails? = null

/**
* Specify the build details for image generation.
*/
fun build(configure: Action<BuildDetails>) {
buildDetails = (buildDetails ?: BuildDetails()).apply(configure::execute)
}

internal var imageArtifactDetails: ArtifactDetails? = null

/**
Expand All @@ -70,6 +79,33 @@ open class BufExtension {
}
}

enum class ImageFormat(
internal val formatName: String,
) {
BINPB("binpb"),
BIN("bin"),
JSON("json"),
TXTPB("txtpb"),
}

enum class CompressionFormat(
internal val ext: String,
) {
GZ("gz"),
ZST("zst"),
}

class BuildDetails(
/**
* The format of the built image.
*/
var imageFormat: ImageFormat = ImageFormat.JSON,
/**
* The compression, if any, of the built image.
*/
var compressionFormat: CompressionFormat? = null,
)

class ArtifactDetails(
var groupId: String? = null,
var artifactId: String? = null,
Expand Down
13 changes: 10 additions & 3 deletions src/main/kotlin/build/buf/gradle/BuildConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin.BUILD_GROUP
import java.io.File

const val BUF_BUILD_TASK_NAME = "bufBuild"
const val BUF_BUILD_PUBLICATION_FILE_NAME = "image.json"
private const val BUF_BUILD_PUBLICATION_FILE_BASE_NAME = "image"
const val BUF_IMAGE_PUBLICATION_NAME = "bufImagePublication"

internal fun Project.configureBuild() {
Expand Down Expand Up @@ -52,13 +52,20 @@ internal fun Project.configureImagePublication(artifactDetails: ArtifactDetails)

artifact(bufBuildPublicationFile) {
builtBy(tasks.named(BUF_BUILD_TASK_NAME))
extension = bufBuildPublicationFileExtension
}
}
}
}

internal val Project.bufBuildPublicationFile
get() = File(bufbuildDir, BUF_BUILD_PUBLICATION_FILE_NAME)
private val Project.bufBuildPublicationFileExtension
get() =
(getExtension().buildDetails ?: BuildDetails()).let { deets ->
deets.imageFormat.formatName + deets.compressionFormat?.let { ".${it.ext}" }.orEmpty()
}

private val Project.bufBuildPublicationFile
get() = File(bufbuildDir, "$BUF_BUILD_PUBLICATION_FILE_BASE_NAME.$bufBuildPublicationFileExtension")

internal val Task.bufBuildPublicationFile
get() = project.bufBuildPublicationFile
14 changes: 14 additions & 0 deletions src/test/kotlin/build/buf/gradle/AbstractBreakingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@

package build.buf.gradle

import build.buf.gradle.ImageGenerationSupport.replaceBuildDetails
import com.google.common.truth.Truth.assertThat
import org.gradle.testkit.runner.TaskOutcome.FAILED
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.nio.file.Path

abstract class AbstractBreakingTest : AbstractBufIntegrationTest() {
Expand All @@ -32,6 +35,17 @@ abstract class AbstractBreakingTest : AbstractBufIntegrationTest() {
checkBreaking()
}

@ParameterizedTest
@MethodSource("build.buf.gradle.ImageGenerationSupport#publicationFileExtensionTestCase")
fun `breaking schema with specified publication file extension`(
format: String,
compression: String?,
) {
replaceBuildDetails(format, compression)
publishRunner().build()
checkBreaking()
}

@Test
fun `normally breaking schema with an ignore`() {
publishRunner().build()
Expand Down
24 changes: 19 additions & 5 deletions src/test/kotlin/build/buf/gradle/AbstractBuildTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@

package build.buf.gradle

import build.buf.gradle.ImageGenerationSupport.replaceBuildDetails
import com.google.common.truth.Truth.assertThat
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.io.File
import java.nio.file.Paths

abstract class AbstractBuildTest : AbstractBufIntegrationTest() {
@Test
fun `build image with explicit artifact details`() {
assertImageGeneration()
assertImageGeneration("image.json")
}

@Test
fun `build image with inferred artifact details`() {
assertImageGeneration()
assertImageGeneration("image.json")
}

@Test
Expand All @@ -38,6 +41,17 @@ abstract class AbstractBuildTest : AbstractBufIntegrationTest() {
assertThat(result.output).contains("found 0")
}

@ParameterizedTest
@MethodSource("build.buf.gradle.ImageGenerationSupport#publicationFileExtensionTestCase")
fun `build image with specified publication file extension`(
format: String,
compression: String?,
) {
replaceBuildDetails(format, compression)
val extension = format + (compression?.let { ".$compression" } ?: "")
assertImageGeneration("image.$extension")
}

@Test
fun `build image with two publications should fail`() {
val result = buildRunner().buildAndFail()
Expand All @@ -47,7 +61,7 @@ abstract class AbstractBuildTest : AbstractBufIntegrationTest() {

@Test
fun `build image with two publications should succeed if details are provided explicitly`() {
assertImageGeneration()
assertImageGeneration("image.json")
}

@Test
Expand All @@ -60,9 +74,9 @@ abstract class AbstractBuildTest : AbstractBufIntegrationTest() {
)
}

private fun assertImageGeneration() {
private fun assertImageGeneration(publicationFileName: String) {
assertThat(buildRunner().build().task(":$BUF_BUILD_TASK_NAME")?.outcome).isEqualTo(SUCCESS)
val image = Paths.get(projectDir.path, "build", "bufbuild", "image.json").toFile().readText()
val image = Paths.get(projectDir.path, "build", "bufbuild", publicationFileName).toFile().readText()
assertThat(image).isNotEmpty()
}

Expand Down
31 changes: 31 additions & 0 deletions src/test/kotlin/build/buf/gradle/ImageGenerationSupport.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package build.buf.gradle

import com.google.common.collect.Lists
import org.junit.jupiter.params.provider.Arguments

private val NULL_SENTINEL = Any()

object ImageGenerationSupport {
@JvmStatic
fun publicationFileExtensionTestCase() =
Lists.cartesianProduct(
ImageFormat.values().map { it.formatName },
CompressionFormat.values().map { it.ext } + NULL_SENTINEL,
).map { imageAndCompression ->
Arguments.of(imageAndCompression[0], imageAndCompression[1].takeIf { it != NULL_SENTINEL })
}

fun AbstractBufIntegrationTest.replaceBuildDetails(
format: String,
compression: String?,
) {
buildFile.replace(
"imageFormat = REPLACEME",
"imageFormat = '$format'",
)
buildFile.replace(
"compressionFormat = REPLACEME",
"compressionFormat = ${compression?.let { "'$it'" }}",
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package buf.test.v1;

message BasicMessage {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
plugins {
id 'java'
id 'build.buf'
id 'maven-publish'
}

repositories {
mavenCentral()
maven { url 'build/repos/test' }
}

publishing {
repositories {
maven { url 'build/repos/test' }
}
}

buf {
publishSchema = true
//previousVersion = '2319'

build {
imageFormat = REPLACEME
compressionFormat = REPLACEME
}

imageArtifact {
groupId = 'foo'
artifactId = 'bar'
version = '2319'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
plugins {
id 'java'
id 'com.google.protobuf' version "$protobufGradleVersion"
id 'build.buf'
id 'maven-publish'
}

repositories {
mavenCentral()
maven { url 'build/repos/test' }
}

protobuf {
protoc {
artifact = "com.google.protobuf:protoc:$protobufVersion"
}
}

compileJava.enabled = false

publishing {
repositories {
maven { url 'build/repos/test' }
}
}

buf {
publishSchema = true
//previousVersion = '2319'

build {
imageFormat = REPLACEME
compressionFormat = REPLACEME
}

imageArtifact {
groupId = 'foo'
artifactId = 'bar'
version = '2319'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package buf.test.v1;

message BasicMessage {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
version: v1
directories:
- workspace
Loading

0 comments on commit 8da0e3a

Please sign in to comment.