Skip to content

Commit bfd0cd6

Browse files
committed
feat: remove .sigstore.asc signatures if added by signing plugin
By default, the plugin will remove .sigstore.asc. Project property dev.sigstore.sign.remove.sigstore.asc=false would keep .sigstore.asc files if they are needed Closes #604 Signed-off-by: Vladimir Sitnikov <[email protected]>
1 parent 38e59f4 commit bfd0cd6

File tree

5 files changed

+247
-12
lines changed

5 files changed

+247
-12
lines changed

sigstore-gradle/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ Automatically signs all Maven publications in Sigstore.
135135
Provides `SigstoreSignFilesTask` task for signing files in Sigstore.
136136
The plugin adds no tasks by default.
137137

138+
Properties:
139+
* `dev.sigstore.sign.remove.sigstore.asc` (since 0.6.0, default: `true`). Removes `.sigstore.asc` files from the publication.
140+
Sonatype OSSRH supports publishing `.sigstore` signatures, and it does not require `.sigstore.asc` files, so
141+
`dev.sigstore.sign` plugin removes them by default. If you need to sign all the files, set this property to `false`.
142+
138143
Extensions:
139144
* `sigstoreSign`: `dev.sigstore.sign.SigstoreSignExtension`
140145

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/main/kotlin/dev/sigstore/sign/SigstoreSignExtension.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import org.gradle.kotlin.dsl.create
3030
import org.gradle.kotlin.dsl.named
3131
import org.gradle.kotlin.dsl.register
3232
import org.gradle.kotlin.dsl.the
33+
import org.gradle.kotlin.dsl.withType
34+
import org.gradle.plugins.signing.Sign
3335
import kotlin.collections.set
3436

3537
abstract class SigstoreSignExtension(private val project: Project) {
@@ -78,6 +80,11 @@ abstract class SigstoreSignExtension(private val project: Project) {
7880
this.signatureDirectory.set(signatureDirectory)
7981
}
8082

83+
val removeSigstoreAsc =
84+
project.findProperty("dev.sigstore.sign.remove.sigstore.asc")?.toString()?.toBoolean() != false
85+
86+
val publicationName = publication.name
87+
8188
val artifacts = mutableMapOf<PublicationArtifact, T>()
8289
publication.allPublishableArtifacts {
8390
val publishableArtifact = this
@@ -92,6 +99,20 @@ abstract class SigstoreSignExtension(private val project: Project) {
9299
publishableArtifact,
93100
DefaultDerivedArtifactFile(project.tasks.named<DefaultTask>(signTask.name), signatureLocation)
94101
).apply { builtBy(signTask) }
102+
// Gradle's signing plugin reacts on adding artifacts, and it might add .asc signature
103+
// So we need to remove .sigstore.asc as it is unwanted in most of the cases
104+
if (removeSigstoreAsc) {
105+
project.tasks.withType<Sign>()
106+
.matching { it.name.contains(publicationName, ignoreCase = true) }
107+
.configureEach {
108+
// Remove .sigstore.asc signature.
109+
// Unfortunately, it will scan all the signatures every time,
110+
// however, it seems to be the only way to do it since the artifacts can be added
111+
// within afterEvaluate block, so we can't use afterEvaluate
112+
// to "remove all .sigstore.asc" at once
113+
signatures.removeIf { it.name.endsWith(".sigstore.asc") }
114+
}
115+
}
95116
}
96117
}
97118
publication.whenPublishableArtifactRemoved {
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright 2022 The Sigstore Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
package dev.sigstore.gradle
18+
19+
import dev.sigstore.testkit.BaseGradleTest
20+
import dev.sigstore.testkit.TestedGradle
21+
import dev.sigstore.testkit.TestedSigstoreJava
22+
import dev.sigstore.testkit.annotations.EnabledIfOidcExists
23+
import org.assertj.core.api.Assertions.assertThat
24+
import org.assertj.core.api.SoftAssertions
25+
import org.gradle.util.GradleVersion
26+
import org.junit.jupiter.params.ParameterizedTest
27+
import org.junit.jupiter.params.provider.Arguments
28+
import org.junit.jupiter.params.provider.Arguments.arguments
29+
import org.junit.jupiter.params.provider.MethodSource
30+
31+
@EnabledIfOidcExists
32+
class RemoveSigstoreAscTest : BaseGradleTest() {
33+
companion object {
34+
@JvmStatic
35+
fun signingSupportedGradleAndSigstoreJavaVersions(): Iterable<Arguments> =
36+
if (isCI) {
37+
gradleAndSigstoreJavaVersions()
38+
} else {
39+
// Find the first version that supports configuration cache for Gradle's signing plugin (8.1+)
40+
listOf(
41+
arguments(
42+
TestedGradle(
43+
// Signing plugin supports configuration cache since 8.1
44+
gradleVersions().first { it >= GradleVersion.version("8.1") },
45+
ConfigurationCache.ON
46+
),
47+
SIGSTORE_JAVA_CURRENT_VERSION
48+
)
49+
)
50+
}
51+
52+
@JvmStatic
53+
fun oneSigningSupportedGradleAndSigstoreJavaVersions(): Iterable<Arguments> =
54+
signingSupportedGradleAndSigstoreJavaVersions().take(1)
55+
}
56+
57+
@ParameterizedTest
58+
@MethodSource("signingSupportedGradleAndSigstoreJavaVersions")
59+
fun `basic configuration avoids signing sigstore with pgp`(gradle: TestedGradle, sigstoreJava: TestedSigstoreJava) {
60+
prepareBuildScripts(gradle, sigstoreJava)
61+
62+
prepare(gradle.version, "publishAllPublicationsToTmpRepository", "-s")
63+
.build()
64+
65+
assertSoftly {
66+
assertSignatures("sigstore-test-1.0.pom")
67+
assertSignatures("sigstore-test-1.0-sources.jar")
68+
assertSignatures("sigstore-test-1.0.module")
69+
assertSignatures("sigstore-test-1.0.pom")
70+
}
71+
72+
if (gradle.configurationCache == ConfigurationCache.ON) {
73+
val result = prepare(gradle.version, "publishAllPublicationsToTmpRepository", "-s")
74+
.build()
75+
76+
assertThat(result.output)
77+
.contains(
78+
"Configuration cache entry reused",
79+
"7 actionable tasks: 4 executed, 3 up-to-date",
80+
)
81+
}
82+
}
83+
84+
@ParameterizedTest
85+
@MethodSource("oneSigningSupportedGradleAndSigstoreJavaVersions")
86+
fun `crossign sigstore with pgp`(gradle: TestedGradle, sigstoreJava: TestedSigstoreJava) {
87+
prepareBuildScripts(gradle, sigstoreJava)
88+
projectDir.resolve("gradle.properties").toFile().appendText(
89+
"""
90+
91+
# By default, dev.sigstore.sign asks Gradle to avoid signing .sigstore as .sigstore.asc
92+
# This is an opt-out hatch for those who need .sigstore.asc
93+
dev.sigstore.sign.remove.sigstore.asc=false
94+
""".trimIndent()
95+
)
96+
prepare(gradle.version, "publishAllPublicationsToTmpRepository", "-s")
97+
.build()
98+
assertSoftly {
99+
assertSignatures("sigstore-test-1.0.pom", expectSigstoreAsc = true)
100+
assertSignatures("sigstore-test-1.0-sources.jar", expectSigstoreAsc = true)
101+
assertSignatures("sigstore-test-1.0.module", expectSigstoreAsc = true)
102+
assertSignatures("sigstore-test-1.0.pom", expectSigstoreAsc = true)
103+
}
104+
}
105+
106+
private fun prepareBuildScripts(gradle: TestedGradle, sigstoreJava: TestedSigstoreJava) {
107+
writeBuildGradle(
108+
"""
109+
plugins {
110+
id("java")
111+
id("signing")
112+
id("maven-publish")
113+
id("dev.sigstore.sign")
114+
}
115+
${declareRepositoryAndDependency(sigstoreJava)}
116+
117+
group = "dev.sigstore.test"
118+
java {
119+
withSourcesJar()
120+
}
121+
publishing {
122+
publications {
123+
maven(MavenPublication) {
124+
groupId = 'dev.sigstore.test'
125+
artifactId = 'sigstore-test'
126+
version = '1.0'
127+
from components.java
128+
}
129+
}
130+
repositories {
131+
maven {
132+
name = "tmp"
133+
url = layout.buildDirectory.dir("tmp-repo")
134+
}
135+
}
136+
}
137+
signing {
138+
useInMemoryPgpKeys(
139+
'''$testOnlySigningKey''',
140+
"testforsigstorejava"
141+
)
142+
sign(publishing.publications.withType(MavenPublication))
143+
}
144+
""".trimIndent()
145+
)
146+
writeSettingsGradle(
147+
"""
148+
rootProject.name = 'sigstore-test'
149+
""".trimIndent()
150+
)
151+
if (gradle.version >= GradleVersion.version("8.1")) {
152+
enableConfigurationCache(gradle)
153+
}
154+
}
155+
156+
private fun SoftAssertions.assertSignatures(name: String, expectSigstoreAsc: Boolean = false) {
157+
assertThat(projectDir.resolve("build/tmp-repo/dev/sigstore/test/sigstore-test/1.0/$name.sigstore"))
158+
.describedAs("$name should be signed with Sigstore")
159+
.content()
160+
.basicSigstoreStructure()
161+
assertThat(projectDir.resolve("build/tmp-repo/dev/sigstore/test/sigstore-test/1.0/$name.asc"))
162+
.describedAs("$name should be signed with PGP")
163+
.isNotEmptyFile()
164+
assertThat(projectDir.resolve("build/tmp-repo/dev/sigstore/test/sigstore-test/1.0/$name.asc.sigstore"))
165+
.describedAs("$name.asc should NOT be signed with Sigstore")
166+
.doesNotExist()
167+
assertThat(projectDir.resolve("build/tmp-repo/dev/sigstore/test/sigstore-test/1.0/$name.sigstore.asc"))
168+
.apply {
169+
if (expectSigstoreAsc) {
170+
describedAs("$name.sigstore should be signed with PGP")
171+
exists()
172+
} else {
173+
// We don't want to sign .sigstore files with PGP
174+
describedAs("$name.sigstore should NOT be signed with PGP")
175+
doesNotExist()
176+
}
177+
}
178+
}
179+
180+
private val testOnlySigningKey = """
181+
-----BEGIN PGP PRIVATE KEY BLOCK-----
182+
183+
lIYEZaDRyxYJKwYBBAHaRw8BAQdAjMi3g07livoPo+se6/+wF7LRv2DDJ6UKVBrp
184+
9rugpwj+BwMCDZlNm7zWHTP6ny1jqI5sdTFaEkHRjFhm63Il9qeF7QcSibgAnBO5
185+
YK0E4vp8MUQxSAwoOV80mO46a2Ci9hA281lXH6fFTP3qyERXl2/ilrQvVGVzdCBL
186+
ZXkgZm9yIFNpZ3N0b3JlIEphdmEgPHNpZ3N0b3JlQGdpdGh1Yi5pbz6IkwQTFgoA
187+
OxYhBNejX8GGaAn2Jspav54UgcovliH1BQJloNHLAhsDBQsJCAcCAiICBhUKCQgL
188+
AgQWAgMBAh4HAheAAAoJEJ4UgcovliH1YDUBAPE1yBo7i4YgHuHKIGLqkOJqEKE5
189+
Jbw8ffyZO6tqud2qAP49liajq/HkdEXgUdA6DySpzLYFtd+F6UlpTQE0TeaLAA==
190+
=6fgq
191+
-----END PGP PRIVATE KEY BLOCK-----
192+
""".trimIndent()
193+
}

sigstore-testkit/src/main/kotlin/dev/sigstore/testkit/BaseGradleTest.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package dev.sigstore.testkit
1818

1919
import org.assertj.core.api.AbstractCharSequenceAssert
20+
import org.assertj.core.api.SoftAssertions
2021
import org.gradle.testkit.runner.GradleRunner
2122
import org.gradle.testkit.runner.internal.DefaultGradleRunner
2223
import org.gradle.util.GradleVersion
@@ -46,23 +47,33 @@ open class BaseGradleTest {
4647
System.getProperty("sigstore.test.current.version")
4748
)
4849

50+
@JvmStatic
51+
fun gradleVersions() = listOf(
52+
// Gradle 7.2 fails with "No service of type ObjectFactory available in default services"
53+
// So we require Gradle 7.3+
54+
"7.3",
55+
"7.5.1",
56+
"8.1",
57+
"8.5",
58+
).map { GradleVersion.version(it) }
59+
4960
@JvmStatic
5061
fun gradleVersionAndSettings(): Iterable<Arguments> {
5162
if (!isCI) {
52-
// Make the test faster, and skip extra tests with the configuration cache to reduce OIDC flows
53-
// Gradle 7.2 fails with "No service of type ObjectFactory available in default services"
54-
return listOf(arguments(TestedGradle("7.3", ConfigurationCache.ON)))
63+
// Execute a single combination only when running locally
64+
return listOf(arguments(TestedGradle(gradleVersions().first(), ConfigurationCache.ON)))
5565
}
56-
return mutableListOf<Arguments>().apply {
57-
add(arguments(TestedGradle("7.3", ConfigurationCache.ON)))
58-
add(arguments(TestedGradle("7.5.1", ConfigurationCache.ON)))
59-
add(arguments(TestedGradle("7.5.1", ConfigurationCache.OFF)))
66+
return buildList {
67+
addAll(
68+
gradleVersions().map { arguments(TestedGradle(it, ConfigurationCache.ON)) }
69+
)
70+
add(arguments(TestedGradle(gradleVersions().first(), ConfigurationCache.OFF)))
6071
}
6172
}
6273

6374
@JvmStatic
6475
fun sigstoreJavaVersions(): Iterable<Arguments> {
65-
return mutableListOf<Arguments>().apply {
76+
return buildList {
6677
add(arguments(SIGSTORE_JAVA_CURRENT_VERSION))
6778
// For now, we test the plugins only with locally-built sigstore-java version
6879
if (isCI && false) {
@@ -150,9 +161,9 @@ open class BaseGradleTest {
150161
)
151162
}
152163

153-
protected fun prepare(gradleVersion: String, vararg arguments: String) =
164+
protected fun prepare(gradleVersion: GradleVersion, vararg arguments: String) =
154165
gradleRunner
155-
.withGradleVersion(gradleVersion)
166+
.withGradleVersion(gradleVersion.version)
156167
.withProjectDir(projectDir.toFile())
157168
.apply {
158169
this as DefaultGradleRunner
@@ -176,7 +187,7 @@ open class BaseGradleTest {
176187
if (gradle.configurationCache != ConfigurationCache.ON) {
177188
return
178189
}
179-
if (GradleVersion.version(gradle.version) < GradleVersion.version("7.0")) {
190+
if (gradle.version < GradleVersion.version("7.0")) {
180191
Assertions.fail<Unit>("Gradle version $gradle does not support configuration cache")
181192
}
182193
// Gradle 6.5 expects values ON, OFF, WARN, so we add the option for 7.0 only
@@ -189,6 +200,9 @@ open class BaseGradleTest {
189200
)
190201
}
191202

203+
protected fun assertSoftly(body: SoftAssertions.() -> Unit) =
204+
SoftAssertions.assertSoftly(body)
205+
192206
protected fun <SELF : AbstractCharSequenceAssert<SELF, ACTUAL>, ACTUAL : CharSequence> AbstractCharSequenceAssert<SELF, ACTUAL>.basicSigstoreStructure() =
193207
contains(
194208
""""mediaType": "application/vnd.dev.sigstore.bundle+json;version\u003d0.2"""",

sigstore-testkit/src/main/kotlin/dev/sigstore/testkit/TestedGradle.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
*/
1717
package dev.sigstore.testkit
1818

19+
import org.gradle.util.GradleVersion
20+
1921
/**
2022
* Lists Gradle versions and its configuration for backward compatibility testing of Sigstore Gradle plugin.
2123
*/
2224
data class TestedGradle(
23-
val version: String,
25+
val version: GradleVersion,
2426
val configurationCache: BaseGradleTest.ConfigurationCache
2527
)

0 commit comments

Comments
 (0)