garydgregory commented on code in PR #422: URL: https://github.com/apache/commons-release-plugin/pull/422#discussion_r3099531006
########## src/test/java/org/apache/commons/release/plugin/internal/BuildDefinitionsTest.java: ########## @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Properties; +import java.util.stream.Stream; + +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionRequest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class BuildDefinitionsTest { + + static Stream<Arguments> commandLineArguments() { + return Stream.of( + Arguments.of("empty", emptyList(), emptyList(), new Properties(), ""), + Arguments.of("single goal", singletonList("verify"), emptyList(), new Properties(), "verify"), + Arguments.of("multiple goals", asList("clean", "verify"), emptyList(), new Properties(), "clean verify"), + Arguments.of("single profile", singletonList("verify"), singletonList("release"), new Properties(), "verify -Prelease"), + Arguments.of("multiple profiles", singletonList("verify"), asList("release", "sign"), new Properties(), "verify -Prelease,sign"), + Arguments.of("user property", singletonList("verify"), emptyList(), props("foo", "bar"), "verify -Dfoo=bar"), + Arguments.of("goals, profile and property", singletonList("verify"), singletonList("release"), props("foo", "bar"), + "verify -Prelease -Dfoo=bar") + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("commandLineArguments") + void commandLineTest(final String description, final List<String> goals, final List<String> profiles, + final Properties userProperties, final String expected) { + MavenExecutionRequest request = new DefaultMavenExecutionRequest(); + request.setGoals(goals); + request.setActiveProfiles(profiles); + request.setUserProperties(userProperties); + assertThat(BuildDefinitions.commandLine(request)).isEqualTo(expected); Review Comment: Please say simple things in a simple way: JUnit's `assertEquals()` is simple, obvious, and is less code. In this case, it's 1 JUnit vs. 2 AssertJ method calls. AssertJ makes tests harder to maintain IMO. I pruned AssertJ, Hamcrest and other obtuse libraries out of Commons, for the most part. ########## src/main/java/org/apache/commons/release/plugin/slsa/v1_2/RunDetails.java: ########## @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.slsa.v1_2; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Details about the build invocation: the builder identity, execution metadata, and any byproduct artifacts. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RunDetails { Review Comment: The indentation in this file doesn't match the other files. ########## src/main/java/org/apache/commons/release/plugin/internal/ArtifactUtils.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; + +/** + * Utilities to convert {@link Artifact} from and to other types. + */ +public final class ArtifactUtils { + + /** No instances. */ + private ArtifactUtils() { + // prevent instantiation + } + + /** + * Returns the conventional filename for the given artifact. + * + * @param artifact A Maven artifact. + * @return A filename. + */ + public static String getFileName(Artifact artifact) { + return getFileName(artifact, artifact.getArtifactHandler().getExtension()); + } + + /** + * Returns the filename for the given artifact with a changed extension. + * + * @param artifact A Maven artifact. + * @param extension The file name extension. + * @return A filename. + */ + public static String getFileName(Artifact artifact, String extension) { + StringBuilder fileName = new StringBuilder(); + fileName.append(artifact.getArtifactId()).append("-").append(artifact.getVersion()); + if (artifact.getClassifier() != null) { + fileName.append("-").append(artifact.getClassifier()); + } + fileName.append(".").append(extension); + return fileName.toString(); + } + + /** + * Returns the Package URL corresponding to this artifact. + * + * @param artifact A maven artifact. + * @return A PURL for the given artifact. + */ + public static String getPackageUrl(Artifact artifact) { Review Comment: Doesn't Maven have an API for this? ########## src/main/java/org/apache/commons/release/plugin/slsa/v1_2/RunDetails.java: ########## @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.slsa.v1_2; + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Details about the build invocation: the builder identity, execution metadata, and any byproduct artifacts. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RunDetails { + + /** Entity that executed the build. */ + @JsonProperty("builder") + private Builder builder; + + /** Metadata about the build invocation. */ + @JsonProperty("metadata") + private BuildMetadata metadata; + + /** Artifacts produced as a side effect of the build. */ + @JsonProperty("byproducts") + private List<ResourceDescriptor> byproducts; + + /** Creates a new RunDetails instance. */ + public RunDetails() { + } + + /** + * Creates a new RunDetails with the given builder and metadata. + * + * @param builder entity that executed the build + * @param metadata metadata about the build invocation + */ + public RunDetails(Builder builder, BuildMetadata metadata) { + this.builder = builder; + this.metadata = metadata; + } + + /** + * Gets the builder that executed the invocation. + * + * <p>Trusted to have correctly performed the operation and populated this provenance.</p> + * + * @return the builder, or {@code null} if not set + */ + public Builder getBuilder() { Review Comment: Please sort these methods in AB order. The setters could/should return `this`. ########## src/site/markdown/slsa/v0.1.0.md: ########## @@ -0,0 +1,131 @@ +<!-- SPDX-License-Identifier: Apache-2.0 --> + +# Build Type: Apache Commons Maven Release + +```jsonc +"buildType": "https://commons.apache.org/proper/commons-release-plugin/slsa/v0.1.0" +``` + +This is a [SLSA Build Provenance](https://slsa.dev/spec/v1.2/build-provenance) build type +that describes releases produced by Apache Commons PMC release managers running Maven on their own equipment. + +## Build definition + +Artifacts are generated by a single Maven execution, typically of the form: + +```shell +mvn -Prelease deploy +``` + +The provenance is recorded by the `build-attestation` goal of the +`commons-release-plugin`, which runs in the `verify` phase. + +### External parameters + +External parameters capture everything supplied by the release manager at invocation time. +All parameters are captured from the running Maven session. + +| Parameter | Type | Description | +|-------------------------|----------|-------------------------------------------------------------------------| +| `maven.goals` | string[] | The list of Maven goals passed on the command line (e.g. `["deploy"]`). | +| `maven.profiles` | string[] | The list of active profiles passed via `-P` (e.g. `["release"]`). | +| `maven.user.properties` | object | User-defined properties passed via `-D` flags. | +| `maven.cmdline` | string | The reconstructed Maven command line. | +| `jvm.args` | string[] | JVM input arguments. | +| `env` | object | A filtered subset of environment variables: `TZ` and locale variables. | + +### Internal parameters + +No internal parameters are recorded for this build type. Review Comment: Do we need to say all the things we don't do? Maybe remove this section. ########## src/main/java/org/apache/commons/release/plugin/mojos/BuildAttestationMojo.java: ########## @@ -0,0 +1,543 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.mojos; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.commons.release.plugin.internal.ArtifactUtils; +import org.apache.commons.release.plugin.internal.BuildDefinitions; +import org.apache.commons.release.plugin.internal.DsseUtils; +import org.apache.commons.release.plugin.internal.GitUtils; +import org.apache.commons.release.plugin.slsa.v1_2.BuildDefinition; +import org.apache.commons.release.plugin.slsa.v1_2.BuildMetadata; +import org.apache.commons.release.plugin.slsa.v1_2.Builder; +import org.apache.commons.release.plugin.slsa.v1_2.DsseEnvelope; +import org.apache.commons.release.plugin.slsa.v1_2.Provenance; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.commons.release.plugin.slsa.v1_2.RunDetails; +import org.apache.commons.release.plugin.slsa.v1_2.Signature; +import org.apache.commons.release.plugin.slsa.v1_2.Statement; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.plugins.gpg.AbstractGpgSigner; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.apache.maven.rtinfo.RuntimeInformation; +import org.apache.maven.scm.CommandParameters; +import org.apache.maven.scm.ScmException; +import org.apache.maven.scm.ScmFileSet; +import org.apache.maven.scm.command.info.InfoItem; +import org.apache.maven.scm.command.info.InfoScmResult; +import org.apache.maven.scm.manager.ScmManager; +import org.apache.maven.scm.repository.ScmRepository; + +/** + * This plugin generates an in-toto attestation for all the artifacts. + */ +@Mojo(name = "build-attestation", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) +public class BuildAttestationMojo extends AbstractMojo { + + /** + * The file extension for in-toto attestation files. Review Comment: No latin please. ########## src/main/java/org/apache/commons/release/plugin/internal/package-info.java: ########## @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ + +/** + * Internal utilities for the commons-release-plugin. Review Comment: I think we know this is "for the commons-release-plugin" 😉 Maybe say something more helpful to maintainers 😉 ########## src/main/java/org/apache/commons/release/plugin/mojos/BuildAttestationMojo.java: ########## @@ -0,0 +1,543 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.mojos; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.commons.release.plugin.internal.ArtifactUtils; +import org.apache.commons.release.plugin.internal.BuildDefinitions; +import org.apache.commons.release.plugin.internal.DsseUtils; +import org.apache.commons.release.plugin.internal.GitUtils; +import org.apache.commons.release.plugin.slsa.v1_2.BuildDefinition; +import org.apache.commons.release.plugin.slsa.v1_2.BuildMetadata; +import org.apache.commons.release.plugin.slsa.v1_2.Builder; +import org.apache.commons.release.plugin.slsa.v1_2.DsseEnvelope; +import org.apache.commons.release.plugin.slsa.v1_2.Provenance; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.commons.release.plugin.slsa.v1_2.RunDetails; +import org.apache.commons.release.plugin.slsa.v1_2.Signature; +import org.apache.commons.release.plugin.slsa.v1_2.Statement; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.plugins.gpg.AbstractGpgSigner; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.apache.maven.rtinfo.RuntimeInformation; +import org.apache.maven.scm.CommandParameters; +import org.apache.maven.scm.ScmException; +import org.apache.maven.scm.ScmFileSet; +import org.apache.maven.scm.command.info.InfoItem; +import org.apache.maven.scm.command.info.InfoScmResult; +import org.apache.maven.scm.manager.ScmManager; +import org.apache.maven.scm.repository.ScmRepository; + +/** + * This plugin generates an in-toto attestation for all the artifacts. + */ +@Mojo(name = "build-attestation", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) +public class BuildAttestationMojo extends AbstractMojo { + + /** + * The file extension for in-toto attestation files. + */ + private static final String ATTESTATION_EXTENSION = "intoto.jsonl"; + + /** + * Shared Jackson object mapper for serializing attestation statements. Review Comment: This is a JSON format, right? We should make it a bit clearer in this comment and docs. ########## src/main/java/org/apache/commons/release/plugin/internal/ArtifactUtils.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; + +/** + * Utilities to convert {@link Artifact} from and to other types. + */ +public final class ArtifactUtils { + + /** No instances. */ + private ArtifactUtils() { + // prevent instantiation + } + + /** + * Returns the conventional filename for the given artifact. Review Comment: A getter method "Gets...", a setter methods "Sets..."; that the convention I've been using in Commons for a while now. What does "conventional" mean here? ########## src/main/java/org/apache/commons/release/plugin/internal/BuildDefinitions.java: ########## @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; + +/** + * Factory methods for the SLSA {@code BuildDefinition} fields: JVM, Maven descriptors and external build parameters. + */ +public final class BuildDefinitions { + + /** + * No instances. + */ + private BuildDefinitions() { + } + + /** + * Creates a {@link ResourceDescriptor} for the JDK used during the build. + * + * @param javaHome path to the JDK home directory (value of the {@code java.home} system property) + * @return a descriptor with digest and annotations populated from system properties + * @throws IOException if hashing the JDK directory fails + */ + public static ResourceDescriptor jvm(Path javaHome) throws IOException { + ResourceDescriptor descriptor = new ResourceDescriptor(); + descriptor.setName("JDK"); + Map<String, String> digest = new HashMap<>(); + digest.put("gitTree", GitUtils.gitTree(javaHome)); + descriptor.setDigest(digest); + String[] propertyNames = { + "java.version", "java.version.date", + "java.vendor", "java.vendor.url", "java.vendor.version", + "java.home", + "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", + "java.vm.version", "java.vm.vendor", "java.vm.name", + "java.specification.version", "java.specification.maintenance.version", + "java.specification.vendor", "java.specification.name", + }; + Map<String, Object> annotations = new HashMap<>(); + for (String prop : propertyNames) { + annotations.put(prop.substring("java.".length()), System.getProperty(prop)); + } + descriptor.setAnnotations(annotations); + return descriptor; + } + + /** + * Creates a {@link ResourceDescriptor} for the Maven installation used during the build. + * + * <p>{@code build.properties} resides in a JAR inside {@code ${maven.home}/lib/}, which is loaded by Maven's Core Classloader. + * Plugin code runs in an isolated Plugin Classloader, which does see that resources. Therefore, we need to pass the classloader from a class from + * Maven Core, such as {@link org.apache.maven.rtinfo.RuntimeInformation}.</p> + * + * @param version Maven version string + * @param mavenHome path to the Maven home directory + * @param coreClassLoader a classloader from Maven's Core Classloader realm, used to load core resources + * @return a descriptor for the Maven installation + * @throws IOException if hashing the Maven home directory fails + */ + public static ResourceDescriptor maven(String version, Path mavenHome, ClassLoader coreClassLoader) throws IOException { + ResourceDescriptor descriptor = new ResourceDescriptor(); + descriptor.setName("Maven"); + descriptor.setUri("pkg:maven/org.apache.maven/apache-maven@" + version); + Map<String, String> digest = new HashMap<>(); + digest.put("gitTree", GitUtils.gitTree(mavenHome)); + descriptor.setDigest(digest); + Properties buildProps = new Properties(); + try (InputStream in = coreClassLoader.getResourceAsStream("org/apache/maven/messages/build.properties")) { + if (in != null) { + buildProps.load(in); + } + } + if (!buildProps.isEmpty()) { + Map<String, Object> annotations = new HashMap<>(); + buildProps.forEach((key, value) -> annotations.put((String) key, value)); + descriptor.setAnnotations(annotations); + } + return descriptor; + } + + /** + * Returns a map of external build parameters captured from the current JVM and Maven session. + * + * @param session the current Maven session + * @return a map of parameter names to values + */ + public static Map<String, Object> externalParameters(final MavenSession session) { + Map<String, Object> params = new HashMap<>(); + params.put("jvm.args", ManagementFactory.getRuntimeMXBean().getInputArguments()); + MavenExecutionRequest request = session.getRequest(); + params.put("maven.goals", request.getGoals()); + params.put("maven.profiles", request.getActiveProfiles()); + params.put("maven.user.properties", request.getUserProperties()); + params.put("maven.cmdline", commandLine(request)); + Map<String, Object> env = new HashMap<>(); + params.put("env", env); + for (Map.Entry<String, String> entry : System.getenv().entrySet()) { + String key = entry.getKey(); + if ("TZ".equals(key) || "LANG".equals(key) || key.startsWith("LC_")) { + env.put(key, entry.getValue()); + } + } + return params; + } + + /** + * Reconstructs the Maven command line string from the given execution request. + * + * @param request the Maven execution request + * @return a string representation of the Maven command line + */ + static String commandLine(final MavenExecutionRequest request) { + StringBuilder sb = new StringBuilder(); + for (String goal : request.getGoals()) { + sb.append(goal).append(" "); + } + List<String> activeProfiles = request.getActiveProfiles(); + if (activeProfiles != null && !activeProfiles.isEmpty()) { + sb.append("-P"); + for (String profile : activeProfiles) { + sb.append(profile).append(","); + } + removeLast(sb); + sb.append(" "); + } + Properties userProperties = request.getUserProperties(); + for (String propertyName : userProperties.stringPropertyNames()) { + sb.append("-D").append(propertyName).append("=").append(userProperties.get(propertyName)).append(" "); + } + removeLast(sb); + return sb.toString(); + } + + /** + * Removes last character from a {@link StringBuilder}. + * + * @param sb The {@link StringBuilder} to use + */ + private static void removeLast(final StringBuilder sb) { Review Comment: The fact that this method exists here hints that we are not using modern APIs or reusing something like StringUtils. ########## src/test/java/org/apache/commons/release/plugin/internal/BuildDefinitionsTest.java: ########## @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Properties; +import java.util.stream.Stream; + +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionRequest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class BuildDefinitionsTest { + + static Stream<Arguments> commandLineArguments() { + return Stream.of( + Arguments.of("empty", emptyList(), emptyList(), new Properties(), ""), + Arguments.of("single goal", singletonList("verify"), emptyList(), new Properties(), "verify"), + Arguments.of("multiple goals", asList("clean", "verify"), emptyList(), new Properties(), "clean verify"), + Arguments.of("single profile", singletonList("verify"), singletonList("release"), new Properties(), "verify -Prelease"), + Arguments.of("multiple profiles", singletonList("verify"), asList("release", "sign"), new Properties(), "verify -Prelease,sign"), + Arguments.of("user property", singletonList("verify"), emptyList(), props("foo", "bar"), "verify -Dfoo=bar"), + Arguments.of("goals, profile and property", singletonList("verify"), singletonList("release"), props("foo", "bar"), + "verify -Prelease -Dfoo=bar") + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("commandLineArguments") + void commandLineTest(final String description, final List<String> goals, final List<String> profiles, + final Properties userProperties, final String expected) { + MavenExecutionRequest request = new DefaultMavenExecutionRequest(); + request.setGoals(goals); + request.setActiveProfiles(profiles); + request.setUserProperties(userProperties); + assertThat(BuildDefinitions.commandLine(request)).isEqualTo(expected); + } + + private static Properties props(final String key, final String value) { Review Comment: This is a bad method name: `toProperties()` would be better for example. ########## pom.xml: ########## @@ -26,7 +26,8 @@ </parent> <artifactId>commons-release-plugin</artifactId> <packaging>maven-plugin</packaging> - <version>1.9.3-SNAPSHOT</version> + <!-- Temporary version change to publish independent snapshot --> + <version>1.9.3.slsa-SNAPSHOT</version> Review Comment: The version should be a new minor version: `1.10.0`, which can be done later and this comment can serve as a reminder. ########## src/main/java/org/apache/commons/release/plugin/internal/ArtifactUtils.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; + +/** + * Utilities to convert {@link Artifact} from and to other types. + */ +public final class ArtifactUtils { + + /** No instances. */ + private ArtifactUtils() { + // prevent instantiation + } + + /** + * Returns the conventional filename for the given artifact. + * + * @param artifact A Maven artifact. + * @return A filename. + */ + public static String getFileName(Artifact artifact) { + return getFileName(artifact, artifact.getArtifactHandler().getExtension()); + } + + /** + * Returns the filename for the given artifact with a changed extension. + * + * @param artifact A Maven artifact. + * @param extension The file name extension. + * @return A filename. + */ + public static String getFileName(Artifact artifact, String extension) { + StringBuilder fileName = new StringBuilder(); + fileName.append(artifact.getArtifactId()).append("-").append(artifact.getVersion()); + if (artifact.getClassifier() != null) { + fileName.append("-").append(artifact.getClassifier()); + } + fileName.append(".").append(extension); + return fileName.toString(); + } + + /** + * Returns the Package URL corresponding to this artifact. + * + * @param artifact A maven artifact. + * @return A PURL for the given artifact. + */ + public static String getPackageUrl(Artifact artifact) { + StringBuilder sb = new StringBuilder(); + sb.append("pkg:maven/").append(artifact.getGroupId()).append("/").append(artifact.getArtifactId()).append("@").append(artifact.getVersion()) + .append("?"); + String classifier = artifact.getClassifier(); + if (classifier != null) { + sb.append("classifier=").append(classifier).append("&"); + } + sb.append("type=").append(artifact.getType()); + return sb.toString(); + } + + /** + * Returns a map of checksum algorithm names to hex-encoded digest values for the given artifact file. + * + * @param artifact A Maven artifact. + * @return A map of checksum algorithm names to hex-encoded digest values. + * @throws IOException If an I/O error occurs reading the artifact file. + */ + private static Map<String, String> getChecksums(Artifact artifact) throws IOException { + Map<String, String> checksums = new HashMap<>(); + DigestUtils digest = new DigestUtils(DigestUtils.getSha256Digest()); Review Comment: Is there a reason to use SHA256 instead of another (512?)? Is that algorithm part of the attestation standard? ########## src/site/markdown/slsa/v0.1.0.md: ########## @@ -0,0 +1,131 @@ +<!-- SPDX-License-Identifier: Apache-2.0 --> + +# Build Type: Apache Commons Maven Release + +```jsonc +"buildType": "https://commons.apache.org/proper/commons-release-plugin/slsa/v0.1.0" +``` + +This is a [SLSA Build Provenance](https://slsa.dev/spec/v1.2/build-provenance) build type +that describes releases produced by Apache Commons PMC release managers running Maven on their own equipment. + +## Build definition + +Artifacts are generated by a single Maven execution, typically of the form: + +```shell +mvn -Prelease deploy +``` + +The provenance is recorded by the `build-attestation` goal of the +`commons-release-plugin`, which runs in the `verify` phase. + +### External parameters + +External parameters capture everything supplied by the release manager at invocation time. +All parameters are captured from the running Maven session. + +| Parameter | Type | Description | +|-------------------------|----------|-------------------------------------------------------------------------| +| `maven.goals` | string[] | The list of Maven goals passed on the command line (e.g. `["deploy"]`). | Review Comment: I try to avoid latin abbreviations. e.g. -> for example, ########## src/site/markdown/slsa/v0.1.0.md: ########## @@ -0,0 +1,131 @@ +<!-- SPDX-License-Identifier: Apache-2.0 --> + +# Build Type: Apache Commons Maven Release + +```jsonc +"buildType": "https://commons.apache.org/proper/commons-release-plugin/slsa/v0.1.0" +``` + +This is a [SLSA Build Provenance](https://slsa.dev/spec/v1.2/build-provenance) build type +that describes releases produced by Apache Commons PMC release managers running Maven on their own equipment. Review Comment: Describe what this goal produces: a file? where? do we release it... where? In our Apache dist tree, or Maven Central, or both? Is it included as a site report? equipment -> hardware? ########## src/main/java/org/apache/commons/release/plugin/internal/BuildDefinitions.java: ########## @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; + +/** + * Factory methods for the SLSA {@code BuildDefinition} fields: JVM, Maven descriptors and external build parameters. + */ +public final class BuildDefinitions { + + /** + * No instances. + */ + private BuildDefinitions() { + } + + /** + * Creates a {@link ResourceDescriptor} for the JDK used during the build. + * + * @param javaHome path to the JDK home directory (value of the {@code java.home} system property) + * @return a descriptor with digest and annotations populated from system properties + * @throws IOException if hashing the JDK directory fails + */ + public static ResourceDescriptor jvm(Path javaHome) throws IOException { + ResourceDescriptor descriptor = new ResourceDescriptor(); + descriptor.setName("JDK"); + Map<String, String> digest = new HashMap<>(); + digest.put("gitTree", GitUtils.gitTree(javaHome)); + descriptor.setDigest(digest); + String[] propertyNames = { + "java.version", "java.version.date", + "java.vendor", "java.vendor.url", "java.vendor.version", + "java.home", + "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", + "java.vm.version", "java.vm.vendor", "java.vm.name", + "java.specification.version", "java.specification.maintenance.version", + "java.specification.vendor", "java.specification.name", + }; + Map<String, Object> annotations = new HashMap<>(); + for (String prop : propertyNames) { + annotations.put(prop.substring("java.".length()), System.getProperty(prop)); + } + descriptor.setAnnotations(annotations); + return descriptor; + } + + /** + * Creates a {@link ResourceDescriptor} for the Maven installation used during the build. + * + * <p>{@code build.properties} resides in a JAR inside {@code ${maven.home}/lib/}, which is loaded by Maven's Core Classloader. + * Plugin code runs in an isolated Plugin Classloader, which does see that resources. Therefore, we need to pass the classloader from a class from + * Maven Core, such as {@link org.apache.maven.rtinfo.RuntimeInformation}.</p> + * + * @param version Maven version string + * @param mavenHome path to the Maven home directory + * @param coreClassLoader a classloader from Maven's Core Classloader realm, used to load core resources + * @return a descriptor for the Maven installation + * @throws IOException if hashing the Maven home directory fails + */ + public static ResourceDescriptor maven(String version, Path mavenHome, ClassLoader coreClassLoader) throws IOException { + ResourceDescriptor descriptor = new ResourceDescriptor(); + descriptor.setName("Maven"); + descriptor.setUri("pkg:maven/org.apache.maven/apache-maven@" + version); + Map<String, String> digest = new HashMap<>(); + digest.put("gitTree", GitUtils.gitTree(mavenHome)); + descriptor.setDigest(digest); + Properties buildProps = new Properties(); + try (InputStream in = coreClassLoader.getResourceAsStream("org/apache/maven/messages/build.properties")) { + if (in != null) { + buildProps.load(in); + } + } + if (!buildProps.isEmpty()) { + Map<String, Object> annotations = new HashMap<>(); + buildProps.forEach((key, value) -> annotations.put((String) key, value)); + descriptor.setAnnotations(annotations); + } + return descriptor; + } + + /** + * Returns a map of external build parameters captured from the current JVM and Maven session. + * + * @param session the current Maven session + * @return a map of parameter names to values + */ + public static Map<String, Object> externalParameters(final MavenSession session) { + Map<String, Object> params = new HashMap<>(); + params.put("jvm.args", ManagementFactory.getRuntimeMXBean().getInputArguments()); + MavenExecutionRequest request = session.getRequest(); + params.put("maven.goals", request.getGoals()); + params.put("maven.profiles", request.getActiveProfiles()); + params.put("maven.user.properties", request.getUserProperties()); + params.put("maven.cmdline", commandLine(request)); + Map<String, Object> env = new HashMap<>(); + params.put("env", env); + for (Map.Entry<String, String> entry : System.getenv().entrySet()) { + String key = entry.getKey(); + if ("TZ".equals(key) || "LANG".equals(key) || key.startsWith("LC_")) { + env.put(key, entry.getValue()); + } + } + return params; + } + + /** + * Reconstructs the Maven command line string from the given execution request. + * + * @param request the Maven execution request + * @return a string representation of the Maven command line + */ + static String commandLine(final MavenExecutionRequest request) { + StringBuilder sb = new StringBuilder(); + for (String goal : request.getGoals()) { + sb.append(goal).append(" "); + } + List<String> activeProfiles = request.getActiveProfiles(); + if (activeProfiles != null && !activeProfiles.isEmpty()) { + sb.append("-P"); + for (String profile : activeProfiles) { Review Comment: Use `forEach()` or `String.join()` if we had an array? Better yet: Use Lang's `StringUtils.join()` ########## src/main/java/org/apache/commons/release/plugin/slsa/v1_2/Builder.java: ########## @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.slsa.v1_2; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Entity that executed the build and is trusted to have correctly performed the operation and populated the provenance. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +public class Builder { + + /** Identifier URI of the builder. */ + @JsonProperty("id") + private String id = "https://commons.apache.org/builds/0.1.0"; + + /** Orchestrator dependencies that may affect provenance generation. */ + @JsonProperty("builderDependencies") + private List<ResourceDescriptor> builderDependencies = new ArrayList<>(); + + /** Map of build platform component names to their versions. */ + @JsonProperty("version") + private Map<String, String> version = new HashMap<>(); + + /** Creates a new Builder instance. */ + public Builder() { + } + + /** + * Gets the identifier of the builder. + * + * @return the builder identifier URI + */ + public String getId() { + return id; + } + + /** + * Sets the identifier of the builder. + * + * @param id the builder identifier URI + */ + public void setId(String id) { Review Comment: The setters could/should return `this`. Please sort in AB order. ########## pom.xml: ########## @@ -113,7 +114,22 @@ <!-- Until Maven plugins used here don't fail the Moditect plugin --> <moditect.skip>true</moditect.skip> <japicmp.skip>true</japicmp.skip> + <!-- Dependency versions --> + <commons.jackson.version>2.21.1</commons.jackson.version> + <commons.jackson.annotations.version>2.21</commons.jackson.annotations.version> Review Comment: Use the [current Jackson version:](https://central.sonatype.com/artifact/com.fasterxml.jackson.core/jackson-databind/versions) `2.21.2`. Also, why isn't the [Jackson BOM](https://central.sonatype.com/artifact/com.fasterxml.jackson/jackson-bom) used? ########## checkstyle.xml: ########## @@ -185,7 +185,7 @@ <module name="UpperEll" /> <module name="ImportOrder"> <property name="option" value="top"/> - <property name="groups" value="java,javax,org"/> + <property name="groups" value="java,javax"/> Review Comment: This change should not be required. ########## src/site/markdown/slsa/v0.1.0.md: ########## @@ -0,0 +1,131 @@ +<!-- SPDX-License-Identifier: Apache-2.0 --> + +# Build Type: Apache Commons Maven Release + +```jsonc +"buildType": "https://commons.apache.org/proper/commons-release-plugin/slsa/v0.1.0" +``` + +This is a [SLSA Build Provenance](https://slsa.dev/spec/v1.2/build-provenance) build type +that describes releases produced by Apache Commons PMC release managers running Maven on their own equipment. + +## Build definition + +Artifacts are generated by a single Maven execution, typically of the form: + +```shell +mvn -Prelease deploy +``` + +The provenance is recorded by the `build-attestation` goal of the +`commons-release-plugin`, which runs in the `verify` phase. + +### External parameters + +External parameters capture everything supplied by the release manager at invocation time. +All parameters are captured from the running Maven session. + +| Parameter | Type | Description | +|-------------------------|----------|-------------------------------------------------------------------------| +| `maven.goals` | string[] | The list of Maven goals passed on the command line (e.g. `["deploy"]`). | +| `maven.profiles` | string[] | The list of active profiles passed via `-P` (e.g. `["release"]`). | +| `maven.user.properties` | object | User-defined properties passed via `-D` flags. | +| `maven.cmdline` | string | The reconstructed Maven command line. | +| `jvm.args` | string[] | JVM input arguments. | +| `env` | object | A filtered subset of environment variables: `TZ` and locale variables. | + +### Internal parameters + +No internal parameters are recorded for this build type. + +### Resolved dependencies + +The `resolvedDependencies` list captures all inputs that contributed to the build output. +It always contains the following entries, in order: + +#### JDK + +Represents the Java Development Kit used to run Maven (`"name": "JDK"`). +To allow verification of the JDK's integrity, a `gitTree` digest is computed over the `java.home` directory. + +The following annotations are recorded from [ +`System.getProperties()`](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/System.html#getProperties()): + +| Annotation key | System property | Description | +|-------------------------------------|------------------------------------------|--------------------------------------------------------------------------| +| `version` | `java.version` | Java Runtime Environment version. | +| `version.date` | `java.version.date` | Java Runtime Environment version date, in ISO-8601 YYYY-MM-DD format. | +| `vendor` | `java.vendor` | Java Runtime Environment vendor. | +| `vendor.url` | `java.vendor.url` | Java vendor URL. | +| `vendor.version` | `java.vendor.version` | Java vendor version _(optional)_. | +| `home` | `java.home` | Java installation directory. | +| `vm.specification.version` | `java.vm.specification.version` | Java Virtual Machine specification version. | +| `vm.specification.vendor` | `java.vm.specification.vendor` | Java Virtual Machine specification vendor. | +| `vm.specification.name` | `java.vm.specification.name` | Java Virtual Machine specification name. | +| `vm.version` | `java.vm.version` | Java Virtual Machine implementation version. | +| `vm.vendor` | `java.vm.vendor` | Java Virtual Machine implementation vendor. | +| `vm.name` | `java.vm.name` | Java Virtual Machine implementation name. | +| `specification.version` | `java.specification.version` | Java Runtime Environment specification version. | +| `specification.maintenance.version` | `java.specification.maintenance.version` | Java Runtime Environment specification maintenance version _(optional)_. | +| `specification.vendor` | `java.specification.vendor` | Java Runtime Environment specification vendor. | +| `specification.name` | `java.specification.name` | Java Runtime Environment specification name. | + +#### Maven + +Represents the Maven installation used to run the build (`"name": "Maven"`). +To allow verification of the installation's integrity, a `gitTree` hash is computed over the `maven.home` directory. + +The `uri` key contains the Package URL of the Maven distribution, as published to Maven Central. + +The following annotations are sourced from Maven's `build.properties`, bundled inside the Maven distribution. +They are only present if the resource is accessible from Maven's Core Classloader at runtime. + +| Annotation key | Description | +|-------------------------|--------------------------------------------------------------| +| `distributionId` | The ID of the Maven distribution. | +| `distributionName` | The full name of the Maven distribution. | +| `distributionShortName` | The short name of the Mavendistribution. | +| `buildNumber` | The Git commit hash from which this Maven release was built. | +| `version` | The Maven version string. | + +#### Source repository + +Represents the source code being built. +The URI follows +the [SPDX Download Location](https://spdx.github.io/spdx-spec/v2.3/package-information/#77-package-download-location-field) +format. + +#### Project dependencies + +One entry per resolved Maven dependency (compile + runtime scope), as declared in the project's POM. +These are appended after the build tool entries above. + +| Field | Value | +|-----------------|-----------------------------------------------------| +| `name` | Artifact filename, e.g. `commons-lang3-3.14.0.jar`. | +| `uri` | Package URL. | +| `digest.sha256` | SHA-256 hex digest of the artifact file on disk. | + +## Run details + +### Builder + +The `builder.id` is always `https://commons.apache.org/builds/0.1.0`. +It represents the commons-release-plugin acting as the build platform. + +## Subjects + +The attestation covers all artifacts attached to the Maven project at the time the `verify` phase runs: +the primary artifact (e.g. the JAR) and any attached artifacts (e.g. sources JAR, javadoc JAR, POM). + +| Field | Value | +|-----------------|------------------------------------------| +| `name` | Artifact filename. | +| `uri` | Package URL. | +| `digest.sha256` | SHA-256 hex digest of the artifact file. | + +## Version history Review Comment: I'm expecting to see what a real attestation file looks like here. I was also expecting to see a real one, in `test/resources` for example. But documenting one here would be nice. ########## src/main/java/org/apache/commons/release/plugin/mojos/BuildAttestationMojo.java: ########## @@ -0,0 +1,543 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.mojos; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.commons.release.plugin.internal.ArtifactUtils; +import org.apache.commons.release.plugin.internal.BuildDefinitions; +import org.apache.commons.release.plugin.internal.DsseUtils; +import org.apache.commons.release.plugin.internal.GitUtils; +import org.apache.commons.release.plugin.slsa.v1_2.BuildDefinition; +import org.apache.commons.release.plugin.slsa.v1_2.BuildMetadata; +import org.apache.commons.release.plugin.slsa.v1_2.Builder; +import org.apache.commons.release.plugin.slsa.v1_2.DsseEnvelope; +import org.apache.commons.release.plugin.slsa.v1_2.Provenance; +import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor; +import org.apache.commons.release.plugin.slsa.v1_2.RunDetails; +import org.apache.commons.release.plugin.slsa.v1_2.Signature; +import org.apache.commons.release.plugin.slsa.v1_2.Statement; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.plugins.gpg.AbstractGpgSigner; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.apache.maven.rtinfo.RuntimeInformation; +import org.apache.maven.scm.CommandParameters; +import org.apache.maven.scm.ScmException; +import org.apache.maven.scm.ScmFileSet; +import org.apache.maven.scm.command.info.InfoItem; +import org.apache.maven.scm.command.info.InfoScmResult; +import org.apache.maven.scm.manager.ScmManager; +import org.apache.maven.scm.repository.ScmRepository; + +/** + * This plugin generates an in-toto attestation for all the artifacts. Review Comment: No latin please: in-toto -> complete? ########## src/main/java/org/apache/commons/release/plugin/internal/GitUtils.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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. + */ +package org.apache.commons.release.plugin.internal; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.codec.digest.GitIdentifiers; + +/** + * Utilities for Git operations. + */ +public final class GitUtils { + + /** The SCM URI prefix for Git repositories. */ + private static final String SCM_GIT_PREFIX = "scm:git:"; + + /** + * Returns the Git tree hash for the given directory. + * + * @param path A directory path. + * @return A hex-encoded SHA-1 tree hash. + * @throws IOException If the path is not a directory or an I/O error occurs. + */ + public static String gitTree(Path path) throws IOException { + if (!Files.isDirectory(path)) { + throw new IOException("Path is not a directory: " + path); + } + MessageDigest digest = DigestUtils.getSha1Digest(); Review Comment: Use final or inline. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
