Skip to content

Commit

Permalink
chore: Make gradle publisher an action (#2497)
Browse files Browse the repository at this point in the history
Closes
#2464 and
#2467

---------

Signed-off-by: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com>
Signed-off-by: AdamKorcz <adam@adalogics.com>
Co-authored-by: Ian Lewis <ianlewis@google.com>
  • Loading branch information
AdamKorcz and ianlewis committed Jul 31, 2023
1 parent ec04624 commit 459c873
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 13 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/builder_gradle_slsa3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ on:
required: false
default: 17
type: number
outputs:
provenance-name:
description: "The file name of the attestation upload artifact."
# NOTE: This is an "untrusted" value returned from the build.
value: "${{ fromJSON(jobs.slsa-run.outputs.build-artifacts-outputs).attestation-name }}"

provenance-download-name:
description: "The name of the provenance attestation uploaded to the workflow run."
value: "${{ jobs.slsa-run.outputs.attestations-download-name }}"

provenance-download-sha256:
description: "The sha256 of the provenance attestation uploaded to the workflow run."
value: "${{ jobs.slsa-run.outputs.attestations-download-sha256 }}"

target-download-sha256:
description: "The sha256 of the target uploaded to the workflow run."
# NOTE: This is an "untrusted" value returned from the build.
value: "${{ fromJSON(jobs.slsa-run.outputs.build-artifacts-outputs).target-download-sha256 }}"
jobs:
slsa-setup:
permissions:
Expand Down
268 changes: 268 additions & 0 deletions actions/gradle/publish/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,271 @@ Before you use this publish Action, you will need to configure your Github proje
Your project needs to be already set up with Gradle and must have a gradle wrapper file in order to use the Action.

The Action expects you to have built the artifacts using the SLSA Gradle builder and that the provenance is available in `./build/libs/slsa-attestations/`.

## Using the Gradle Publish action

To use the Gradle action you need to:

1. Modify your `build.gradle.kts` file.
2. Add the step in your release workflow that invokes it.

### Modify your `build.gradle.kts` file

Assuming you have already configured your Gradle repository to release to Maven Central, your `build.gradle.kts` looks something like this:

```kotlin
import java.io.File

plugins {
`java-library`
`maven-publish`
`signing`
}

repositories {
mavenLocal()
maven {
url = uri("https://repo.maven.apache.org/maven2/")
}
}

group = "io.github.adamkorcz"
version = "0.1.18"
description = "Adam's test java project"
java.sourceCompatibility = JavaVersion.VERSION_1_8

java {
withSourcesJar()
withJavadocJar()
}

publishing {
publications {
create<MavenPublication>("maven") {
artifactId = "test-java-project"
from(components["java"])

pom {
name.set("test-java-project")
description.set("Adam's test java project")
url.set("https://github.com/AdamKorcz/test-java-project")
licenses {
license {
name.set("MIT License")
url.set("http://www.opensource.org/licenses/mit-license.php")
}
}
developers {
developer {
id.set("adamkrocz")
name.set("Adam K")
email.set("Adam@adalogics.com")
}
}
scm {
connection.set("scm:git:git://github.com/adamkorcz/test-java-project.git")
developerConnection.set("scm:git:ssh://github.com:simpligility/test-java-project.git")
url.set("http://github.com/adamkorcz/test-java-project/tree/main")
}
}
}
}
repositories {
maven {
credentials {
username = System.getenv("MAVEN_USERNAME")
password = System.getenv("MAVEN_PASSWORD")
}
name = "test-java-project"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
}
}
}

signing {
useGpgCmd()
sign(publishing.publications["maven"])
}
```

You need to add the following lines to your `build.gradle.kts` at the top inside of `create<MavenPublication>("maven")`:

```kotlin
val base_dir = "build/libs/slsa-attestations"
File(base_dir).walkTopDown().forEach {
if (it.isFile()) {
var path = it.getName()
val name = path.replace(project.name + "-" + project.version, "").split(".", limit=2)
if (name.size != 2) {
throw StopExecutionException("Found incorrect file name: " + path)
}
var cls = name[0]
var ext = name[1]
if (cls.startsWith("-")) {
cls = cls.substring(1)
}
artifact (base_dir + "/" + path) {
classifier = cls
extension = ext
}
}
}
```

Your final `build.gradle.kts` file should look like this:

```kotlin
import java.io.File

plugins {
`java-library`
`maven-publish`
`signing`
}

repositories {
mavenLocal()
maven {
url = uri("https://repo.maven.apache.org/maven2/")
}
}

group = "io.github.adamkorcz"
version = "0.1.18"
description = "Adams test java project"
java.sourceCompatibility = JavaVersion.VERSION_1_8

java {
withSourcesJar()
withJavadocJar()
}

publishing {
publications {
create<MavenPublication>("maven") {
artifactId = "test-java-project"
from(components["java"])
val base_dir = "build/libs/slsa-attestations"
File(base_dir).walkTopDown().forEach {
if (it.isFile()) {
var path = it.getName()
val name = path.replace(project.name + "-" + project.version, "").split(".", limit=2)
if (name.size != 2) {
throw StopExecutionException("Found incorrect file name: " + path)
}
var cls = name[0]
var ext = name[1]
if (cls.startsWith("-")) {
cls = cls.substring(1)
}
artifact (base_dir + "/" + path) {
classifier = cls
extension = ext
}
}
}
pom {
name.set("test-java-project")
description.set("Adams test java project")
url.set("https://github.com/AdamKorcz/test-java-project")
licenses {
license {
name.set("MIT License")
url.set("http://www.opensource.org/licenses/mit-license.php")
}
}
developers {
developer {
id.set("adamkrocz")
name.set("Adam K")
email.set("Adam@adalogics.com")
}
}
scm {
connection.set("scm:git:git://github.com/adamkorcz/test-java-project.git")
developerConnection.set("scm:git:ssh://github.com:simpligility/test-java-project.git")
url.set("http://github.com/adamkorcz/test-java-project/tree/main")
}
}
}
}
repositories {
maven {
credentials {
username = System.getenv("MAVEN_USERNAME")
password = System.getenv("MAVEN_PASSWORD")
}
name = "test-java-project"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
}
}
}

signing {
useGpgCmd()
sign(publishing.publications["maven"])
}
```

You don't need to configure anything inside that code snippet; Adding them to your `build.gradle.kts` file is enough.

### Add the publish action to your release workflow

Before using the Gradle publish action, you should have a workflow that invokes the Gradle builder. It will look something like this:

```yaml
name: Publish Gradle with action
on:
- workflow_dispatch

permissions: read-all

jobs:
build:
permissions:
id-token: write
contents: read
actions: read
packages: read
uses: slsa-framework/slsa-github-generator/.github/workflows/builder_gradle_slsa3.yml@v1.7.0
with:
rekor-log-public: true
artifact-list: build/libs/artifact1-0.1.18.jar,build/libs/artifact-0.1.18-javadoc.jar,build/libs/artifact-0.1.18-sources.jar
```

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/gradle/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 }}
jdk-version: "17"
```

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)
50 changes: 37 additions & 13 deletions actions/gradle/publish/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@

name: Gradle publish Action for Maven Central
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
jdk-version:
description: "The JDK version for the action"
required: true
Expand All @@ -38,30 +50,42 @@ runs:
- name: Set up JDK
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
env:
MAVEN_USERNAME: ${{ secrets.maven-username }}
MAVEN_PASSWORD: ${{ secrets.maven-password }}
GPG_KEY_PASS: ${{ secrets.gpg-key-pass }}
MAVEN_USERNAME: ${{ inputs.maven-username }}
MAVEN_PASSWORD: ${{ inputs.maven-password }}
GPG_KEY_PASS: ${{ inputs.gpg-key-pass }}
with:
distribution: temurin
java-version: ${{ inputs.jdk-version }}
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.gpg-private-key }}
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: ./
sha256: "${{ inputs.provenance-download-sha256 }}"

- name: Download the target dir
uses: slsa-framework/slsa-github-generator/.github/actions/secure-download-folder@main
with:
name: build
path: ./
sha256: "${{ inputs.target-download-sha256 }}"
- name: Upload to Maven Central
shell: bash
env:
GPG_PRIVATE_KEY: ${{ secrets.gpg-private-key }}
GPG_PASSPHRASE: ${{ secrets.gpg-key-pass }}
GPG_PRIVATE_KEY: ${{ inputs.gpg-private-key }}
GPG_PASSPHRASE: ${{ inputs.gpg-key-pass }}
SLSA_DIR: "${{ inputs.provenance-download-name }}"
MAVEN_USERNAME: ${{ inputs.maven-username }}
MAVEN_PASSWORD: ${{ inputs.maven-password }}
PROVENANCE: "build/libs/slsa-attestations/"
run: |
# The Gradle publisher expect the provenance to exist in "./build/libs/slsa-attestations/"
if [ ! -d "./build/libs/slsa-attestations" ]
then
echo "Could not find ./build/libs/slsa-attestations"
echo "./build/libs/slsa-attestations should contain the provenance files"
exit 1
fi
# Collect the provenance files into a subdirectory of "./build"
mv "${SLSA_DIR}" ./build/libs/slsa-attestations
# Import GPG signing key
echo "${GPG_PRIVATE_KEY}" | gpg --batch --import --import-options import-show
GPG_KEYNAME="$(echo "${GPG_PRIVATE_KEY}" | gpg --batch --show-keys --with-colons | awk -F: '$1 == "sec" { print $5 }')"
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions internal/builders/gradle/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ inputs:
slsa-workflow-secret13: {}
slsa-workflow-secret14: {}
slsa-workflow-secret15: {}
outputs:
target-download-sha256:
description: >
The sha256 digest of the "build" directory.
Users should verify the download against this digest to prevent tampering.
value: ${{ steps.upload-build-dir.outputs.sha256 }}

on:
workflow_call:
Expand Down Expand Up @@ -71,3 +78,9 @@ runs:
env:
SLSA_OUTPUTS_ARTIFACTS_FILE: ${{ inputs.slsa-layout-file }}
run: ./../__TOOL_ACTION_DIR__/create_attestation.sh
- name: Upload build dir
id: upload-build-dir
uses: slsa-framework/slsa-github-generator/.github/actions/secure-upload-folder@main
with:
name: build
path: build

0 comments on commit 459c873

Please sign in to comment.