diff --git a/.github/workflows/publish_maven.yml b/.github/workflows/publish_maven.yml
deleted file mode 100644
index cac8f2f10c..0000000000
--- a/.github/workflows/publish_maven.yml
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright 2023 SLSA Authors
-#
-# 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.
-
-permissions:
- contents: read
-
-on:
- workflow_call:
- inputs:
- provenance-download-name:
- description: "The artifact name for the package provenance."
- required: true
- type: string
- provenance-download-sha256:
- description: "The sha256 of the package provenance artifact."
- required: false
- type: string
- target-download-sha256:
- description: "The sha256 of the target directory."
- required: true
- type: string
- secrets:
- maven-username:
- description: "Maven username"
- required: false
- maven-password:
- description: "Maven password"
- required: false
- gpg-key-pass:
- description: "gpg-key-pass"
- required: false
- gpg-private-key:
- description: "gpg-key-pass"
- required: false
-
-jobs:
- setup-java:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout the project repository
- uses: slsa-framework/slsa-github-generator/.github/actions/secure-project-checkout@main
- - name: Set up Java for publishing to Maven Central Repository
- uses: actions/setup-java@cd89f46ac9d01407894225f350157564c9c7cee2 # v3.12.0
- env:
- MAVEN_USERNAME: ${{ secrets.maven-username }}
- MAVEN_PASSWORD: ${{ secrets.maven-password }}
- GPG_KEY_PASS: ${{ secrets.gpg-key-pass }}
- with:
- java-version: '11'
- distribution: 'temurin'
- server-id: ossrh
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
- gpg-private-key: ${{ secrets.gpg-private-key }}
- gpg-passphrase: GPG_KEY_PASS
-
- - name: Download the slsa attestation
- uses: slsa-framework/slsa-github-generator/.github/actions/secure-download-folder@main
- with:
- name: "${{ inputs.provenance-download-name }}"
- path: slsa-attestations
- sha256: "${{ inputs.provenance-download-sha256 }}"
-
- - name: Download the target dir
- uses: slsa-framework/slsa-github-generator/.github/actions/secure-download-folder@main
- with:
- name: target
- path: ./
- sha256: "${{ inputs.target-download-sha256 }}"
-
- - name: Publish to the Maven Central Repository
- shell: bash
- env:
- MAVEN_USERNAME: "${{ secrets.maven-username }}"
- MAVEN_PASSWORD: "${{ secrets.maven-password }}"
- GPG_KEY_PASS: "${{ secrets.gpg-key-pass }}"
- SLSA_DIR: "${{ inputs.provenance-download-name }}"
- PROVENANCE_FILES: "${{ inputs.provenance-download-name }}"
- run: |
- # Build and run custom plugin
- cd plugin && mvn clean install && cd ..
- # Re-indexing the secondary jar files for deploy
- mvn javadoc:jar source:jar
- # Retrieve project version
- VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)
- ARTIFACTID=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.artifactId -q -DforceStdout)
- # Reset the environment variables add in the base provenance
- FILES="slsa-attestations/${PROVENANCE_FILES}/${ARTIFACTID}-${VERSION}.jar.intoto.build.slsa"
- TYPES=slsa
- CLASSIFIERS=jar.intoto.build
- # Find all necessary built jar files and attach them to the environment variable deploy
- # shellcheck disable=SC2044 # We don't spawn a new sub shell since we are appending to global env vars
- for name in $(find ./ -name "$ARTIFACTID-$VERSION-*.jar")
- do
- # shellcheck disable=SC1001 # shellcheck complains over \- but the line does what it should.
- TARGET=$(echo "${name}" | rev | cut -d\- -f1 | rev)
- FILES=$FILES,$name
- TYPES=$TYPES,${TARGET##*.}
- CLASSIFIERS=$CLASSIFIERS,${TARGET%.*}
- done
-
- # Find all generated provenance files and attach them the the environment variable for deploy
- # shellcheck disable=SC2044 # We don't spawn a new sub shell since we are appending to global env vars
- for name in $(find ./ -name "$ARTIFACTID-$VERSION-*.jar.intoto.build.slsa")
- do
- # shellcheck disable=SC1001 # shellcheck complains over \- but the line does what it should.
- TARGET=$(echo "${name}" | rev | cut -d\- -f1 | rev)
- FILES=$FILES,$name
- TYPES=$TYPES",slsa"
- CLASSIFIERS=$CLASSIFIERS,${TARGET::-9}
- done
- # Sign and deploy the files to the ossrh remote repository
- mvn validate jar:jar -Dfile=target/"${ARTIFACTID}"-"${VERSION}".jar -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=ossrh -Dfiles="${FILES}" -Dtypes="${TYPES}" -Dclassifiers="${CLASSIFIERS}" -DpomFile=pom.xml gpg:sign-and-deploy-file
diff --git a/actions/maven/publish/README.md b/actions/maven/publish/README.md
new file mode 100644
index 0000000000..42a35a23b7
--- /dev/null
+++ b/actions/maven/publish/README.md
@@ -0,0 +1,69 @@
+# Publishing SLSA3+ provenance to Maven Central
+
+This document explains how to publish SLSA3+ artifacts and provenance to Maven central.
+
+The publish Action is in its early stages and is likely to develop over time. Future breaking changes may occur.
+
+To get started with publishing artifacts to Maven Central Repository, see [this guide](https://maven.apache.org/repository/guide-central-repository-upload.html).
+
+Before you use this publish Action, you will need to configure your Github project with the correct secrets. See [this guide](https://docs.github.com/en/actions/publishing-packages/publishing-java-packages-with-maven) for more.
+
+## Using the Maven Publish action
+
+To use the Maven action you need to add the step in your release workflow that invokes it.
+
+Before using the Maven publish action, you should have a workflow that invokes the [Maven builder](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/maven/README.md). It will look something like this:
+
+```yaml
+name: Release Maven project
+on:
+ - workflow_dispatch
+
+permissions: read-all
+
+jobs:
+ build:
+ permissions:
+ id-token: write
+ contents: read
+ actions: read
+ uses: slsa-framework/slsa-github-generator/.github/workflows/builder_maven_slsa3.yml@v1.7.0
+ with:
+ rekor-log-public: true
+```
+
+To use the Publish action, you need to add another job:
+
+```yaml
+publish:
+ runs-on: ubuntu-latest
+ needs: build
+ permissions:
+ id-token: write
+ contents: read
+ actions: read
+ steps:
+ - name: publish
+ id: publish
+ uses: slsa-framework/slsa-github-generator/actions/maven/publish@v1.7.0
+ with:
+ provenance-download-name: "${{ needs.build.outputs.provenance-download-name }}"
+ provenance-download-sha256: "${{ needs.build.outputs.provenance-download-sha256 }}"
+ target-download-sha256: "${{ needs.build.outputs.target-download-sha256 }}"
+ maven-username: ${{ secrets.OSSRH_USERNAME }}
+ maven-password: ${{ secrets.OSSRH_PASSWORD }}
+ gpg-key-pass: ${{ secrets.GPG_PASSPHRASE }}
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+```
+
+Set the values of "maven-username", "maven-password", "gpg-key-pass" and " gpg-private-key" for your account. The parameters to `provenance-download-name`, `provenance-download-sha256` and `target-download-sha256` should not be changed.
+
+Once you trigger this workflow, your artifacts and provenance files will be added to a staging repository in Maven Central. You need to close the staging repository and then release:
+
+Closing the staging repository:
+
+![closing the staging repository](/actions/gradle/publish/images/gradle-publisher-staging-repository.png)
+
+Releasing:
+
+![releasing the Gradle artefacts](/actions/gradle/publish/images/gradle-publisher-release-closed-repository.png)
diff --git a/actions/maven/publish/action.yml b/actions/maven/publish/action.yml
new file mode 100644
index 0000000000..529be1bad3
--- /dev/null
+++ b/actions/maven/publish/action.yml
@@ -0,0 +1,115 @@
+# Copyright 2023 SLSA Authors
+#
+# 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.
+
+
+inputs:
+ provenance-download-name:
+ description: "The artifact name for the package provenance."
+ required: true
+ type: string
+ provenance-download-sha256:
+ description: "The sha256 of the package provenance artifact."
+ required: true
+ type: string
+ target-download-sha256:
+ description: "The sha256 of the target directory."
+ required: true
+ type: string
+ maven-username:
+ description: "Maven username"
+ required: true
+ maven-password:
+ description: "Maven password"
+ required: true
+ gpg-key-pass:
+ description: "gpg-key-pass"
+ required: true
+ gpg-private-key:
+ description: "gpg-key-pass"
+ required: true
+runs:
+ using: "composite"
+ steps:
+ - name: Checkout the project repository
+ uses: slsa-framework/slsa-github-generator/.github/actions/secure-project-checkout@main # needed because we run javadoc and sources.
+ - name: Set up Java for publishing to Maven Central Repository
+ uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3
+ env:
+ MAVEN_USERNAME: ${{ inputs.maven-username }}
+ MAVEN_PASSWORD: ${{ inputs.maven-password }}
+ GPG_KEY_PASS: ${{ inputs.gpg-key-pass }}
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ server-id: ossrh
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ inputs.gpg-private-key }}
+ gpg-passphrase: GPG_KEY_PASS
+
+ - name: Download the slsa attestation
+ uses: slsa-framework/slsa-github-generator/.github/actions/secure-download-folder@main
+ with:
+ name: "${{ inputs.provenance-download-name }}"
+ path: slsa-attestations
+ sha256: "${{ inputs.provenance-download-sha256 }}"
+
+ - name: Download the target dir
+ uses: slsa-framework/slsa-github-generator/.github/actions/secure-download-folder@main
+ with:
+ name: target
+ path: ./
+ sha256: "${{ inputs.target-download-sha256 }}"
+
+ - name: Checkout the framework repository
+ uses: slsa-framework/slsa-github-generator/.github/actions/secure-builder-checkout@main
+ with:
+ repository: slsa-framework/slsa-github-generator
+ ref: v1.8.0
+ path: __BUILDER_CHECKOUT_DIR__
+
+ - name: Publish to the Maven Central Repository
+ shell: bash
+ env:
+ MAVEN_USERNAME: "${{ inputs.maven-username }}"
+ MAVEN_PASSWORD: "${{ inputs.maven-password }}"
+ GPG_KEY_PASS: "${{ inputs.gpg-key-pass }}"
+ SLSA_DIR: "${{ inputs.provenance-download-name }}"
+ PROVENANCE_FILES: "${{ inputs.provenance-download-name }}"
+ run: |
+ cd __BUILDER_CHECKOUT_DIR__/actions/maven/publish/slsa-hashing-plugin && mvn clean install && cd -
+ mvn javadoc:jar source:jar
+ # Retrieve project version
+ export version=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)
+ export artifactid=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.artifactId -q -DforceStdout)
+ # Reset the environment variables add in the base provenance
+ export files="slsa-attestations/${PROVENANCE_FILES}/${artifactid}-${version}.jar.build.slsa"
+ export types=slsa
+ export classifiers=jar.build
+ # Find all necessary built jar files and attach them to the environment variable deploy
+ while read -r name; do
+ target=$(echo "${name}" | rev | cut -d- -f1 | rev)
+ files=$files,$name
+ types=$types,${target##*.}
+ classifiers=$classifiers,${target%.*}
+ done <<<"$(find ./ -name "$artifactid-$version-*.jar")"
+ # Find all generated provenance files and attach them the the environment variable for deploy
+ while read -r name; do
+ target=$(echo "${name}" | rev | cut -d- -f1 | rev)
+ files=$files,$name
+ types=$types",slsa"
+ classifiers=$classifiers,${target::-9}
+ done <<<"$(find ./ -name "$artifactid-$version-*.jar.build.slsa")"
+ # Sign and deploy the files to the ossrh remote repository
+ mvn validate jar:jar -Dfile=target/"${artifactid}"-"${version}".jar -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=ossrh -Dfiles="${files}" -Dtypes="${types}" -Dclassifiers="${classifiers}" -DpomFile=pom.xml gpg:sign-and-deploy-file
diff --git a/actions/maven/publish/slsa-hashing-plugin/pom.xml b/actions/maven/publish/slsa-hashing-plugin/pom.xml
new file mode 100644
index 0000000000..dcf8b39851
--- /dev/null
+++ b/actions/maven/publish/slsa-hashing-plugin/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+ io.github.slsa-framework.slsa-github-generator
+ hash-maven-plugin
+ maven-plugin
+ 0.0.1
+
+ Jarfile Hashing Maven Mojo
+ http://maven.apache.org
+
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ 3.6.3
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.6.0
+ provided
+
+
+ org.apache.maven
+ maven-project
+ 2.2.1
+
+
+ org.json
+ json
+ 20230227
+
+
+
diff --git a/actions/maven/publish/slsa-hashing-plugin/src/main/java/io/github/slsa-framework/JarfileHashMojo.java b/actions/maven/publish/slsa-hashing-plugin/src/main/java/io/github/slsa-framework/JarfileHashMojo.java
new file mode 100644
index 0000000000..036004a849
--- /dev/null
+++ b/actions/maven/publish/slsa-hashing-plugin/src/main/java/io/github/slsa-framework/JarfileHashMojo.java
@@ -0,0 +1,108 @@
+// Copyright 2023 SLSA Authors
+//
+// 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.
+
+package io.github.slsaframework;
+
+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.project.MavenProject;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.LinkedList;
+
+@Mojo(name = "hash-jarfile", defaultPhase = LifecyclePhase.PACKAGE)
+public class JarfileHashMojo extends AbstractMojo {
+ private final String jsonBase = "{\"version\": 1, \"attestations\":[%ATTESTATIONS%]}";
+ private final String attestationTemplate = "{\"name\": \"%NAME%\",\"subjects\":[{\"name\": \"%NAME%\",\"digest\":{\"sha256\":\"%HASH%\"}}]}";
+
+ @Parameter(defaultValue = "${project}", required = true, readonly = true)
+ private MavenProject project;
+
+ @Parameter(property = "hash-jarfile.outputJsonPath", defaultValue = "")
+ private String outputJsonPath;
+
+ @Parameter(property = "run.hash.jarfile", defaultValue = "false")
+ private Boolean runHashJarfile;
+
+
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if (!runHashJarfile) {
+ getLog().info("SLSA Hash Jarfile plugin is skipped.");
+ return;
+ }
+
+ getLog().info("Start running SLSA Hash Jarfile plugin.");
+
+ try {
+ StringBuilder attestations = new StringBuilder();
+
+ File targetDir = new File(project.getBasedir(), "target");
+ File outputJson = this.getOutputJsonFile(targetDir.getAbsolutePath());
+ for (File file : targetDir.listFiles()) {
+ String filePath = file.getAbsolutePath();
+ if (!filePath.endsWith("original") && (filePath.endsWith(".pom") || filePath.endsWith(".jar"))) {
+ byte[] data = Files.readAllBytes(file.toPath());
+ byte[] hash = MessageDigest.getInstance("SHA-256").digest(data);
+ String checksum = new BigInteger(1, hash).toString(16);
+
+ String attestation = attestationTemplate.replaceAll("%NAME%", file.getName());
+ attestation = attestation.replaceAll("%HASH%", checksum);
+ if (attestations.length() > 0) {
+ attestations.append(",");
+ }
+ attestations.append(attestation);
+ }
+ }
+ String json = jsonBase.replaceAll("%ATTESTATIONS%", attestations.toString());
+
+ Files.write(outputJson.toPath(), new JSONObject(json).toString(4).getBytes());
+ } catch (IOException | NoSuchAlgorithmException e) {
+ throw new MojoFailureException("Fail to generate hash for the jar files", e);
+ }
+
+ }
+
+ private File getOutputJsonFile(String targetDir) {
+ try {
+ if (this.outputJsonPath != null && this.outputJsonPath.length() > 0) {
+ File outputJson = new File(outputJsonPath);
+ if (!outputJson.exists() || !outputJson.isFile()) {
+ outputJson.getParentFile().mkdirs();
+ Files.createFile(outputJson.toPath());
+ }
+
+ if (Files.isWritable(outputJson.toPath())) {
+ return outputJson;
+ }
+ }
+ getLog().error("Could not generate the output json file.");
+ return new File(targetDir, "hash.json");
+ } catch (IOException e) {
+ getLog().error("Could not generate the output json file.");
+ return new File(targetDir, "hash.json");
+ }
+ }
+}
diff --git a/internal/builders/maven/README.md b/internal/builders/maven/README.md
index d6606194a9..6c92522f60 100644
--- a/internal/builders/maven/README.md
+++ b/internal/builders/maven/README.md
@@ -19,6 +19,8 @@ workflow the "Maven builder" from now on.
- [Limitations](#limitations)
- [Generating Provenance](#generating-provenance)
- [Getting Started](#getting-started)
+ - [Releasing to Maven Central](#releasing-to-maven-central)
+ - [Action requirements](#action-requirements)
- [Private Repositories](#private-repositories)
- [Verification](#verification)
@@ -86,25 +88,32 @@ jobs:
Now, when you invoke this workflow, the Maven builder will build both your artifacts and the provenance files for them.
-You can also release artifacts to Maven Central by adding the following step to your workflow:
-
-```yaml
- publish:
- needs: build
- uses: slsa-framework/slsa-github-generator/.github/workflows/publish_maven.yml@v1.7.0
- with:
- provenance-download-name: "${{ needs.build.outputs.provenance-download-name }}"
- provenance-download-sha256: "${{ needs.build.outputs.provenance-download-sha256 }}"
- target-download-sha256: "${{ needs.build.outputs.target-download-sha256 }}"
- secrets:
- maven-username: ${{ secrets.OSSRH_USERNAME }}
- maven-password: ${{ secrets.OSSRH_PASSWORD }}
- gpg-key-pass: ${{ secrets.GPG_PASSPHRASE }}
- gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+### Releasing to Maven Central
+
+You can also release artifacts to Maven Central with [the slsa-github-generator Maven publish action](https://github.com/slsa-framework/slsa-github-generator/blob/main/actions/maven/publish/README.md).
+
+#### Action requirements
+
+Besides adding the above workflow to your CI pipeline, you also need to add the following plugin to your `pom.xml`:
+
+```xml
+
+ io.github.slsa-framework.slsa-github-generator
+ hash-maven-plugin
+ 0.0.1
+
+
+
+ hash-jarfile
+
+
+
+
+ ${SLSA_OUTPUTS_ARTIFACTS_FILE}
+
+
```
-Now your workflow will build your artifacts and publish them to a staging repository in Maven Central.
-
### Private Repositories
The builder records all provenance signatures in the [Rekor](https://github.com/sigstore/rekor) public transparency log. This record includes the repository name. To acknowledge you're aware that your repository name will be public, set the flag `rekor-log-public: true` when calling the builder:
diff --git a/internal/builders/maven/action.yml b/internal/builders/maven/action.yml
index 0341de7353..70b814ebc4 100644
--- a/internal/builders/maven/action.yml
+++ b/internal/builders/maven/action.yml
@@ -58,11 +58,22 @@ runs:
with:
distribution: temurin
java-version: ${{ fromJson(inputs.slsa-workflow-inputs).jdk-version }}
+ - name: Checkout the tool repository
+ uses: slsa-framework/slsa-github-generator/.github/actions/secure-builder-checkout@main
+ with:
+ repository: slsa-framework/slsa-github-generator
+ ref: main
+ path: __BUILDER_CHECKOUT_DIR__
- name: Run mvn package
shell: bash
env:
SLSA_OUTPUTS_ARTIFACTS_FILE: ${{ inputs.slsa-layout-file }}
- run: cd plugin && mvn clean install && cd .. && mvn package
+ run: |
+ mv ./__BUILDER_CHECKOUT_DIR__ ../__BUILDER_CHECKOUT_DIR__ \
+ && cd ../__BUILDER_CHECKOUT_DIR__/actions/maven/publish/slsa-hashing-plugin \
+ && mvn clean install \
+ && cd - \
+ && mvn package -Drun.hash.jarfile=true
- name: Upload target
id: upload-target
uses: slsa-framework/slsa-github-generator/.github/actions/secure-upload-folder@main